From ee7256809cc30d2b9e0d31fe7fd98fd4d70df33d Mon Sep 17 00:00:00 2001 From: jheer Date: Mon, 1 Apr 2024 12:25:01 -0700 Subject: [PATCH 01/25] chore: Update TypeScript, add some jsdoc typings, address TS errors. --- package-lock.json | 866 +++++------------- package.json | 8 +- packages/core/src/Coordinator.js | 14 +- packages/core/src/MosaicClient.js | 16 +- packages/core/src/Param.js | 6 +- packages/core/src/QueryConsolidator.js | 6 + packages/core/src/Selection.js | 13 +- packages/core/src/connectors/wasm.js | 4 +- packages/core/src/util/AsyncDispatch.js | 7 +- packages/core/src/util/convert-arrow.js | 8 +- packages/core/src/util/query-result.js | 7 +- packages/inputs/src/Menu.js | 4 + packages/inputs/src/Search.js | 4 + packages/inputs/src/Slider.js | 6 +- packages/inputs/src/Table.js | 4 + packages/inputs/src/util/format.js | 7 +- packages/plot/src/interactors/Interval1D.js | 2 +- packages/plot/src/interactors/Toggle.js | 7 +- .../src/interactors/util/patchScreenCTM.js | 2 + packages/plot/src/legend.js | 3 +- packages/plot/src/marks/ConnectedMark.js | 6 + packages/plot/src/marks/ContourMark.js | 20 +- packages/plot/src/marks/DenseLineMark.js | 14 +- packages/plot/src/marks/Density1DMark.js | 12 +- packages/plot/src/marks/Grid2DMark.js | 41 +- packages/plot/src/marks/HexbinMark.js | 12 +- packages/plot/src/marks/Mark.js | 19 +- packages/plot/src/marks/RasterMark.js | 1 + packages/plot/src/marks/RasterTileMark.js | 25 +- packages/plot/src/marks/RegressionMark.js | 17 +- packages/plot/src/marks/util/grid.js | 28 +- packages/plot/src/marks/util/handle-param.js | 21 +- packages/plot/src/marks/util/stats.js | 34 +- .../plot/src/marks/util/to-data-columns.js | 20 +- packages/sql/src/Query.js | 109 +++ packages/sql/src/aggregates.js | 56 +- packages/sql/src/desc.js | 4 +- packages/sql/src/expression.js | 17 +- packages/sql/src/load/load.js | 2 +- packages/sql/src/load/sql-from.js | 7 + packages/sql/src/ref.js | 8 +- packages/sql/src/scales.js | 2 +- packages/sql/src/windows.js | 44 +- packages/vgplot/src/layout/concat.js | 2 +- packages/vgplot/src/layout/space.js | 4 +- packages/widget/src/index.js | 9 +- packages/widget/tsconfig.json | 19 +- 47 files changed, 785 insertions(+), 762 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3b6f9d5c..b41eaad4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,13 +14,13 @@ "eslint": "^8.57.0", "eslint-plugin-jsdoc": "^48.2.1", "lerna": "^8.1.2", - "mocha": "^10.3.0", + "mocha": "^10.4.0", "nodemon": "^3.1.0", "rimraf": "^5.0.5", "timezone-mock": "^1.3.6", - "typescript": "^5.3.3", - "vite": "^5.1.6", - "vitepress": "1.0.0-rc.45", + "typescript": "^5.4.3", + "vite": "^5.2.6", + "vitepress": "1.0.1", "yaml": "^2.4.1" } }, @@ -99,132 +99,151 @@ } }, "node_modules/@algolia/cache-browser-local-storage": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.22.1.tgz", - "integrity": "sha512-Sw6IAmOCvvP6QNgY9j+Hv09mvkvEIDKjYW8ow0UDDAxSXy664RBNQk3i/0nt7gvceOJ6jGmOTimaZoY1THmU7g==", + "version": "4.23.2", + "resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.23.2.tgz", + "integrity": "sha512-PvRQdCmtiU22dw9ZcTJkrVKgNBVAxKgD0/cfiqyxhA5+PHzA2WDt6jOmZ9QASkeM2BpyzClJb/Wr1yt2/t78Kw==", "dev": true, "dependencies": { - "@algolia/cache-common": "4.22.1" + "@algolia/cache-common": "4.23.2" } }, "node_modules/@algolia/cache-common": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.22.1.tgz", - "integrity": "sha512-TJMBKqZNKYB9TptRRjSUtevJeQVXRmg6rk9qgFKWvOy8jhCPdyNZV1nB3SKGufzvTVbomAukFR8guu/8NRKBTA==", + "version": "4.23.2", + "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.23.2.tgz", + "integrity": "sha512-OUK/6mqr6CQWxzl/QY0/mwhlGvS6fMtvEPyn/7AHUx96NjqDA4X4+Ju7aXFQKh+m3jW9VPB0B9xvEQgyAnRPNw==", "dev": true }, "node_modules/@algolia/cache-in-memory": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.22.1.tgz", - "integrity": "sha512-ve+6Ac2LhwpufuWavM/aHjLoNz/Z/sYSgNIXsinGofWOysPilQZPUetqLj8vbvi+DHZZaYSEP9H5SRVXnpsNNw==", + "version": "4.23.2", + "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.23.2.tgz", + "integrity": "sha512-rfbi/SnhEa3MmlqQvgYz/9NNJ156NkU6xFxjbxBtLWnHbpj+qnlMoKd+amoiacHRITpajg6zYbLM9dnaD3Bczw==", "dev": true, "dependencies": { - "@algolia/cache-common": "4.22.1" + "@algolia/cache-common": "4.23.2" } }, "node_modules/@algolia/client-account": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.22.1.tgz", - "integrity": "sha512-k8m+oegM2zlns/TwZyi4YgCtyToackkOpE+xCaKCYfBfDtdGOaVZCM5YvGPtK+HGaJMIN/DoTL8asbM3NzHonw==", + "version": "4.23.2", + "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.23.2.tgz", + "integrity": "sha512-VbrOCLIN/5I7iIdskSoSw3uOUPF516k4SjDD4Qz3BFwa3of7D9A0lzBMAvQEJJEPHWdVraBJlGgdJq/ttmquJQ==", "dev": true, "dependencies": { - "@algolia/client-common": "4.22.1", - "@algolia/client-search": "4.22.1", - "@algolia/transporter": "4.22.1" + "@algolia/client-common": "4.23.2", + "@algolia/client-search": "4.23.2", + "@algolia/transporter": "4.23.2" } }, "node_modules/@algolia/client-analytics": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.22.1.tgz", - "integrity": "sha512-1ssi9pyxyQNN4a7Ji9R50nSdISIumMFDwKNuwZipB6TkauJ8J7ha/uO60sPJFqQyqvvI+px7RSNRQT3Zrvzieg==", + "version": "4.23.2", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.23.2.tgz", + "integrity": "sha512-lLj7irsAztGhMoEx/SwKd1cwLY6Daf1Q5f2AOsZacpppSvuFvuBrmkzT7pap1OD/OePjLKxicJS8wNA0+zKtuw==", "dev": true, "dependencies": { - "@algolia/client-common": "4.22.1", - "@algolia/client-search": "4.22.1", - "@algolia/requester-common": "4.22.1", - "@algolia/transporter": "4.22.1" + "@algolia/client-common": "4.23.2", + "@algolia/client-search": "4.23.2", + "@algolia/requester-common": "4.23.2", + "@algolia/transporter": "4.23.2" } }, "node_modules/@algolia/client-common": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.22.1.tgz", - "integrity": "sha512-IvaL5v9mZtm4k4QHbBGDmU3wa/mKokmqNBqPj0K7lcR8ZDKzUorhcGp/u8PkPC/e0zoHSTvRh7TRkGX3Lm7iOQ==", + "version": "4.23.2", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.23.2.tgz", + "integrity": "sha512-Q2K1FRJBern8kIfZ0EqPvUr3V29ICxCm/q42zInV+VJRjldAD9oTsMGwqUQ26GFMdFYmqkEfCbY4VGAiQhh22g==", "dev": true, "dependencies": { - "@algolia/requester-common": "4.22.1", - "@algolia/transporter": "4.22.1" + "@algolia/requester-common": "4.23.2", + "@algolia/transporter": "4.23.2" } }, "node_modules/@algolia/client-personalization": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.22.1.tgz", - "integrity": "sha512-sl+/klQJ93+4yaqZ7ezOttMQ/nczly/3GmgZXJ1xmoewP5jmdP/X/nV5U7EHHH3hCUEHeN7X1nsIhGPVt9E1cQ==", + "version": "4.23.2", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.23.2.tgz", + "integrity": "sha512-vwPsgnCGhUcHhhQG5IM27z8q7dWrN9itjdvgA6uKf2e9r7vB+WXt4OocK0CeoYQt3OGEAExryzsB8DWqdMK5wg==", "dev": true, "dependencies": { - "@algolia/client-common": "4.22.1", - "@algolia/requester-common": "4.22.1", - "@algolia/transporter": "4.22.1" + "@algolia/client-common": "4.23.2", + "@algolia/requester-common": "4.23.2", + "@algolia/transporter": "4.23.2" } }, "node_modules/@algolia/client-search": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.22.1.tgz", - "integrity": "sha512-yb05NA4tNaOgx3+rOxAmFztgMTtGBi97X7PC3jyNeGiwkAjOZc2QrdZBYyIdcDLoI09N0gjtpClcackoTN0gPA==", + "version": "4.23.2", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.23.2.tgz", + "integrity": "sha512-CxSB29OVGSE7l/iyoHvamMonzq7Ev8lnk/OkzleODZ1iBcCs3JC/XgTIKzN/4RSTrJ9QybsnlrN/bYCGufo7qw==", "dev": true, "dependencies": { - "@algolia/client-common": "4.22.1", - "@algolia/requester-common": "4.22.1", - "@algolia/transporter": "4.22.1" + "@algolia/client-common": "4.23.2", + "@algolia/requester-common": "4.23.2", + "@algolia/transporter": "4.23.2" } }, "node_modules/@algolia/logger-common": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.22.1.tgz", - "integrity": "sha512-OnTFymd2odHSO39r4DSWRFETkBufnY2iGUZNrMXpIhF5cmFE8pGoINNPzwg02QLBlGSaLqdKy0bM8S0GyqPLBg==", + "version": "4.23.2", + "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.23.2.tgz", + "integrity": "sha512-jGM49Q7626cXZ7qRAWXn0jDlzvoA1FvN4rKTi1g0hxKsTTSReyYk0i1ADWjChDPl3Q+nSDhJuosM2bBUAay7xw==", "dev": true }, "node_modules/@algolia/logger-console": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.22.1.tgz", - "integrity": "sha512-O99rcqpVPKN1RlpgD6H3khUWylU24OXlzkavUAMy6QZd1776QAcauE3oP8CmD43nbaTjBexZj2nGsBH9Tc0FVA==", + "version": "4.23.2", + "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.23.2.tgz", + "integrity": "sha512-oo+lnxxEmlhTBTFZ3fGz1O8PJ+G+8FiAoMY2Qo3Q4w23xocQev6KqDTA1JQAGPDxAewNA2VBwWOsVXeXFjrI/Q==", "dev": true, "dependencies": { - "@algolia/logger-common": "4.22.1" + "@algolia/logger-common": "4.23.2" + } + }, + "node_modules/@algolia/recommend": { + "version": "4.23.2", + "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-4.23.2.tgz", + "integrity": "sha512-Q75CjnzRCDzgIlgWfPnkLtrfF4t82JCirhalXkSSwe/c1GH5pWh4xUyDOR3KTMo+YxxX3zTlrL/FjHmUJEWEcg==", + "dev": true, + "dependencies": { + "@algolia/cache-browser-local-storage": "4.23.2", + "@algolia/cache-common": "4.23.2", + "@algolia/cache-in-memory": "4.23.2", + "@algolia/client-common": "4.23.2", + "@algolia/client-search": "4.23.2", + "@algolia/logger-common": "4.23.2", + "@algolia/logger-console": "4.23.2", + "@algolia/requester-browser-xhr": "4.23.2", + "@algolia/requester-common": "4.23.2", + "@algolia/requester-node-http": "4.23.2", + "@algolia/transporter": "4.23.2" } }, "node_modules/@algolia/requester-browser-xhr": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.22.1.tgz", - "integrity": "sha512-dtQGYIg6MteqT1Uay3J/0NDqD+UciHy3QgRbk7bNddOJu+p3hzjTRYESqEnoX/DpEkaNYdRHUKNylsqMpgwaEw==", + "version": "4.23.2", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.23.2.tgz", + "integrity": "sha512-TO9wLlp8+rvW9LnIfyHsu8mNAMYrqNdQ0oLF6eTWFxXfxG3k8F/Bh7nFYGk2rFAYty4Fw4XUtrv/YjeNDtM5og==", "dev": true, "dependencies": { - "@algolia/requester-common": "4.22.1" + "@algolia/requester-common": "4.23.2" } }, "node_modules/@algolia/requester-common": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.22.1.tgz", - "integrity": "sha512-dgvhSAtg2MJnR+BxrIFqlLtkLlVVhas9HgYKMk2Uxiy5m6/8HZBL40JVAMb2LovoPFs9I/EWIoFVjOrFwzn5Qg==", + "version": "4.23.2", + "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.23.2.tgz", + "integrity": "sha512-3EfpBS0Hri0lGDB5H/BocLt7Vkop0bTTLVUBB844HH6tVycwShmsV6bDR7yXbQvFP1uNpgePRD3cdBCjeHmk6Q==", "dev": true }, "node_modules/@algolia/requester-node-http": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.22.1.tgz", - "integrity": "sha512-JfmZ3MVFQkAU+zug8H3s8rZ6h0ahHZL/SpMaSasTCGYR5EEJsCc8SI5UZ6raPN2tjxa5bxS13BRpGSBUens7EA==", + "version": "4.23.2", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.23.2.tgz", + "integrity": "sha512-SVzgkZM/malo+2SB0NWDXpnT7nO5IZwuDTaaH6SjLeOHcya1o56LSWXk+3F3rNLz2GVH+I/rpYKiqmHhSOjerw==", "dev": true, "dependencies": { - "@algolia/requester-common": "4.22.1" + "@algolia/requester-common": "4.23.2" } }, "node_modules/@algolia/transporter": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.22.1.tgz", - "integrity": "sha512-kzWgc2c9IdxMa3YqA6TN0NW5VrKYYW/BELIn7vnLyn+U/RFdZ4lxxt9/8yq3DKV5snvoDzzO4ClyejZRdV3lMQ==", + "version": "4.23.2", + "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.23.2.tgz", + "integrity": "sha512-GY3aGKBy+8AK4vZh8sfkatDciDVKad5rTY2S10Aefyjh7e7UGBP4zigf42qVXwU8VOPwi7l/L7OACGMOFcjB0Q==", "dev": true, "dependencies": { - "@algolia/cache-common": "4.22.1", - "@algolia/logger-common": "4.22.1", - "@algolia/requester-common": "4.22.1" + "@algolia/cache-common": "4.23.2", + "@algolia/logger-common": "4.23.2", + "@algolia/requester-common": "4.23.2" } }, "node_modules/@anywidget/types": { @@ -436,30 +455,30 @@ } }, "node_modules/@docsearch/css": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.5.2.tgz", - "integrity": "sha512-SPiDHaWKQZpwR2siD0KQUwlStvIAnEyK6tAE2h2Wuoq8ue9skzhlyVQ1ddzOxX6khULnAALDiR/isSF3bnuciA==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.6.0.tgz", + "integrity": "sha512-+sbxb71sWre+PwDK7X2T8+bhS6clcVMLwBPznX45Qu6opJcgRjAp7gYSDzVFp187J+feSj5dNBN1mJoi6ckkUQ==", "dev": true }, "node_modules/@docsearch/js": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/@docsearch/js/-/js-3.5.2.tgz", - "integrity": "sha512-p1YFTCDflk8ieHgFJYfmyHBki1D61+U9idwrLh+GQQMrBSP3DLGKpy0XUJtPjAOPltcVbqsTjiPFfH7JImjUNg==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@docsearch/js/-/js-3.6.0.tgz", + "integrity": "sha512-QujhqINEElrkIfKwyyyTfbsfMAYCkylInLYMRqHy7PHc8xTBQCow73tlo/Kc7oIwBrCLf0P3YhjlOeV4v8hevQ==", "dev": true, "dependencies": { - "@docsearch/react": "3.5.2", + "@docsearch/react": "3.6.0", "preact": "^10.0.0" } }, "node_modules/@docsearch/react": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.5.2.tgz", - "integrity": "sha512-9Ahcrs5z2jq/DcAvYtvlqEBHImbm4YJI8M9y0x6Tqg598P40HTEkX7hsMcIuThI+hTFxRGZ9hll0Wygm2yEjng==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.6.0.tgz", + "integrity": "sha512-HUFut4ztcVNmqy9gp/wxNbC7pTOHhgVVkHVGCACTuLhUKUhKAF9KYHJtMiLUJxEqiFLQiuri1fWF8zqwM/cu1w==", "dev": true, "dependencies": { "@algolia/autocomplete-core": "1.9.3", "@algolia/autocomplete-preset-algolia": "1.9.3", - "@docsearch/css": "3.5.2", + "@docsearch/css": "3.6.0", "algoliasearch": "^4.19.1" }, "peerDependencies": { @@ -2445,9 +2464,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.12.0.tgz", - "integrity": "sha512-+ac02NL/2TCKRrJu2wffk1kZ+RyqxVUlbjSagNgPm94frxtr+XDL12E5Ll1enWskLrtrZ2r8L3wED1orIibV/w==", + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.1.tgz", + "integrity": "sha512-4C4UERETjXpC4WpBXDbkgNVgHyWfG3B/NKY46e7w5H134UDOFqUJKpsLm0UYmuupW+aJmRgeScrDNfvZ5WV80A==", "cpu": [ "arm" ], @@ -2458,9 +2477,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.12.0.tgz", - "integrity": "sha512-OBqcX2BMe6nvjQ0Nyp7cC90cnumt8PXmO7Dp3gfAju/6YwG0Tj74z1vKrfRz7qAv23nBcYM8BCbhrsWqO7PzQQ==", + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.1.tgz", + "integrity": "sha512-TrTaFJ9pXgfXEiJKQ3yQRelpQFqgRzVR9it8DbeRzG0RX7mKUy0bqhCFsgevwXLJepQKTnLl95TnPGf9T9AMOA==", "cpu": [ "arm64" ], @@ -2471,9 +2490,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.12.0.tgz", - "integrity": "sha512-X64tZd8dRE/QTrBIEs63kaOBG0b5GVEd3ccoLtyf6IdXtHdh8h+I56C2yC3PtC9Ucnv0CpNFJLqKFVgCYe0lOQ==", + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.1.tgz", + "integrity": "sha512-fz7jN6ahTI3cKzDO2otQuybts5cyu0feymg0bjvYCBrZQ8tSgE8pc0sSNEuGvifrQJWiwx9F05BowihmLxeQKw==", "cpu": [ "arm64" ], @@ -2484,9 +2503,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.12.0.tgz", - "integrity": "sha512-cc71KUZoVbUJmGP2cOuiZ9HSOP14AzBAThn3OU+9LcA1+IUqswJyR1cAJj3Mg55HbjZP6OLAIscbQsQLrpgTOg==", + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.1.tgz", + "integrity": "sha512-WTvdz7SLMlJpektdrnWRUN9C0N2qNHwNbWpNo0a3Tod3gb9leX+yrYdCeB7VV36OtoyiPAivl7/xZ3G1z5h20g==", "cpu": [ "x64" ], @@ -2497,9 +2516,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.12.0.tgz", - "integrity": "sha512-a6w/Y3hyyO6GlpKL2xJ4IOh/7d+APaqLYdMf86xnczU3nurFTaVN9s9jOXQg97BE4nYm/7Ga51rjec5nfRdrvA==", + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.1.tgz", + "integrity": "sha512-dBHQl+7wZzBYcIF6o4k2XkAfwP2ks1mYW2q/Gzv9n39uDcDiAGDqEyml08OdY0BIct0yLSPkDTqn4i6czpBLLw==", "cpu": [ "arm" ], @@ -2510,9 +2529,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.12.0.tgz", - "integrity": "sha512-0fZBq27b+D7Ar5CQMofVN8sggOVhEtzFUwOwPppQt0k+VR+7UHMZZY4y+64WJ06XOhBTKXtQB/Sv0NwQMXyNAA==", + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.1.tgz", + "integrity": "sha512-bur4JOxvYxfrAmocRJIW0SADs3QdEYK6TQ7dTNz6Z4/lySeu3Z1H/+tl0a4qDYv0bCdBpUYM0sYa/X+9ZqgfSQ==", "cpu": [ "arm64" ], @@ -2523,9 +2542,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.12.0.tgz", - "integrity": "sha512-eTvzUS3hhhlgeAv6bfigekzWZjaEX9xP9HhxB0Dvrdbkk5w/b+1Sxct2ZuDxNJKzsRStSq1EaEkVSEe7A7ipgQ==", + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.1.tgz", + "integrity": "sha512-ssp77SjcDIUSoUyj7DU7/5iwM4ZEluY+N8umtCT9nBRs3u045t0KkW02LTyHouHDomnMXaXSZcCSr2bdMK63kA==", "cpu": [ "arm64" ], @@ -2536,9 +2555,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.12.0.tgz", - "integrity": "sha512-ix+qAB9qmrCRiaO71VFfY8rkiAZJL8zQRXveS27HS+pKdjwUfEhqo2+YF2oI+H/22Xsiski+qqwIBxVewLK7sw==", + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.1.tgz", + "integrity": "sha512-Jv1DkIvwEPAb+v25/Unrnnq9BO3F5cbFPT821n3S5litkz+O5NuXuNhqtPx5KtcwOTtaqkTsO+IVzJOsxd11aQ==", "cpu": [ "riscv64" ], @@ -2548,10 +2567,23 @@ "linux" ] }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.13.1.tgz", + "integrity": "sha512-U564BrhEfaNChdATQaEODtquCC7Ez+8Hxz1h5MAdMYj0AqD0GA9rHCpElajb/sQcaFL6NXmHc5O+7FXpWMa73Q==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.12.0.tgz", - "integrity": "sha512-TenQhZVOtw/3qKOPa7d+QgkeM6xY0LtwzR8OplmyL5LrgTWIXpTQg2Q2ycBf8jm+SFW2Wt/DTn1gf7nFp3ssVA==", + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.1.tgz", + "integrity": "sha512-zGRDulLTeDemR8DFYyFIQ8kMP02xpUsX4IBikc7lwL9PrwR3gWmX2NopqiGlI2ZVWMl15qZeUjumTwpv18N7sQ==", "cpu": [ "x64" ], @@ -2562,9 +2594,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.12.0.tgz", - "integrity": "sha512-LfFdRhNnW0zdMvdCb5FNuWlls2WbbSridJvxOvYWgSBOYZtgBfW9UGNJG//rwMqTX1xQE9BAodvMH9tAusKDUw==", + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.1.tgz", + "integrity": "sha512-VTk/MveyPdMFkYJJPCkYBw07KcTkGU2hLEyqYMsU4NjiOfzoaDTW9PWGRsNwiOA3qI0k/JQPjkl/4FCK1smskQ==", "cpu": [ "x64" ], @@ -2575,9 +2607,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.12.0.tgz", - "integrity": "sha512-JPDxovheWNp6d7AHCgsUlkuCKvtu3RB55iNEkaQcf0ttsDU/JZF+iQnYcQJSk/7PtT4mjjVG8N1kpwnI9SLYaw==", + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.1.tgz", + "integrity": "sha512-L+hX8Dtibb02r/OYCsp4sQQIi3ldZkFI0EUkMTDwRfFykXBPptoz/tuuGqEd3bThBSLRWPR6wsixDSgOx/U3Zw==", "cpu": [ "arm64" ], @@ -2588,9 +2620,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.12.0.tgz", - "integrity": "sha512-fjtuvMWRGJn1oZacG8IPnzIV6GF2/XG+h71FKn76OYFqySXInJtseAqdprVTDTyqPxQOG9Exak5/E9Z3+EJ8ZA==", + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.1.tgz", + "integrity": "sha512-+dI2jVPfM5A8zme8riEoNC7UKk0Lzc7jCj/U89cQIrOjrZTCWZl/+IXUeRT2rEZ5j25lnSA9G9H1Ob9azaF/KQ==", "cpu": [ "ia32" ], @@ -2601,9 +2633,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.12.0.tgz", - "integrity": "sha512-ZYmr5mS2wd4Dew/JjT0Fqi2NPB/ZhZ2VvPp7SmvPZb4Y1CG/LRcS6tcRo2cYU7zLK5A7cdbhWnnWmUjoI4qapg==", + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.1.tgz", + "integrity": "sha512-YY1Exxo2viZ/O2dMHuwQvimJ0SqvL+OAWQLLY6rvXavgQKjhQUzn7nc1Dd29gjB5Fqi00nrBWctJBOyfVMIVxw==", "cpu": [ "x64" ], @@ -2614,18 +2646,18 @@ ] }, "node_modules/@shikijs/core": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.1.7.tgz", - "integrity": "sha512-gTYLUIuD1UbZp/11qozD3fWpUTuMqPSf3svDMMrL0UmlGU7D9dPw/V1FonwAorCUJBltaaESxq90jrSjQyGixg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.2.1.tgz", + "integrity": "sha512-KaIS0H4EQ3KI2d++TjYqRNgwp8E3M/68e9veR4QtInzA7kKFgcjeiJqb80fuXW+blDy5fmd11PN9g9soz/3ANQ==", "dev": true }, "node_modules/@shikijs/transformers": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-1.1.7.tgz", - "integrity": "sha512-lXz011ao4+rvweps/9h3CchBfzb1U5OtP5D51Tqc9lQYdLblWMIxQxH6Ybe1GeGINcEVM4goMyPrI0JvlIp4UQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-1.2.1.tgz", + "integrity": "sha512-H7cVtrdv6BW2kx83t2IQgP5ri1IA50mE3QnzgJ0AvOKCGtCEieXu0JIP3245cgjNLrL+LBwb8DtTXdky1iQL9Q==", "dev": true, "dependencies": { - "shiki": "1.1.7" + "shiki": "1.2.1" } }, "node_modules/@sigstore/bundle": { @@ -3193,21 +3225,21 @@ } }, "node_modules/@vue/devtools-api": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.0.15.tgz", - "integrity": "sha512-kgEYWosDyWpS1vFSuJNNWUnHkP+VkL3Y+9mw+rf7ex41SwbYL/WdC3KXqAtjiSrEs7r/FrHmUTh0BkINJPFkbA==", + "version": "7.0.24", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.0.24.tgz", + "integrity": "sha512-4dsRfL+7CnRZ9TgkmFNHRc7fKSE6v74GNojwxop+F5lotUTxNDbN7HTYg/vJ7DfJtwKFYGQjMFbHxEDZQ4Sahg==", "dev": true, "dependencies": { - "@vue/devtools-kit": "^7.0.15" + "@vue/devtools-kit": "^7.0.24" } }, "node_modules/@vue/devtools-kit": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.0.15.tgz", - "integrity": "sha512-dT7OeCe1LUCIhHIb/yRR6Hn+XHh73r1o78onqCrxEKHdoZwBItiIeVnmJZPEUDFstIxfs+tJL231mySk3laTow==", + "version": "7.0.24", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.0.24.tgz", + "integrity": "sha512-6XD4ZRjbnk8XC5IM/GfuqB9O9UlmUU53pybuxg0/xBI9pxQfH3mOu5Gpyb55cG18uMW4c7hdEOgkxwFpUgYIrg==", "dev": true, "dependencies": { - "@vue/devtools-shared": "^7.0.15", + "@vue/devtools-shared": "^7.0.24", "hookable": "^5.5.3", "mitt": "^3.0.1", "perfect-debounce": "^1.0.0", @@ -3218,9 +3250,9 @@ } }, "node_modules/@vue/devtools-shared": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.0.15.tgz", - "integrity": "sha512-fpfvMVvS7aDgO7x2JPFiTQ1MHcCc63/bE7yTgs278gMBybuO9b3hdiZ/k0Pw1rN+RefaU9yQiFA+5CCFc1D+6w==", + "version": "7.0.24", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.0.24.tgz", + "integrity": "sha512-hocNGlaQuc0s3hLNky64zXQK6DjQcIGEI0TJbizEAH7l5eZ6BCPB3P19ofu9HyUbbIn/PTJWqyLT8clzWqo0Xw==", "dev": true, "dependencies": { "rfdc": "^1.3.1" @@ -3602,25 +3634,26 @@ } }, "node_modules/algoliasearch": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.22.1.tgz", - "integrity": "sha512-jwydKFQJKIx9kIZ8Jm44SdpigFwRGPESaxZBaHSV0XWN2yBJAOT4mT7ppvlrpA4UGzz92pqFnVKr/kaZXrcreg==", - "dev": true, - "dependencies": { - "@algolia/cache-browser-local-storage": "4.22.1", - "@algolia/cache-common": "4.22.1", - "@algolia/cache-in-memory": "4.22.1", - "@algolia/client-account": "4.22.1", - "@algolia/client-analytics": "4.22.1", - "@algolia/client-common": "4.22.1", - "@algolia/client-personalization": "4.22.1", - "@algolia/client-search": "4.22.1", - "@algolia/logger-common": "4.22.1", - "@algolia/logger-console": "4.22.1", - "@algolia/requester-browser-xhr": "4.22.1", - "@algolia/requester-common": "4.22.1", - "@algolia/requester-node-http": "4.22.1", - "@algolia/transporter": "4.22.1" + "version": "4.23.2", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.23.2.tgz", + "integrity": "sha512-8aCl055IsokLuPU8BzLjwzXjb7ty9TPcUFFOk0pYOwsE5DMVhE3kwCMFtsCFKcnoPZK7oObm+H5mbnSO/9ioxQ==", + "dev": true, + "dependencies": { + "@algolia/cache-browser-local-storage": "4.23.2", + "@algolia/cache-common": "4.23.2", + "@algolia/cache-in-memory": "4.23.2", + "@algolia/client-account": "4.23.2", + "@algolia/client-analytics": "4.23.2", + "@algolia/client-common": "4.23.2", + "@algolia/client-personalization": "4.23.2", + "@algolia/client-search": "4.23.2", + "@algolia/logger-common": "4.23.2", + "@algolia/logger-console": "4.23.2", + "@algolia/recommend": "4.23.2", + "@algolia/requester-browser-xhr": "4.23.2", + "@algolia/requester-common": "4.23.2", + "@algolia/requester-node-http": "4.23.2", + "@algolia/transporter": "4.23.2" } }, "node_modules/ansi-colors": { @@ -8597,9 +8630,9 @@ } }, "node_modules/mocha": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.3.0.tgz", - "integrity": "sha512-uF2XJs+7xSLsrmIvn37i/wnc91nw7XjOQB8ccyx5aEgdnohr7n+rEiZP23WkCYHjilR6+EboEnbq/ZQDz4LSbg==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.4.0.tgz", + "integrity": "sha512-eqhGB8JKapEYcC4ytX/xrzKforgEc3j1pGlAXVy3eRwrtAy5/nIfT1SvgGzfN0XZZxeLq0aQWkOUAmqIJiv+bA==", "dev": true, "dependencies": { "ansi-colors": "4.1.1", @@ -10388,9 +10421,9 @@ } }, "node_modules/postcss": { - "version": "8.4.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", - "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", + "version": "8.4.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", "dev": true, "funding": [ { @@ -10409,16 +10442,16 @@ "dependencies": { "nanoid": "^3.3.7", "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "source-map-js": "^1.2.0" }, "engines": { "node": "^10 || ^12 || >=14" } }, "node_modules/preact": { - "version": "10.19.6", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.19.6.tgz", - "integrity": "sha512-gympg+T2Z1fG1unB8NH29yHJwnEaCH37Z32diPDku316OTnRPeMbiRV9kTrfZpocXjdfnWuFUl/Mj4BHaf6gnw==", + "version": "10.20.1", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.20.1.tgz", + "integrity": "sha512-JIFjgFg9B2qnOoGiYMVBtrcFxHqn+dNXbq76bVmcaHYJFYR4lW67AOcXgAYQQTDYXDOg/kTZrKPNCdRgJ2UJmw==", "dev": true, "funding": { "type": "opencollective", @@ -11172,9 +11205,9 @@ "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==" }, "node_modules/rollup": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.12.0.tgz", - "integrity": "sha512-wz66wn4t1OHIJw3+XU7mJJQV/2NAfw5OAk6G6Hoo3zcvz/XOfQ52Vgi+AN4Uxoxi0KBBwk2g8zPrTDA4btSB/Q==", + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.1.tgz", + "integrity": "sha512-hFi+fU132IvJ2ZuihN56dwgpltpmLZHZWsx27rMCTZ2sYwrqlgL5sECGy1eeV2lAihD8EzChBVVhsXci0wD4Tg==", "dev": true, "dependencies": { "@types/estree": "1.0.5" @@ -11187,19 +11220,20 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.12.0", - "@rollup/rollup-android-arm64": "4.12.0", - "@rollup/rollup-darwin-arm64": "4.12.0", - "@rollup/rollup-darwin-x64": "4.12.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.12.0", - "@rollup/rollup-linux-arm64-gnu": "4.12.0", - "@rollup/rollup-linux-arm64-musl": "4.12.0", - "@rollup/rollup-linux-riscv64-gnu": "4.12.0", - "@rollup/rollup-linux-x64-gnu": "4.12.0", - "@rollup/rollup-linux-x64-musl": "4.12.0", - "@rollup/rollup-win32-arm64-msvc": "4.12.0", - "@rollup/rollup-win32-ia32-msvc": "4.12.0", - "@rollup/rollup-win32-x64-msvc": "4.12.0", + "@rollup/rollup-android-arm-eabi": "4.13.1", + "@rollup/rollup-android-arm64": "4.13.1", + "@rollup/rollup-darwin-arm64": "4.13.1", + "@rollup/rollup-darwin-x64": "4.13.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.13.1", + "@rollup/rollup-linux-arm64-gnu": "4.13.1", + "@rollup/rollup-linux-arm64-musl": "4.13.1", + "@rollup/rollup-linux-riscv64-gnu": "4.13.1", + "@rollup/rollup-linux-s390x-gnu": "4.13.1", + "@rollup/rollup-linux-x64-gnu": "4.13.1", + "@rollup/rollup-linux-x64-musl": "4.13.1", + "@rollup/rollup-win32-arm64-msvc": "4.13.1", + "@rollup/rollup-win32-ia32-msvc": "4.13.1", + "@rollup/rollup-win32-x64-msvc": "4.13.1", "fsevents": "~2.3.2" } }, @@ -11363,12 +11397,12 @@ } }, "node_modules/shiki": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.1.7.tgz", - "integrity": "sha512-9kUTMjZtcPH3i7vHunA6EraTPpPOITYTdA5uMrvsJRexktqP0s7P3s9HVK80b4pP42FRVe03D7fT3NmJv2yYhw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.2.1.tgz", + "integrity": "sha512-u+XW6o0vCkUNlneZb914dLO+AayEIwK5tI62WeS//R5HIXBFiYaj/Hc5xcq27Yh83Grr4JbNtUBV8W6zyK4hWg==", "dev": true, "dependencies": { - "@shikijs/core": "1.1.7" + "@shikijs/core": "1.2.1" } }, "node_modules/signal-exit": { @@ -11687,9 +11721,9 @@ } }, "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", "dev": true, "engines": { "node": ">=0.10.0" @@ -12477,9 +12511,9 @@ "dev": true }, "node_modules/typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", + "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -13096,14 +13130,14 @@ } }, "node_modules/vite": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.6.tgz", - "integrity": "sha512-yYIAZs9nVfRJ/AiOLCA91zzhjsHUgMjB+EigzFb6W2XTLO8JixBCKCjvhKZaye+NKYHCrkv3Oh50dH9EdLU2RA==", + "version": "5.2.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.6.tgz", + "integrity": "sha512-FPtnxFlSIKYjZ2eosBQamz4CbyrTizbZ3hnGJlh/wMtCrlp1Hah6AzBLjGI5I2urTfNnpovpHdrL6YRuBOPnCA==", "dev": true, "dependencies": { - "esbuild": "^0.19.3", - "postcss": "^8.4.35", - "rollup": "^4.2.0" + "esbuild": "^0.20.1", + "postcss": "^8.4.36", + "rollup": "^4.13.0" }, "bin": { "vite": "bin/vite.js" @@ -13150,440 +13184,34 @@ } } }, - "node_modules/vite/node_modules/@esbuild/aix-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", - "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/android-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", - "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/android-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", - "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/android-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", - "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/darwin-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", - "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/darwin-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", - "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", - "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/freebsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", - "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", - "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", - "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", - "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-loong64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", - "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", - "cpu": [ - "loong64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-mips64el": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", - "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", - "cpu": [ - "mips64el" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", - "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-riscv64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", - "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-s390x": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", - "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", - "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/netbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", - "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/openbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", - "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/sunos-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", - "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", - "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", - "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", - "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/esbuild": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", - "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", - "dev": true, - "hasInstallScript": true, - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.19.12", - "@esbuild/android-arm": "0.19.12", - "@esbuild/android-arm64": "0.19.12", - "@esbuild/android-x64": "0.19.12", - "@esbuild/darwin-arm64": "0.19.12", - "@esbuild/darwin-x64": "0.19.12", - "@esbuild/freebsd-arm64": "0.19.12", - "@esbuild/freebsd-x64": "0.19.12", - "@esbuild/linux-arm": "0.19.12", - "@esbuild/linux-arm64": "0.19.12", - "@esbuild/linux-ia32": "0.19.12", - "@esbuild/linux-loong64": "0.19.12", - "@esbuild/linux-mips64el": "0.19.12", - "@esbuild/linux-ppc64": "0.19.12", - "@esbuild/linux-riscv64": "0.19.12", - "@esbuild/linux-s390x": "0.19.12", - "@esbuild/linux-x64": "0.19.12", - "@esbuild/netbsd-x64": "0.19.12", - "@esbuild/openbsd-x64": "0.19.12", - "@esbuild/sunos-x64": "0.19.12", - "@esbuild/win32-arm64": "0.19.12", - "@esbuild/win32-ia32": "0.19.12", - "@esbuild/win32-x64": "0.19.12" - } - }, "node_modules/vitepress": { - "version": "1.0.0-rc.45", - "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-1.0.0-rc.45.tgz", - "integrity": "sha512-/OiYsu5UKpQKA2c0BAZkfyywjfauDjvXyv6Mo4Ra57m5n4Bxg1HgUGoth1CLH2vwUbR/BHvDA9zOM0RDvgeSVQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-1.0.1.tgz", + "integrity": "sha512-eNr5pOBppYUUjEhv8S0S2t9Tv95LQ6mMeHj6ivaGwfHxpov70Vduuwl/QQMDRznKDSaP0WKV7a82Pb4JVOaqEw==", "dev": true, "dependencies": { - "@docsearch/css": "^3.5.2", - "@docsearch/js": "^3.5.2", - "@shikijs/core": "^1.1.5", - "@shikijs/transformers": "^1.1.5", + "@docsearch/css": "^3.6.0", + "@docsearch/js": "^3.6.0", + "@shikijs/core": "^1.2.0", + "@shikijs/transformers": "^1.2.0", "@types/markdown-it": "^13.0.7", "@vitejs/plugin-vue": "^5.0.4", - "@vue/devtools-api": "^7.0.14", - "@vueuse/core": "^10.7.2", - "@vueuse/integrations": "^10.7.2", + "@vue/devtools-api": "^7.0.16", + "@vueuse/core": "^10.9.0", + "@vueuse/integrations": "^10.9.0", "focus-trap": "^7.5.4", "mark.js": "8.11.1", "minisearch": "^6.3.0", - "shiki": "^1.1.5", - "vite": "^5.1.3", - "vue": "^3.4.19" + "shiki": "^1.2.0", + "vite": "^5.2.2", + "vue": "^3.4.21" }, "bin": { "vitepress": "bin/vitepress.js" }, "peerDependencies": { - "markdown-it-mathjax3": "^4.3.2", - "postcss": "^8.4.35" + "markdown-it-mathjax3": "^4", + "postcss": "^8" }, "peerDependenciesMeta": { "markdown-it-mathjax3": { diff --git a/package.json b/package.json index 81926157..e15b8539 100644 --- a/package.json +++ b/package.json @@ -27,13 +27,13 @@ "eslint": "^8.57.0", "eslint-plugin-jsdoc": "^48.2.1", "lerna": "^8.1.2", - "mocha": "^10.3.0", + "mocha": "^10.4.0", "nodemon": "^3.1.0", "rimraf": "^5.0.5", "timezone-mock": "^1.3.6", - "typescript": "^5.3.3", - "vite": "^5.1.6", - "vitepress": "1.0.0-rc.45", + "typescript": "^5.4.3", + "vite": "^5.2.6", + "vitepress": "1.0.1", "yaml": "^2.4.1" }, "workspaces": [ diff --git a/packages/core/src/Coordinator.js b/packages/core/src/Coordinator.js index bef80c37..43a6129a 100644 --- a/packages/core/src/Coordinator.js +++ b/packages/core/src/Coordinator.js @@ -8,8 +8,7 @@ let _instance; /** * Set or retrieve the coordinator instance. - * - * @param {Coordinator} instance the coordinator instance to set + * @param {Coordinator} [instance] the coordinator instance to set * @returns {Coordinator} the coordinator instance */ export function coordinator(instance) { @@ -42,7 +41,15 @@ export class Coordinator { return this._logger; } - configure({ cache = true, consolidate = true, indexes = true }) { + /** + * Set configuration options for this coordinator. + * @param {object} [options] Configration options. + * @param {boolean} [options.cache=true] Boolean flag to enable/disable query caching. + * @param {boolean} [options.consolidate=true] Boolean flag to enable/disable query consolidation. + * @param {boolean|object} [options.indexes=true] Boolean flag to enable/disable + * automatic data cube indexes or an index options object. + */ + configure({ cache = true, consolidate = true, indexes = true } = {}) { this.manager.cache(cache); this.manager.consolidate(consolidate); this.indexes = indexes; @@ -116,7 +123,6 @@ export class Coordinator { /** * Connect a client to the coordinator. - * * @param {import('./MosaicClient.js').MosaicClient} client the client to disconnect */ async connect(client) { diff --git a/packages/core/src/MosaicClient.js b/packages/core/src/MosaicClient.js index c90e4c32..4f1d477c 100644 --- a/packages/core/src/MosaicClient.js +++ b/packages/core/src/MosaicClient.js @@ -48,6 +48,7 @@ export class MosaicClient { /** * Return an array of fields queried by this client. + * @returns {object[]|null} The fields to retrieve info for. */ fields() { return null; @@ -55,21 +56,25 @@ export class MosaicClient { /** * Called by the coordinator to set the field info for this client. + * @param {*} info The field info result. * @returns {this} */ - fieldInfo() { + fieldInfo(info) { // eslint-disable-line no-unused-vars return this; } /** * Return a query specifying the data needed by this client. + * @param {*} [filter] The filtering criteria to apply in the query. + * @returns {*} The client query */ - query() { + query(filter) { // eslint-disable-line no-unused-vars return null; } /** * Called by the coordinator to inform the client that a query is pending. + * @returns {this} */ queryPending() { return this; @@ -77,16 +82,17 @@ export class MosaicClient { /** * Called by the coordinator to return a query result. - * - * @param {*} data the query result + * @param {*} data The query result. * @returns {this} */ - queryResult() { + queryResult(data) { // eslint-disable-line no-unused-vars return this; } /** * Called by the coordinator to report a query execution error. + * @param {*} error + * @returns {this} */ queryError(error) { console.error(error); diff --git a/packages/core/src/Param.js b/packages/core/src/Param.js index d3b650c6..ecacabfa 100644 --- a/packages/core/src/Param.js +++ b/packages/core/src/Param.js @@ -4,7 +4,7 @@ import { distinct } from './util/distinct.js'; /** * Test if a value is a Param instance. * @param {*} x The value to test. - * @returns {boolean} True if the input is a Param, false otherwise. + * @returns {x is Param} True if the input is a Param, false otherwise. */ export function isParam(x) { return x instanceof Param; @@ -43,7 +43,9 @@ export class Param extends AsyncDispatch { static array(values) { if (values.some(v => isParam(v))) { const p = new Param(); - const update = () => p.update(values.map(v => isParam(v) ? v.value : v)); + const update = () => { + p.update(values.map(v => isParam(v) ? v.value : v)); + }; update(); values.forEach(v => isParam(v) ? v.addEventListener('value', update) : 0); return p; diff --git a/packages/core/src/QueryConsolidator.js b/packages/core/src/QueryConsolidator.js index f332ba93..39891f35 100644 --- a/packages/core/src/QueryConsolidator.js +++ b/packages/core/src/QueryConsolidator.js @@ -5,6 +5,7 @@ function wait(callback) { const method = typeof requestAnimationFrame !== 'undefined' ? requestAnimationFrame : typeof setImmediate !== 'undefined' ? setImmediate : setTimeout; + // @ts-ignore return method(callback); } @@ -82,7 +83,9 @@ function consolidationKey(query, cache) { const sql = `${query}`; if (query instanceof Query && !cache.get(sql)) { if ( + // @ts-ignore query.orderby().length || query.where().length || + // @ts-ignore query.qualify().length || query.having().length ) { // do not try to analyze if query includes clauses @@ -97,9 +100,12 @@ function consolidationKey(query, cache) { // queries may refer to *derived* columns as group by criteria // we resolve these against the true grouping expressions const groupby = query.groupby(); + // @ts-ignore if (groupby.length) { const map = {}; // expression map (as -> expr) + // @ts-ignore query.select().forEach(({ as, expr }) => map[as] = expr); + // @ts-ignore q.$groupby(groupby.map(e => (e instanceof Ref && map[e.column]) || e)); } diff --git a/packages/core/src/Selection.js b/packages/core/src/Selection.js index d1a4655d..b9702ddb 100644 --- a/packages/core/src/Selection.js +++ b/packages/core/src/Selection.js @@ -4,7 +4,7 @@ import { Param } from './Param.js'; /** * Test if a value is a Selection instance. * @param {*} x The value to test. - * @returns {boolean} True if the input is a Selection, false otherwise. + * @returns {x is Selection} True if the input is a Selection, false otherwise. */ export function isSelection(x) { return x instanceof Selection; @@ -76,7 +76,7 @@ export class Selection extends Param { /** * Create a cloned copy of this Selection instance. - * @returns {this} A clone of this selection. + * @returns {Selection} A clone of this selection. */ clone() { const s = new Selection(this._resolver); @@ -88,7 +88,7 @@ export class Selection extends Param { * Create a clone of this Selection with clauses corresponding * to the provided source removed. * @param {*} source The clause source to remove. - * @returns {this} A cloned and updated Selection. + * @returns {Selection} A cloned and updated Selection. */ remove(source) { const s = this.clone(); @@ -168,9 +168,9 @@ export class Selection extends Param { * Upon value-typed updates, returns a dispatch queue filter function. * The return value depends on the selection resolution strategy. * @param {string} type The event type. - * @param {*} value The input event value. - * @returns {*} For value-typed events, returns a dispatch queue filter - * function. Otherwise returns null. + * @param {*} value The new event value that will be enqueued. + * @returns {(value: *) => boolean|null} For value-typed events, + * returns a dispatch queue filter function. Otherwise returns null. */ emitQueueFilter(type, value) { return type === 'value' @@ -285,5 +285,6 @@ export class SelectionResolver { const source = value.active?.source; return clauses => clauses.active?.source !== source; } + return null; } } diff --git a/packages/core/src/connectors/wasm.js b/packages/core/src/connectors/wasm.js index d42fcdc6..be550fde 100644 --- a/packages/core/src/connectors/wasm.js +++ b/packages/core/src/connectors/wasm.js @@ -22,7 +22,7 @@ export function wasmConnector(options = {}) { /** * Get the backing DuckDB-WASM instance. * Will lazily initialize DuckDB-WASM if not already loaded. - * @returns {duckdb.AsyncDuckDB} The DuckDB-WASM instance. + * @returns {Promise} The DuckDB-WASM instance. */ async function getDuckDB() { if (!db) await load(); @@ -32,7 +32,7 @@ export function wasmConnector(options = {}) { /** * Get the backing DuckDB-WASM connection. * Will lazily initialize DuckDB-WASM if not already loaded. - * @returns {duckdb.AsyncDuckDBConnection} The DuckDB-WASM connection. + * @returns {Promise} The DuckDB-WASM connection. */ async function getConnection() { if (!con) await load(); diff --git a/packages/core/src/util/AsyncDispatch.js b/packages/core/src/util/AsyncDispatch.js index 1f789586..ad7b3d63 100644 --- a/packages/core/src/util/AsyncDispatch.js +++ b/packages/core/src/util/AsyncDispatch.js @@ -16,7 +16,7 @@ export class AsyncDispatch { /** * Add an event listener callback for the provided event type. * @param {string} type The event type. - * @param {(value: *) => Promise?} callback The event handler + * @param {(value: *) => void | Promise} callback The event handler * callback function to add. If the callback has already been * added for the event type, this method has no effect. */ @@ -35,7 +35,7 @@ export class AsyncDispatch { /** * Remove an event listener callback for the provided event type. * @param {string} type The event type. - * @param {(value: *) => Promise?} callback The event handler + * @param {(value: *) => void | Promise} callback The event handler * callback function to remove. */ removeEventListener(type, callback) { @@ -64,11 +64,12 @@ export class AsyncDispatch { * This default implementation simply returns null, indicating that * any other unemitted event values should be dropped (that is, all * queued events are filtered) + * @param {string} type The event type. * @param {*} value The new event value that will be enqueued. * @returns {(value: *) => boolean|null} A dispatch queue filter * function, or null if all unemitted event values should be filtered. */ - emitQueueFilter() { + emitQueueFilter(type, value) { // eslint-disable-line no-unused-vars // removes all pending items return null; } diff --git a/packages/core/src/util/convert-arrow.js b/packages/core/src/util/convert-arrow.js index f264d35a..3cf5aba2 100644 --- a/packages/core/src/util/convert-arrow.js +++ b/packages/core/src/util/convert-arrow.js @@ -5,7 +5,7 @@ import { DataType } from 'apache-arrow'; * As sometimes multiple Arrow versions may be used simultaneously, * we use a "duck typing" approach and check for a getChild function. * @param {*} values The value to test - * @returns true if the value duck types as Apache Arrow data + * @returns {values is import('apache-arrow').Table} true if the value duck types as Apache Arrow data */ export function isArrowTable(values) { return typeof values?.getChild === 'function'; @@ -28,7 +28,7 @@ export function convertArrowArrayType(type) { * Large integers (BigInt) are converted to Float64 numbers. * Fixed-point decimal values are convert to Float64 numbers. * Otherwise, the default Arrow values are used. - * @param {*} type an Apache Arrow column type + * @param {DataType} type an Apache Arrow column type * @returns a value conversion function */ export function convertArrowValue(type) { @@ -112,10 +112,10 @@ const BASE32 = Array.from( /** * Convert a fixed point decimal value to a double precision number. * Note: if the value is sufficiently large the conversion may be lossy! - * @param {Uint32Array} v a fixed decimal value + * @param {Uint32Array & { signed: boolean }} v a fixed decimal value * @param {number} scale a scale factor, corresponding to the * number of fractional decimal digits in the fixed point value - * @returns the resulting number + * @returns {number} the resulting number */ function decimalToNumber(v, scale) { const n = v.length; diff --git a/packages/core/src/util/query-result.js b/packages/core/src/util/query-result.js index cdccbccc..530c6422 100644 --- a/packages/core/src/util/query-result.js +++ b/packages/core/src/util/query-result.js @@ -2,7 +2,8 @@ export function queryResult() { let resolve; let reject; const p = new Promise((r, e) => { resolve = r; reject = e; }); - p.fulfill = value => (resolve(value), p); - p.reject = err => (reject(err), p); - return p; + return Object.assign(p, { + fulfill: value => (resolve(value), p), + reject: err => (reject(err), p) + }); } diff --git a/packages/inputs/src/Menu.js b/packages/inputs/src/Menu.js index a7ea0214..ed798bbf 100644 --- a/packages/inputs/src/Menu.js +++ b/packages/inputs/src/Menu.js @@ -9,6 +9,10 @@ const isObject = v => { export const menu = options => input(Menu, options); export class Menu extends MosaicClient { + /** + * Create a new Menu instance. + * @param {object} options Options object + */ constructor({ element, filterBy, diff --git a/packages/inputs/src/Search.js b/packages/inputs/src/Search.js index 9f7cef57..cbdfa751 100644 --- a/packages/inputs/src/Search.js +++ b/packages/inputs/src/Search.js @@ -10,6 +10,10 @@ let _id = 0; export const search = options => input(Search, options); export class Search extends MosaicClient { + /** + * Create a new Search instance. + * @param {object} options Options object + */ constructor({ element, filterBy, diff --git a/packages/inputs/src/Slider.js b/packages/inputs/src/Slider.js index dcb15cf2..2b3d88b5 100644 --- a/packages/inputs/src/Slider.js +++ b/packages/inputs/src/Slider.js @@ -7,6 +7,10 @@ let _id = 0; export const slider = options => input(Slider, options); export class Slider extends MosaicClient { + /** + * Create a new Slider instance. + * @param {object} options Options object + */ constructor({ element, filterBy, @@ -80,7 +84,7 @@ export class Slider extends MosaicClient { const { min, max } = Array.from(data)[0]; if (this.min == null) this.slider.setAttribute('min', min); if (this.max == null) this.slider.setAttribute('max', max); - if (this.step == null) this.slider.setAttribute('step', (max - min) / 500); + if (this.step == null) this.slider.setAttribute('step', String((max - min) / 500)); return this; } diff --git a/packages/inputs/src/Table.js b/packages/inputs/src/Table.js index 8af4f9b1..e66b28be 100644 --- a/packages/inputs/src/Table.js +++ b/packages/inputs/src/Table.js @@ -8,6 +8,10 @@ let _id = -1; export const table = options => input(Table, options); export class Table extends MosaicClient { + /** + * Create a new Table instance. + * @param {object} options Options object + */ constructor({ element, filterBy, diff --git a/packages/inputs/src/util/format.js b/packages/inputs/src/util/format.js index b5737a84..6a54ea65 100644 --- a/packages/inputs/src/util/format.js +++ b/packages/inputs/src/util/format.js @@ -42,6 +42,9 @@ export function formatDate(date) { // Memoize the last-returned locale. export function localize(f) { - let key = localize, value; - return (locale = 'en') => locale === key ? value : (value = f(key = locale)); + let key = null; + let value; + return (locale = 'en') => locale === key + ? value + : (value = f(key = locale)); } diff --git a/packages/plot/src/interactors/Interval1D.js b/packages/plot/src/interactors/Interval1D.js index 07e45996..a3702dcb 100644 --- a/packages/plot/src/interactors/Interval1D.js +++ b/packages/plot/src/interactors/Interval1D.js @@ -11,7 +11,7 @@ export class Interval1D { constructor(mark, { channel, selection, - field, + field = undefined, pixelSize = 1, peers = true, brush: style diff --git a/packages/plot/src/interactors/Toggle.js b/packages/plot/src/interactors/Toggle.js index 4b6d30c4..a95d8546 100644 --- a/packages/plot/src/interactors/Toggle.js +++ b/packages/plot/src/interactors/Toggle.js @@ -2,8 +2,8 @@ import { and, or, isNotDistinct, literal } from '@uwdata/mosaic-sql'; export class Toggle { /** - * @param {import('@uwdata/mosaic-plot').Mark} mark The mark. - * @param {*} options The options. + * @param {*} mark The mark to interact with. + * @param {*} options The interactor options. */ constructor(mark, { selection, @@ -11,7 +11,6 @@ export class Toggle { peers = true }) { this.value = null; - /** @type {import('@uwdata/mosaic-plot').Mark} */ this.mark = mark; this.selection = selection; this.peers = peers; @@ -56,7 +55,7 @@ export class Toggle { init(svg, selector, accessor) { const { mark, channels, selection } = this; - const { data: { columns } = {} } = mark; + const { data: { columns = {} } = {} } = mark; accessor ??= target => channels.map(c => columns[c.as][target.__data__]); selector ??= `[data-index="${mark.index}"]`; const groups = new Set(svg.querySelectorAll(selector)); diff --git a/packages/plot/src/interactors/util/patchScreenCTM.js b/packages/plot/src/interactors/util/patchScreenCTM.js index e455bc0a..631f74dd 100644 --- a/packages/plot/src/interactors/util/patchScreenCTM.js +++ b/packages/plot/src/interactors/util/patchScreenCTM.js @@ -4,6 +4,8 @@ * even after the node is removed from the DOM. */ export function patchScreenCTM() { + /** @type {SVGGraphicsElement} */ + // @ts-ignore const node = this; const getScreenCTM = node.getScreenCTM; let memo; diff --git a/packages/plot/src/legend.js b/packages/plot/src/legend.js index 1584b402..e1fad8eb 100644 --- a/packages/plot/src/legend.js +++ b/packages/plot/src/legend.js @@ -15,10 +15,11 @@ export class Legend { this.handler = null; this.selection = as; this.field = field; + this.legend = null; this.element = document.createElement('div'); this.element.setAttribute('class', 'legend'); - this.element.value = this; + Object.assign(this.element, { value: this }); } setPlot(plot) { diff --git a/packages/plot/src/marks/ConnectedMark.js b/packages/plot/src/marks/ConnectedMark.js index d409bb73..44247688 100644 --- a/packages/plot/src/marks/ConnectedMark.js +++ b/packages/plot/src/marks/ConnectedMark.js @@ -11,6 +11,11 @@ export class ConnectedMark extends Mark { this.dim = dim; } + /** + * Return a query specifying the data needed by this Mark client. + * @param {*} [filter] The filtering criteria to apply in the query. + * @returns {*} The client query + */ query(filter = []) { const { plot, dim, source } = this; const { optimize = true } = source.options || {}; @@ -28,6 +33,7 @@ export class ConnectedMark extends Mark { const [lo, hi] = filteredExtent(filter, field) || [min, max]; const [expr] = binExpr(this, dim, size, [lo, hi], 1, as); const cols = q.select() + // @ts-ignore .map(c => c.as) .filter(c => c !== as && c !== value); return m4(q, expr, as, value, cols); diff --git a/packages/plot/src/marks/ContourMark.js b/packages/plot/src/marks/ContourMark.js index 9649bc79..07188d59 100644 --- a/packages/plot/src/marks/ContourMark.js +++ b/packages/plot/src/marks/ContourMark.js @@ -13,8 +13,11 @@ export class ContourMark extends Grid2DMark { pixelSize: 2, ...channels }); - handleParam(this, 'thresholds', thresholds, () => { - return this.grids ? this.contours().update() : null + + /** @type {number|number[]} */ + this.thresholds = handleParam(thresholds, value => { + this.thresholds = value; + return this.grids ? this.contours().update() : null; }); } @@ -26,10 +29,13 @@ export class ContourMark extends Grid2DMark { const { bins, densityMap, grids, thresholds, plot } = this; const { numRows, columns } = grids; - let tz = thresholds; - if (!Array.isArray(tz)) { + let t = thresholds; + let tz; + if (Array.isArray(t)) { + tz = t; + } else { const [, hi] = gridDomainContinuous(columns.density); - tz = Array.from({length: tz - 1}, (_, i) => (hi * (i + 1)) / tz); + tz = Array.from({length: t - 1}, (_, i) => (hi * (i + 1)) / t); } if (densityMap.fill || densityMap.stroke) { @@ -52,7 +58,7 @@ export class ContourMark extends Grid2DMark { const contour = contours().size(bins); // generate contours - const data = this.data = Array(numRows * tz.length); + const data = this.contourData = Array(numRows * tz.length); const { density, ...groupby } = columns; const groups = Object.entries(groupby); for (let i = 0, k = 0; i < numRows; ++i) { @@ -72,7 +78,7 @@ export class ContourMark extends Grid2DMark { } plotSpecs() { - const { type, channels, densityMap, data } = this; + const { type, channels, densityMap, contourData: data } = this; const options = {}; for (const c of channels) { const { channel } = c; diff --git a/packages/plot/src/marks/DenseLineMark.js b/packages/plot/src/marks/DenseLineMark.js index 383219ff..bf8cc798 100644 --- a/packages/plot/src/marks/DenseLineMark.js +++ b/packages/plot/src/marks/DenseLineMark.js @@ -8,14 +8,18 @@ export class DenseLineMark extends RasterMark { constructor(source, options) { const { normalize = true, ...rest } = options; super(source, rest); - handleParam(this, 'normalize', normalize); + + /** @type {boolean} */ + this.normalize = handleParam(normalize, value => { + return (this.normalize = value, this.requestUpdate()); + }); } query(filter = []) { - const { channels, normalize, source, binPad } = this; - const [nx, ny] = this.bins = this.binDimensions(this); - const [x] = binExpr(this, 'x', nx, extentX(this, filter), binPad); - const [y] = binExpr(this, 'y', ny, extentY(this, filter), binPad); + const { channels, normalize, source, pad } = this; + const [nx, ny] = this.bins = this.binDimensions(); + const [x] = binExpr(this, 'x', nx, extentX(this, filter), pad); + const [y] = binExpr(this, 'y', ny, extentY(this, filter), pad); const q = Query .from(source.table) diff --git a/packages/plot/src/marks/Density1DMark.js b/packages/plot/src/marks/Density1DMark.js index 5949c4c8..fe3a9a04 100644 --- a/packages/plot/src/marks/Density1DMark.js +++ b/packages/plot/src/marks/Density1DMark.js @@ -16,9 +16,15 @@ export class Density1DMark extends Mark { super(type, source, channels, dim === 'x' ? xext : yext); this.dim = dim; - handleParam(this, 'bins', bins); - handleParam(this, 'bandwidth', bandwidth, () => { - return this.grid ? this.convolve().update() : null + /** @type {number} */ + this.bins = handleParam(bins, value => { + return (this.bins = value, this.requestUpdate()); + }); + + /** @type {number} */ + this.bandwidth = handleParam(bandwidth, value => { + this.bandwidth = value; + return this.grid ? this.convolve().update() : null; }); } diff --git a/packages/plot/src/marks/Grid2DMark.js b/packages/plot/src/marks/Grid2DMark.js index 1d2d7d3d..7a61b19d 100644 --- a/packages/plot/src/marks/Grid2DMark.js +++ b/packages/plot/src/marks/Grid2DMark.js @@ -27,14 +27,36 @@ export class Grid2DMark extends Mark { super(type, source, channels, xyext); this.densityMap = densityMap; - handleParam(this, 'bandwidth', bandwidth, () => { + /** @type {number} */ + this.bandwidth = handleParam(bandwidth, value => { + this.bandwidth = value; return this.grids ? this.convolve().update() : null; }); - handleParam(this, 'pixelSize', pixelSize); - handleParam(this, 'interpolate', interpolate); - handleParam(this, 'pad', pad); - handleParam(this, 'width', width); - handleParam(this, 'height', height); + + /** @type {string} */ + this.interpolate = handleParam(interpolate, value => { + return (this.interpolate = value, this.requestUpdate()); + }); + + /** @type {number} */ + this.pixelSize = handleParam(pixelSize, value => { + return (this.pixelSize = value, this.requestUpdate()); + }); + + /** @type {number} */ + this.pad = handleParam(pad, value => { + return (this.pad = value, this.requestUpdate()); + }); + + /** @type {number|undefined} */ + this.width = handleParam(width, value => { + return (this.width = value, this.requestUpdate()); + }); + + /** @type {number|undefined} */ + this.height = handleParam(height, value => { + return (this.height = value, this.requestUpdate()); + }); } setPlot(plot, index) { @@ -54,7 +76,7 @@ export class Grid2DMark extends Mark { const { interpolate, pad, channels, densityMap, source } = this; const [x0, x1] = this.extentX = extentX(this, filter); const [y0, y1] = this.extentY = extentY(this, filter); - const [nx, ny] = this.bins = this.binDimensions(this); + const [nx, ny] = this.bins = this.binDimensions(); const [x, bx] = binExpr(this, 'x', nx, [x0, x1], pad); const [y, by] = binExpr(this, 'y', ny, [y0, y1], pad); @@ -160,6 +182,7 @@ export class Grid2DMark extends Mark { numRows: grids0.numRows, columns: { ...grids0.columns, + // @ts-ignore [prop]: g.map(grid => dericheConv2d(configX, configY, grid, bins)) } }; @@ -167,10 +190,6 @@ export class Grid2DMark extends Mark { return this; } - - plotSpecs() { - throw new Error('Unimplemented. Use a Grid2D mark subclass.'); - } } /** diff --git a/packages/plot/src/marks/HexbinMark.js b/packages/plot/src/marks/HexbinMark.js index 47e60cb1..d7c9e087 100644 --- a/packages/plot/src/marks/HexbinMark.js +++ b/packages/plot/src/marks/HexbinMark.js @@ -2,12 +2,17 @@ import { Query, isNotNull, sql } from '@uwdata/mosaic-sql'; import { Transient } from '../symbols.js'; import { extentX, extentY, xyext } from './util/extent.js'; import { Mark } from './Mark.js'; +import { handleParam } from './util/handle-param.js'; export class HexbinMark extends Mark { constructor(source, options) { const { type = 'hexagon', binWidth = 20, ...channels } = options; super(type, source, { r: binWidth / 2, clip: true, ...channels }, xyext); - this.binWidth = binWidth; + + /** @type {number} */ + this.binWidth = handleParam(binWidth, value => { + return (this.binWidth = value, this.requestUpdate()); + }); } get filterIndexable() { @@ -39,9 +44,10 @@ export class HexbinMark extends Mark { let x, y; const aggr = new Set; const cols = {}; + let orderby; for (const c of channels) { if (c.channel === 'orderby') { - q.orderby(c.value); // TODO revisit once groupby is added + orderby = c.value; // TODO revisit once groupby is added } else if (c.channel === 'x') { x = c; } else if (c.channel === 'y') { @@ -63,6 +69,8 @@ export class HexbinMark extends Mark { ...cols }).groupby('x', 'y'); + if (orderby) q.orderby(orderby); + // Map x/y channels to screen space const xx = `${xr} * (${x.field} - ${x1}::DOUBLE)`; const yy = `${yr} * (${y2}::DOUBLE - ${y.field})`; diff --git a/packages/plot/src/marks/Mark.js b/packages/plot/src/marks/Mark.js index 91381459..ad947a0b 100644 --- a/packages/plot/src/marks/Mark.js +++ b/packages/plot/src/marks/Mark.js @@ -64,12 +64,15 @@ export class Mark extends MosaicClient { } } else if (isParamLike(entry)) { if (Array.isArray(entry.columns)) { + // we currently duck-type to having a columns array + // as a check that this is SQLExpression-compatible channels.push(fieldEntry(channel, entry)); params.add(entry); } else { const c = valueEntry(channel, entry.value); channels.push(c); entry.addEventListener('value', value => { + // update immediately, the value is simply passed to Plot c.value = value; return this.update(); }); @@ -105,7 +108,7 @@ export class Mark extends MosaicClient { return this.channels.find(c => c.channel === channel); } - channelField(channel, { exact } = {}) { + channelField(channel, { exact = false } = {}) { const c = exact ? this.channel(channel) : this.channels.find(c => c.channel.startsWith(channel)); @@ -141,6 +144,11 @@ export class Mark extends MosaicClient { return this; } + /** + * Return a query specifying the data needed by this Mark client. + * @param {*} [filter] The filtering criteria to apply in the query. + * @returns {*} The client query + */ query(filter = []) { if (this.hasOwnData()) return null; const { channels, source: { table } } = this; @@ -154,8 +162,6 @@ export class Mark extends MosaicClient { /** * Provide query result data to the mark. - * @param {import('apache-arrow').Table} data The backing mark data. - * @returns {this} */ queryResult(data) { this.data = toDataColumns(data); @@ -166,8 +172,13 @@ export class Mark extends MosaicClient { return this.plot.update(this); } + /** + * Generate an array of Plot mark specifications. + * @returns {object[]} + */ plotSpecs() { const { type, detail, channels } = this; + // @ts-ignore const { numRows: length, values, columns } = this.data || {}; // populate plot specification options @@ -213,7 +224,7 @@ export function channelOption(c, columns) { * @param {*} table the table to query. * @param {*} skip an optional array of channels to skip. * Mark subclasses can skip channels that require special handling. - * @returns a Query instance + * @returns {Query} a Query instance */ export function markQuery(channels, table, skip = []) { const q = Query.from({ source: table }); diff --git a/packages/plot/src/marks/RasterMark.js b/packages/plot/src/marks/RasterMark.js index b9f3fb35..16b10bfa 100644 --- a/packages/plot/src/marks/RasterMark.js +++ b/packages/plot/src/marks/RasterMark.js @@ -20,6 +20,7 @@ import { Fixed, Transient } from '../symbols.js'; export class RasterMark extends Grid2DMark { constructor(source, options) { super('image', source, options); + this.image = null; } setPlot(plot, index) { diff --git a/packages/plot/src/marks/RasterTileMark.js b/packages/plot/src/marks/RasterTileMark.js index fe252019..16c36006 100644 --- a/packages/plot/src/marks/RasterTileMark.js +++ b/packages/plot/src/marks/RasterTileMark.js @@ -10,6 +10,7 @@ export class RasterTileMark extends Grid2DMark { constructor(source, options) { const { origin = [0, 0], dim = 'xy', ...markOptions } = options; super('image', source, markOptions); + this.image = null; // TODO: make part of data source instead of options? this.origin = origin; @@ -34,15 +35,15 @@ export class RasterTileMark extends Grid2DMark { } tileQuery(extent) { - const { binType, binPad, channels, densityMap, source } = this; + const { interpolate, pad, channels, densityMap, source } = this; const [[x0, x1], [y0, y1]] = extent; const [nx, ny] = this.bins; - const [x, bx] = binExpr(this, 'x', nx, [x0, x1], binPad); - const [y, by] = binExpr(this, 'y', ny, [y0, y1], binPad); + const [x, bx] = binExpr(this, 'x', nx, [x0, x1], pad); + const [y, by] = binExpr(this, 'y', ny, [y0, y1], pad); // with padded bins, include the entire domain extent // if the bins are flush, exclude the extent max - const bounds = binPad + const bounds = pad ? [isBetween(bx, [+x0, +x1]), isBetween(by, [+y0, +y1])] : [lte(+x0, bx), lt(bx, +x1), lte(+y0, by), lt(by, +y1)]; @@ -83,7 +84,7 @@ export class RasterTileMark extends Grid2DMark { } // generate grid binning query - if (binType === 'linear') { + if (interpolate === 'linear') { if (aggr.length > 1) { throw new Error('Linear binning not applicable to multiple aggregates.'); } @@ -102,14 +103,14 @@ export class RasterTileMark extends Grid2DMark { if (this.prefetch) mc.cancel(this.prefetch); // get view extent info - const { binPad, tileX, tileY, origin: [tx, ty] } = this; - const [m, n] = this.bins = this.binDimensions(this); + const { pad, tileX, tileY, origin: [tx, ty] } = this; + const [m, n] = this.bins = this.binDimensions(); const [x0, x1] = extentX(this, this._filter); const [y0, y1] = extentY(this, this._filter); const xspan = x1 - x0; const yspan = y1 - y0; - const xx = Math.floor((x0 - tx) * (m - binPad) / xspan); - const yy = Math.floor((y0 - ty) * (n - binPad) / yspan); + const xx = Math.floor((x0 - tx) * (m - pad) / xspan); + const yy = Math.floor((y0 - ty) * (n - pad) / yspan); const tileExtent = (i, j) => [ [tx + i * xspan, tx + (i + 1) * xspan], @@ -155,7 +156,11 @@ export class RasterTileMark extends Grid2DMark { // wait for tile queries to complete, then update const tiles = await Promise.all(queries); - this.grids = [{ density: processTiles(m, n, xx, yy, coords, tiles) }]; + const density = processTiles(m, n, xx, yy, coords, tiles); + this.grids0 = { + numRows: density.length, + columns: { density: [density] } + }; this.convolve().update(); } diff --git a/packages/plot/src/marks/RegressionMark.js b/packages/plot/src/marks/RegressionMark.js index 84bf91c1..e8bbdc3b 100644 --- a/packages/plot/src/marks/RegressionMark.js +++ b/packages/plot/src/marks/RegressionMark.js @@ -13,11 +13,18 @@ export class RegressionMark extends Mark { constructor(source, options) { const { ci = 0.95, precision = 4, ...channels } = options; super('line', source, channels); - const update = () => { - return this.modelFit ? this.confidenceBand().update() : null - }; - handleParam(this, 'ci', ci, update); - handleParam(this, 'precision', precision, update); + + const update = () => this.modelFit ? this.confidenceBand().update() : null; + + /** @type {number} */ + this.ci = handleParam(ci, value => { + return (this.ci = value, update()); + }); + + /** @type {number} */ + this.precision = handleParam(precision, value => { + return (this.precision = value, update()); + }); } query(filter = []) { diff --git a/packages/plot/src/marks/util/grid.js b/packages/plot/src/marks/util/grid.js index 9d623f8d..433994bc 100644 --- a/packages/plot/src/marks/util/grid.js +++ b/packages/plot/src/marks/util/grid.js @@ -1,22 +1,30 @@ import { InternSet, ascending } from 'd3'; +/** + * @typedef {Array | Int8Array | Uint8Array | Uint8ClampedArray + * | Int16Array | Uint16Array | Int32Array | Uint32Array + * | Float32Array | Float64Array + * } Arrayish - an Array or TypedArray + */ + /** * Generate a new array with designated size and type. * @param {number} size The size of the array - * @param {ArrayLike} [proto] A prototype object of the desired array type. + * @param {Arrayish} [proto] A prototype object of the desired array type. * This may be a typed array or standard array (the default). - * @returns {ArrayLike} The generated array. + * @returns {Arrayish} The generated array. */ export function array(size, proto = []) { + // @ts-ignore return new proto.constructor(size); } /** * Create a 1D grid for the given sample values * @param {number} size The grid size. - * @param {ArrayLike} index The grid indices for sample points. - * @param {ArrayLike} value The sample point values. - * @returns {ArrayLike} The generated value grid. + * @param {Arrayish} index The grid indices for sample points. + * @param {Arrayish} value The sample point values. + * @returns {Arrayish} The generated value grid. */ export function grid1d(size, index, value) { const G = array(size, value); @@ -32,14 +40,17 @@ export function grid1d(size, index, value) { * Can handle multiple grids and groupby values per output row. * @param {number} w The grid width. * @param {number} h The grid height. - * @param {ArrayLike} index The grid indices for sample points. + * @param {Arrayish} index The grid indices for sample points. * An index value is an integer of the form (y * w + x). - * @param {ArrayLike} columns Named column arrays with sample point values. + * @param {Record} columns Named column arrays with sample point values. * @param {string[]} aggregates The names of aggregate columns to grid. * @param {string[]} groupby The names of additional columns to group by. * @param {function} [interpolate] A grid interpolation function. * By default sample values are directly copied to output grid arrays. - * @returns {object} Named column arrays of generated grid values. + * @returns {{ + * numRows: number; + * columns: { [key:string]: Arrayish } + * }} Named column arrays of generated grid values. */ export function grid2d(w, h, index, columns, aggregates, groupby, interpolate) { const numRows = index.length; @@ -87,6 +98,7 @@ export function grid2d(w, h, index, columns, aggregates, groupby, interpolate) { }); } + // @ts-ignore return { numRows: cells.length, columns: result }; } diff --git a/packages/plot/src/marks/util/handle-param.js b/packages/plot/src/marks/util/handle-param.js index e00bb285..bf6ca91d 100644 --- a/packages/plot/src/marks/util/handle-param.js +++ b/packages/plot/src/marks/util/handle-param.js @@ -1,14 +1,13 @@ import { isParam } from '@uwdata/mosaic-core'; -export function handleParam(client, key, param, update) { - if (isParam(param)) { - update = update || (() => client.requestUpdate()); - param.addEventListener('value', value => { - client[key] = value; - return update(); - }); - client[key] = param.value; - } else { - client[key] = param; - } +/** + * Utility to check if a value is a Param, and if so, bind a listener. + * @param {*} value A potentially Param-typed value. + * @param {(value: *) => Promise|void} update Update callback + * @returns the input value or (if a Param) the current Param value. + */ +export function handleParam(value, update) { + return isParam(value) + ? (value.addEventListener('value', update), value.value) + : value; } diff --git a/packages/plot/src/marks/util/stats.js b/packages/plot/src/marks/util/stats.js index e4fee82f..779a5eab 100644 --- a/packages/plot/src/marks/util/stats.js +++ b/packages/plot/src/marks/util/stats.js @@ -20,6 +20,13 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +/** + * ibetainv function + * @param {number} p + * @param {number} a + * @param {number} b + * @returns {number} + */ export function ibetainv(p, a, b) { var EPS = 1e-8; var a1 = a - 1; @@ -60,11 +67,18 @@ export function ibetainv(p, a, b) { return x; } +/** + * ibeta function + * @param {number} x + * @param {number} a + * @param {number} b + * @returns {number} + */ export function ibeta(x, a, b) { // Factors in front of the continued fraction. var bt = x === 0 || x === 1 ? 0 : Math.exp(gammaln(a + b) - gammaln(a) - gammaln(b) + a * Math.log(x) + b * Math.log(1 - x)); - if (x < 0 || x > 1) return false; + if (x < 0 || x > 1) return 0; if (x < (a + 1) / (a + b + 2)) // Use continued fraction directly. return (bt * betacf(x, a, b)) / a; @@ -72,6 +86,13 @@ export function ibeta(x, a, b) { return 1 - (bt * betacf(1 - x, b, a)) / b; } +/** + * betacf function + * @param {number} x + * @param {number} a + * @param {number} b + * @returns {number} + */ export function betacf(x, a, b) { var fpmin = 1e-30; var m = 1; @@ -112,6 +133,11 @@ export function betacf(x, a, b) { return h; } +/** + * gammaln function + * @param {number} x + * @returns {number} + */ export function gammaln(x) { var j = 0; var cof = [ @@ -126,6 +152,12 @@ export function gammaln(x) { return Math.log((2.506628274631 * ser) / xx) - tmp; } +/** + * qt function + * @param {number} p + * @param {number} dof + * @returns {number} + */ export function qt(p, dof) { var x = ibetainv(2 * Math.min(p, 1 - p), 0.5 * dof, 0.5); x = Math.sqrt((dof * (1 - x)) / x); diff --git a/packages/plot/src/marks/util/to-data-columns.js b/packages/plot/src/marks/util/to-data-columns.js index 050ad5b2..3758e07c 100644 --- a/packages/plot/src/marks/util/to-data-columns.js +++ b/packages/plot/src/marks/util/to-data-columns.js @@ -1,9 +1,23 @@ import { convertArrowColumn, isArrowTable } from '@uwdata/mosaic-core'; +/** + * @typedef {Array | Int8Array | Uint8Array | Uint8ClampedArray + * | Int16Array | Uint16Array | Int32Array | Uint32Array + * | Float32Array | Float64Array + * } Arrayish - an Array or TypedArray + */ + +/** + * @typedef { + * | { numRows: number, columns: Record } + * | { numRows: number, values: Arrayish; } + * } DataColumns +*/ + /** * Convert input data to a set of column arrays. * @param {any} data The input data. - * @returns An object of named column arrays. + * @returns {DataColumns} An object with named column arrays. */ export function toDataColumns(data) { return isArrowTable(data) @@ -14,7 +28,7 @@ export function toDataColumns(data) { /** * Convert an Arrow table to a set of column arrays. * @param {import('apache-arrow').Table} data An Apache Arrow Table. - * @returns An object of named column arrays. + * @returns {DataColumns} An object with named column arrays. */ function arrowToColumns(data) { const { numRows, numCols, schema: { fields } } = data; @@ -38,7 +52,7 @@ function arrowToColumns(data) { * We use the keys of the first object as the column names. * Otherwise, use a special "values" array. * @param {object[]} data An array of data objects. - * @returns An object of named column arrays. + * @returns {DataColumns} An object with named column arrays. */ function arrayToColumns(data) { const numRows = data.length; diff --git a/packages/sql/src/Query.js b/packages/sql/src/Query.js index d14e4441..8cd1bf41 100644 --- a/packages/sql/src/Query.js +++ b/packages/sql/src/Query.js @@ -53,6 +53,7 @@ export class Query { qualify: [], orderby: [] }; + this.cteFor = null; } clone() { @@ -61,9 +62,18 @@ export class Query { return q; } + /** + * Retrieve current WITH common table expressions (CTEs). + * @returns {any[]} + *//** + * Add WITH common table expressions (CTEs). + * @param {...any} expr Expressions to add. + * @returns {this} + */ with(...expr) { const { query } = this; if (expr.length === 0) { + // @ts-ignore return query.with; } else { const list = []; @@ -88,9 +98,18 @@ export class Query { } } + /** + * Retrieve current SELECT expressions. + * @returns {any[]} + *//** + * Add SELECT expressions. + * @param {...any} expr Expressions to add. + * @returns {this} + */ select(...expr) { const { query } = this; if (expr.length === 0) { + // @ts-ignore return query.select; } else { const list = []; @@ -124,9 +143,18 @@ export class Query { return this; } + /** + * Retrieve current from expressions. + * @returns {any[]} + *//** + * Provide table from expressions. + * @param {...any} expr + * @returns {this} + */ from(...expr) { const { query } = this; if (expr.length === 0) { + // @ts-ignore return query.from; } else { const list = []; @@ -157,9 +185,19 @@ export class Query { return this.from(...expr); } + /** + * Retrieve current SAMPLE settings. + * @returns {any[]} + *//** + * Set SAMPLE settings. + * @param {number|object} value The percentage or number of rows to sample. + * @param {string} [method] The sampling method to use. + * @returns {this} + */ sample(value, method) { const { query } = this; if (arguments.length === 0) { + // @ts-ignore return query.sample; } else { let spec = value; @@ -173,9 +211,18 @@ export class Query { } } + /** + * Retrieve current WHERE expressions. + * @returns {any[]} + *//** + * Add WHERE expressions. + * @param {...any} expr Expressions to add. + * @returns {this} + */ where(...expr) { const { query } = this; if (expr.length === 0) { + // @ts-ignore return query.where; } else { query.where = query.where.concat( @@ -190,9 +237,18 @@ export class Query { return this.where(...expr); } + /** + * Retrieve current GROUP BY expressions. + * @returns {any[]} + *//** + * Add GROUP BY expressions. + * @param {...any} expr Expressions to add. + * @returns {this} + */ groupby(...expr) { const { query } = this; if (expr.length === 0) { + // @ts-ignore return query.groupby; } else { query.groupby = query.groupby.concat( @@ -207,9 +263,18 @@ export class Query { return this.groupby(...expr); } + /** + * Retrieve current HAVING expressions. + * @returns {any[]} + *//** + * Add HAVING expressions. + * @param {...any} expr Expressions to add. + * @returns {this} + */ having(...expr) { const { query } = this; if (expr.length === 0) { + // @ts-ignore return query.having; } else { query.having = query.having.concat( @@ -219,9 +284,18 @@ export class Query { } } + /** + * Retrieve current WINDOW definitions. + * @returns {any[]} + *//** + * Add WINDOW definitions. + * @param {...any} expr Expressions to add. + * @returns {this} + */ window(...expr) { const { query } = this; if (expr.length === 0) { + // @ts-ignore return query.window; } else { const list = []; @@ -239,9 +313,18 @@ export class Query { } } + /** + * Retrieve current QUALIFY expressions. + * @returns {any[]} + *//** + * Add QUALIFY expressions. + * @param {...any} expr Expressions to add. + * @returns {this} + */ qualify(...expr) { const { query } = this; if (expr.length === 0) { + // @ts-ignore return query.qualify; } else { query.qualify = query.qualify.concat( @@ -251,9 +334,18 @@ export class Query { } } + /** + * Retrieve current ORDER BY expressions. + * @returns {any[]} + *//** + * Add ORDER BY expressions. + * @param {...any} expr Expressions to add. + * @returns {this} + */ orderby(...expr) { const { query } = this; if (expr.length === 0) { + // @ts-ignore return query.orderby; } else { query.orderby = query.orderby.concat( @@ -263,6 +355,14 @@ export class Query { } } + /** + * Retrieve current LIMIT value. + * @returns {number|null} + *//** + * Set the query result LIMIT. + * @param {number} value The limit value. + * @returns {this} + */ limit(value) { const { query } = this; if (arguments.length === 0) { @@ -273,6 +373,14 @@ export class Query { } } + /** + * Retrieve current OFFSET value. + * @returns {number|null} + *//** + * Set the query result OFFSET. + * @param {number} value The offset value. + * @returns {this} + */ offset(value) { const { query } = this; if (arguments.length === 0) { @@ -391,6 +499,7 @@ export class SetOperation { this.op = op; this.queries = queries.map(q => q.clone()); this.query = { orderby: [] }; + this.cteFor = null; } clone() { diff --git a/packages/sql/src/aggregates.js b/packages/sql/src/aggregates.js index 30574889..e6637a2d 100644 --- a/packages/sql/src/aggregates.js +++ b/packages/sql/src/aggregates.js @@ -19,11 +19,24 @@ export function agg(strings, ...exprs) { * rather than instantiate this class. */ export class AggregateFunction extends SQLExpression { + /** + * Create a new AggregateFunction instance. + * @param {*} op The aggregate operation. + * @param {*} [args] The aggregate function arguments. + * @param {*} [type] The SQL data type to cast to. + * @param {boolean} [isDistinct] Flag indicating if this is a distinct value aggregate. + * @param {*} [filter] Filtering expression to apply prior to aggregation. + */ constructor(op, args, type, isDistinct, filter) { args = (args || []).map(asColumn); const { strings, exprs } = aggExpr(op, args, type, isDistinct, filter); const { spans, cols } = parseSQL(strings, exprs); - super(spans, cols, { aggregate: op, args, type, isDistinct, filter }); + super(spans, cols); + this.aggregate = op; + this.args = args; + this.type = type; + this.isDistinct = isDistinct; + this.filter = filter; } get basis() { @@ -37,36 +50,69 @@ export class AggregateFunction extends SQLExpression { return `${op.toLowerCase()}${tail}`; } + /** + * Return a new derived aggregate function over distinct values. + * @returns {AggregateFunction} A new aggregate function. + */ distinct() { const { aggregate: op, args, type, filter } = this; return new AggregateFunction(op, args, type, true, filter); } + /** + * Return a new derived aggregate function that filters values. + * @param {*} filter The filter expresion. + * @returns {AggregateFunction} A new aggregate function. + */ where(filter) { const { aggregate: op, args, type, isDistinct } = this; return new AggregateFunction(op, args, type, isDistinct, filter); } + /** + * Return a new window function over this aggregate. + * @returns {WindowFunction} A new aggregate function. + */ window() { const { aggregate: op, args, type, isDistinct } = this; const func = new AggregateFunction(op, args, null, isDistinct); return new WindowFunction(op, func, type); } + /** + * Return a window function over this aggregate with the given partitioning. + * @param {*} expr The grouping (partition by) criteria for the window function. + * @returns {WindowFunction} A new window function. + */ partitionby(...expr) { return this.window().partitionby(...expr); } + /** + * Return a window function over this aggregate with the given ordering. + * @param {*} expr The sorting (order by) criteria for the window function. + * @returns {WindowFunction} A new window function. + */ orderby(...expr) { return this.window().orderby(...expr); } - rows(prev, next) { - return this.window().rows(prev, next); + /** + * Return a window function over this aggregate with the given row frame. + * @param {(number|null)[] | import('./expression.js').ParamLike} frame The row-based window frame. + * @returns {WindowFunction} A new window function. + */ + rows(frame) { + return this.window().rows(frame); } - range(prev, next) { - return this.window().range(prev, next); + /** + * Return a window function over this aggregate with the given range frame. + * @param {(number|null)[] | import('./expression.js').ParamLike} frame The range-based window frame. + * @returns {WindowFunction} A new window function. + */ + range(frame) { + return this.window().range(frame); } } diff --git a/packages/sql/src/desc.js b/packages/sql/src/desc.js index df1f0861..2723988d 100644 --- a/packages/sql/src/desc.js +++ b/packages/sql/src/desc.js @@ -4,8 +4,8 @@ import { asColumn } from './ref.js'; /** * Annotate an expression to indicate descending sort order. * Null values are ordered last. - * @param {SQLExpression|string} expr A SQL expression or column name string. - * @returns {SQLExpression} An expression with descending order. + * @param {import('./expression.js').SQLExpression|string} expr A SQL expression or column name string. + * @returns {import('./expression.js').SQLExpression} An expression with descending order. */ export function desc(expr) { const e = asColumn(expr); diff --git a/packages/sql/src/expression.js b/packages/sql/src/expression.js index 585e0b85..9c4b6f6a 100644 --- a/packages/sql/src/expression.js +++ b/packages/sql/src/expression.js @@ -1,16 +1,25 @@ import { literalToSQL } from './to-sql.js'; +/** + * @typedef {{ + * value: any; + * addEventListener(type: string, callback: Function): any; + * column?: string, + * columns?: string[] + * }} ParamLike + */ + /** * Test if a value is parameter-like. Parameters have addEventListener methods. * @param {*} value The value to test. - * @returns True if the value is param-like, false otherwise. + * @returns {value is ParamLike} True if the value is param-like, false otherwise. */ export const isParamLike = value => typeof value?.addEventListener === 'function'; /** * Test if a value is a SQL expression instance. * @param {*} value The value to test. - * @returns {boolean} True if value is a SQL expression, false otherwise. + * @returns {value is SQLExpression} True if value is a SQL expression, false otherwise. */ export function isSQLExpression(value) { return value instanceof SQLExpression; @@ -24,7 +33,7 @@ export class SQLExpression { /** * Create a new SQL expression instance. - * @param {(string|SQLExpression|Ref)[]} parts The parts of the expression. + * @param {(string | ParamLike | SQLExpression | import('./ref.js').Ref)[]} parts The parts of the expression. * @param {string[]} [columns=[]] The column dependencies * @param {object} [props] Additional properties for this expression. */ @@ -35,6 +44,8 @@ export class SQLExpression { const params = this._expr.filter(part => isParamLike(part)); if (params.length > 0) { + /** @type {ParamLike[]} */ + // @ts-ignore this._params = Array.from(new Set(params)); this._params.forEach(param => { param.addEventListener('value', () => update(this, this.map?.get('value'))); diff --git a/packages/sql/src/load/load.js b/packages/sql/src/load/load.js index b2cc9ee7..2132091c 100644 --- a/packages/sql/src/load/load.js +++ b/packages/sql/src/load/load.js @@ -38,7 +38,7 @@ export function loadSpatial(tableName, fileName, options = {}) { : Object.entries(opt) .map(([key, value]) => `${key}=${value}`) .join(', '); - rest.open_options = open.toUpperCase(); + Object.assign(rest, { open_options: open.toUpperCase() }); } // TODO: handle box_2d for spatial_filter_box option // TODO: handle wkb_blob for spatial_filter option diff --git a/packages/sql/src/load/sql-from.js b/packages/sql/src/load/sql-from.js index a0e7ff31..8feecf00 100644 --- a/packages/sql/src/load/sql-from.js +++ b/packages/sql/src/load/sql-from.js @@ -1,5 +1,12 @@ import { literalToSQL } from '../to-sql.js'; +/** + * Create a SQL query that embeds the given data for loading. + * @param {*} data The dataset + * @param {object} [options] Loading options + * @param {string[]|object} [options.columns] The columns to include + * @returns {string} SQL query string to load data + */ export function sqlFrom(data, { columns = Object.keys(data?.[0] || {}) } = {}) { diff --git a/packages/sql/src/ref.js b/packages/sql/src/ref.js index 95b788f6..616e4cf2 100644 --- a/packages/sql/src/ref.js +++ b/packages/sql/src/ref.js @@ -5,7 +5,7 @@ export class Ref { /** * Create a new Ref instance. * @param {string|Ref|null} table The table name. - * @param {string|null} column The column name. + * @param {string|null} [column] The column name. */ constructor(table, column) { if (table) this.table = String(table); @@ -87,11 +87,11 @@ export function relation(name) { /** * Create a column reference. - * @param {string} [table] The table name (optional). - * @param {string} column The column name. + * @param {string} table The table name (optional). + * @param {string} [column] The column name. * @returns {Ref} The generated column reference. */ -export function column(table, column) { +export function column(table, column = null) { if (arguments.length === 1) { column = table; table = null; diff --git a/packages/sql/src/scales.js b/packages/sql/src/scales.js index 95175c74..42976792 100644 --- a/packages/sql/src/scales.js +++ b/packages/sql/src/scales.js @@ -13,7 +13,7 @@ function scaleLinear() { }; } -function scaleLog({ base } = {}) { +function scaleLog({ base = null } = {}) { if (base == null || base === Math.E) { return { apply: Math.log, diff --git a/packages/sql/src/windows.js b/packages/sql/src/windows.js index 0143eb9b..c1a0ed07 100644 --- a/packages/sql/src/windows.js +++ b/packages/sql/src/windows.js @@ -9,6 +9,16 @@ import { repeat } from './repeat.js'; * rather than instantiate this class. */ export class WindowFunction extends SQLExpression { + /** + * Create a new WindowFunction instance. + * @param {string} op The window operation indicator. + * @param {*} func The window function expression. + * @param {*} [type] The SQL data type to cast to. + * @param {string} [name] The window definition name. + * @param {*} [group] Grouping (partition by) expressions. + * @param {*} [order] Sorting (order by) expressions. + * @param {*} [frame] The window frame definition. + */ constructor(op, func, type, name, group = '', order = '', frame = '') { // build and parse expression let expr; @@ -24,7 +34,14 @@ export class WindowFunction extends SQLExpression { expr = sql`(${expr})::${type}`; } const { _expr, _deps } = expr; - super(_expr, _deps, { window: op, func, type, name, group, order, frame }); + super(_expr, _deps); + this.window = op; + this.func = func; + this.type = type; + this.name = name; + this.group = group; + this.order = order; + this.frame = frame; } get basis() { @@ -36,11 +53,21 @@ export class WindowFunction extends SQLExpression { return func.label ?? func.toString(); } + /** + * Return an updated window function over a named window definition. + * @param {string} name The window definition name. + * @returns {WindowFunction} A new window function. + */ over(name) { const { window: op, func, type, group, order, frame } = this; return new WindowFunction(op, func, type, name, group, order, frame); } + /** + * Return an updated window function with the given partitioning. + * @param {*} expr The grouping (partition by) criteria for the window function. + * @returns {WindowFunction} A new window function. + */ partitionby(...expr) { const exprs = expr.flat().filter(x => x).map(asColumn); const group = sql( @@ -51,6 +78,11 @@ export class WindowFunction extends SQLExpression { return new WindowFunction(op, func, type, name, group, order, frame); } + /** + * Return an updated window function with the given ordering. + * @param {*} expr The sorting (order by) criteria for the window function. + * @returns {WindowFunction} A new window function. + */ orderby(...expr) { const exprs = expr.flat().filter(x => x).map(asColumn); const order = sql( @@ -61,12 +93,22 @@ export class WindowFunction extends SQLExpression { return new WindowFunction(op, func, type, name, group, order, frame); } + /** + * Return an updated window function with the given rows frame. + * @param {(number|null)[] | import('./expression.js').ParamLike} expr The row-based window frame. + * @returns {WindowFunction} A new window function. + */ rows(expr) { const frame = windowFrame('ROWS', expr); const { window: op, func, type, name, group, order } = this; return new WindowFunction(op, func, type, name, group, order, frame); } + /** + * Return an updated window function with the given range frame. + * @param {(number|null)[] | import('./expression.js').ParamLike} expr The range-based window frame. + * @returns {WindowFunction} A new window function. + */ range(expr) { const frame = windowFrame('RANGE', expr); const { window: op, func, type, name, group, order } = this; diff --git a/packages/vgplot/src/layout/concat.js b/packages/vgplot/src/layout/concat.js index b7bc4634..2f0db197 100644 --- a/packages/vgplot/src/layout/concat.js +++ b/packages/vgplot/src/layout/concat.js @@ -6,7 +6,7 @@ export function concat({ direction = 'vertical', wrap = false }, children) { div.style.justifyContent = 'flex-start'; div.style.alignItems = 'flex-start'; children.forEach(child => div.appendChild(child)); - div.value = { element: div }; + Object.assign(div, { value: { element: div } }); return div; } diff --git a/packages/vgplot/src/layout/space.js b/packages/vgplot/src/layout/space.js index 29b67602..392735ff 100644 --- a/packages/vgplot/src/layout/space.js +++ b/packages/vgplot/src/layout/space.js @@ -2,9 +2,7 @@ export function space({ dim = 'width', size = 10 }) { const span = document.createElement('span'); span.style.display = 'inline-block'; span.style[dim] = Number.isNaN(+size) ? size : `${size}px`; - const obj = { element: span }; - span.value = obj; - return span; + return Object.assign(span, { value: { element: span } }); } export function vspace(size) { diff --git a/packages/widget/src/index.js b/packages/widget/src/index.js index dcfc93d2..f1c949e7 100644 --- a/packages/widget/src/index.js +++ b/packages/widget/src/index.js @@ -1,6 +1,7 @@ // @ts-check import { coordinator, namedPlots } from '@uwdata/vgplot'; +import { isSelection } from '@uwdata/mosaic-core'; import { parseSpec, astToDOM } from '@uwdata/mosaic-spec'; import * as arrow from 'apache-arrow'; import './style.css'; @@ -74,11 +75,14 @@ export default { for (const [name, param] of dom.params) { params[name] = { value: param.value, - ...(param.predicate ? { predicate: String(param.predicate()) } : {}), + ...(isSelection(param) ? { predicate: String(param.predicate()) } : {}), }; param.addEventListener('value', (value) => { - params[name] = { value, predicate: String(param.predicate()) }; + params[name] = { + value, + ...(isSelection(param) ? { predicate: String(param.predicate()) } : {}), + }; view.model.set('params', params); view.model.save_changes(); }); @@ -142,7 +146,6 @@ export default { }, }; -/** @param {Record} spec */ function instantiateSpec(spec) { const ast = parseSpec(spec); return astToDOM(ast); diff --git a/packages/widget/tsconfig.json b/packages/widget/tsconfig.json index 9958b4dc..3896179a 100644 --- a/packages/widget/tsconfig.json +++ b/packages/widget/tsconfig.json @@ -1,9 +1,14 @@ { - "compilerOptions": { - "module": "nodenext", - "checkJs": true, - "noEmit": true, - "skipLibCheck": true - }, - "include": ["src/**/*"], + "compilerOptions": { + "checkJs": true, + "noEmit": true, + "noImplicitAny": false, + "module": "Node16", + "baseUrl": "./src", + "skipLibCheck": true, + "paths": { + "@uwdata/mosaic-widget": ["index"] + } + }, + "include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.ts"] } From ce98e355cf85c46b15ec709eb255f2cafce78d22 Mon Sep 17 00:00:00 2001 From: jheer Date: Mon, 1 Apr 2024 12:34:39 -0700 Subject: [PATCH 02/25] feat: Add initial TS types for JSON specs. --- packages/spec/package.json | 3 +- packages/spec/src/ast-to-dom.d.ts | 29 +++++++ packages/spec/src/ast-to-esm.d.ts | 37 +++++++++ packages/spec/src/ast/DataNode.d.ts | 82 +++++++++++++++++++ packages/spec/src/ast/HConcatNode.d.ts | 5 ++ packages/spec/src/ast/HSpaceNode.d.ts | 3 + packages/spec/src/ast/InputNode.d.ts | 5 ++ packages/spec/src/ast/ParamNode.d.ts | 21 +++++ packages/spec/src/ast/ParamRefNode.d.ts | 1 + packages/spec/src/ast/PlotAttributeNode.d.ts | 3 + packages/spec/src/ast/PlotFromNode.d.ts | 14 ++++ packages/spec/src/ast/PlotInteractorNode.d.ts | 6 ++ packages/spec/src/ast/PlotLegendNode.d.ts | 16 ++++ packages/spec/src/ast/PlotMarkNode.d.ts | 71 ++++++++++++++++ packages/spec/src/ast/PlotNode.d.ts | 13 +++ packages/spec/src/ast/SelectionNode.d.ts | 10 +++ packages/spec/src/ast/SpecNode.d.ts | 55 +++++++++++++ packages/spec/src/ast/VConcatNode.d.ts | 5 ++ packages/spec/src/ast/VSpaceNode.d.ts | 3 + packages/spec/src/index.d.ts | 4 + packages/spec/src/parse-spec.d.ts | 18 ++++ packages/spec/tsconfig.json | 14 ++++ 22 files changed, 417 insertions(+), 1 deletion(-) create mode 100644 packages/spec/src/ast-to-dom.d.ts create mode 100644 packages/spec/src/ast-to-esm.d.ts create mode 100644 packages/spec/src/ast/DataNode.d.ts create mode 100644 packages/spec/src/ast/HConcatNode.d.ts create mode 100644 packages/spec/src/ast/HSpaceNode.d.ts create mode 100644 packages/spec/src/ast/InputNode.d.ts create mode 100644 packages/spec/src/ast/ParamNode.d.ts create mode 100644 packages/spec/src/ast/ParamRefNode.d.ts create mode 100644 packages/spec/src/ast/PlotAttributeNode.d.ts create mode 100644 packages/spec/src/ast/PlotFromNode.d.ts create mode 100644 packages/spec/src/ast/PlotInteractorNode.d.ts create mode 100644 packages/spec/src/ast/PlotLegendNode.d.ts create mode 100644 packages/spec/src/ast/PlotMarkNode.d.ts create mode 100644 packages/spec/src/ast/PlotNode.d.ts create mode 100644 packages/spec/src/ast/SelectionNode.d.ts create mode 100644 packages/spec/src/ast/SpecNode.d.ts create mode 100644 packages/spec/src/ast/VConcatNode.d.ts create mode 100644 packages/spec/src/ast/VSpaceNode.d.ts create mode 100644 packages/spec/src/index.d.ts create mode 100644 packages/spec/src/parse-spec.d.ts create mode 100644 packages/spec/tsconfig.json diff --git a/packages/spec/package.json b/packages/spec/package.json index f56100c0..7b69391d 100644 --- a/packages/spec/package.json +++ b/packages/spec/package.json @@ -16,6 +16,7 @@ "module": "src/index.js", "jsdelivr": "dist/mosaic-spec.min.js", "unpkg": "dist/mosaic-spec.min.js", + "types": "src/index.d.ts", "repository": { "type": "git", "url": "https://github.com/uwdata/mosaic.git" @@ -24,7 +25,7 @@ "prebuild": "rimraf dist && mkdir dist", "build": "node ../../esbuild.js mosaic-spec", "lint": "eslint src test --ext .js", - "test": "mocha 'test/**/*-test.js'", + "test": "mocha 'test/**/*-test.js' && tsc", "prepublishOnly": "npm run test && npm run lint && npm run build" }, "dependencies": { diff --git a/packages/spec/src/ast-to-dom.d.ts b/packages/spec/src/ast-to-dom.d.ts new file mode 100644 index 00000000..174a7d81 --- /dev/null +++ b/packages/spec/src/ast-to-dom.d.ts @@ -0,0 +1,29 @@ +import { Param, Selection } from '@uwdata/mosaic-core'; +import { SpecNode } from './ast/SpecNode.js'; + +/** + * Generate running web application (DOM content) for a Mosaic spec AST. + * @param ast Mosaic AST root node. + * @param options Instantiation options. + * @returns An object with the resulting DOM element, and a map of named parameters (Param and Selection instances). + */ +export function astToDOM( + ast: SpecNode, + options?: AstToDOMOptions +) : Promise; + +/** + * Mosaic specification instantiation options. + */ +export interface AstToDOMOptions { + api?: object; + plotDefaults?: any[]; + params?: Map; + /** The base URL for loading data files. */ + baseURL?: string; +} + +export type AstToDOMResult = { + element: HTMLElement | SVGSVGElement, + params: Map +}; diff --git a/packages/spec/src/ast-to-esm.d.ts b/packages/spec/src/ast-to-esm.d.ts new file mode 100644 index 00000000..beb31891 --- /dev/null +++ b/packages/spec/src/ast-to-esm.d.ts @@ -0,0 +1,37 @@ +import { SpecNode } from './ast/SpecNode.js'; + +/** + * Generate ESM (ECMAScript Module) code for a Mosaic specification AST. + * @param ast Mosaic AST root node. + * @param options Code generation options. + * @returns Generated ESM code using the vgplot API. + */ +export function astToESM( + ast: SpecNode, + options?: AstToESMOptions +) : string; + +/** + * Mosaic specification code generation options. + */ +export interface AstToESMOptions { + /** The base URL for loading data files. */ + baseURL?: string; + /** + * A database connector to initialize. + * Valid values are 'wasm', 'socket', and 'rest'. + * If undefined, no connector code is generated. + */ + connector?: 'wasm' | 'socket' | 'rest' | string; + /** The vgplot API namespace object (default 'vg'). */ + namespace?: string; + /** The starting indentation depth (default 0). */ + depth?: number; + /** + * A Map of imports to include in generated code. Keys are packages (e.g., + * '@uwdata/vgplot') and values indicate what to import (e.g., '* as vg'). + */ + imports?: Map; + /** Code to include after imports. */ + preamble?: string | string[]; +} diff --git a/packages/spec/src/ast/DataNode.d.ts b/packages/spec/src/ast/DataNode.d.ts new file mode 100644 index 00000000..7e6b28bf --- /dev/null +++ b/packages/spec/src/ast/DataNode.d.ts @@ -0,0 +1,82 @@ +export type SpecDataObjects = object[]; + +export type SpecDataDefinition = + | SpecDataQuery + | SpecDataArray + | SpecDataTable + | SpecDataParquet + | SpecDataCSV + | SpecDataSpatial + | SpecDataJSON + | SpecDataJSONObjects; + +export type SpecDataQuery = string; + +export type SpecDataArray = Array; + +export type SpecDataBaseOptions = { + select?: string[]; + where?: string | string[]; + view?: boolean; + temp?: boolean; + replace?: boolean; +}; + +export type SpecDataObject = + | SpecDataTable; + +export type SpecDataTable = { + type: 'table'; + query: string; +} & SpecDataBaseOptions; + +export type SpecDataParquet = + & ( + { type: 'parquet'; file: string; } | + { file: `${string}.parquet` } + ) + & SpecDataBaseOptions; + +export type SpecDataCSV = + & ( + { type: 'csv'; file: string; } | + { file: `${string}.csv` } + ) + & SpecDataCSVOptions + & SpecDataBaseOptions; + +export interface SpecDataCSVOptions { + delimiter?: string, + sample_size?: number; +} + +export type SpecDataSpatial = + & { + type: 'spatial', + file: string, + } + & SpecDataSpatialOptions + & SpecDataBaseOptions; + +export type SpecDataSpatialOptions = { + [key: string]: any +}; + +export type SpecDataJSON = + & ( + { type: 'json'; file: string; } | + { file: `${string}.json` } + ) + & SpecDataJSONOptions + & SpecDataBaseOptions; + +export type SpecDataJSONOptions = { + [key: string]: any +}; + +export type SpecDataJSONObjects = + & { + type?: 'json', + data: SpecDataObjects + } + & SpecDataBaseOptions; diff --git a/packages/spec/src/ast/HConcatNode.d.ts b/packages/spec/src/ast/HConcatNode.d.ts new file mode 100644 index 00000000..0cff18a7 --- /dev/null +++ b/packages/spec/src/ast/HConcatNode.d.ts @@ -0,0 +1,5 @@ +import { SpecComponent } from './SpecNode.js'; + +export type SpecHConcatNode = { + hconcat: SpecComponent[] +}; diff --git a/packages/spec/src/ast/HSpaceNode.d.ts b/packages/spec/src/ast/HSpaceNode.d.ts new file mode 100644 index 00000000..c696e8b2 --- /dev/null +++ b/packages/spec/src/ast/HSpaceNode.d.ts @@ -0,0 +1,3 @@ +export type SpecHSpace = { + hspace: number | string; +}; diff --git a/packages/spec/src/ast/InputNode.d.ts b/packages/spec/src/ast/InputNode.d.ts new file mode 100644 index 00000000..15764e85 --- /dev/null +++ b/packages/spec/src/ast/InputNode.d.ts @@ -0,0 +1,5 @@ +export type SpecInput = { + input: 'menu' | 'search' | 'slider' | 'table'; +} & { + [key: string]: any +}; diff --git a/packages/spec/src/ast/ParamNode.d.ts b/packages/spec/src/ast/ParamNode.d.ts new file mode 100644 index 00000000..0ab0cf65 --- /dev/null +++ b/packages/spec/src/ast/ParamNode.d.ts @@ -0,0 +1,21 @@ +import { SpecSelection } from './SelectionNode.js'; + +export type SpecParamDefinition = + | SpecParamValue + | SpecParam + | SpecSelection; + +export type SpecParamValue = + | SpecParamLiteral + | Array; + +export type SpecParamLiteral = + | string + | number + | boolean; + +export type SpecParamRef = `$${string}`; + +export type SpecParam = + | { select?: 'value', value: SpecParamValue } + | { select?: 'value', date: string }; diff --git a/packages/spec/src/ast/ParamRefNode.d.ts b/packages/spec/src/ast/ParamRefNode.d.ts new file mode 100644 index 00000000..cacc55c1 --- /dev/null +++ b/packages/spec/src/ast/ParamRefNode.d.ts @@ -0,0 +1 @@ +export type SpecParamRef = `$${string}`; diff --git a/packages/spec/src/ast/PlotAttributeNode.d.ts b/packages/spec/src/ast/PlotAttributeNode.d.ts new file mode 100644 index 00000000..06609fc6 --- /dev/null +++ b/packages/spec/src/ast/PlotAttributeNode.d.ts @@ -0,0 +1,3 @@ +export type SpecPlotAttributes = { + [key: string]: any +}; diff --git a/packages/spec/src/ast/PlotFromNode.d.ts b/packages/spec/src/ast/PlotFromNode.d.ts new file mode 100644 index 00000000..eab6bb1f --- /dev/null +++ b/packages/spec/src/ast/PlotFromNode.d.ts @@ -0,0 +1,14 @@ +import { SpecParamRef } from './ParamRefNode.js'; + +export type SpecPlotMarkData = + | SpecPlotDataInline + | SpecPlotFrom; + +export type SpecPlotDataInline = any[]; + +export type SpecPlotFrom = { + from: string; + filterBy?: SpecParamRef; +} & { + [key: string]: any +}; diff --git a/packages/spec/src/ast/PlotInteractorNode.d.ts b/packages/spec/src/ast/PlotInteractorNode.d.ts new file mode 100644 index 00000000..54ed54ad --- /dev/null +++ b/packages/spec/src/ast/PlotInteractorNode.d.ts @@ -0,0 +1,6 @@ +export type SpecPlotInteractor = { + select: string; +} & { + // todo: specific interactors + [key: string]: any; +}; diff --git a/packages/spec/src/ast/PlotLegendNode.d.ts b/packages/spec/src/ast/PlotLegendNode.d.ts new file mode 100644 index 00000000..8408dee1 --- /dev/null +++ b/packages/spec/src/ast/PlotLegendNode.d.ts @@ -0,0 +1,16 @@ +import { SpecParamRef } from './ParamNode.js'; + +export const LegendType = 'color' | 'opacity' | 'symbol'; + +export type SpecPlotLegend = { + legend: LegendType; + as?: SpecParamRef; + field?: string; +} & { + // todo: legend options + [key: string]: any +}; + +export type SpecLegend = + & SpecPlotLegend + & { for: string }; diff --git a/packages/spec/src/ast/PlotMarkNode.d.ts b/packages/spec/src/ast/PlotMarkNode.d.ts new file mode 100644 index 00000000..ddb92b5a --- /dev/null +++ b/packages/spec/src/ast/PlotMarkNode.d.ts @@ -0,0 +1,71 @@ +import { SpecPlotMarkData } from './PlotFromNode.js'; + +export type MarkType = + | 'area' + | 'areaX' + | 'areaY' + | 'line' + | 'lineX' + | 'lineY' + | 'barX' + | 'barY' + | 'cell' + | 'cellX' + | 'cellY' + | 'rect' + | 'rectX' + | 'rectY' + | 'dot' + | 'dotX' + | 'dotY' + | 'circle' + | 'hexagon' + | 'text' + | 'textX' + | 'textY' + | 'ruleX' + | 'ruleY' + | 'tickX' + | 'tickY' + | 'vector' + | 'vectorX' + | 'vectorY' + | 'spike' + | 'image' + | 'densityX' + | 'densityY' + | 'density' + | 'denseLine' + | 'contour' + | 'heatmap' + | 'raster' + | 'rasterTile' + | 'hexbin' + | 'hexgrid' + | 'regressionY' + | 'voronoi' + | 'voronoiMesh' + | 'delaunayLink' + | 'delaunayMesh' + | 'hull' + | 'arrow' + | 'link' + | 'frame' + | 'axisX' + | 'axisY' + | 'axisFx' + | 'axisFy' + | 'gridX' + | 'gridY' + | 'gridFx' + | 'gridFy' + | 'geo' + | 'sphere' + | 'graticule'; + +export type SpecPlotMark = { + mark: MarkType, + data?: SpecPlotMarkData +} & { + [key: string]: any +} diff --git a/packages/spec/src/ast/PlotNode.d.ts b/packages/spec/src/ast/PlotNode.d.ts new file mode 100644 index 00000000..2a0cf5d7 --- /dev/null +++ b/packages/spec/src/ast/PlotNode.d.ts @@ -0,0 +1,13 @@ +import { SpecPlotAttributes } from './PlotAttributeNode.js'; +import { SpecPlotInteractor } from './PlotInteractorNode.js'; +import { SpecPlotLegend } from './PlotLegendNode.js'; +import { SpecPlotMark } from './PlotMarkNode.js'; + +export type SpecPlot = { + plot: SpecPlotEntry[] +} & SpecPlotAttributes; + +export type SpecPlotEntry = + | SpecPlotMark + | SpecPlotInteractor + | SpecPlotLegend; \ No newline at end of file diff --git a/packages/spec/src/ast/SelectionNode.d.ts b/packages/spec/src/ast/SelectionNode.d.ts new file mode 100644 index 00000000..49e22772 --- /dev/null +++ b/packages/spec/src/ast/SelectionNode.d.ts @@ -0,0 +1,10 @@ +export type SpecSelection = { + select: SelectionType, + cross?: boolean +}; + +export type SelectionType = + | 'crossfilter' + | 'intersect' + | 'single' + | 'union'; diff --git a/packages/spec/src/ast/SpecNode.d.ts b/packages/spec/src/ast/SpecNode.d.ts new file mode 100644 index 00000000..be60b124 --- /dev/null +++ b/packages/spec/src/ast/SpecNode.d.ts @@ -0,0 +1,55 @@ +import { SpecDataDefinition } from './DataNode.js'; +import { SpecParamDefinition } from './ParamNode.js'; +import { SpecHConcatNode } from './HConcatNode.js'; +import { SpecVConcatNode } from './VConcatNode.js'; +import { SpecHSpace } from './HSpaceNode.js'; +import { SpecVSpace } from './VSpaceNode.js'; +import { SpecInput } from './InputNode.js'; +import { SpecPlot } from './PlotNode.js'; +import { SpecPlotMark } from './PlotMarkNode.js'; +import { SpecLegend } from './PlotLegendNode.js'; +import { SpecPlotAttributes } from './PlotAttributeNode.js'; +import { DataNode } from './DataNode.js'; +import { ParamNode } from './ParamNode.js'; +import { SelectionNode } from './SelectionNode.js'; +import { PlotAttributeNode } from './PlotAttributeNode.js'; +import { InputNode } from './InputNode.js'; +import { HConcatNode } from './HConcatNode.js'; +import { VConcatNode } from './VConcatNode.js'; +import { HSpaceNode } from './HSpaceNode.js'; +import { VSpaceNode } from './VSpaceNode.js'; +import { PlotNode } from './PlotNode.js'; +import { PlotMarkNode } from './PlotMarkNode.js'; +import { PlotLegendNode } from './PlotLegendNode.js'; + +export type Spec = { + meta?: SpecMeta; + config?: SpecConfig; + data?: SpecData; + params?: SpecParams; + plotDefaults?: SpecPlotAttributes; +} & SpecComponent; + +export type SpecMeta = { + title?: string; + description?: string; + credit?: string; +} & Record; + +export type SpecConfig = { + extensions?: string | string[]; +} & Record; + +export type SpecData = Record; + +export type SpecParams = Record; + +export type SpecComponent = + | SpecHConcatNode + | SpecVConcatNode + | SpecHSpace + | SpecVSpace + | SpecInput + | SpecPlot + | SpecPlotMark + | SpecLegend; diff --git a/packages/spec/src/ast/VConcatNode.d.ts b/packages/spec/src/ast/VConcatNode.d.ts new file mode 100644 index 00000000..4b4fe8a6 --- /dev/null +++ b/packages/spec/src/ast/VConcatNode.d.ts @@ -0,0 +1,5 @@ +import { SpecComponent } from './SpecNode.js'; + +export type SpecVConcatNode = { + vconcat: SpecComponent[] +}; diff --git a/packages/spec/src/ast/VSpaceNode.d.ts b/packages/spec/src/ast/VSpaceNode.d.ts new file mode 100644 index 00000000..f2aa35bb --- /dev/null +++ b/packages/spec/src/ast/VSpaceNode.d.ts @@ -0,0 +1,3 @@ +export type SpecVSpace = { + vspace: number | string; +}; diff --git a/packages/spec/src/index.d.ts b/packages/spec/src/index.d.ts new file mode 100644 index 00000000..5856e727 --- /dev/null +++ b/packages/spec/src/index.d.ts @@ -0,0 +1,4 @@ +export * from './ast/SpecNode.js'; +export * from './ast-to-dom.js'; +export * from './ast-to-esm.js'; +export * from './parse-spec.js'; diff --git a/packages/spec/src/parse-spec.d.ts b/packages/spec/src/parse-spec.d.ts new file mode 100644 index 00000000..abc334d6 --- /dev/null +++ b/packages/spec/src/parse-spec.d.ts @@ -0,0 +1,18 @@ +import { Spec, SpecNode } from './ast/SpecNode.js'; + +/** + * Parse a Mosaic specification to an AST (abstract syntax tree). + */ +export function parseSpec( + spec: Spec | string, + options?: ParseSpecOptions +) : SpecNode; + +export interface ParseSpecOptions { + components?: Map, + transforms?: string[], + inputs?: string[], + plot?: string[], + params?: any[], + datasets?: any[] +} diff --git a/packages/spec/tsconfig.json b/packages/spec/tsconfig.json new file mode 100644 index 00000000..1461db67 --- /dev/null +++ b/packages/spec/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "allowJs": true, + "noEmit": true, + "noImplicitAny": false, + "module": "Node16", + "baseUrl": "./src", + "skipLibCheck": true, + "paths": { + "@uwdata/mosaic-spec": ["index"] + } + }, + "include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.ts"] +} \ No newline at end of file From 02256d44ce5676561759b6a2b44601acfa675dbe Mon Sep 17 00:00:00 2001 From: jheer Date: Mon, 1 Apr 2024 12:34:59 -0700 Subject: [PATCH 03/25] test: Add TypeScript spec tests. --- bin/prepare-examples.js | 13 + packages/spec/tsconfig.json | 2 +- specs/ts/aeromagnetic-survey.ts | 67 ++++ specs/ts/airline-travelers.ts | 70 +++++ specs/ts/athletes.ts | 117 +++++++ specs/ts/axes.ts | 60 ++++ specs/ts/bias.ts | 43 +++ specs/ts/contours.ts | 84 +++++ specs/ts/crossfilter.ts | 72 +++++ specs/ts/density1d.ts | 78 +++++ specs/ts/density2d.ts | 81 +++++ specs/ts/driving-shifts.ts | 46 +++ specs/ts/earthquakes-feed.ts | 51 +++ specs/ts/earthquakes-globe.ts | 85 +++++ specs/ts/flights-10m.ts | 99 ++++++ specs/ts/flights-200k.ts | 101 ++++++ specs/ts/flights-density.ts | 75 +++++ specs/ts/flights-hexbin.ts | 161 ++++++++++ specs/ts/gaia.ts | 149 +++++++++ specs/ts/legends.ts | 329 ++++++++++++++++++++ specs/ts/line-density.ts | 115 +++++++ specs/ts/line.ts | 22 ++ specs/ts/linear-regression.ts | 46 +++ specs/ts/mark-types.ts | 235 ++++++++++++++ specs/ts/moving-average.ts | 92 ++++++ specs/ts/normalize.ts | 74 +++++ specs/ts/nyc-taxi-rides.ts | 159 ++++++++++ specs/ts/observable-latency.ts | 123 ++++++++ specs/ts/overview-detail.ts | 51 +++ specs/ts/pan-zoom.ts | 132 ++++++++ specs/ts/population-arrows.ts | 72 +++++ specs/ts/presidential-opinion.ts | 64 ++++ specs/ts/seattle-temp.ts | 66 ++++ specs/ts/sorted-bars.ts | 50 +++ specs/ts/splom.ts | 511 +++++++++++++++++++++++++++++++ specs/ts/symbols.ts | 72 +++++ specs/ts/table.ts | 16 + specs/ts/unemployment.ts | 47 +++ specs/ts/us-county-map.ts | 56 ++++ specs/ts/us-state-map.ts | 44 +++ specs/ts/voronoi.ts | 113 +++++++ specs/ts/walmart-openings.ts | 67 ++++ specs/ts/weather.ts | 129 ++++++++ specs/ts/wind-map.ts | 59 ++++ 44 files changed, 4197 insertions(+), 1 deletion(-) create mode 100644 specs/ts/aeromagnetic-survey.ts create mode 100644 specs/ts/airline-travelers.ts create mode 100644 specs/ts/athletes.ts create mode 100644 specs/ts/axes.ts create mode 100644 specs/ts/bias.ts create mode 100644 specs/ts/contours.ts create mode 100644 specs/ts/crossfilter.ts create mode 100644 specs/ts/density1d.ts create mode 100644 specs/ts/density2d.ts create mode 100644 specs/ts/driving-shifts.ts create mode 100644 specs/ts/earthquakes-feed.ts create mode 100644 specs/ts/earthquakes-globe.ts create mode 100644 specs/ts/flights-10m.ts create mode 100644 specs/ts/flights-200k.ts create mode 100644 specs/ts/flights-density.ts create mode 100644 specs/ts/flights-hexbin.ts create mode 100644 specs/ts/gaia.ts create mode 100644 specs/ts/legends.ts create mode 100644 specs/ts/line-density.ts create mode 100644 specs/ts/line.ts create mode 100644 specs/ts/linear-regression.ts create mode 100644 specs/ts/mark-types.ts create mode 100644 specs/ts/moving-average.ts create mode 100644 specs/ts/normalize.ts create mode 100644 specs/ts/nyc-taxi-rides.ts create mode 100644 specs/ts/observable-latency.ts create mode 100644 specs/ts/overview-detail.ts create mode 100644 specs/ts/pan-zoom.ts create mode 100644 specs/ts/population-arrows.ts create mode 100644 specs/ts/presidential-opinion.ts create mode 100644 specs/ts/seattle-temp.ts create mode 100644 specs/ts/sorted-bars.ts create mode 100644 specs/ts/splom.ts create mode 100644 specs/ts/symbols.ts create mode 100644 specs/ts/table.ts create mode 100644 specs/ts/unemployment.ts create mode 100644 specs/ts/us-county-map.ts create mode 100644 specs/ts/us-state-map.ts create mode 100644 specs/ts/voronoi.ts create mode 100644 specs/ts/walmart-openings.ts create mode 100644 specs/ts/weather.ts create mode 100644 specs/ts/wind-map.ts diff --git a/bin/prepare-examples.js b/bin/prepare-examples.js index 952ccb10..a46b0c4a 100644 --- a/bin/prepare-examples.js +++ b/bin/prepare-examples.js @@ -14,6 +14,7 @@ import { parse } from 'yaml'; const specDir = join('specs', 'yaml'); const esmTestDir = join('specs', 'esm'); const jsonTestDir = join('specs', 'json'); +const tsTestDir = join('specs', 'ts'); const docsDir = 'docs'; const yamlDocsDir = join(docsDir, 'public', 'specs', 'yaml'); @@ -21,6 +22,13 @@ const jsonDocsDir = join(docsDir, 'public', 'specs', 'json'); const esmDocsDir = join(docsDir, 'public', 'specs', 'esm'); const exampleDir = join(docsDir, 'examples'); +const specToTS = spec => { + return `import { Spec } from '@uwdata/mosaic-spec'; + +export const spec : Spec = ${JSON.stringify(spec, 0, 2)}; +`; +} + const files = await Promise.allSettled((await readdir(specDir)) .filter(name => extname(name) === '.yaml') .map(async name => { @@ -43,6 +51,11 @@ const files = await Promise.allSettled((await readdir(specDir)) resolve(jsonTestDir, `${base}.json`), JSON.stringify(ast.toJSON(), 0, 2) ), + // write TS JSON spec to tests + writeFile( + resolve(tsTestDir, `${base}.ts`), + specToTS(spec) + ), // copy YAML file to docs copyFile(file, resolve(yamlDocsDir, `${base}.yaml`)), // write JSON spec to docs diff --git a/packages/spec/tsconfig.json b/packages/spec/tsconfig.json index 1461db67..da04a4c0 100644 --- a/packages/spec/tsconfig.json +++ b/packages/spec/tsconfig.json @@ -10,5 +10,5 @@ "@uwdata/mosaic-spec": ["index"] } }, - "include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.ts"] + "include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.ts", "../../specs/ts/*.ts"] } \ No newline at end of file diff --git a/specs/ts/aeromagnetic-survey.ts b/specs/ts/aeromagnetic-survey.ts new file mode 100644 index 00000000..9fee1766 --- /dev/null +++ b/specs/ts/aeromagnetic-survey.ts @@ -0,0 +1,67 @@ +import { Spec } from '@uwdata/mosaic-spec'; + +export const spec : Spec = { + "meta": { + "title": "Aeromagnetic Survey", + "description": "A raster visualization of the 1955 [Great Britain aeromagnetic survey](https://www.bgs.ac.uk/datasets/gb-aeromagnetic-survey/), which measured the Earth’s magnetic field by plane. Each sample recorded the longitude and latitude alongside the strength of the [IGRF](https://www.ncei.noaa.gov/products/international-geomagnetic-reference-field) in [nanoteslas](https://en.wikipedia.org/wiki/Tesla_(unit)). This example demonstrates both raster interpolation and smoothing (blur) options.\n", + "credit": "Adapted from an [Observable Plot example](https://observablehq.com/@observablehq/plot-igfr90-raster)." + }, + "data": { + "ca55": { + "file": "data/ca55-south.parquet" + } + }, + "params": { + "interp": "random-walk", + "blur": 0 + }, + "vconcat": [ + { + "hconcat": [ + { + "input": "menu", + "label": "Interpolation Method", + "options": [ + "none", + "nearest", + "barycentric", + "random-walk" + ], + "as": "$interp" + }, + { + "hspace": "1em" + }, + { + "input": "slider", + "label": "Blur", + "min": 0, + "max": 100, + "as": "$blur" + } + ] + }, + { + "vspace": "1em" + }, + { + "plot": [ + { + "mark": "raster", + "data": { + "from": "ca55" + }, + "x": "LONGITUDE", + "y": "LATITUDE", + "fill": { + "max": "MAG_IGRF90" + }, + "interpolate": "$interp", + "bandwidth": "$blur" + } + ], + "colorScale": "diverging", + "colorDomain": "Fixed" + } + ] +}; diff --git a/specs/ts/airline-travelers.ts b/specs/ts/airline-travelers.ts new file mode 100644 index 00000000..156cbc0a --- /dev/null +++ b/specs/ts/airline-travelers.ts @@ -0,0 +1,70 @@ +import { Spec } from '@uwdata/mosaic-spec'; + +export const spec : Spec = { + "meta": { + "title": "Airline Travelers", + "description": "A labeled line chart comparing airport travelers in 2019 and 2020.", + "credit": "Adapted from an [Observable Plot example](https://observablehq.com/@observablehq/plot-labeled-line-chart)." + }, + "data": { + "travelers": { + "file": "data/travelers.parquet" + }, + "endpoint": "SELECT * FROM travelers ORDER BY date DESC LIMIT 1\n" + }, + "plot": [ + { + "mark": "ruleY", + "data": [ + 0 + ] + }, + { + "mark": "lineY", + "data": { + "from": "travelers" + }, + "x": "date", + "y": "previous", + "strokeOpacity": 0.35 + }, + { + "mark": "lineY", + "data": { + "from": "travelers" + }, + "x": "date", + "y": "current" + }, + { + "mark": "text", + "data": { + "from": "endpoint" + }, + "x": "date", + "y": "previous", + "text": [ + "2019" + ], + "fillOpacity": 0.5, + "lineAnchor": "bottom", + "dy": -6 + }, + { + "mark": "text", + "data": { + "from": "endpoint" + }, + "x": "date", + "y": "current", + "text": [ + "2020" + ], + "lineAnchor": "top", + "dy": 6 + } + ], + "yGrid": true, + "yLabel": "↑ Travelers per day", + "yTickFormat": "s" +}; diff --git a/specs/ts/athletes.ts b/specs/ts/athletes.ts new file mode 100644 index 00000000..28f5bc33 --- /dev/null +++ b/specs/ts/athletes.ts @@ -0,0 +1,117 @@ +import { Spec } from '@uwdata/mosaic-spec'; + +export const spec : Spec = { + "meta": { + "title": "Olympic Athletes", + "description": "An interactive dashboard of athlete statistics. The input menus and searchbox filter the display and are automatically populated by backing data columns.\n" + }, + "data": { + "athletes": { + "file": "data/athletes.parquet" + } + }, + "hconcat": [ + { + "vconcat": [ + { + "hconcat": [ + { + "input": "menu", + "label": "Sport", + "as": "$query", + "from": "athletes", + "column": "sport" + }, + { + "input": "menu", + "label": "Sex", + "as": "$query", + "from": "athletes", + "column": "sex" + }, + { + "input": "search", + "label": "Name", + "as": "$query", + "from": "athletes", + "column": "name", + "type": "contains" + } + ] + }, + { + "vspace": 10 + }, + { + "plot": [ + { + "mark": "dot", + "data": { + "from": "athletes", + "filterBy": "$query" + }, + "x": "weight", + "y": "height", + "fill": "sex", + "r": 2, + "opacity": 0.1 + }, + { + "mark": "regressionY", + "data": { + "from": "athletes", + "filterBy": "$query" + }, + "x": "weight", + "y": "height", + "stroke": "sex" + }, + { + "select": "intervalXY", + "as": "$query", + "brush": { + "fillOpacity": 0, + "stroke": "black" + } + } + ], + "xyDomain": "Fixed", + "colorDomain": "Fixed", + "margins": { + "left": 35, + "top": 20, + "right": 1 + }, + "width": 570, + "height": 350 + }, + { + "vspace": 5 + }, + { + "input": "table", + "from": "athletes", + "maxWidth": 570, + "height": 250, + "filterBy": "$query", + "columns": [ + "name", + "nationality", + "sex", + "height", + "weight", + "sport" + ], + "width": { + "name": 180, + "nationality": 100, + "sex": 50, + "height": 50, + "weight": 50, + "sport": 100 + } + } + ] + } + ] +}; diff --git a/specs/ts/axes.ts b/specs/ts/axes.ts new file mode 100644 index 00000000..7d74ac3d --- /dev/null +++ b/specs/ts/axes.ts @@ -0,0 +1,60 @@ +import { Spec } from '@uwdata/mosaic-spec'; + +export const spec : Spec = { + "meta": { + "title": "Axes & Gridlines", + "description": "Customized axis and gridline marks can be used in addition to standard scale attributes such as `xAxis`, `yGrid`, etc. Just add data!\n" + }, + "plot": [ + { + "mark": "gridY", + "strokeDasharray": [ + 0.75, + 2 + ], + "strokeOpacity": 1 + }, + { + "mark": "axisY", + "anchor": "left", + "tickSize": 0, + "dx": 38, + "dy": -4, + "lineAnchor": "bottom" + }, + { + "mark": "axisY", + "anchor": "right", + "tickSize": 0, + "tickPadding": 5, + "label": "y-axis", + "labelAnchor": "center" + }, + { + "mark": "axisX", + "label": "x-axis", + "labelAnchor": "center" + }, + { + "mark": "gridX" + }, + { + "mark": "ruleY", + "data": [ + 0 + ] + } + ], + "xDomain": [ + 0, + 100 + ], + "yDomain": [ + 0, + 100 + ], + "xInsetLeft": 36, + "marginLeft": 0, + "marginRight": 35, + "width": 680 +}; diff --git a/specs/ts/bias.ts b/specs/ts/bias.ts new file mode 100644 index 00000000..c611e47a --- /dev/null +++ b/specs/ts/bias.ts @@ -0,0 +1,43 @@ +import { Spec } from '@uwdata/mosaic-spec'; + +export const spec : Spec = { + "meta": { + "title": "Bias Parameter", + "description": "Dynamically adjust queried values by adding a Param value. The SQL expression is re-computed in the database upon updates.\n" + }, + "data": { + "walk": { + "file": "data/random-walk.parquet" + } + }, + "params": { + "point": 0 + }, + "vconcat": [ + { + "input": "slider", + "label": "Bias", + "as": "$point", + "min": 1, + "max": 1000, + "step": 0.1 + }, + { + "plot": [ + { + "mark": "areaY", + "data": { + "from": "walk" + }, + "x": "t", + "y": { + "sql": "v + $point" + }, + "fill": "steelblue" + } + ], + "width": 680, + "height": 200 + } + ] +}; diff --git a/specs/ts/contours.ts b/specs/ts/contours.ts new file mode 100644 index 00000000..1282471d --- /dev/null +++ b/specs/ts/contours.ts @@ -0,0 +1,84 @@ +import { Spec } from '@uwdata/mosaic-spec'; + +export const spec : Spec = { + "meta": { + "title": "Contour Plot", + "description": "Here `heatmap` and `contour` marks visualize the density of data points in a scatter plot of penguin measurments. Setting the `fill` color to `\"species\"` subdivides the data into three sets of densities.\n" + }, + "data": { + "penguins": { + "file": "data/penguins.parquet" + } + }, + "params": { + "bandwidth": 40, + "thresholds": 10 + }, + "vconcat": [ + { + "hconcat": [ + { + "input": "slider", + "label": "Bandwidth (σ)", + "as": "$bandwidth", + "min": 1, + "max": 100 + }, + { + "input": "slider", + "label": "Thresholds", + "as": "$thresholds", + "min": 2, + "max": 20 + } + ] + }, + { + "plot": [ + { + "mark": "heatmap", + "data": { + "from": "penguins" + }, + "x": "bill_length", + "y": "bill_depth", + "fill": "species", + "bandwidth": "$bandwidth" + }, + { + "mark": "contour", + "data": { + "from": "penguins" + }, + "x": "bill_length", + "y": "bill_depth", + "stroke": "species", + "bandwidth": "$bandwidth", + "thresholds": "$thresholds" + }, + { + "mark": "dot", + "data": { + "from": "penguins" + }, + "x": "bill_length", + "y": "bill_depth", + "fill": "currentColor", + "r": 1 + } + ], + "xAxis": "bottom", + "xLabelAnchor": "center", + "yAxis": "right", + "yLabelAnchor": "center", + "margins": { + "top": 5, + "bottom": 30, + "left": 5, + "right": 50 + }, + "width": 700, + "height": 480 + } + ] +}; diff --git a/specs/ts/crossfilter.ts b/specs/ts/crossfilter.ts new file mode 100644 index 00000000..62c670f5 --- /dev/null +++ b/specs/ts/crossfilter.ts @@ -0,0 +1,72 @@ +import { Spec } from '@uwdata/mosaic-spec'; + +export const spec : Spec = { + "data": { + "flights": { + "file": "data/flights-200k.parquet" + } + }, + "params": { + "brush": { + "select": "crossfilter" + } + }, + "vconcat": [ + { + "plot": [ + { + "mark": "rectY", + "data": { + "from": "flights", + "filterBy": "$brush" + }, + "x": { + "bin": "delay" + }, + "y": { + "count": null + }, + "fill": "steelblue", + "inset": 0.5 + }, + { + "select": "intervalX", + "as": "$brush" + } + ], + "xDomain": "Fixed", + "xLabel": "Arrival Delay (min)", + "xLabelAnchor": "center", + "yTickFormat": "s", + "height": 200 + }, + { + "plot": [ + { + "mark": "rectY", + "data": { + "from": "flights", + "filterBy": "$brush" + }, + "x": { + "bin": "time" + }, + "y": { + "count": null + }, + "fill": "steelblue", + "inset": 0.5 + }, + { + "select": "intervalX", + "as": "$brush" + } + ], + "xDomain": "Fixed", + "xLabel": "Departure Time (hour)", + "xLabelAnchor": "center", + "yTickFormat": "s", + "height": 200 + } + ] +}; diff --git a/specs/ts/density1d.ts b/specs/ts/density1d.ts new file mode 100644 index 00000000..c0630403 --- /dev/null +++ b/specs/ts/density1d.ts @@ -0,0 +1,78 @@ +import { Spec } from '@uwdata/mosaic-spec'; + +export const spec : Spec = { + "meta": { + "title": "Density 1D", + "description": "Density plots (`densityY` mark) for over 200,000 flights, created using kernel density estimation. Binning is performned in-database, subsequent smoothing in-browser. The distance density uses a log-scaled domain. To change the amount of smoothing, use the slider to set the kernel bandwidth.\n" + }, + "data": { + "flights": { + "file": "data/flights-200k.parquet" + } + }, + "params": { + "brush": { + "select": "crossfilter" + }, + "bandwidth": 20 + }, + "vconcat": [ + { + "input": "slider", + "label": "Bandwidth (σ)", + "as": "$bandwidth", + "min": 0.1, + "max": 100, + "step": 0.1 + }, + { + "plot": [ + { + "mark": "densityY", + "data": { + "from": "flights", + "filterBy": "$brush" + }, + "x": "delay", + "fill": "#888", + "fillOpacity": 0.5, + "bandwidth": "$bandwidth" + }, + { + "select": "intervalX", + "as": "$brush" + } + ], + "yAxis": null, + "xDomain": "Fixed", + "width": 600, + "marginLeft": 10, + "height": 200 + }, + { + "plot": [ + { + "mark": "densityY", + "data": { + "from": "flights", + "filterBy": "$brush" + }, + "x": "distance", + "fill": "#888", + "fillOpacity": 0.5, + "bandwidth": "$bandwidth" + }, + { + "select": "intervalX", + "as": "$brush" + } + ], + "yAxis": null, + "xScale": "log", + "xDomain": "Fixed", + "width": 600, + "marginLeft": 10, + "height": 200 + } + ] +}; diff --git a/specs/ts/density2d.ts b/specs/ts/density2d.ts new file mode 100644 index 00000000..e3e5cea0 --- /dev/null +++ b/specs/ts/density2d.ts @@ -0,0 +1,81 @@ +import { Spec } from '@uwdata/mosaic-spec'; + +export const spec : Spec = { + "meta": { + "title": "Density 2D", + "description": "A 2D `density` plot in which circle size indicates the point density. The data is divided by fill color into three sets of densities. To change the amount of smoothing, use the slider to set the kernel bandwidth.\n" + }, + "data": { + "penguins": { + "file": "data/penguins.parquet" + } + }, + "params": { + "bandwidth": 20, + "bins": 20 + }, + "vconcat": [ + { + "hconcat": [ + { + "input": "slider", + "label": "Bandwidth (σ)", + "as": "$bandwidth", + "min": 1, + "max": 100 + }, + { + "input": "slider", + "label": "Bins", + "as": "$bins", + "min": 10, + "max": 60 + } + ] + }, + { + "plot": [ + { + "mark": "density", + "data": { + "from": "penguins" + }, + "x": "bill_length", + "y": "bill_depth", + "r": "density", + "fill": "species", + "fillOpacity": 0.5, + "width": "$bins", + "height": "$bins", + "bandwidth": "$bandwidth" + }, + { + "mark": "dot", + "data": { + "from": "penguins" + }, + "x": "bill_length", + "y": "bill_depth", + "fill": "currentColor", + "r": 1 + } + ], + "rRange": [ + 0, + 16 + ], + "xAxis": "bottom", + "xLabelAnchor": "center", + "yAxis": "right", + "yLabelAnchor": "center", + "margins": { + "top": 5, + "bottom": 30, + "left": 5, + "right": 50 + }, + "width": 700, + "height": 480 + } + ] +}; diff --git a/specs/ts/driving-shifts.ts b/specs/ts/driving-shifts.ts new file mode 100644 index 00000000..1292eea3 --- /dev/null +++ b/specs/ts/driving-shifts.ts @@ -0,0 +1,46 @@ +import { Spec } from '@uwdata/mosaic-spec'; + +export const spec : Spec = { + "meta": { + "title": "Driving Shifts into Reverse", + "description": "A connected scatter plot of miles driven vs. gas prices.", + "credit": "Adapted from an [Observable Plot example](https://observablehq.com/@observablehq/plot-connected-scatterplot), which in turn adapts Hannah Fairfield's [New York Times article](http://www.nytimes.com/imagepages/2010/05/02/business/02metrics.html).\n" + }, + "data": { + "driving": { + "file": "data/driving.parquet" + } + }, + "plot": [ + { + "mark": "line", + "data": { + "from": "driving" + }, + "x": "miles", + "y": "gas", + "curve": "catmull-rom", + "marker": true + }, + { + "mark": "text", + "data": { + "from": "driving" + }, + "x": "miles", + "y": "gas", + "text": { + "sql": "year::VARCHAR" + }, + "dy": -6, + "lineAnchor": "bottom", + "filter": { + "sql": "year % 5 = 0" + } + } + ], + "inset": 10, + "grid": true, + "xLabel": "Miles driven (per person-year)", + "yLabel": "Cost of gasoline ($ per gallon)" +}; diff --git a/specs/ts/earthquakes-feed.ts b/specs/ts/earthquakes-feed.ts new file mode 100644 index 00000000..ccfbe091 --- /dev/null +++ b/specs/ts/earthquakes-feed.ts @@ -0,0 +1,51 @@ +import { Spec } from '@uwdata/mosaic-spec'; + +export const spec : Spec = { + "meta": { + "title": "Earthquakes Feed", + "description": "Earthquake data from the USGS live feed. To show land masses, this example loads and parses TopoJSON data in the database. Requires the DuckDB `spatial` extension.\n", + "credit": "Adapted from an [Observable Plot example](https://observablehq.com/@observablehq/plot-live-earthquake-map)." + }, + "data": { + "feed": { + "type": "spatial", + "file": "data/usgs-feed.geojson" + }, + "world": { + "type": "spatial", + "file": "data/countries-110m.json", + "layer": "land" + } + }, + "plot": [ + { + "mark": "geo", + "data": { + "from": "world" + }, + "fill": "currentColor", + "fillOpacity": 0.2 + }, + { + "mark": "sphere", + "strokeWidth": 0.5 + }, + { + "mark": "geo", + "data": { + "from": "feed" + }, + "r": { + "sql": "POW(10, mag)" + }, + "stroke": "red", + "fill": "red", + "fillOpacity": 0.2, + "title": "title", + "href": "url", + "target": "_blank" + } + ], + "margin": 2, + "projectionType": "equirectangular" +}; diff --git a/specs/ts/earthquakes-globe.ts b/specs/ts/earthquakes-globe.ts new file mode 100644 index 00000000..ab33acbc --- /dev/null +++ b/specs/ts/earthquakes-globe.ts @@ -0,0 +1,85 @@ +import { Spec } from '@uwdata/mosaic-spec'; + +export const spec : Spec = { + "meta": { + "title": "Earthquakes Globe", + "description": "A rotatable globe of earthquake activity. To show land masses, this example loads and parses TopoJSON data in the database. Requires the DuckDB `spatial` extension.\n", + "credit": "Adapted from an [Observable Plot example](https://observablehq.com/@observablehq/plot-earthquake-globe)." + }, + "data": { + "earthquakes": { + "file": "data/earthquakes.parquet" + }, + "land": { + "type": "spatial", + "file": "data/countries-110m.json", + "layer": "land" + } + }, + "params": { + "longitude": -180, + "latitude": -30, + "rotate": [ + "$longitude", + "$latitude" + ] + }, + "vconcat": [ + { + "hconcat": [ + { + "input": "slider", + "label": "Longitude", + "as": "$longitude", + "min": -180, + "max": 180, + "step": 1 + }, + { + "input": "slider", + "label": "Latitude", + "as": "$latitude", + "min": -90, + "max": 90, + "step": 1 + } + ] + }, + { + "plot": [ + { + "mark": "geo", + "data": { + "from": "land" + }, + "geometry": { + "geojson": "geom" + }, + "fill": "currentColor", + "fillOpacity": 0.2 + }, + { + "mark": "sphere" + }, + { + "mark": "dot", + "data": { + "from": "earthquakes" + }, + "x": "longitude", + "y": "latitude", + "r": { + "sql": "POW(10, magnitude)" + }, + "stroke": "red", + "fill": "red", + "fillOpacity": 0.2 + } + ], + "margin": 10, + "style": "overflow: visible;", + "projectionType": "orthographic", + "projectionRotate": "$rotate" + } + ] +}; diff --git a/specs/ts/flights-10m.ts b/specs/ts/flights-10m.ts new file mode 100644 index 00000000..4c421db5 --- /dev/null +++ b/specs/ts/flights-10m.ts @@ -0,0 +1,99 @@ +import { Spec } from '@uwdata/mosaic-spec'; + +export const spec : Spec = { + "meta": { + "title": "Cross-Filter Flights (10M)", + "description": "Histograms showing arrival delay, departure time, and distance flown for 10 million flights.\nOnce loaded, automatically-generated indexes enable efficient cross-filtered selections.\n\n_You may need to wait a few seconds for the dataset to load._\n" + }, + "data": { + "flights10m": "SELECT GREATEST(-60, LEAST(ARR_DELAY, 180))::DOUBLE AS delay, DISTANCE AS distance, DEP_TIME AS time FROM 'https://uwdata.github.io/mosaic-datasets/data/flights-10m.parquet'" + }, + "params": { + "brush": { + "select": "crossfilter" + } + }, + "vconcat": [ + { + "plot": [ + { + "mark": "rectY", + "data": { + "from": "flights10m", + "filterBy": "$brush" + }, + "x": { + "bin": "delay" + }, + "y": { + "count": null + }, + "fill": "steelblue", + "inset": 0.5 + }, + { + "select": "intervalX", + "as": "$brush" + } + ], + "xDomain": "Fixed", + "marginLeft": 75, + "width": 600, + "height": 200 + }, + { + "plot": [ + { + "mark": "rectY", + "data": { + "from": "flights10m", + "filterBy": "$brush" + }, + "x": { + "bin": "time" + }, + "y": { + "count": null + }, + "fill": "steelblue", + "inset": 0.5 + }, + { + "select": "intervalX", + "as": "$brush" + } + ], + "xDomain": "Fixed", + "marginLeft": 75, + "width": 600, + "height": 200 + }, + { + "plot": [ + { + "mark": "rectY", + "data": { + "from": "flights10m", + "filterBy": "$brush" + }, + "x": { + "bin": "distance" + }, + "y": { + "count": null + }, + "fill": "steelblue", + "inset": 0.5 + }, + { + "select": "intervalX", + "as": "$brush" + } + ], + "xDomain": "Fixed", + "marginLeft": 75, + "width": 600, + "height": 200 + } + ] +}; diff --git a/specs/ts/flights-200k.ts b/specs/ts/flights-200k.ts new file mode 100644 index 00000000..1f5db65f --- /dev/null +++ b/specs/ts/flights-200k.ts @@ -0,0 +1,101 @@ +import { Spec } from '@uwdata/mosaic-spec'; + +export const spec : Spec = { + "meta": { + "title": "Cross-Filter Flights (200k)", + "description": "Histograms showing arrival delay, departure time, and distance flown for over 200,000 flights. Select a histogram region to cross-filter the charts. Each plot uses an `intervalX` interactor to populate a shared Selection with `crossfilter` resolution.\n" + }, + "data": { + "flights": { + "file": "data/flights-200k.parquet" + } + }, + "params": { + "brush": { + "select": "crossfilter" + } + }, + "vconcat": [ + { + "plot": [ + { + "mark": "rectY", + "data": { + "from": "flights", + "filterBy": "$brush" + }, + "x": { + "bin": "delay" + }, + "y": { + "count": null + }, + "fill": "steelblue", + "inset": 0.5 + }, + { + "select": "intervalX", + "as": "$brush" + } + ], + "xDomain": "Fixed", + "yTickFormat": "s", + "width": 600, + "height": 200 + }, + { + "plot": [ + { + "mark": "rectY", + "data": { + "from": "flights", + "filterBy": "$brush" + }, + "x": { + "bin": "time" + }, + "y": { + "count": null + }, + "fill": "steelblue", + "inset": 0.5 + }, + { + "select": "intervalX", + "as": "$brush" + } + ], + "xDomain": "Fixed", + "yTickFormat": "s", + "width": 600, + "height": 200 + }, + { + "plot": [ + { + "mark": "rectY", + "data": { + "from": "flights", + "filterBy": "$brush" + }, + "x": { + "bin": "distance" + }, + "y": { + "count": null + }, + "fill": "steelblue", + "inset": 0.5 + }, + { + "select": "intervalX", + "as": "$brush" + } + ], + "xDomain": "Fixed", + "yTickFormat": "s", + "width": 600, + "height": 200 + } + ] +}; diff --git a/specs/ts/flights-density.ts b/specs/ts/flights-density.ts new file mode 100644 index 00000000..74c346ba --- /dev/null +++ b/specs/ts/flights-density.ts @@ -0,0 +1,75 @@ +import { Spec } from '@uwdata/mosaic-spec'; + +export const spec : Spec = { + "meta": { + "title": "Flights Density", + "description": "Density `heatmap` and `contour` lines for 200,000+ flights by departure hour and arrival delay. The sliders adjust the smoothing (bandwidth) and number of contour thresholds.\n" + }, + "data": { + "flights": { + "file": "data/flights-200k.parquet" + } + }, + "params": { + "bandwidth": 7, + "thresholds": 10 + }, + "vconcat": [ + { + "hconcat": [ + { + "input": "slider", + "label": "Bandwidth (σ)", + "as": "$bandwidth", + "min": 1, + "max": 100 + }, + { + "input": "slider", + "label": "Thresholds", + "as": "$thresholds", + "min": 2, + "max": 20 + } + ] + }, + { + "plot": [ + { + "mark": "heatmap", + "data": { + "from": "flights" + }, + "x": "time", + "y": "delay", + "fill": "density", + "bandwidth": "$bandwidth" + }, + { + "mark": "contour", + "data": { + "from": "flights" + }, + "x": "time", + "y": "delay", + "stroke": "white", + "strokeOpacity": 0.5, + "bandwidth": "$bandwidth", + "thresholds": "$thresholds" + } + ], + "colorScale": "symlog", + "colorScheme": "ylgnbu", + "xAxis": "top", + "xLabelAnchor": "center", + "xZero": true, + "yAxis": "right", + "yLabelAnchor": "center", + "marginTop": 30, + "marginLeft": 5, + "marginRight": 40, + "width": 700, + "height": 500 + } + ] +}; diff --git a/specs/ts/flights-hexbin.ts b/specs/ts/flights-hexbin.ts new file mode 100644 index 00000000..d64db241 --- /dev/null +++ b/specs/ts/flights-hexbin.ts @@ -0,0 +1,161 @@ +import { Spec } from '@uwdata/mosaic-spec'; + +export const spec : Spec = { + "meta": { + "title": "Flights Hexbin", + "description": "Hexagonal bins show the density of over 200,000 flights by departure time and arrival delay. Select regions in the marginal histograms to filter the density display.\n" + }, + "data": { + "flights": { + "file": "data/flights-200k.parquet" + } + }, + "params": { + "scale": { + "value": "log" + }, + "query": { + "select": "intersect" + } + }, + "vconcat": [ + { + "hconcat": [ + { + "input": "menu", + "label": "Color Scale", + "as": "$scale", + "options": [ + "log", + "linear", + "sqrt" + ] + }, + { + "hspace": 10 + }, + { + "legend": "color", + "for": "hexbins" + } + ] + }, + { + "hconcat": [ + { + "plot": [ + { + "mark": "rectY", + "data": { + "from": "flights" + }, + "x": { + "bin": "time" + }, + "y": { + "count": null + }, + "fill": "steelblue", + "inset": 0.5 + }, + { + "select": "intervalX", + "as": "$query" + } + ], + "margins": { + "left": 5, + "right": 5, + "top": 30, + "bottom": 0 + }, + "xDomain": "Fixed", + "xAxis": "top", + "yAxis": null, + "xLabelAnchor": "center", + "width": 605, + "height": 70 + }, + { + "hspace": 80 + } + ] + }, + { + "hconcat": [ + { + "name": "hexbins", + "plot": [ + { + "mark": "hexbin", + "data": { + "from": "flights", + "filterBy": "$query" + }, + "x": "time", + "y": "delay", + "fill": { + "count": null + }, + "binWidth": 10 + }, + { + "mark": "hexgrid", + "binWidth": 10 + } + ], + "colorScheme": "ylgnbu", + "colorScale": "$scale", + "margins": { + "left": 5, + "right": 0, + "top": 0, + "bottom": 5 + }, + "xAxis": null, + "yAxis": null, + "xyDomain": "Fixed", + "width": 600, + "height": 455 + }, + { + "plot": [ + { + "mark": "rectX", + "data": { + "from": "flights" + }, + "x": { + "count": null + }, + "y": { + "bin": "delay" + }, + "fill": "steelblue", + "inset": 0.5 + }, + { + "select": "intervalY", + "as": "$query" + } + ], + "margins": { + "left": 0, + "right": 50, + "top": 4, + "bottom": 5 + }, + "yDomain": [ + -60, + 180 + ], + "xAxis": null, + "yAxis": "right", + "yLabelAnchor": "center", + "width": 80, + "height": 455 + } + ] + } + ] +}; diff --git a/specs/ts/gaia.ts b/specs/ts/gaia.ts new file mode 100644 index 00000000..792c97aa --- /dev/null +++ b/specs/ts/gaia.ts @@ -0,0 +1,149 @@ +import { Spec } from '@uwdata/mosaic-spec'; + +export const spec : Spec = { + "meta": { + "title": "Gaia Star Catalog", + "description": "A 5M row sample of the 1.8B element Gaia star catalog.\nA `raster` sky map reveals our Milky Way galaxy. Select high parallax stars in the histogram to reveal a\n[Hertzsprung-Russel diagram](https://en.wikipedia.org/wiki/Hertzsprung%E2%80%93Russell_diagram)\nin the plot of stellar color vs. magnitude on the right.\n\n_You may need to wait a few seconds for the dataset to load._\n" + }, + "data": { + "gaia": "-- compute u and v with natural earth projection\nWITH prep AS (\n SELECT\n radians((-l + 540) % 360 - 180) AS lambda,\n radians(b) AS phi,\n asin(sqrt(3)/2 * sin(phi)) AS t,\n t^2 AS t2,\n t2^3 AS t6,\n *\n FROM 'https://uwdata.github.io/mosaic-datasets/data/gaia-5m.parquet'\n)\nSELECT\n (1.340264 * lambda * cos(t)) / (sqrt(3)/2 * (1.340264 + (-0.081106 * 3 * t2) + (t6 * (0.000893 * 7 + 0.003796 * 9 * t2)))) AS u,\n t * (1.340264 + (-0.081106 * t2) + (t6 * (0.000893 + 0.003796 * t2))) AS v,\n * EXCLUDE('t', 't2', 't6')\nFROM prep\nWHERE parallax BETWEEN -5 AND 20\n" + }, + "params": { + "brush": { + "select": "crossfilter" + }, + "bandwidth": 0, + "pixelSize": 2, + "scaleType": "sqrt" + }, + "hconcat": [ + { + "vconcat": [ + { + "plot": [ + { + "mark": "raster", + "data": { + "from": "gaia", + "filterBy": "$brush" + }, + "x": "u", + "y": "v", + "fill": "density", + "bandwidth": "$bandwidth", + "pixelSize": "$pixelSize" + }, + { + "select": "intervalXY", + "pixelSize": 2, + "as": "$brush" + } + ], + "xyDomain": "Fixed", + "colorScale": "$scaleType", + "colorScheme": "viridis", + "width": 440, + "height": 250, + "marginLeft": 25, + "marginTop": 20, + "marginRight": 1 + }, + { + "hconcat": [ + { + "plot": [ + { + "mark": "rectY", + "data": { + "from": "gaia", + "filterBy": "$brush" + }, + "x": { + "bin": "phot_g_mean_mag" + }, + "y": { + "count": null + }, + "fill": "steelblue", + "inset": 0.5 + }, + { + "select": "intervalX", + "as": "$brush" + } + ], + "xDomain": "Fixed", + "yScale": "$scaleType", + "yGrid": true, + "width": 220, + "height": 120, + "marginLeft": 65 + }, + { + "plot": [ + { + "mark": "rectY", + "data": { + "from": "gaia", + "filterBy": "$brush" + }, + "x": { + "bin": "parallax" + }, + "y": { + "count": null + }, + "fill": "steelblue", + "inset": 0.5 + }, + { + "select": "intervalX", + "as": "$brush" + } + ], + "xDomain": "Fixed", + "yScale": "$scaleType", + "yGrid": true, + "width": 220, + "height": 120, + "marginLeft": 65 + } + ] + } + ] + }, + { + "hspace": 10 + }, + { + "plot": [ + { + "mark": "raster", + "data": { + "from": "gaia", + "filterBy": "$brush" + }, + "x": "bp_rp", + "y": "phot_g_mean_mag", + "fill": "density", + "bandwidth": "$bandwidth", + "pixelSize": "$pixelSize" + }, + { + "select": "intervalXY", + "pixelSize": 2, + "as": "$brush" + } + ], + "xyDomain": "Fixed", + "colorScale": "$scaleType", + "colorScheme": "viridis", + "yReverse": true, + "width": 230, + "height": 370, + "marginLeft": 25, + "marginTop": 20, + "marginRight": 1 + } + ] +}; diff --git a/specs/ts/legends.ts b/specs/ts/legends.ts new file mode 100644 index 00000000..c74e4f3b --- /dev/null +++ b/specs/ts/legends.ts @@ -0,0 +1,329 @@ +import { Spec } from '@uwdata/mosaic-spec'; + +export const spec : Spec = { + "meta": { + "title": "Legends", + "description": "Tests for different legend types and configurations. We test both legends defined within plots (with a zero-size frame) and external legends that reference a named plot.\n" + }, + "params": { + "toggle": { + "select": "single" + }, + "interval": { + "select": "intersect" + }, + "domain": [ + "foo", + "bar", + "baz", + "bop", + "doh" + ] + }, + "plotDefaults": { + "margin": 0, + "width": 0, + "height": 20 + }, + "vconcat": [ + { + "hconcat": [ + { + "plot": [ + { + "legend": "color", + "label": "Color Swatch", + "as": "$toggle" + } + ], + "name": "color-categorical", + "colorScale": "categorical", + "colorDomain": "$domain" + }, + { + "hspace": 35 + }, + { + "legend": "color", + "for": "color-categorical", + "label": "Color Swatch (External)", + "as": "$toggle" + } + ] + }, + { + "hconcat": [ + { + "plot": [ + { + "legend": "symbol", + "label": "Symbol Swatch", + "as": "$toggle" + } + ], + "name": "symbol-categorical", + "symbolDomain": "$domain" + }, + { + "hspace": 35 + }, + { + "legend": "symbol", + "for": "symbol-categorical", + "label": "Symbol Swatch (External)", + "as": "$toggle" + } + ] + }, + { + "vspace": "1em" + }, + { + "hconcat": [ + { + "plot": [ + { + "legend": "opacity", + "label": "Opacity Ramp", + "as": "$interval" + } + ], + "name": "opacity-linear", + "opacityDomain": [ + 0, + 100 + ] + }, + { + "hspace": 30 + }, + { + "legend": "opacity", + "for": "opacity-linear", + "label": "Opacity Ramp (External)", + "as": "$interval" + } + ] + }, + { + "hconcat": [ + { + "plot": [ + { + "legend": "opacity" + } + ], + "name": "opacity-linear-no-label", + "opacityDomain": [ + 0, + 100 + ] + }, + { + "hspace": 30 + }, + { + "legend": "opacity", + "for": "opacity-linear-no-label" + } + ] + }, + { + "vspace": "1em" + }, + { + "hconcat": [ + { + "plot": [ + { + "legend": "color", + "label": "Linear Color Ramp", + "as": "$interval" + } + ], + "name": "color-linear", + "colorDomain": [ + 0, + 100 + ] + }, + { + "hspace": 30 + }, + { + "legend": "color", + "for": "color-linear", + "label": "Linear Color Ramp (External)", + "as": "$interval" + } + ] + }, + { + "hconcat": [ + { + "plot": [ + { + "legend": "color" + } + ], + "name": "color-linear-no-label", + "colorDomain": [ + 0, + 100 + ] + }, + { + "hspace": 30 + }, + { + "legend": "color", + "for": "color-linear-no-label" + } + ] + }, + { + "vspace": "1em" + }, + { + "hconcat": [ + { + "plot": [ + { + "legend": "color", + "label": "Logarithmic Color Ramp", + "as": "$interval" + } + ], + "name": "color-log", + "colorScale": "log", + "colorDomain": [ + 1, + 100 + ] + }, + { + "hspace": 30 + }, + { + "legend": "color", + "for": "color-log", + "label": "Logarithmic Color Ramp (External)", + "as": "$interval" + } + ] + }, + { + "hconcat": [ + { + "plot": [ + { + "legend": "color", + "label": "Diverging Color Ramp", + "as": "$interval" + } + ], + "name": "color-diverging", + "colorScale": "diverging", + "colorDomain": [ + -100, + 100 + ], + "colorConstant": 20 + }, + { + "hspace": 30 + }, + { + "legend": "color", + "for": "color-diverging", + "label": "Diverging Color Ramp (External)", + "as": "$interval" + } + ] + }, + { + "hconcat": [ + { + "plot": [ + { + "legend": "color", + "label": "Diverging Symlog Color Ramp", + "as": "$interval" + } + ], + "name": "color-diverging-symlog", + "colorScale": "diverging-symlog", + "colorDomain": [ + -100, + 100 + ], + "colorConstant": 20 + }, + { + "hspace": 30 + }, + { + "legend": "color", + "for": "color-diverging-symlog", + "label": "Diverging Symlog Color Ramp (External)", + "as": "$interval" + } + ] + }, + { + "hconcat": [ + { + "plot": [ + { + "legend": "color", + "label": "Quantize Color Ramp" + } + ], + "name": "color-quantize", + "colorScale": "quantize", + "colorDomain": [ + 0, + 100 + ] + }, + { + "hspace": 30 + }, + { + "legend": "color", + "for": "color-quantize", + "label": "Quantize Color Ramp (External)" + } + ] + }, + { + "hconcat": [ + { + "plot": [ + { + "legend": "color", + "label": "Threshold Color Ramp" + } + ], + "name": "color-threshold", + "colorScale": "threshold", + "colorDomain": [ + 0, + 10, + 20, + 40, + 80 + ] + }, + { + "hspace": 30 + }, + { + "legend": "color", + "for": "color-threshold", + "label": "Threshold Color Ramp (External)" + } + ] + } + ] +}; diff --git a/specs/ts/line-density.ts b/specs/ts/line-density.ts new file mode 100644 index 00000000..6ad72402 --- /dev/null +++ b/specs/ts/line-density.ts @@ -0,0 +1,115 @@ +import { Spec } from '@uwdata/mosaic-spec'; + +export const spec : Spec = { + "meta": { + "title": "Line Density", + "description": "The `denseLine` mark shows the densities of line series, here for a collection of stock prices. The top plot normalizes by arc length to remove the vertical artifacts visible in the unnormalized plot below. Select a region in the lower plot to zoom the upper plot. The bandwidth slider smooths the data, while the pixel size menu adjusts the raster resolution.\n" + }, + "data": { + "stocks_after_2006": { + "file": "data/stocks_after_2006.parquet", + "select": [ + "Symbol", + "Close", + "Date" + ], + "where": "Close < 100" + } + }, + "params": { + "brush": { + "select": "intersect" + }, + "bandwidth": 0, + "pixelSize": 2, + "schemeColor": "pubugn", + "scaleColor": "sqrt" + }, + "vconcat": [ + { + "hconcat": [ + { + "input": "slider", + "label": "Bandwidth (σ)", + "as": "$bandwidth", + "min": 0, + "max": 10, + "step": 0.1 + }, + { + "input": "menu", + "label": "Pixel Size", + "as": "$pixelSize", + "options": [ + 0.5, + 1, + 2 + ] + } + ] + }, + { + "vspace": 10 + }, + { + "plot": [ + { + "mark": "denseLine", + "data": { + "from": "stocks_after_2006", + "filterBy": "$brush" + }, + "x": "Date", + "y": "Close", + "z": "Symbol", + "fill": "density", + "bandwidth": "$bandwidth", + "pixelSize": "$pixelSize" + } + ], + "colorScheme": "$schemeColor", + "colorScale": "$scaleColor", + "yLabel": "Close (Normalized) ↑", + "yNice": true, + "margins": { + "left": 30, + "top": 20, + "right": 0 + }, + "width": 680, + "height": 240 + }, + { + "plot": [ + { + "mark": "denseLine", + "data": { + "from": "stocks_after_2006" + }, + "x": "Date", + "y": "Close", + "z": "Symbol", + "fill": "density", + "normalize": false, + "bandwidth": "$bandwidth", + "pixelSize": "$pixelSize" + }, + { + "select": "intervalXY", + "as": "$brush" + } + ], + "colorScheme": "$schemeColor", + "colorScale": "$scaleColor", + "yLabel": "Close (Unnormalized) ↑", + "yNice": true, + "margins": { + "left": 30, + "top": 20, + "right": 0 + }, + "width": 680, + "height": 240 + } + ] +}; diff --git a/specs/ts/line.ts b/specs/ts/line.ts new file mode 100644 index 00000000..ad5a5a09 --- /dev/null +++ b/specs/ts/line.ts @@ -0,0 +1,22 @@ +import { Spec } from '@uwdata/mosaic-spec'; + +export const spec : Spec = { + "data": { + "aapl": { + "file": "data/stocks.parquet", + "where": "Symbol = 'AAPL'" + } + }, + "plot": [ + { + "mark": "lineY", + "data": { + "from": "aapl" + }, + "x": "Date", + "y": "Close" + } + ], + "width": 680, + "height": 200 +}; diff --git a/specs/ts/linear-regression.ts b/specs/ts/linear-regression.ts new file mode 100644 index 00000000..f908a01b --- /dev/null +++ b/specs/ts/linear-regression.ts @@ -0,0 +1,46 @@ +import { Spec } from '@uwdata/mosaic-spec'; + +export const spec : Spec = { + "meta": { + "title": "Linear Regression", + "description": "A linear regression plot predicting athletes' heights based on their weights. Regression computation is performed in the database. The area around a regression line shows a 95% confidence interval. Select a region to view regression results for a data subset.\n" + }, + "data": { + "athletes": { + "file": "data/athletes.parquet" + } + }, + "plot": [ + { + "mark": "dot", + "data": { + "from": "athletes" + }, + "x": "weight", + "y": "height", + "fill": "sex", + "r": 2, + "opacity": 0.05 + }, + { + "mark": "regressionY", + "data": { + "from": "athletes", + "filterBy": "$query" + }, + "x": "weight", + "y": "height", + "stroke": "sex" + }, + { + "select": "intervalXY", + "as": "$query", + "brush": { + "fillOpacity": 0, + "stroke": "currentColor" + } + } + ], + "xyDomain": "Fixed", + "colorDomain": "Fixed" +}; diff --git a/specs/ts/mark-types.ts b/specs/ts/mark-types.ts new file mode 100644 index 00000000..0e0bb61c --- /dev/null +++ b/specs/ts/mark-types.ts @@ -0,0 +1,235 @@ +import { Spec } from '@uwdata/mosaic-spec'; + +export const spec : Spec = { + "meta": { + "title": "Mark Types", + "description": "A subset of supported mark types.\n\n- Row 1: `barY`, `lineY`, `text`, `tickY`, `areaY`\n- Row 2: `regressionY`, `hexbin`, `contour`, `heatmap`, `denseLine`\n" + }, + "data": { + "md": [ + { + "i": 0, + "u": "A", + "v": 2 + }, + { + "i": 1, + "u": "B", + "v": 8 + }, + { + "i": 2, + "u": "C", + "v": 3 + }, + { + "i": 3, + "u": "D", + "v": 7 + }, + { + "i": 4, + "u": "E", + "v": 5 + }, + { + "i": 5, + "u": "F", + "v": 4 + }, + { + "i": 6, + "u": "G", + "v": 6 + }, + { + "i": 7, + "u": "H", + "v": 1 + } + ] + }, + "plotDefaults": { + "xAxis": null, + "yAxis": null, + "margins": { + "left": 5, + "top": 5, + "right": 5, + "bottom": 5 + }, + "width": 160, + "height": 100, + "yDomain": [ + 0, + 9 + ] + }, + "vconcat": [ + { + "hconcat": [ + { + "mark": "barY", + "data": { + "from": "md" + }, + "x": "u", + "y": "v", + "fill": "steelblue" + }, + { + "mark": "lineY", + "data": { + "from": "md" + }, + "x": "u", + "y": "v", + "stroke": "steelblue", + "curve": "monotone-x", + "marker": "circle" + }, + { + "mark": "text", + "data": { + "from": "md" + }, + "x": "u", + "y": "v", + "text": "u", + "fill": "steelblue" + }, + { + "mark": "tickY", + "data": { + "from": "md" + }, + "x": "u", + "y": "v", + "stroke": "steelblue" + }, + { + "mark": "areaY", + "data": { + "from": "md" + }, + "x": "u", + "y": "v", + "fill": "steelblue" + } + ] + }, + { + "hconcat": [ + { + "plot": [ + { + "mark": "dot", + "data": { + "from": "md" + }, + "x": "i", + "y": "v", + "fill": "currentColor", + "r": 1.5 + }, + { + "mark": "regressionY", + "data": { + "from": "md" + }, + "x": "i", + "y": "v", + "stroke": "steelblue" + } + ], + "xDomain": [ + -0.5, + 7.5 + ] + }, + { + "plot": [ + { + "mark": "hexgrid", + "stroke": "#aaa", + "strokeOpacity": 0.5 + }, + { + "mark": "hexbin", + "data": { + "from": "md" + }, + "x": "i", + "y": "v", + "fill": { + "count": null + } + } + ], + "colorScheme": "blues", + "xDomain": [ + -1, + 8 + ] + }, + { + "plot": [ + { + "mark": "contour", + "data": { + "from": "md" + }, + "x": "i", + "y": "v", + "stroke": "steelblue", + "bandwidth": 15 + } + ], + "xDomain": [ + -1, + 8 + ] + }, + { + "plot": [ + { + "mark": "heatmap", + "data": { + "from": "md" + }, + "x": "i", + "y": "v", + "fill": "density", + "bandwidth": 15 + } + ], + "colorScheme": "blues", + "xDomain": [ + -1, + 8 + ] + }, + { + "plot": [ + { + "mark": "denseLine", + "data": { + "from": "md" + }, + "x": "i", + "y": "v", + "fill": "density", + "bandwidth": 2, + "pixelSize": 1 + } + ], + "colorScheme": "blues", + "xDomain": [ + -1, + 8 + ] + } + ] + } + ] +}; diff --git a/specs/ts/moving-average.ts b/specs/ts/moving-average.ts new file mode 100644 index 00000000..2ecb6258 --- /dev/null +++ b/specs/ts/moving-average.ts @@ -0,0 +1,92 @@ +import { Spec } from '@uwdata/mosaic-spec'; + +export const spec : Spec = { + "meta": { + "title": "Moving Average", + "description": "This plot shows daily reported COVID-19 cases from March 3 (day 1) to May 5, 2020 (day 64) in Berlin, Germany, as reported by the [Robert Koch Institute](https://www.rki.de/DE/Content/InfAZ/N/Neuartiges_Coronavirus/nCoV.html). We can smooth the raw counts using a moving average over various choices of window query frames.\n", + "credit": "Adapted from the [Arquero window query tutorial](https://observablehq.com/@uwdata/working-with-window-queries)." + }, + "data": { + "cases": { + "file": "data/berlin-covid.parquet" + } + }, + "params": { + "frame": [ + -6, + 0 + ] + }, + "vconcat": [ + { + "plot": [ + { + "mark": "rectY", + "data": { + "from": "cases" + }, + "x1": "day", + "x2": { + "sql": "day + 1" + }, + "inset": 1, + "y": "cases", + "fill": "steelblue" + }, + { + "mark": "lineY", + "data": { + "from": "cases" + }, + "x": { + "sql": "day + 0.5" + }, + "y": { + "avg": "cases", + "orderby": "day", + "rows": "$frame" + }, + "curve": "monotone-x", + "stroke": "currentColor" + } + ], + "width": 680, + "height": 300 + }, + { + "input": "menu", + "label": "Window Frame", + "as": "$frame", + "options": [ + { + "label": "7-day moving average, with prior 6 days: [-6, 0]", + "value": [ + -6, + 0 + ] + }, + { + "label": "7-day moving average, centered at current day: [-3, 3]", + "value": [ + -3, + 3 + ] + }, + { + "label": "Moving average, with all prior days [-∞, 0]", + "value": [ + null, + 0 + ] + }, + { + "label": "Global average [-∞, +∞]", + "value": [ + null, + null + ] + } + ] + } + ] +}; diff --git a/specs/ts/normalize.ts b/specs/ts/normalize.ts new file mode 100644 index 00000000..254b1861 --- /dev/null +++ b/specs/ts/normalize.ts @@ -0,0 +1,74 @@ +import { Spec } from '@uwdata/mosaic-spec'; + +export const spec : Spec = { + "meta": { + "title": "Normalized Stock Prices", + "description": "What is the return on investment for different days? Hover over the chart to normalize the stock prices for the percentage return on a given day. A `nearestX` interactor selects the nearest date, and parameterized expressions reactively update in response.\n" + }, + "data": { + "stocks": { + "file": "data/stocks.parquet" + }, + "labels": "SELECT MAX(Date) as Date, ARGMAX(Close, Date) AS Close, Symbol FROM stocks GROUP BY Symbol" + }, + "params": { + "point": { + "date": "2013-05-13" + } + }, + "plot": [ + { + "mark": "ruleX", + "x": "$point" + }, + { + "mark": "textX", + "x": "$point", + "text": "$point", + "frameAnchor": "top", + "lineAnchor": "bottom", + "dy": -7 + }, + { + "mark": "text", + "data": { + "from": "labels" + }, + "x": "Date", + "y": { + "sql": "Close / (SELECT MAX(Close) FROM stocks WHERE Symbol = source.Symbol AND Date = $point)" + }, + "dx": 2, + "text": "Symbol", + "fill": "Symbol", + "textAnchor": "start" + }, + { + "mark": "lineY", + "data": { + "from": "stocks" + }, + "x": "Date", + "y": { + "sql": "Close / (SELECT MAX(Close) FROM stocks WHERE Symbol = source.Symbol AND Date = $point)" + }, + "stroke": "Symbol" + }, + { + "select": "nearestX", + "as": "$point" + } + ], + "yScale": "log", + "yDomain": [ + 0.2, + 6 + ], + "yGrid": true, + "xLabel": null, + "yLabel": null, + "yTickFormat": "%", + "width": 680, + "height": 400, + "marginRight": 35 +}; diff --git a/specs/ts/nyc-taxi-rides.ts b/specs/ts/nyc-taxi-rides.ts new file mode 100644 index 00000000..e971459d --- /dev/null +++ b/specs/ts/nyc-taxi-rides.ts @@ -0,0 +1,159 @@ +import { Spec } from '@uwdata/mosaic-spec'; + +export const spec : Spec = { + "meta": { + "title": "NYC Taxi Rides", + "description": "Pickup and dropoff points for 1M NYC taxi rides on Jan 1-3, 2010.\nThis example projects lon/lat coordinates in the database upon load.\nSelect a region in one plot to filter the other.\nWhat spatial patterns can you find?\nRequires the DuckDB `spatial` extension.\n\n_You may need to wait a few seconds for the dataset to load._\n" + }, + "config": { + "extensions": "spatial" + }, + "data": { + "rides": { + "file": "https://uwdata.github.io/mosaic-datasets/data/nyc-rides-2010.parquet", + "select": [ + "pickup_datetime::TIMESTAMP AS datetime", + "ST_Transform(ST_Point(pickup_latitude, pickup_longitude), 'EPSG:4326', 'ESRI:102718') AS pick", + "ST_Transform(ST_Point(dropoff_latitude, dropoff_longitude), 'EPSG:4326', 'ESRI:102718') AS drop" + ] + }, + "trips": "SELECT\n (HOUR(datetime) + MINUTE(datetime)/60) AS time,\n ST_X(pick) AS px, ST_Y(pick) AS py,\n ST_X(drop) AS dx, ST_Y(drop) AS dy\nFROM rides\n" + }, + "params": { + "filter": { + "select": "crossfilter" + } + }, + "vconcat": [ + { + "hconcat": [ + { + "plot": [ + { + "mark": "raster", + "data": { + "from": "trips", + "filterBy": "$filter" + }, + "x": "px", + "y": "py", + "bandwidth": 0 + }, + { + "select": "intervalXY", + "as": "$filter" + }, + { + "mark": "text", + "data": [ + { + "label": "Taxi Pickups" + } + ], + "dx": 10, + "dy": 10, + "text": "label", + "fill": "black", + "fontSize": "1.2em", + "frameAnchor": "top-left" + } + ], + "width": 335, + "height": 550, + "margin": 0, + "xAxis": null, + "yAxis": null, + "xDomain": [ + 975000, + 1005000 + ], + "yDomain": [ + 190000, + 240000 + ], + "colorScale": "symlog", + "colorScheme": "blues" + }, + { + "hspace": 10 + }, + { + "plot": [ + { + "mark": "raster", + "data": { + "from": "trips", + "filterBy": "$filter" + }, + "x": "dx", + "y": "dy", + "bandwidth": 0 + }, + { + "select": "intervalXY", + "as": "$filter" + }, + { + "mark": "text", + "data": [ + { + "label": "Taxi Dropoffs" + } + ], + "dx": 10, + "dy": 10, + "text": "label", + "fill": "black", + "fontSize": "1.2em", + "frameAnchor": "top-left" + } + ], + "width": 335, + "height": 550, + "margin": 0, + "xAxis": null, + "yAxis": null, + "xDomain": [ + 975000, + 1005000 + ], + "yDomain": [ + 190000, + 240000 + ], + "colorScale": "symlog", + "colorScheme": "oranges" + } + ] + }, + { + "vspace": 10 + }, + { + "plot": [ + { + "mark": "rectY", + "data": { + "from": "trips" + }, + "x": { + "bin": "time" + }, + "y": { + "count": null + }, + "fill": "steelblue", + "inset": 0.5 + }, + { + "select": "intervalX", + "as": "$filter" + } + ], + "yTickFormat": "s", + "xLabel": "Pickup Hour →", + "width": 680, + "height": 100 + } + ] +}; diff --git a/specs/ts/observable-latency.ts b/specs/ts/observable-latency.ts new file mode 100644 index 00000000..ff301bc0 --- /dev/null +++ b/specs/ts/observable-latency.ts @@ -0,0 +1,123 @@ +import { Spec } from '@uwdata/mosaic-spec'; + +export const spec : Spec = { + "meta": { + "title": "Observable Latency", + "description": "Web request latency on Observable.com.\nEach pixel in the heatmap shows the most common route (URL pattern) at a given response latency within a time interval.\nUse the bar chart of most-requested routes to filter the heatmap and isolate specific patterns.\nOr, select a range in the heatmap to show the corresponding most-requested routes.\n\n_You may need to wait a few seconds for the dataset to load._\n", + "credit": "Adapted from an [Observable Framework example](https://observablehq.com/framework/examples/api/)." + }, + "data": { + "latency": { + "file": "https://uwdata.github.io/mosaic-datasets/data/observable-latency.parquet" + } + }, + "params": { + "filter": { + "select": "crossfilter" + } + }, + "vconcat": [ + { + "plot": [ + { + "mark": "frame", + "fill": "black" + }, + { + "mark": "raster", + "data": { + "from": "latency", + "filterBy": "$filter" + }, + "x": "time", + "y": "latency", + "fill": { + "argmax": [ + "route", + "count" + ] + }, + "fillOpacity": { + "sum": "count" + }, + "width": 2016, + "height": 500, + "imageRendering": "pixelated" + }, + { + "select": "intervalXY", + "as": "$filter" + } + ], + "colorDomain": "Fixed", + "colorScheme": "observable10", + "opacityDomain": [ + 0, + 25 + ], + "opacityClamp": true, + "yScale": "log", + "yLabel": "↑ Duration (ms)", + "yDomain": [ + 0.5, + 10000 + ], + "yTickFormat": "s", + "xScale": "utc", + "xLabel": null, + "xDomain": [ + 1706227200000, + 1706832000000 + ], + "width": 680, + "height": 300, + "margins": { + "left": 35, + "top": 20, + "bottom": 30, + "right": 20 + } + }, + { + "plot": [ + { + "mark": "barX", + "data": { + "from": "latency", + "filterBy": "$filter" + }, + "x": { + "sum": "count" + }, + "y": "route", + "fill": "route", + "sort": { + "y": "-x", + "limit": 15 + } + }, + { + "select": "toggleY", + "as": "$filter" + }, + { + "select": "toggleY", + "as": "$highlight" + }, + { + "select": "highlight", + "by": "$highlight" + } + ], + "colorDomain": "Fixed", + "xLabel": "Routes by Total Requests", + "xTickFormat": "s", + "yLabel": null, + "width": 680, + "height": 300, + "marginTop": 5, + "marginLeft": 220, + "marginBottom": 35 + } + ] +}; diff --git a/specs/ts/overview-detail.ts b/specs/ts/overview-detail.ts new file mode 100644 index 00000000..dfeb53fa --- /dev/null +++ b/specs/ts/overview-detail.ts @@ -0,0 +1,51 @@ +import { Spec } from '@uwdata/mosaic-spec'; + +export const spec : Spec = { + "meta": { + "title": "Overview + Detail", + "description": "Select the top \"overview\" series to zoom the \"focus\" view below. An `intervalX` interactor updates a selection that filters the focus view. The `line` and `area` marks can apply [M4](https://observablehq.com/@uwdata/m4-scalable-time-series-visualization) optimization to reduce the number of data points returned: rather than draw all points, a dramatically smaller subset can still faithfully represent these area charts.\n" + }, + "data": { + "walk": { + "file": "data/random-walk.parquet" + } + }, + "vconcat": [ + { + "plot": [ + { + "mark": "areaY", + "data": { + "from": "walk" + }, + "x": "t", + "y": "v", + "fill": "steelblue" + }, + { + "select": "intervalX", + "as": "$brush" + } + ], + "width": 680, + "height": 200 + }, + { + "plot": [ + { + "mark": "areaY", + "data": { + "from": "walk", + "filterBy": "$brush" + }, + "x": "t", + "y": "v", + "fill": "steelblue" + } + ], + "yDomain": "Fixed", + "width": 680, + "height": 200 + } + ] +}; diff --git a/specs/ts/pan-zoom.ts b/specs/ts/pan-zoom.ts new file mode 100644 index 00000000..f30f9f24 --- /dev/null +++ b/specs/ts/pan-zoom.ts @@ -0,0 +1,132 @@ +import { Spec } from '@uwdata/mosaic-spec'; + +export const spec : Spec = { + "meta": { + "title": "Pan & Zoom", + "description": "Linked panning and zooming across plots: drag to pan, scroll to zoom. `panZoom` interactors update a set of bound selections, one per unique axis.\n" + }, + "data": { + "penguins": { + "file": "data/penguins.parquet" + } + }, + "hconcat": [ + { + "vconcat": [ + { + "plot": [ + { + "mark": "frame" + }, + { + "mark": "dot", + "data": { + "from": "penguins" + }, + "x": "bill_length", + "y": "bill_depth", + "fill": "species", + "r": 2, + "clip": true + }, + { + "select": "panZoom", + "x": "$xs", + "y": "$ys" + } + ], + "width": 320, + "height": 240 + }, + { + "vspace": 10 + }, + { + "plot": [ + { + "mark": "frame" + }, + { + "mark": "dot", + "data": { + "from": "penguins" + }, + "x": "bill_length", + "y": "flipper_length", + "fill": "species", + "r": 2, + "clip": true + }, + { + "select": "panZoom", + "x": "$xs", + "y": "$zs" + } + ], + "width": 320, + "height": 240 + } + ] + }, + { + "hspace": 10 + }, + { + "vconcat": [ + { + "plot": [ + { + "mark": "frame" + }, + { + "mark": "dot", + "data": { + "from": "penguins" + }, + "x": "body_mass", + "y": "bill_depth", + "fill": "species", + "r": 2, + "clip": true + }, + { + "select": "panZoom", + "x": "$ws", + "y": "$ys" + } + ], + "width": 320, + "height": 240 + }, + { + "vspace": 10 + }, + { + "plot": [ + { + "mark": "frame" + }, + { + "mark": "dot", + "data": { + "from": "penguins" + }, + "x": "body_mass", + "y": "flipper_length", + "fill": "species", + "r": 2, + "clip": true + }, + { + "select": "panZoom", + "x": "$ws", + "y": "$zs" + } + ], + "width": 320, + "height": 240 + } + ] + } + ] +}; diff --git a/specs/ts/population-arrows.ts b/specs/ts/population-arrows.ts new file mode 100644 index 00000000..3fe33b42 --- /dev/null +++ b/specs/ts/population-arrows.ts @@ -0,0 +1,72 @@ +import { Spec } from '@uwdata/mosaic-spec'; + +export const spec : Spec = { + "meta": { + "title": "Population Change Arrows", + "description": "An `arrow` connects the positions in 1980 and 2015 of each city on this population × inequality chart. Color encodes variation.\n", + "credit": "Adapted from an [Observable Plot example](https://observablehq.com/@observablehq/plot-arrow-variation-chart)." + }, + "data": { + "metros": { + "file": "data/metros.parquet" + } + }, + "params": { + "bend": true + }, + "vconcat": [ + { + "legend": "color", + "for": "arrows", + "label": "Change in inequality from 1980 to 2015" + }, + { + "name": "arrows", + "plot": [ + { + "mark": "arrow", + "data": { + "from": "metros" + }, + "x1": "POP_1980", + "y1": "R90_10_1980", + "x2": "POP_2015", + "y2": "R90_10_2015", + "bend": "$bend", + "stroke": { + "sql": "R90_10_2015 - R90_10_1980" + } + }, + { + "mark": "text", + "data": { + "from": "metros" + }, + "x": "POP_2015", + "y": "R90_10_2015", + "filter": "highlight", + "text": "nyt_display", + "fill": "currentColor", + "dy": -6 + } + ], + "grid": true, + "inset": 10, + "xScale": "log", + "xLabel": "Population →", + "yLabel": "↑ Inequality", + "yTicks": 4, + "colorScheme": "BuRd", + "colorTickFormat": "+f" + }, + { + "input": "menu", + "label": "Bend Arrows?", + "options": [ + true, + false + ], + "as": "$bend" + } + ] +}; diff --git a/specs/ts/presidential-opinion.ts b/specs/ts/presidential-opinion.ts new file mode 100644 index 00000000..4cd9f179 --- /dev/null +++ b/specs/ts/presidential-opinion.ts @@ -0,0 +1,64 @@ +import { Spec } from '@uwdata/mosaic-spec'; + +export const spec : Spec = { + "meta": { + "title": "Presidential Opinion", + "description": "Opinion poll data on historical U.S. presidents. Image marks are used to show presidential pictures. The dropdown menu toggles the opinion metric shown.\n", + "credit": "Adapted from an [Observable Plot example](https://observablehq.com/@observablehq/plot-image-medals)." + }, + "data": { + "presidents": { + "file": "data/us-president-favorability.parquet" + } + }, + "params": { + "sign": 1 + }, + "vconcat": [ + { + "plot": [ + { + "mark": "ruleY", + "data": [ + 0 + ] + }, + { + "mark": "image", + "data": { + "from": "presidents" + }, + "x": "First Inauguration Date", + "y": { + "sql": "\"Very Favorable %\" + \"Somewhat Favorable %\" + $sign * (\"Very Unfavorable %\" + \"Somewhat Unfavorable %\")" + }, + "src": "Portrait URL", + "r": 20, + "preserveAspectRatio": "xMidYMin slice", + "title": "Name" + } + ], + "xInset": 20, + "xLabel": "First inauguration date →", + "yInsetTop": 4, + "yGrid": true, + "yLabel": "↑ Opinion (%)", + "yTickFormat": "+f" + }, + { + "input": "menu", + "label": "Opinion Metric", + "options": [ + { + "label": "Any Opinion", + "value": 1 + }, + { + "label": "Net Favorability", + "value": -1 + } + ], + "as": "$sign" + } + ] +}; diff --git a/specs/ts/seattle-temp.ts b/specs/ts/seattle-temp.ts new file mode 100644 index 00000000..901f392e --- /dev/null +++ b/specs/ts/seattle-temp.ts @@ -0,0 +1,66 @@ +import { Spec } from '@uwdata/mosaic-spec'; + +export const spec : Spec = { + "meta": { + "title": "Seattle Temperatures", + "description": "Historical monthly temperatures in Seattle, WA. The gray range shows the minimum and maximum recorded temperatures. The blue range shows the average lows and highs.\n" + }, + "data": { + "weather": { + "file": "data/seattle-weather.parquet" + } + }, + "plot": [ + { + "mark": "areaY", + "data": { + "from": "weather" + }, + "x": { + "dateMonth": "date" + }, + "y1": { + "max": "temp_max" + }, + "y2": { + "min": "temp_min" + }, + "fill": "#ccc", + "fillOpacity": 0.25, + "curve": "monotone-x" + }, + { + "mark": "areaY", + "data": { + "from": "weather" + }, + "x": { + "dateMonth": "date" + }, + "y1": { + "avg": "temp_max" + }, + "y2": { + "avg": "temp_min" + }, + "fill": "steelblue", + "fillOpacity": 0.75, + "curve": "monotone-x" + }, + { + "mark": "ruleY", + "data": [ + 15 + ], + "strokeOpacity": 0.5, + "strokeDasharray": [ + 5, + 5 + ] + } + ], + "xTickFormat": "%b", + "yLabel": "Temperature Range (°C)", + "width": 680, + "height": 300 +}; diff --git a/specs/ts/sorted-bars.ts b/specs/ts/sorted-bars.ts new file mode 100644 index 00000000..de2afb50 --- /dev/null +++ b/specs/ts/sorted-bars.ts @@ -0,0 +1,50 @@ +import { Spec } from '@uwdata/mosaic-spec'; + +export const spec : Spec = { + "meta": { + "title": "Sorted Bars", + "description": "Sort and limit an aggregate bar chart of gold medals by country.\n" + }, + "data": { + "athletes": { + "file": "data/athletes.parquet" + } + }, + "vconcat": [ + { + "input": "menu", + "label": "Sport", + "as": "$query", + "from": "athletes", + "column": "sport", + "value": "aquatics" + }, + { + "vspace": 10 + }, + { + "plot": [ + { + "mark": "barX", + "data": { + "from": "athletes", + "filterBy": "$query" + }, + "x": { + "sum": "gold" + }, + "y": "nationality", + "fill": "steelblue", + "sort": { + "y": "-x", + "limit": 10 + } + } + ], + "xLabel": "Gold Medals", + "yLabel": "Nationality", + "yLabelAnchor": "top", + "marginTop": 15 + } + ] +}; diff --git a/specs/ts/splom.ts b/specs/ts/splom.ts new file mode 100644 index 00000000..4bc95356 --- /dev/null +++ b/specs/ts/splom.ts @@ -0,0 +1,511 @@ +import { Spec } from '@uwdata/mosaic-spec'; + +export const spec : Spec = { + "meta": { + "title": "Scatter Plot Matrix (SPLOM)", + "description": "A scatter plot matrix enables inspection of pairwise bivariate distributions. Do points cluster or separate in some dimensions but not others? Select a region to highlight corresponding points across all plots.\n" + }, + "data": { + "penguins": { + "file": "data/penguins.parquet" + } + }, + "params": { + "brush": { + "select": "single" + } + }, + "plotDefaults": { + "xTicks": 3, + "yTicks": 4, + "xDomain": "Fixed", + "yDomain": "Fixed", + "colorDomain": "Fixed", + "marginTop": 5, + "marginBottom": 10, + "marginLeft": 10, + "marginRight": 5, + "xAxis": null, + "yAxis": null, + "xLabelAnchor": "center", + "yLabelAnchor": "center", + "xTickFormat": "s", + "yTickFormat": "s", + "width": 150, + "height": 150 + }, + "vconcat": [ + { + "hconcat": [ + { + "plot": [ + { + "mark": "frame", + "stroke": "#ccc" + }, + { + "mark": "dot", + "data": { + "from": "penguins" + }, + "x": "bill_length", + "y": "body_mass", + "fill": "species", + "r": 2 + }, + { + "select": "intervalXY", + "as": "$brush" + }, + { + "select": "highlight", + "by": "$brush", + "opacity": 0.1 + } + ], + "yAxis": "left", + "marginLeft": 45, + "width": 185 + }, + { + "plot": [ + { + "mark": "frame", + "stroke": "#ccc" + }, + { + "mark": "dot", + "data": { + "from": "penguins" + }, + "x": "bill_depth", + "y": "body_mass", + "fill": "species", + "r": 2 + }, + { + "select": "intervalXY", + "as": "$brush" + }, + { + "select": "highlight", + "by": "$brush", + "opacity": 0.1 + } + ] + }, + { + "plot": [ + { + "mark": "frame", + "stroke": "#ccc" + }, + { + "mark": "dot", + "data": { + "from": "penguins" + }, + "x": "flipper_length", + "y": "body_mass", + "fill": "species", + "r": 2 + }, + { + "select": "intervalXY", + "as": "$brush" + }, + { + "select": "highlight", + "by": "$brush", + "opacity": 0.1 + } + ] + }, + { + "plot": [ + { + "mark": "frame", + "stroke": "#ccc" + }, + { + "mark": "dot", + "data": { + "from": "penguins" + }, + "x": "body_mass", + "y": "body_mass", + "fill": "species", + "r": 2 + }, + { + "select": "intervalXY", + "as": "$brush" + }, + { + "select": "highlight", + "by": "$brush", + "opacity": 0.1 + } + ] + } + ] + }, + { + "hconcat": [ + { + "plot": [ + { + "mark": "frame", + "stroke": "#ccc" + }, + { + "mark": "dot", + "data": { + "from": "penguins" + }, + "x": "bill_length", + "y": "flipper_length", + "fill": "species", + "r": 2 + }, + { + "select": "intervalXY", + "as": "$brush" + }, + { + "select": "highlight", + "by": "$brush", + "opacity": 0.1 + } + ], + "yAxis": "left", + "marginLeft": 45, + "width": 185 + }, + { + "plot": [ + { + "mark": "frame", + "stroke": "#ccc" + }, + { + "mark": "dot", + "data": { + "from": "penguins" + }, + "x": "bill_depth", + "y": "flipper_length", + "fill": "species", + "r": 2 + }, + { + "select": "intervalXY", + "as": "$brush" + }, + { + "select": "highlight", + "by": "$brush", + "opacity": 0.1 + } + ] + }, + { + "plot": [ + { + "mark": "frame", + "stroke": "#ccc" + }, + { + "mark": "dot", + "data": { + "from": "penguins" + }, + "x": "flipper_length", + "y": "flipper_length", + "fill": "species", + "r": 2 + }, + { + "select": "intervalXY", + "as": "$brush" + }, + { + "select": "highlight", + "by": "$brush", + "opacity": 0.1 + } + ] + }, + { + "plot": [ + { + "mark": "frame", + "stroke": "#ccc" + }, + { + "mark": "dot", + "data": { + "from": "penguins" + }, + "x": "body_mass", + "y": "flipper_length", + "fill": "species", + "r": 2 + }, + { + "select": "intervalXY", + "as": "$brush" + }, + { + "select": "highlight", + "by": "$brush", + "opacity": 0.1 + } + ] + } + ] + }, + { + "hconcat": [ + { + "plot": [ + { + "mark": "frame", + "stroke": "#ccc" + }, + { + "mark": "dot", + "data": { + "from": "penguins" + }, + "x": "bill_length", + "y": "bill_depth", + "fill": "species", + "r": 2 + }, + { + "select": "intervalXY", + "as": "$brush" + }, + { + "select": "highlight", + "by": "$brush", + "opacity": 0.1 + } + ], + "yAxis": "left", + "marginLeft": 45, + "width": 185 + }, + { + "plot": [ + { + "mark": "frame", + "stroke": "#ccc" + }, + { + "mark": "dot", + "data": { + "from": "penguins" + }, + "x": "bill_depth", + "y": "bill_depth", + "fill": "species", + "r": 2 + }, + { + "select": "intervalXY", + "as": "$brush" + }, + { + "select": "highlight", + "by": "$brush", + "opacity": 0.1 + } + ] + }, + { + "plot": [ + { + "mark": "frame", + "stroke": "#ccc" + }, + { + "mark": "dot", + "data": { + "from": "penguins" + }, + "x": "flipper_length", + "y": "bill_depth", + "fill": "species", + "r": 2 + }, + { + "select": "intervalXY", + "as": "$brush" + }, + { + "select": "highlight", + "by": "$brush", + "opacity": 0.1 + } + ] + }, + { + "plot": [ + { + "mark": "frame", + "stroke": "#ccc" + }, + { + "mark": "dot", + "data": { + "from": "penguins" + }, + "x": "body_mass", + "y": "bill_depth", + "fill": "species", + "r": 2 + }, + { + "select": "intervalXY", + "as": "$brush" + }, + { + "select": "highlight", + "by": "$brush", + "opacity": 0.1 + } + ] + } + ] + }, + { + "hconcat": [ + { + "plot": [ + { + "mark": "frame", + "stroke": "#ccc" + }, + { + "mark": "dot", + "data": { + "from": "penguins" + }, + "x": "bill_length", + "y": "bill_length", + "fill": "species", + "r": 2 + }, + { + "select": "intervalXY", + "as": "$brush" + }, + { + "select": "highlight", + "by": "$brush", + "opacity": 0.1 + } + ], + "yAxis": "left", + "xAxis": "bottom", + "marginLeft": 45, + "marginBottom": 35, + "width": 185, + "height": 175 + }, + { + "plot": [ + { + "mark": "frame", + "stroke": "#ccc" + }, + { + "mark": "dot", + "data": { + "from": "penguins" + }, + "x": "bill_depth", + "y": "bill_length", + "fill": "species", + "r": 2 + }, + { + "select": "intervalXY", + "as": "$brush" + }, + { + "select": "highlight", + "by": "$brush", + "opacity": 0.1 + } + ], + "xAxis": "bottom", + "height": 175, + "marginBottom": 35 + }, + { + "plot": [ + { + "mark": "frame", + "stroke": "#ccc" + }, + { + "mark": "dot", + "data": { + "from": "penguins" + }, + "x": "flipper_length", + "y": "bill_length", + "fill": "species", + "r": 2 + }, + { + "select": "intervalXY", + "as": "$brush" + }, + { + "select": "highlight", + "by": "$brush", + "opacity": 0.1 + } + ], + "xAxis": "bottom", + "height": 175, + "marginBottom": 35 + }, + { + "plot": [ + { + "mark": "frame", + "stroke": "#ccc" + }, + { + "mark": "dot", + "data": { + "from": "penguins" + }, + "x": "body_mass", + "y": "bill_length", + "fill": "species", + "r": 2 + }, + { + "select": "intervalXY", + "as": "$brush" + }, + { + "select": "highlight", + "by": "$brush", + "opacity": 0.1 + } + ], + "xAxis": "bottom", + "height": 175, + "marginBottom": 35 + } + ] + } + ] +}; diff --git a/specs/ts/symbols.ts b/specs/ts/symbols.ts new file mode 100644 index 00000000..01021d90 --- /dev/null +++ b/specs/ts/symbols.ts @@ -0,0 +1,72 @@ +import { Spec } from '@uwdata/mosaic-spec'; + +export const spec : Spec = { + "meta": { + "title": "Symbol Plots", + "description": "Two scatter plots with `dot` marks: one with stroked symbols, the other filled.\n" + }, + "data": { + "penguins": { + "file": "data/penguins.parquet" + } + }, + "vconcat": [ + { + "hconcat": [ + { + "name": "stroked", + "plot": [ + { + "mark": "dot", + "data": { + "from": "penguins" + }, + "x": "body_mass", + "y": "flipper_length", + "stroke": "species", + "symbol": "species" + } + ], + "grid": true, + "xLabel": "Body mass (g) →", + "yLabel": "↑ Flipper length (mm)" + }, + { + "legend": "symbol", + "for": "stroked", + "columns": 1 + } + ] + }, + { + "vspace": 20 + }, + { + "hconcat": [ + { + "name": "filled", + "plot": [ + { + "mark": "dot", + "data": { + "from": "penguins" + }, + "x": "body_mass", + "y": "flipper_length", + "fill": "species", + "symbol": "species" + } + ], + "grid": true, + "xLabel": "Body mass (g) →", + "yLabel": "↑ Flipper length (mm)" + }, + { + "legend": "symbol", + "for": "filled", + "columns": 1 + } + ] + } + ] +}; diff --git a/specs/ts/table.ts b/specs/ts/table.ts new file mode 100644 index 00000000..7d80b11c --- /dev/null +++ b/specs/ts/table.ts @@ -0,0 +1,16 @@ +import { Spec } from '@uwdata/mosaic-spec'; + +export const spec : Spec = { + "meta": { + "title": "Sortable Table", + "description": "A sortable, \"infinite scroll\" `table` view over a backing database table. Click column headers to sort, or command-click to reset the order. Data is queried as needed as the table is sorted or scrolled.\n" + }, + "data": { + "flights": { + "file": "data/flights-200k.parquet" + } + }, + "input": "table", + "from": "flights", + "height": 300 +}; diff --git a/specs/ts/unemployment.ts b/specs/ts/unemployment.ts new file mode 100644 index 00000000..7b84049b --- /dev/null +++ b/specs/ts/unemployment.ts @@ -0,0 +1,47 @@ +import { Spec } from '@uwdata/mosaic-spec'; + +export const spec : Spec = { + "meta": { + "title": "U.S. Unemployment", + "description": "A choropleth map of unemployment rates for U.S. counties. Requires the DuckDB `spatial` extension.\n", + "credit": "Adapted from an [Observable Plot example](https://observablehq.com/@observablehq/plot-us-choropleth)." + }, + "data": { + "counties": { + "type": "spatial", + "file": "data/us-counties-10m.json", + "layer": "counties" + }, + "rates": { + "file": "data/us-county-unemployment.parquet" + }, + "combined": "SELECT a.geom AS geom, b.rate AS rate FROM counties AS a, rates AS b WHERE a.id = b.id\n" + }, + "vconcat": [ + { + "legend": "color", + "for": "county-map", + "label": "Unemployment (%)" + }, + { + "name": "county-map", + "plot": [ + { + "mark": "geo", + "data": { + "from": "combined" + }, + "fill": "rate", + "title": { + "sql": "concat(rate, '%')" + } + } + ], + "margin": 0, + "colorScale": "quantile", + "colorN": 9, + "colorScheme": "blues", + "projectionType": "albers-usa" + } + ] +}; diff --git a/specs/ts/us-county-map.ts b/specs/ts/us-county-map.ts new file mode 100644 index 00000000..a9431146 --- /dev/null +++ b/specs/ts/us-county-map.ts @@ -0,0 +1,56 @@ +import { Spec } from '@uwdata/mosaic-spec'; + +export const spec : Spec = { + "meta": { + "title": "U.S. Counties", + "description": "A map of U.S. counties. County name tooltips are anchored to invisible centroid dot marks. Requires the DuckDB `spatial` extension.\n" + }, + "data": { + "counties": { + "type": "spatial", + "file": "data/us-counties-10m.json", + "layer": "counties" + }, + "states": { + "type": "spatial", + "file": "data/us-counties-10m.json", + "layer": "states" + } + }, + "plot": [ + { + "mark": "geo", + "data": { + "from": "counties" + }, + "stroke": "currentColor", + "strokeWidth": 0.25 + }, + { + "mark": "geo", + "data": { + "from": "states" + }, + "stroke": "currentColor", + "strokeWidth": 1 + }, + { + "mark": "dot", + "data": { + "from": "counties" + }, + "x": { + "centroidX": "geom" + }, + "y": { + "centroidY": "geom" + }, + "r": 2, + "fill": "transparent", + "tip": true, + "title": "name" + } + ], + "margin": 0, + "projectionType": "albers" +}; diff --git a/specs/ts/us-state-map.ts b/specs/ts/us-state-map.ts new file mode 100644 index 00000000..992e7a97 --- /dev/null +++ b/specs/ts/us-state-map.ts @@ -0,0 +1,44 @@ +import { Spec } from '@uwdata/mosaic-spec'; + +export const spec : Spec = { + "meta": { + "title": "U.S. States", + "description": "A map of U.S. states overlaid with computed centroids. Requires the DuckDB `spatial` extension.\n", + "credit": "Adapted from an [Observable Plot example](https://observablehq.com/@observablehq/plot-state-centroids)." + }, + "data": { + "states": { + "type": "spatial", + "file": "data/us-counties-10m.json", + "layer": "states" + } + }, + "plot": [ + { + "mark": "geo", + "data": { + "from": "states" + }, + "stroke": "currentColor", + "strokeWidth": 1 + }, + { + "mark": "dot", + "data": { + "from": "states" + }, + "x": { + "centroidX": "geom" + }, + "y": { + "centroidY": "geom" + }, + "r": 2, + "fill": "steelblue", + "tip": true, + "title": "name" + } + ], + "margin": 0, + "projectionType": "albers" +}; diff --git a/specs/ts/voronoi.ts b/specs/ts/voronoi.ts new file mode 100644 index 00000000..085acfc9 --- /dev/null +++ b/specs/ts/voronoi.ts @@ -0,0 +1,113 @@ +import { Spec } from '@uwdata/mosaic-spec'; + +export const spec : Spec = { + "meta": { + "title": "Voronoi Diagram", + "description": "The `voronoi` mark shows the regions closest to each point. It is [constructed from its dual](https://observablehq.com/@mbostock/the-delaunays-dual), a Delaunay triangle mesh. Reveal triangulations or convex hulls using the dropdowns.\n", + "credit": "Adapted from an [Observable Plot example](https://observablehq.com/@observablehq/plot-voronoi-scatterplot)." + }, + "data": { + "penguins": { + "file": "data/penguins.parquet" + } + }, + "params": { + "mesh": 0, + "hull": 0 + }, + "vconcat": [ + { + "plot": [ + { + "mark": "voronoi", + "data": { + "from": "penguins" + }, + "x": "bill_length", + "y": "bill_depth", + "stroke": "white", + "strokeWidth": 1, + "strokeOpacity": 0.5, + "inset": 1, + "fill": "species", + "fillOpacity": 0.2 + }, + { + "mark": "hull", + "data": { + "from": "penguins" + }, + "x": "bill_length", + "y": "bill_depth", + "stroke": "species", + "strokeOpacity": "$hull", + "strokeWidth": 1.5 + }, + { + "mark": "delaunayMesh", + "data": { + "from": "penguins" + }, + "x": "bill_length", + "y": "bill_depth", + "z": "species", + "stroke": "species", + "strokeOpacity": "$mesh", + "strokeWidth": 1 + }, + { + "mark": "dot", + "data": { + "from": "penguins" + }, + "x": "bill_length", + "y": "bill_depth", + "fill": "species", + "r": 2 + }, + { + "mark": "frame" + } + ], + "inset": 10, + "width": 680 + }, + { + "hconcat": [ + { + "input": "menu", + "label": "Delaunay Mesh", + "options": [ + { + "value": 0, + "label": "Hide" + }, + { + "value": 0.5, + "label": "Show" + } + ], + "as": "$mesh" + }, + { + "hspace": 5 + }, + { + "input": "menu", + "label": "Convex Hull", + "options": [ + { + "value": 0, + "label": "Hide" + }, + { + "value": 1, + "label": "Show" + } + ], + "as": "$hull" + } + ] + } + ] +}; diff --git a/specs/ts/walmart-openings.ts b/specs/ts/walmart-openings.ts new file mode 100644 index 00000000..7ec4fa3c --- /dev/null +++ b/specs/ts/walmart-openings.ts @@ -0,0 +1,67 @@ +import { Spec } from '@uwdata/mosaic-spec'; + +export const spec : Spec = { + "meta": { + "title": "Walmart Openings", + "description": "Maps showing Walmart store openings per decade. Requires the DuckDB `spatial` extension.\n", + "credit": "Adapted from an [Observable Plot example](https://observablehq.com/@observablehq/plot-map-large-multiples)." + }, + "data": { + "states": { + "type": "spatial", + "file": "data/us-counties-10m.json", + "layer": "states" + }, + "nation": { + "type": "spatial", + "file": "data/us-counties-10m.json", + "layer": "nation" + }, + "walmarts": { + "file": "data/walmarts.parquet" + } + }, + "vconcat": [ + { + "plot": [ + { + "mark": "geo", + "data": { + "from": "states" + }, + "stroke": "#aaa", + "strokeWidth": 1 + }, + { + "mark": "geo", + "data": { + "from": "nation" + }, + "stroke": "currentColor" + }, + { + "mark": "dot", + "data": { + "from": "walmarts" + }, + "x": "longitude", + "y": "latitude", + "fy": { + "sql": "10 * decade(date)" + }, + "r": 1.5, + "fill": "blue" + }, + { + "mark": "axisFy", + "frameAnchor": "top", + "dy": 30, + "tickFormat": "d" + } + ], + "margin": 0, + "fyLabel": null, + "projectionType": "albers" + } + ] +}; diff --git a/specs/ts/weather.ts b/specs/ts/weather.ts new file mode 100644 index 00000000..5f01f802 --- /dev/null +++ b/specs/ts/weather.ts @@ -0,0 +1,129 @@ +import { Spec } from '@uwdata/mosaic-spec'; + +export const spec : Spec = { + "meta": { + "title": "Seattle Weather", + "description": "An interactive view of Seattle’s weather, including maximum temperature, amount of precipitation, and type of weather. By dragging on the scatter plot, you can see the proportion of days in that range that have sun, fog, drizzle, rain, or snow.\n", + "credit": "Based on a [Vega-Lite/Altair example](https://vega.github.io/vega-lite/examples/interactive_seattle_weather.html) by Jake Vanderplas." + }, + "data": { + "weather": { + "file": "data/seattle-weather.parquet" + } + }, + "params": { + "click": { + "select": "single" + }, + "domain": [ + "sun", + "fog", + "drizzle", + "rain", + "snow" + ], + "colors": [ + "#e7ba52", + "#a7a7a7", + "#aec7e8", + "#1f77b4", + "#9467bd" + ] + }, + "vconcat": [ + { + "hconcat": [ + { + "plot": [ + { + "mark": "dot", + "data": { + "from": "weather", + "filterBy": "$click" + }, + "x": { + "dateMonthDay": "date" + }, + "y": "temp_max", + "fill": "weather", + "r": "precipitation", + "fillOpacity": 0.7 + }, + { + "select": "intervalX", + "as": "$range", + "brush": { + "fill": "none", + "stroke": "#888" + } + }, + { + "select": "highlight", + "by": "$range", + "fill": "#ccc", + "fillOpacity": 0.2 + }, + { + "legend": "color", + "as": "$click", + "columns": 1 + } + ], + "xyDomain": "Fixed", + "xTickFormat": "%b", + "colorDomain": "$domain", + "colorRange": "$colors", + "rDomain": "Fixed", + "rRange": [ + 2, + 10 + ], + "width": 680, + "height": 300 + } + ] + }, + { + "plot": [ + { + "mark": "barX", + "data": { + "from": "weather" + }, + "x": { + "count": null + }, + "y": "weather", + "fill": "#ccc", + "fillOpacity": 0.2 + }, + { + "mark": "barX", + "data": { + "from": "weather", + "filterBy": "$range" + }, + "x": { + "count": null + }, + "y": "weather", + "fill": "weather" + }, + { + "select": "toggleY", + "as": "$click" + }, + { + "select": "highlight", + "by": "$click" + } + ], + "xDomain": "Fixed", + "yDomain": "$domain", + "yLabel": null, + "colorDomain": "$domain", + "colorRange": "$colors", + "width": 680 + } + ] +}; diff --git a/specs/ts/wind-map.ts b/specs/ts/wind-map.ts new file mode 100644 index 00000000..b0cccc24 --- /dev/null +++ b/specs/ts/wind-map.ts @@ -0,0 +1,59 @@ +import { Spec } from '@uwdata/mosaic-spec'; + +export const spec : Spec = { + "meta": { + "title": "Wind Map", + "description": "`vector` marks on a grid show both direction and intensity—here, the speed of winds. Expressions for `rotate`, `length`, and `stroke` values are evaluated in the database.\n", + "credit": "Adapted from an [Observable Plot example](https://observablehq.com/@observablehq/plot-wind-map)." + }, + "data": { + "wind": { + "file": "data/wind.parquet" + } + }, + "params": { + "length": 2 + }, + "vconcat": [ + { + "legend": "color", + "for": "wind-map", + "label": "Speed (m/s)" + }, + { + "name": "wind-map", + "plot": [ + { + "mark": "vector", + "data": { + "from": "wind" + }, + "x": "longitude", + "y": "latitude", + "rotate": { + "sql": "degrees(atan2(u, v))" + }, + "length": { + "sql": "$length * sqrt(u * u + v * v)" + }, + "stroke": { + "sql": "sqrt(u * u + v * v)" + } + } + ], + "lengthScale": "identity", + "colorZero": true, + "inset": 10, + "aspectRatio": 1, + "width": 680 + }, + { + "input": "slider", + "min": 1, + "max": 7, + "step": 0.1, + "as": "$length", + "label": "Vector Length" + } + ] +}; From 4d6afe3d7978600e046833437931694103140883 Mon Sep 17 00:00:00 2001 From: jheer Date: Tue, 2 Apr 2024 08:58:44 -0700 Subject: [PATCH 04/25] feat: Incremental progress on JSON specification types. --- packages/spec/src/ast/DataNode.d.ts | 109 ++++----- packages/spec/src/ast/ExpressionNode.d.ts | 3 + packages/spec/src/ast/HConcatNode.d.ts | 9 +- packages/spec/src/ast/HSpaceNode.d.ts | 9 +- packages/spec/src/ast/InputNode.d.ts | 145 ++++++++++- packages/spec/src/ast/ParamNode.d.ts | 30 ++- packages/spec/src/ast/PlotAttributeNode.d.ts | 241 ++++++++++++++++++- packages/spec/src/ast/PlotFromNode.d.ts | 18 +- packages/spec/src/ast/PlotLegendNode.d.ts | 26 +- packages/spec/src/ast/PlotMarkNode.d.ts | 95 +++++++- packages/spec/src/ast/PlotNode.d.ts | 18 +- packages/spec/src/ast/SelectionNode.d.ts | 10 +- packages/spec/src/ast/SpecNode.d.ts | 48 ++-- packages/spec/src/ast/TransformNode.d.ts | 191 +++++++++++++++ packages/spec/src/ast/VConcatNode.d.ts | 9 +- packages/spec/src/ast/VSpaceNode.d.ts | 9 +- 16 files changed, 835 insertions(+), 135 deletions(-) create mode 100644 packages/spec/src/ast/ExpressionNode.d.ts create mode 100644 packages/spec/src/ast/TransformNode.d.ts diff --git a/packages/spec/src/ast/DataNode.d.ts b/packages/spec/src/ast/DataNode.d.ts index 7e6b28bf..86dfbc20 100644 --- a/packages/spec/src/ast/DataNode.d.ts +++ b/packages/spec/src/ast/DataNode.d.ts @@ -1,82 +1,63 @@ -export type SpecDataObjects = object[]; - -export type SpecDataDefinition = - | SpecDataQuery - | SpecDataArray - | SpecDataTable - | SpecDataParquet - | SpecDataCSV - | SpecDataSpatial - | SpecDataJSON - | SpecDataJSONObjects; - -export type SpecDataQuery = string; - -export type SpecDataArray = Array; - -export type SpecDataBaseOptions = { +export interface SpecDataBaseOptions { select?: string[]; where?: string | string[]; view?: boolean; temp?: boolean; replace?: boolean; -}; +} + +export type SpecDataQuery = string; -export type SpecDataObject = - | SpecDataTable; +export type SpecDataArray = Array; -export type SpecDataTable = { +export interface SpecDataFile { + /** + * The data file to load. If no type option is provided, + * the file suffix must be one of `.csv`, `.json`, or `.parquet`. + */ + file: `${string}.parquet` | `${string}.csv` | `${string}.json`; +} + +export interface SpecDataTable extends SpecDataBaseOptions { type: 'table'; query: string; -} & SpecDataBaseOptions; - -export type SpecDataParquet = - & ( - { type: 'parquet'; file: string; } | - { file: `${string}.parquet` } - ) - & SpecDataBaseOptions; +} -export type SpecDataCSV = - & ( - { type: 'csv'; file: string; } | - { file: `${string}.csv` } - ) - & SpecDataCSVOptions - & SpecDataBaseOptions; +export interface SpecDataParquet extends SpecDataBaseOptions { + type: 'parquet'; + file: string; +} -export interface SpecDataCSVOptions { +export interface SpecDataCSV extends SpecDataBaseOptions { + type: 'csv'; + file: string; delimiter?: string, sample_size?: number; } -export type SpecDataSpatial = - & { - type: 'spatial', - file: string, - } - & SpecDataSpatialOptions - & SpecDataBaseOptions; - -export type SpecDataSpatialOptions = { - [key: string]: any -}; +export interface SpecDataSpatial extends SpecDataBaseOptions { + type: 'spatial'; + file: string; + layer?: string; +} -export type SpecDataJSON = - & ( - { type: 'json'; file: string; } | - { file: `${string}.json` } - ) - & SpecDataJSONOptions - & SpecDataBaseOptions; +export interface SpecDataJSON extends SpecDataBaseOptions { + type: 'json'; + file: string; +} -export type SpecDataJSONOptions = { - [key: string]: any -}; +export interface SpecDataJSONObjects extends SpecDataBaseOptions { + type?: 'json'; + data: object[]; +} -export type SpecDataJSONObjects = - & { - type?: 'json', - data: SpecDataObjects - } - & SpecDataBaseOptions; +export type SpecDataDefinition = + | SpecDataQuery + | SpecDataArray + | SpecDataFile + | SpecDataTable + | SpecDataParquet + | SpecDataCSV + | SpecDataSpatial + | SpecDataJSON + | SpecDataJSONObjects; diff --git a/packages/spec/src/ast/ExpressionNode.d.ts b/packages/spec/src/ast/ExpressionNode.d.ts new file mode 100644 index 00000000..1474a37a --- /dev/null +++ b/packages/spec/src/ast/ExpressionNode.d.ts @@ -0,0 +1,3 @@ +export type SpecExpression = + | { sql: string, label?: string } + | { agg: string, label?: string }; diff --git a/packages/spec/src/ast/HConcatNode.d.ts b/packages/spec/src/ast/HConcatNode.d.ts index 0cff18a7..4aee8c55 100644 --- a/packages/spec/src/ast/HConcatNode.d.ts +++ b/packages/spec/src/ast/HConcatNode.d.ts @@ -1,5 +1,8 @@ import { SpecComponent } from './SpecNode.js'; -export type SpecHConcatNode = { - hconcat: SpecComponent[] -}; +export interface SpecHConcatNode { + /** + * Horizontally concatenate components in a row layout. + */ + hconcat: SpecComponent[]; +} diff --git a/packages/spec/src/ast/HSpaceNode.d.ts b/packages/spec/src/ast/HSpaceNode.d.ts index c696e8b2..7cdbef43 100644 --- a/packages/spec/src/ast/HSpaceNode.d.ts +++ b/packages/spec/src/ast/HSpaceNode.d.ts @@ -1,3 +1,8 @@ -export type SpecHSpace = { +export interface SpecHSpace { + /** + * Horizontal space to place between components. + * Number values indicate screen pixels. + * String values may use CSS units (em, pt, px, etc). + */ hspace: number | string; -}; +} diff --git a/packages/spec/src/ast/InputNode.d.ts b/packages/spec/src/ast/InputNode.d.ts index 15764e85..6fe2fde8 100644 --- a/packages/spec/src/ast/InputNode.d.ts +++ b/packages/spec/src/ast/InputNode.d.ts @@ -1,5 +1,140 @@ -export type SpecInput = { - input: 'menu' | 'search' | 'slider' | 'table'; -} & { - [key: string]: any -}; +import { SpecParamRef } from './ParamRefNode.js'; + +export type SpecInput = + | SpecMenu + | SpecSearch + | SpecSlider + | SpecTable; + +export interface SpecMenu { + /** Create a menu input widget. */ + input: 'menu'; + /** A selection to which the current menu option is added. */ + as?: SpecParamRef; + /** + * A backing data table from which to pull menu options. + * Used in conjunction with the column property. + */ + from?: any; + /** + * A backing data column from which to pull menu options. + * Used in conjunction with the from property. + */ + column?: any; + /** + * A selection by which to filter a backing data table. + * Used in conjunction with the from and column properties. + */ + filterBy?: SpecParamRef; + /** A text label for this input. */ + label?: any; + /** + * An array of menu options, as literal values or option objects. + * Option objects have a `value` property and an optional `label` property. + * If no label is provided, the (string-coerced) value is used. + */ + options?: any; + /** The initial selected menu option. */ + value?: any; +} + +export interface SpecSearch { + /** Create a text search input widget. */ + input: 'search'; + /** A selection to which the text search query is added. */ + as?: SpecParamRef; + /** + * A backing data table from which to pull valid search results. + * Used in conjunction with the column property. + */ + from?: any; + /** + * A backing data column from which to pull valid search results. + * Used in conjunction with the from property. + */ + column?: any; + /** + * A selection by which to filter a backing data table. + * Used in conjunction with the from and column properties. + */ + filterBy?: SpecParamRef; + /** A text label for this input. */ + label?: any; + /** + * The type of text search to perform. + * One of: `"contains"` (default), `"prefix"`, `"suffix"`, or `"regexp"`. + */ + type?: any; +} + +export interface SpecSlider { + /** Create a slider input widget. */ + input: 'slider'; + /** A selection to which the current slider value is added. */ + as?: SpecParamRef; + /** + * A backing data table from which to determine the slider range. + * Used in conjunction with the column property. + */ + from?: any; + /** + * A backing data column from which to determine the slider range. + * Used in conjunction with the from property. + */ + column?: any; + /** + * A selection by which to filter a backing data table. + * Used in conjunction with the from and column properties. + */ + filterBy?: SpecParamRef; + /** The minumum slider value. */ + min?: any; + /** The maximum slider value. */ + max?: any; + /** The slider step, the increment between consecutive values. */ + step?: any; + /** A text label for this input. */ + label?: any; + /** The initial slider value. */ + value?: any; + /** The width of the slider in screen pixels. */ + width?: any; +} + +export interface SpecTable { + /** Create a table grid widget. */ + input: 'table'; + /** + * A backing data table from which to pull data for this widget. + */ + from?: any; + /** + * A list of column names to include in the table widget. + * If unspecified, all table columns are shown. + */ + columns?: any; + /** + * A selection by which to filter a backing data table. + * Used in conjunction with the from and column properties. + */ + filterBy?: SpecParamRef; + /** + * An object of per-column alignment values. + * Column names should be object keys, mapped to alignment values. + * Valid alignment values are: `"left"`, `"right"`, `"center"`, and `"justify"`. + * By default, numbers are right-aligned and other values are left-aligned. + */ + align?: any; + /** + * If a number, sets the total width of the table widget, in pixels. + * If an object, provides per-column pixel width values. + * Column names should be object keys, mapped to numeric width values. + */ + width?: any; + /** The maximum width of the table widget, in pixels. */ + maxWidth?: any; + /** The height of the table widget, in pixels. */ + height?: any; + /** The number of rows load in a new batch upon table scroll. */ + rowBatch?: any; +} diff --git a/packages/spec/src/ast/ParamNode.d.ts b/packages/spec/src/ast/ParamNode.d.ts index 0ab0cf65..af7978e1 100644 --- a/packages/spec/src/ast/ParamNode.d.ts +++ b/packages/spec/src/ast/ParamNode.d.ts @@ -1,21 +1,29 @@ +import { SpecParamRef } from './ParamRefNode.js'; import { SpecSelection } from './SelectionNode.js'; -export type SpecParamDefinition = - | SpecParamValue - | SpecParam - | SpecSelection; +export interface SpecParamBase { + select?: 'value'; +} -export type SpecParamValue = - | SpecParamLiteral - | Array; +export interface SpecParam extends SpecParamBase { + value: SpecParamValue; +} + +export interface SpecParamDate extends SpecParamBase { + date: string; +} export type SpecParamLiteral = | string | number | boolean; -export type SpecParamRef = `$${string}`; +export type SpecParamValue = + | SpecParamLiteral + | Array; -export type SpecParam = - | { select?: 'value', value: SpecParamValue } - | { select?: 'value', date: string }; + export type SpecParamDefinition = + | SpecParamValue + | SpecParam + | SpecParamDate + | SpecSelection; diff --git a/packages/spec/src/ast/PlotAttributeNode.d.ts b/packages/spec/src/ast/PlotAttributeNode.d.ts index 06609fc6..e05618ca 100644 --- a/packages/spec/src/ast/PlotAttributeNode.d.ts +++ b/packages/spec/src/ast/PlotAttributeNode.d.ts @@ -1,3 +1,240 @@ -export type SpecPlotAttributes = { - [key: string]: any +import { SpecParamRef } from './ParamRefNode.js'; + +export interface SpecPlotAttributes { + name?: string; + margins?: { + top?: number | SpecParamRef; + bottom?: number | SpecParamRef; + left?: number | SpecParamRef; + right?: number | SpecParamRef; + }; + margin?: number | SpecParamRef; + xyDomain?: any; + + // plot-level attributes + style?: any; + width?: any; + height?: any; + marginLeft?: any; + marginRight?: any; + marginTop?: any; + marginBottom?: any; + align?: any; + aspectRatio?: any; + axis?: any; + inset?: any; + grid?: any; + label?: any; + padding?: any; + round?: any; + + // x scale attributes + xScale?: any; + xDomain?: any; + xRange?: any; + xNice?: any; + xInset?: any; + xInsetLeft?: any; + xInsetRight?: any; + xClamp?: any; + xRound?: any; + xAlign?: any; + xPadding?: any; + xPaddingInner?: any; + xPaddingOuter?: any; + xAxis?: any; + xTicks?: any; + xTickSize?: any; + xTickSpacing?: any; + xTickPadding?: any; + xTickFormat?: any; + xTickRotate?: any; + xGrid?: any; + xLine?: any; + xLabel?: any; + xLabelAnchor?: any; + xLabelOffset?: any; + xFontVariant?: any; + xAriaLabel?: any; + xAriaDescription?: any; + xReverse?: any; + xZero?: any; + xBase?: any; + xExponent?: any; + xConstant?: any; + + // y scale attributes + yScale?: any; + yDomain?: any; + yRange?: any; + yNice?: any; + yInset?: any; + yInsetTop?: any; + yInsetBottom?: any; + yClamp?: any; + yRound?: any; + yAlign?: any; + yPadding?: any; + yPaddingInner?: any; + yPaddingOuter?: any; + yAxis?: any; + yTicks?: any; + yTickSize?: any; + yTickSpacing?: any; + yTickPadding?: any; + yTickFormat?: any; + yTickRotate?: any; + yGrid?: any; + yLine?: any; + yLabel?: any; + yLabelAnchor?: any; + yLabelOffset?: any; + yFontVariant?: any; + yAriaLabel?: any; + yAriaDescription?: any; + yReverse?: any; + yZero?: any; + yBase?: any; + yExponent?: any; + yConstant?: any; + + // facet attributes + facetMargin?: any; + facetMarginTop?: any; + facetMarginBottom?: any; + facetMarginLeft?: any; + facetMarginRight?: any; + facetGrid?: any; + facetLabel?: any; + + // fx scale attributes + fxDomain?: any; + fxRange?: any; + fxNice?: any; + fxInset?: any; + fxInsetLeft?: any; + fxInsetRight?: any; + fxRound?: any; + fxAlign?: any; + fxPadding?: any; + fxPaddingInner?: any; + fxPaddingOuter?: any; + fxAxis?: any; + fxTicks?: any; + fxTickSize?: any; + fxTickSpacing?: any; + fxTickPadding?: any; + fxTickFormat?: any; + fxTickRotate?: any; + fxGrid?: any; + fxLine?: any; + fxLabel?: any; + fxLabelAnchor?: any; + fxLabelOffset?: any; + fxFontVariant?: any; + fxAriaLabel?: any; + fxAriaDescription?: any; + fxReverse?: any; + + // fy scale attributes + fyDomain?: any; + fyRange?: any; + fyNice?: any; + fyInset?: any; + fyInsetTop?: any; + fyInsetBottom?: any; + fyRound?: any; + fyAlign?: any; + fyPadding?: any; + fyPaddingInner?: any; + fyPaddingOuter?: any; + fyAxis?: any; + fyTicks?: any; + fyTickSize?: any; + fyTickSpacing?: any; + fyTickPadding?: any; + fyTickFormat?: any; + fyTickRotate?: any; + fyGrid?: any; + fyLine?: any; + fyLabel?: any; + fyLabelAnchor?: any; + fyLabelOffset?: any; + fyFontVariant?: any; + fyAriaLabel?: any; + fyAriaDescription?: any; + fyReverse?: any; + + // color scale attributes + colorScale?: any; + colorDomain?: any; + colorRange?: any; + colorClamp?: any; + colorN?: any; + colorNice?: any; + colorScheme?: any; + colorInterpolate?: any; + colorPivot?: any; + colorSymmetric?: any; + colorLabel?: any; + colorReverse?: any; + colorZero?: any; + colorTickFormat?: any; + colorBase?: any; + colorExponent?: any; + colorConstant?: any; + + // opacity scale attributes + opacityScale?: any; + opacityDomain?: any; + opacityRange?: any; + opacityClamp?: any; + opacityNice?: any; + opacityLabel?: any; + opacityReverse?: any; + opacityZero?: any; + opacityTickFormat?: any; + opacityBase?: any; + opacityExponent?: any; + opacityConstant?: any; + + // symbol scale attributes + symbolScale?: any; + symbolDomain?: any; + symbolRange?: any; + + // r scale attributes + rScale?: any; + rDomain?: any; + rRange?: any; + rClamp?: any; + rNice?: any; + rZero?: any; + rBase?: any; + rExponent?: any; + rConstant?: any; + + // length scale attributes + lengthScale?: any; + lengthDomain?: any; + lengthRange?: any; + lengthClamp?: any; + lengthNice?: any; + lengthZero?: any; + lengthBase?: any; + lengthExponent?: any; + lengthConstant?: any; + + // projection attributes + projectionType?: any; + projectionParallels?: any; + projectionPrecision?: any; + projectionRotate?: any; + projectionDomain?: any; + projectionInset?: any; + projectionInsetLeft?: any; + projectionInsetRight?: any; + projectionInsetTop?: any; + projectionInsetBottom?: any; + projectionClip?: any; }; diff --git a/packages/spec/src/ast/PlotFromNode.d.ts b/packages/spec/src/ast/PlotFromNode.d.ts index eab6bb1f..19271eed 100644 --- a/packages/spec/src/ast/PlotFromNode.d.ts +++ b/packages/spec/src/ast/PlotFromNode.d.ts @@ -1,14 +1,24 @@ import { SpecParamRef } from './ParamRefNode.js'; +/** Input data for marks. */ export type SpecPlotMarkData = | SpecPlotDataInline | SpecPlotFrom; +/** + * An array of inline data values to visualize. As this data does not come + * from a database, it can not be filtered by interactive selections. + */ export type SpecPlotDataInline = any[]; -export type SpecPlotFrom = { +export interface SpecPlotFrom { + /** The name of the backing data table. */ from: string; + /** A selection with which to filter the mark data. */ filterBy?: SpecParamRef; -} & { - [key: string]: any -}; + /** + * A flag (default `true`) to enable any mark-specific query optimizations. + * Set to `false` to disable optimizations to aid testing and debugging. + */ + optimize?: boolean; +}; \ No newline at end of file diff --git a/packages/spec/src/ast/PlotLegendNode.d.ts b/packages/spec/src/ast/PlotLegendNode.d.ts index 8408dee1..fe28839b 100644 --- a/packages/spec/src/ast/PlotLegendNode.d.ts +++ b/packages/spec/src/ast/PlotLegendNode.d.ts @@ -1,16 +1,24 @@ import { SpecParamRef } from './ParamNode.js'; -export const LegendType = 'color' | 'opacity' | 'symbol'; +export type LegendType = + | 'color' + | 'opacity' + | 'symbol'; -export type SpecPlotLegend = { +export interface SpecPlotLegend { legend: LegendType; as?: SpecParamRef; field?: string; -} & { - // todo: legend options - [key: string]: any -}; + tickSize?: number; + marginTop?: number; + marginBottom?: number; + marginLeft?: number; + marginRight?: number; + width?: number; + height?: number; + columns?: number; +} -export type SpecLegend = - & SpecPlotLegend - & { for: string }; +export interface SpecLegend extends SpecPlotLegend { + for: string; +} diff --git a/packages/spec/src/ast/PlotMarkNode.d.ts b/packages/spec/src/ast/PlotMarkNode.d.ts index ddb92b5a..33c9a2dd 100644 --- a/packages/spec/src/ast/PlotMarkNode.d.ts +++ b/packages/spec/src/ast/PlotMarkNode.d.ts @@ -1,4 +1,7 @@ +import { SpecExpression } from './ExpressionNode.js'; +import { SpecParamRef } from './ParamRefNode.js'; import { SpecPlotMarkData } from './PlotFromNode.js'; +import { SpecTransform } from './TransformNode.js'; export type MarkType = | 'area' @@ -63,9 +66,91 @@ export type MarkType = | 'sphere' | 'graticule'; -export type SpecPlotMark = { - mark: MarkType, - data?: SpecPlotMarkData -} & { - [key: string]: any +export type MarkOption = + | SpecParamRef + | number + | string + | boolean + | SpecExpression + | SpecTransform + | any[]; + +/** + * A graphical mark (layer) for a plot. + */ +export interface SpecPlotMark { + /** The mark type. */ + mark: MarkType; + /** The data the mark should visualize. */ + data?: SpecPlotMarkData; + + filter?: MarkOption; + sort?: any; + clip?: MarkOption; + + title?: MarkOption; + tip?: MarkOption; + href?: MarkOption; + target?: MarkOption; + frameAnchor?: MarkOption; + + x?: MarkOption; + x1?: MarkOption; + x2?: MarkOption; + y?: MarkOption; + y1?: MarkOption; + y2?: MarkOption; + fx?: MarkOption; + fy?: MarkOption; + + z?: MarkOption; + r?: MarkOption; + length?: MarkOption; + rotate?: MarkOption; + symbol?: MarkOption; + opacity?: MarkOption; + fill?: MarkOption; + fillOpacity?: MarkOption; + stroke?: MarkOption; + strokeOpacity?: MarkOption; + strokeWidth?: MarkOption; + strokeDasharray?: MarkOption; + + geometry?: MarkOption; + + text?: MarkOption; + textAnchor?: MarkOption; + lineAnchor?: MarkOption; + fontSize?: MarkOption; + dx?: MarkOption; + dy?: MarkOption; + + anchor?: MarkOption; + label?: MarkOption; + labelAnchor?: MarkOption; + tickFormat?: MarkOption; + tickSize?: MarkOption; + tickPadding?: MarkOption; + + bandwidth?: MarkOption; + pixelSize?: MarkOption; + interpolate?: MarkOption; + pad?: MarkOption; + + width?: MarkOption; + height?: MarkOption; + + src?: MarkOption; + imageRendering?: MarkOption; + preserveAspectRatio?: MarkOption; + + thresholds?: MarkOption; + + normalize?: MarkOption; + + binWidth?: MarkOption; + inset?: MarkOption; + curve?: MarkOption; + marker?: MarkOption; + bend?: MarkOption; } diff --git a/packages/spec/src/ast/PlotNode.d.ts b/packages/spec/src/ast/PlotNode.d.ts index 2a0cf5d7..1441a544 100644 --- a/packages/spec/src/ast/PlotNode.d.ts +++ b/packages/spec/src/ast/PlotNode.d.ts @@ -3,11 +3,17 @@ import { SpecPlotInteractor } from './PlotInteractorNode.js'; import { SpecPlotLegend } from './PlotLegendNode.js'; import { SpecPlotMark } from './PlotMarkNode.js'; -export type SpecPlot = { - plot: SpecPlotEntry[] -} & SpecPlotAttributes; - export type SpecPlotEntry = - | SpecPlotMark | SpecPlotInteractor - | SpecPlotLegend; \ No newline at end of file + | SpecPlotLegend + | SpecPlotMark; + +export interface SpecPlot extends SpecPlotAttributes { + /** + * An array of plot marks, interactors, or legends. + * Marks are graphical elements that make up plot layers. + * Unless otherwise configured, interactors will use the nearest + * previous mark as a basis for which data fields to select. + */ + plot: SpecPlotEntry[]; +} diff --git a/packages/spec/src/ast/SelectionNode.d.ts b/packages/spec/src/ast/SelectionNode.d.ts index 49e22772..4f355bd6 100644 --- a/packages/spec/src/ast/SelectionNode.d.ts +++ b/packages/spec/src/ast/SelectionNode.d.ts @@ -1,10 +1,10 @@ -export type SpecSelection = { - select: SelectionType, - cross?: boolean -}; - export type SelectionType = | 'crossfilter' | 'intersect' | 'single' | 'union'; + +export interface SpecSelection { + select: SelectionType; + cross?: boolean; +} diff --git a/packages/spec/src/ast/SpecNode.d.ts b/packages/spec/src/ast/SpecNode.d.ts index be60b124..029471b9 100644 --- a/packages/spec/src/ast/SpecNode.d.ts +++ b/packages/spec/src/ast/SpecNode.d.ts @@ -22,28 +22,34 @@ import { PlotNode } from './PlotNode.js'; import { PlotMarkNode } from './PlotMarkNode.js'; import { PlotLegendNode } from './PlotLegendNode.js'; -export type Spec = { - meta?: SpecMeta; - config?: SpecConfig; - data?: SpecData; - params?: SpecParams; - plotDefaults?: SpecPlotAttributes; -} & SpecComponent; - -export type SpecMeta = { +/** Specification metadata. */ +export interface SpecMeta extends Record { + /** The specification title. */ title?: string; + /** A description of the specification content. */ description?: string; + /** Credits or other acknowledgements. */ credit?: string; -} & Record; +} -export type SpecConfig = { +/** Configuration options. */ +export interface SpecConfig extends Record { extensions?: string | string[]; -} & Record; +} -export type SpecData = Record; +/** Top-level dataset definitions. */ +export interface SpecData { + /** A dataset name and definition. */ + [name: string]: SpecDataDefinition; +} -export type SpecParams = Record; +/** Top-level Param and Selection definitions. */ +export interface SpecData { + /** A parameter name and the Param or Selection definition. */ + [name: string]: SpecParamDefinition; +} +/** A specifcation component such as a plot, input widget, or layout. */ export type SpecComponent = | SpecHConcatNode | SpecVConcatNode @@ -53,3 +59,17 @@ export type SpecComponent = | SpecPlot | SpecPlotMark | SpecLegend; + +/** A declarative Mosaic specification. */ +export type Spec = { + /** Specification metadata. */ + meta?: SpecMeta; + /** Configuration options. */ + config?: SpecConfig; + /** Dataset definitions. */ + data?: SpecData; + /** Param and Selection definitions. */ + params?: SpecParams; + /** A default set of attributes to apply to all plot components. */ + plotDefaults?: SpecPlotAttributes; +} & SpecComponent; diff --git a/packages/spec/src/ast/TransformNode.d.ts b/packages/spec/src/ast/TransformNode.d.ts new file mode 100644 index 00000000..381250f3 --- /dev/null +++ b/packages/spec/src/ast/TransformNode.d.ts @@ -0,0 +1,191 @@ +import { SpecParamRef } from './ParamRefNode.js'; + +export type SpecTransformField = + | string + | SpecParamRef; + +export interface SpecWindowOptions { + orderby?: SpecTransformField | SpecTransformField[]; + partitionby?: SpecTransformField | SpecTransformField[]; + rows?: SpecParamRef | (number | null)[]; + range?: SpecParamRef | (number | null)[]; +} + +export interface SpecAggregateOptions { + distinct?: boolean; +} + +export type SpecTransformArgument = + | string + | number + | boolean; + +export interface SpecBinTransform { + bin: SpecTransfromArgument | [SpecTransfromArgument]; +} + +export interface SpecDateMonthTransform { + dateMonth: SpecTransfromArgument | [SpecTransfromArgument]; +} + +export interface SpecDateMonthDayTransform { + dateMonthDay: SpecTransfromArgument | [SpecTransfromArgument]; +} + +export interface SpecDateDayTransform { + dateDay: SpecTransfromArgument | [SpecTransfromArgument]; +} + +export interface SpecCentroidTransform { + centroid: SpecTransfromArgument | [SpecTransfromArgument]; +} + +export interface SpecCentroidXTransform { + centroidX: SpecTransfromArgument | [SpecTransfromArgument]; +} + +export interface SpecCentroidYTransform { + centroidY: SpecTransfromArgument | [SpecTransfromArgument]; +} + +export interface SpecGeoJSONTransform { + geojson: SpecTransfromArgument | [SpecTransfromArgument]; +} + +export interface SpecArgmaxTransform extends SpecAggregateOptions, SpecWindowOptions { + argmax: [SpecTransfromArgument, SpecTransfromArgument]; +} + +export interface SpecArgminTransform extends SpecAggregateOptions, SpecWindowOptions { + argmin: [SpecTransfromArgument, SpecTransfromArgument]; +} + +export interface SpecAvgTransform extends SpecAggregateOptions, SpecWindowOptions { + avg: SpecTransfromArgument | [SpecTransfromArgument]; +} + +export interface SpecCountTransform extends SpecAggregateOptions, SpecWindowOptions { + count: []; +} + +export interface SpecFirstTransform extends SpecAggregateOptions, SpecWindowOptions { + first: SpecTransfromArgument | [SpecTransfromArgument]; +} + +export interface SpecLastTransform extends SpecAggregateOptions, SpecWindowOptions { + last: SpecTransfromArgument | [SpecTransfromArgument]; +} + +export interface SpecMaxTransform extends SpecAggregateOptions, SpecWindowOptions { + max: SpecTransfromArgument | [SpecTransfromArgument]; +} + +export interface SpecMinTransform extends SpecAggregateOptions, SpecWindowOptions { + min: SpecTransfromArgument | [SpecTransfromArgument]; +} + +export interface SpecMedianTransform extends SpecAggregateOptions, SpecWindowOptions { + median: SpecTransfromArgument | [SpecTransfromArgument]; +} + +export interface SpecModeTransform extends SpecAggregateOptions, SpecWindowOptions { + mode: SpecTransfromArgument | [SpecTransfromArgument]; +} + +export interface SpecProductTransform extends SpecAggregateOptions, SpecWindowOptions { + product: SpecTransfromArgument | [SpecTransfromArgument]; +} + +export interface SpecQuantileTransform extends SpecAggregateOptions, SpecWindowOptions { + quantile: [SpecTransfromArgument, SpecTransfromArgument]; +} + +export interface SpecSumTransform extends SpecAggregateOptions, SpecWindowOptions { + sum: SpecTransfromArgument | [SpecTransfromArgument]; +} + +export interface SpecRowNumberTransform extends SpecWindowOptions { + row_number: []; +} + +export interface SpecRankTransform extends SpecWindowOptions { + rank: []; +} + +export interface SpecDenseRankTransform extends SpecWindowOptions { + dense_rank: []; +} + +export interface SpecPercentRankTransform extends SpecWindowOptions { + percent_rank: []; +} + +export interface SpecCumeDistTransform extends SpecWindowOptions { + cume_dist: []; +} + +export interface SpecNTileTransform extends SpecWindowOptions { + ntile: SpecTransfromArgument | [SpecTransfromArgument]; +} + +export interface SpecRankTransform extends SpecWindowOptions { + rank: SpecTransfromArgument | [SpecTransfromArgument]; +} + +export interface SpecLagTransform extends SpecWindowOptions { + lag: SpecTransfromArgument | [SpecTransfromArgument, SpecTransfromArgument?, SpecTransfromArgument?]; +} + +export interface SpecLeadTransform extends SpecWindowOptions { + lag: SpecTransfromArgument | [SpecTransfromArgument, SpecTransfromArgument?, SpecTransfromArgument?]; +} + +export interface SpecFirstValueTransform extends SpecWindowOptions { + first_value: SpecTransfromArgument | [SpecTransfromArgument]; +} + +export interface SpecLastValueTransform extends SpecWindowOptions { + last_value: SpecTransfromArgument | [SpecTransfromArgument]; +} + +export interface SpecNthValueTransform extends SpecWindowOptions { + nth_value: SpecTransfromArgument | [SpecTransfromArgument, SpecTransfromArgument?]; +} + +export type SpecTransform = + | SpecBinTransform + | SpecDateMonthTransform + | SpecDateMonthDayTransform + | SpecDateDayTransform + | SpecCentroidTransform + | SpecCentroidXTransform + | SpecCentroidYTransform + | SpecGeoJSONTransform + | SpecArgmaxTransform + | SpecArgminTransform + | SpecAvgTransform + | SpecCountTransform + | SpecMaxTransform + | SpecMinTransform + | SpecFirstTransform + | SpecLastTransform + | SpecMaxTransform + | SpecMinTransform + | SpecMedianTransform + | SpecModeTransform + | SpecProductTransform + | SpecQuantileTransform + | SpecSumTransform + | SpecRowNumberTransform + | SpecRowNumberTransform + | SpecRankTransform + | SpecDenseRankTransform + | SpecPercentRankTransform + | SpecCumeDistTransform + | SpecNTileTransform + | SpecRankTransform + | SpecLagTransform + | SpecLeadTransform + | SpecFirstValueTransform + | SpecLastValueTransform + | SpecNthValueTransform; diff --git a/packages/spec/src/ast/VConcatNode.d.ts b/packages/spec/src/ast/VConcatNode.d.ts index 4b4fe8a6..4d5bbab9 100644 --- a/packages/spec/src/ast/VConcatNode.d.ts +++ b/packages/spec/src/ast/VConcatNode.d.ts @@ -1,5 +1,8 @@ import { SpecComponent } from './SpecNode.js'; -export type SpecVConcatNode = { - vconcat: SpecComponent[] -}; +export interface SpecVConcatNode { + /** + * Vertically concatenate components in a column layout. + */ + vconcat: SpecComponent[]; +} diff --git a/packages/spec/src/ast/VSpaceNode.d.ts b/packages/spec/src/ast/VSpaceNode.d.ts index f2aa35bb..8a6c53bf 100644 --- a/packages/spec/src/ast/VSpaceNode.d.ts +++ b/packages/spec/src/ast/VSpaceNode.d.ts @@ -1,3 +1,8 @@ -export type SpecVSpace = { +export interface SpecVSpace { + /** + * Vertical space to place between components. + * Number values indicate screen pixels. + * String values may use CSS units (em, pt, px, etc). + */ vspace: number | string; -}; +} From f4645bcc365259b8bbab89b6e0d8870a73272afa Mon Sep 17 00:00:00 2001 From: Dominik Moritz Date: Tue, 2 Apr 2024 12:14:34 -0400 Subject: [PATCH 05/25] docs: add types for setPlot and plot attributes (#353) * chore(deps): bump actions/configure-pages from 4 to 5 (#352) * docs: add types for setPlot and plot attributes --------- Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/deploy.yml | 2 +- packages/plot/src/marks/Grid2DMark.js | 5 +++++ packages/plot/src/marks/Mark.js | 5 +++++ packages/plot/src/plot.js | 20 ++++++++++++++++++++ 4 files changed, 31 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 6577201e..627eb3d6 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -39,7 +39,7 @@ jobs: - name: Build run: npm run docs:build - - uses: actions/configure-pages@v4 + - uses: actions/configure-pages@v5 - uses: actions/upload-pages-artifact@v3 with: path: docs/.vitepress/dist diff --git a/packages/plot/src/marks/Grid2DMark.js b/packages/plot/src/marks/Grid2DMark.js index 7a61b19d..35275e24 100644 --- a/packages/plot/src/marks/Grid2DMark.js +++ b/packages/plot/src/marks/Grid2DMark.js @@ -59,6 +59,11 @@ export class Grid2DMark extends Mark { }); } + /** + * @param {import('../plot.js').Plot} plot The plot. + * @param {number} index + * @returns {this} + */ setPlot(plot, index) { const update = () => { if (this.hasFieldInfo()) this.requestUpdate(); }; plot.addAttributeListener('xDomain', update); diff --git a/packages/plot/src/marks/Mark.js b/packages/plot/src/marks/Mark.js index ad947a0b..a75725eb 100644 --- a/packages/plot/src/marks/Mark.js +++ b/packages/plot/src/marks/Mark.js @@ -89,6 +89,11 @@ export class Mark extends MosaicClient { } } + /** + * @param {import('../plot.js').Plot} plot The plot. + * @param {number} index + * @returns {this} + */ setPlot(plot, index) { this.plot = plot; this.index = index; diff --git a/packages/plot/src/plot.js b/packages/plot/src/plot.js index 1fbf3a9d..c2b138bb 100644 --- a/packages/plot/src/plot.js +++ b/packages/plot/src/plot.js @@ -72,10 +72,20 @@ export class Plot { this.synch.resolve(); } + /** + * @param {string} name The attribute to return. + * @returns {*} The value of the attribute. + */ getAttribute(name) { return this.attributes[name]; } + /** + * @param {string} name The name of the attribute to set. + * @param {*} value The value to set. + * @param {{silent: boolean}?} options Options for setting the attribute. + * @returns {boolean} whether the value changed. + */ setAttribute(name, value, options) { if (distinct(this.attributes[name], value)) { if (value === undefined) { @@ -91,6 +101,11 @@ export class Plot { return false; } + /** + * @param {string} name The attribute name. + * @param {*} callback The function to call when the attribute changes. + * @returns {this} + */ addAttributeListener(name, callback) { const map = this.listeners || (this.listeners = new Map); if (!map.has(name)) map.set(name, new Set); @@ -98,6 +113,11 @@ export class Plot { return this; } + /** + * @param {string} name The attribute name. + * @param {*} callback The function to call when the attribute changes. + * @returns {void} + */ removeAttributeListener(name, callback) { return this.listeners?.get(name)?.delete(callback); } From 278991c45f1236d75ae72c08ab222f5831d180bc Mon Sep 17 00:00:00 2001 From: Dominik Moritz Date: Tue, 2 Apr 2024 12:40:20 -0400 Subject: [PATCH 06/25] Use jsconfig and fix types --- packages/core/src/MosaicClient.js | 2 ++ packages/plot/src/marks/Grid2DMark.js | 6 ++++-- packages/plot/src/marks/Mark.js | 1 - packages/plot/src/plot.js | 2 +- packages/widget/{tsconfig.json => jsconfig.json} | 4 ++-- packages/widget/package.json | 2 +- packages/widget/src/index.js | 2 -- 7 files changed, 10 insertions(+), 9 deletions(-) rename packages/widget/{tsconfig.json => jsconfig.json} (79%) diff --git a/packages/core/src/MosaicClient.js b/packages/core/src/MosaicClient.js index 4f1d477c..a7a471f6 100644 --- a/packages/core/src/MosaicClient.js +++ b/packages/core/src/MosaicClient.js @@ -122,6 +122,8 @@ export class MosaicClient { /** * Requests a client update. * For example to (re-)render an interface component. + * + * @returns {this | Promise} */ update() { return this; diff --git a/packages/plot/src/marks/Grid2DMark.js b/packages/plot/src/marks/Grid2DMark.js index 35275e24..554f76c0 100644 --- a/packages/plot/src/marks/Grid2DMark.js +++ b/packages/plot/src/marks/Grid2DMark.js @@ -62,13 +62,12 @@ export class Grid2DMark extends Mark { /** * @param {import('../plot.js').Plot} plot The plot. * @param {number} index - * @returns {this} */ setPlot(plot, index) { const update = () => { if (this.hasFieldInfo()) this.requestUpdate(); }; plot.addAttributeListener('xDomain', update); plot.addAttributeListener('yDomain', update); - return super.setPlot(plot, index); + super.setPlot(plot, index); } get filterIndexable() { @@ -141,6 +140,9 @@ export class Grid2DMark extends Mark { } } + /** + * @returns {[number, number]} The bin dimensions. + */ binDimensions() { const { plot, pixelSize, width, height } = this; return [ diff --git a/packages/plot/src/marks/Mark.js b/packages/plot/src/marks/Mark.js index a75725eb..f73c7ad7 100644 --- a/packages/plot/src/marks/Mark.js +++ b/packages/plot/src/marks/Mark.js @@ -92,7 +92,6 @@ export class Mark extends MosaicClient { /** * @param {import('../plot.js').Plot} plot The plot. * @param {number} index - * @returns {this} */ setPlot(plot, index) { this.plot = plot; diff --git a/packages/plot/src/plot.js b/packages/plot/src/plot.js index c2b138bb..d2706faa 100644 --- a/packages/plot/src/plot.js +++ b/packages/plot/src/plot.js @@ -83,7 +83,7 @@ export class Plot { /** * @param {string} name The name of the attribute to set. * @param {*} value The value to set. - * @param {{silent: boolean}?} options Options for setting the attribute. + * @param {{silent: boolean}} [options] Options for setting the attribute. * @returns {boolean} whether the value changed. */ setAttribute(name, value, options) { diff --git a/packages/widget/tsconfig.json b/packages/widget/jsconfig.json similarity index 79% rename from packages/widget/tsconfig.json rename to packages/widget/jsconfig.json index 3896179a..377c9f13 100644 --- a/packages/widget/tsconfig.json +++ b/packages/widget/jsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { - "checkJs": true, "noEmit": true, + "checkJs": true, "noImplicitAny": false, "module": "Node16", "baseUrl": "./src", @@ -10,5 +10,5 @@ "@uwdata/mosaic-widget": ["index"] } }, - "include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.ts"] + "files": ["src/index.js"] } diff --git a/packages/widget/package.json b/packages/widget/package.json index a256d687..c519c7a5 100644 --- a/packages/widget/package.json +++ b/packages/widget/package.json @@ -12,7 +12,7 @@ "scripts": { "build": "vite build", "dev": "vite", - "test": "tsc", + "test": "tsc -p jsconfig.json", "lint": "eslint src --ext .js", "prepublishOnly": "npm run test && npm run lint && hatch fmt --check && npm run build && hatch build", "publish": "hatch publish" diff --git a/packages/widget/src/index.js b/packages/widget/src/index.js index f1c949e7..a765e715 100644 --- a/packages/widget/src/index.js +++ b/packages/widget/src/index.js @@ -1,5 +1,3 @@ -// @ts-check - import { coordinator, namedPlots } from '@uwdata/vgplot'; import { isSelection } from '@uwdata/mosaic-core'; import { parseSpec, astToDOM } from '@uwdata/mosaic-spec'; From 1ed053d18c5a6caff3e8e683b20210e6f1493b95 Mon Sep 17 00:00:00 2001 From: Dominik Moritz Date: Tue, 2 Apr 2024 12:46:11 -0400 Subject: [PATCH 07/25] Enable checks for spec js --- packages/spec/{tsconfig.json => jsconfig.json} | 2 +- packages/spec/package.json | 2 +- packages/spec/src/ast-to-dom.js | 5 ++--- 3 files changed, 4 insertions(+), 5 deletions(-) rename packages/spec/{tsconfig.json => jsconfig.json} (93%) diff --git a/packages/spec/tsconfig.json b/packages/spec/jsconfig.json similarity index 93% rename from packages/spec/tsconfig.json rename to packages/spec/jsconfig.json index da04a4c0..b341ff38 100644 --- a/packages/spec/tsconfig.json +++ b/packages/spec/jsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "allowJs": true, + "checkJs": true, "noEmit": true, "noImplicitAny": false, "module": "Node16", diff --git a/packages/spec/package.json b/packages/spec/package.json index 7b69391d..69f7a705 100644 --- a/packages/spec/package.json +++ b/packages/spec/package.json @@ -25,7 +25,7 @@ "prebuild": "rimraf dist && mkdir dist", "build": "node ../../esbuild.js mosaic-spec", "lint": "eslint src test --ext .js", - "test": "mocha 'test/**/*-test.js' && tsc", + "test": "mocha 'test/**/*-test.js' && tsc -p jsconfig.json", "prepublishOnly": "npm run test && npm run lint && npm run build" }, "dependencies": { diff --git a/packages/spec/src/ast-to-dom.js b/packages/spec/src/ast-to-dom.js index 332cef2c..7f8975c0 100644 --- a/packages/spec/src/ast-to-dom.js +++ b/packages/spec/src/ast-to-dom.js @@ -1,14 +1,13 @@ import { createAPIContext, loadExtension } from '@uwdata/vgplot'; -import { SpecNode } from './ast/SpecNode.js'; import { resolveExtensions } from './config/extensions.js'; import { error } from './util.js'; /** * Generate running web application (DOM content) for a Mosaic spec AST. - * @param {SpecNode} ast Mosaic AST root node. + * @param {import('ast/SpecNode.js').SpecNode)} ast Mosaic AST root node. * @param {object} [options] Instantiation options. * @param {string} [options.baseURL] The base URL for loading data files. - * @returns {object} An object with the resulting DOM element, and + * @returns {Promise} An object with the resulting DOM element, and * a map of named parameters (Param and Selection instances). */ export async function astToDOM(ast, options) { From 955db5b1619c42d005d0d5da0b18fa423870a214 Mon Sep 17 00:00:00 2001 From: Dominik Moritz Date: Tue, 2 Apr 2024 12:53:23 -0400 Subject: [PATCH 08/25] Fix type errors with ASTNode --- packages/spec/src/ast/ASTNode.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/spec/src/ast/ASTNode.js b/packages/spec/src/ast/ASTNode.js index c8081be6..aea11ae8 100644 --- a/packages/spec/src/ast/ASTNode.js +++ b/packages/spec/src/ast/ASTNode.js @@ -4,15 +4,27 @@ export class ASTNode { this.children = children; } + /** + * @returns {void} + */ instantiate(/* ctx */) { + // @ts-ignore throw Error('instantiate not implemented'); } + /** + * @returns {string} The for for the node. + */ codegen(/* ctx */) { + // @ts-ignore return Error('codegen not implemented'); } + /** + * @returns {object} The node as JSON. + */ toJSON() { + // @ts-ignore return Error('toJSON not implemented'); } } From 1fb10d85b9c529e15c681e02f53f55d12c10a55a Mon Sep 17 00:00:00 2001 From: Dominik Moritz Date: Tue, 2 Apr 2024 12:57:19 -0400 Subject: [PATCH 09/25] Disable js tests in spec package --- packages/spec/jsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/spec/jsconfig.json b/packages/spec/jsconfig.json index b341ff38..4be2f3fd 100644 --- a/packages/spec/jsconfig.json +++ b/packages/spec/jsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "checkJs": true, + "checkJs": false, "noEmit": true, "noImplicitAny": false, "module": "Node16", From a5460211330f9f25672349dab4b4ccee977a6918 Mon Sep 17 00:00:00 2001 From: jheer Date: Tue, 2 Apr 2024 22:48:19 -0700 Subject: [PATCH 10/25] fix: Remove invalid plot attribute. --- packages/plot/src/plot-attributes.js | 1 - packages/vgplot/src/api.js | 1 - packages/vgplot/src/plot/attributes.js | 1 - 3 files changed, 3 deletions(-) diff --git a/packages/plot/src/plot-attributes.js b/packages/plot/src/plot-attributes.js index 4ac581ba..9fcafc12 100644 --- a/packages/plot/src/plot-attributes.js +++ b/packages/plot/src/plot-attributes.js @@ -14,7 +14,6 @@ export const attributeMap = new Map([ ['grid', 'grid'], ['label', 'label'], ['padding', 'padding'], - ['round', 'round'], ['xScale', 'x.type'], ['xDomain', 'x.domain'], ['xRange', 'x.range'], diff --git a/packages/vgplot/src/api.js b/packages/vgplot/src/api.js index 58c6aad7..91eaf28e 100644 --- a/packages/vgplot/src/api.js +++ b/packages/vgplot/src/api.js @@ -100,7 +100,6 @@ export { grid, label, padding, - round, xScale, xDomain, xRange, diff --git a/packages/vgplot/src/plot/attributes.js b/packages/vgplot/src/plot/attributes.js index d6aac195..47ed8b8d 100644 --- a/packages/vgplot/src/plot/attributes.js +++ b/packages/vgplot/src/plot/attributes.js @@ -71,7 +71,6 @@ export const inset = attrf('inset'); export const grid = attrf('grid'); export const label = attrf('label'); export const padding = attrf('padding'); -export const round = attrf('round'); // x scale attributes export const xScale = attrf('xScale'); From 1c8a9e29193732cd16d84a049ef5ef3fc808538d Mon Sep 17 00:00:00 2001 From: jheer Date: Tue, 2 Apr 2024 22:49:45 -0700 Subject: [PATCH 11/25] feat: Publish types from mosaic-spec. --- packages/spec/jsconfig.json | 14 +- packages/spec/package.json | 8 +- packages/spec/src/ast-to-dom.d.ts | 29 -- packages/spec/src/ast-to-dom.js | 15 +- packages/spec/src/ast-to-esm.d.ts | 37 -- packages/spec/src/ast-to-esm.js | 33 +- packages/spec/src/ast/ASTNode.js | 19 +- packages/spec/src/ast/DataNode.d.ts | 63 --- packages/spec/src/ast/DataNode.js | 100 ++++- packages/spec/src/ast/ExpressionNode.d.ts | 3 - packages/spec/src/ast/HConcatNode.d.ts | 8 - packages/spec/src/ast/InputNode.d.ts | 140 ------- packages/spec/src/ast/ParamNode.d.ts | 29 -- packages/spec/src/ast/ParamNode.js | 3 +- packages/spec/src/ast/ParamRefNode.d.ts | 1 - packages/spec/src/ast/PlotAttributeNode.d.ts | 240 ----------- packages/spec/src/ast/PlotFromNode.d.ts | 24 -- packages/spec/src/ast/PlotLegendNode.d.ts | 24 -- packages/spec/src/ast/PlotNode.d.ts | 19 - packages/spec/src/ast/SelectionNode.d.ts | 10 - packages/spec/src/ast/SpecNode.d.ts | 75 ---- packages/spec/src/ast/TransformNode.d.ts | 191 --------- packages/spec/src/ast/VConcatNode.d.ts | 8 - packages/spec/src/config/inputs.js | 1 + packages/spec/src/config/plots.js | 4 + packages/spec/src/index.d.ts | 4 - packages/spec/src/index.js | 4 + packages/spec/src/parse-spec.d.ts | 18 - packages/spec/src/parse-spec.js | 43 +- packages/spec/src/spec/Data.ts | 69 ++++ packages/spec/src/spec/Expression.ts | 13 + packages/spec/src/spec/HConcat.ts | 8 + .../{ast/HSpaceNode.d.ts => spec/HSpace.ts} | 2 +- packages/spec/src/spec/Input.ts | 178 ++++++++ packages/spec/src/spec/Param.ts | 39 ++ packages/spec/src/spec/Plot.ts | 19 + packages/spec/src/spec/PlotAttribute.ts | 383 ++++++++++++++++++ packages/spec/src/spec/PlotFrom.ts | 28 ++ .../PlotInteractor.ts} | 2 +- packages/spec/src/spec/PlotLegend.ts | 19 + .../PlotMarkNode.d.ts => spec/PlotMark.ts} | 18 +- packages/spec/src/spec/Spec.ts | 60 +++ packages/spec/src/spec/Transform.ts | 187 +++++++++ packages/spec/src/spec/VConcat.ts | 8 + .../{ast/VSpaceNode.d.ts => spec/VSpace.ts} | 2 +- packages/spec/src/util.js | 8 + packages/spec/tsconfig.json | 11 + packages/widget/jsconfig.json | 12 +- packages/widget/package.json | 1 + packages/widget/src/index.js | 2 +- 50 files changed, 1234 insertions(+), 1002 deletions(-) delete mode 100644 packages/spec/src/ast-to-dom.d.ts delete mode 100644 packages/spec/src/ast-to-esm.d.ts delete mode 100644 packages/spec/src/ast/DataNode.d.ts delete mode 100644 packages/spec/src/ast/ExpressionNode.d.ts delete mode 100644 packages/spec/src/ast/HConcatNode.d.ts delete mode 100644 packages/spec/src/ast/InputNode.d.ts delete mode 100644 packages/spec/src/ast/ParamNode.d.ts delete mode 100644 packages/spec/src/ast/ParamRefNode.d.ts delete mode 100644 packages/spec/src/ast/PlotAttributeNode.d.ts delete mode 100644 packages/spec/src/ast/PlotFromNode.d.ts delete mode 100644 packages/spec/src/ast/PlotLegendNode.d.ts delete mode 100644 packages/spec/src/ast/PlotNode.d.ts delete mode 100644 packages/spec/src/ast/SelectionNode.d.ts delete mode 100644 packages/spec/src/ast/SpecNode.d.ts delete mode 100644 packages/spec/src/ast/TransformNode.d.ts delete mode 100644 packages/spec/src/ast/VConcatNode.d.ts delete mode 100644 packages/spec/src/index.d.ts delete mode 100644 packages/spec/src/parse-spec.d.ts create mode 100644 packages/spec/src/spec/Data.ts create mode 100644 packages/spec/src/spec/Expression.ts create mode 100644 packages/spec/src/spec/HConcat.ts rename packages/spec/src/{ast/HSpaceNode.d.ts => spec/HSpace.ts} (86%) create mode 100644 packages/spec/src/spec/Input.ts create mode 100644 packages/spec/src/spec/Param.ts create mode 100644 packages/spec/src/spec/Plot.ts create mode 100644 packages/spec/src/spec/PlotAttribute.ts create mode 100644 packages/spec/src/spec/PlotFrom.ts rename packages/spec/src/{ast/PlotInteractorNode.d.ts => spec/PlotInteractor.ts} (69%) create mode 100644 packages/spec/src/spec/PlotLegend.ts rename packages/spec/src/{ast/PlotMarkNode.d.ts => spec/PlotMark.ts} (87%) create mode 100644 packages/spec/src/spec/Spec.ts create mode 100644 packages/spec/src/spec/Transform.ts create mode 100644 packages/spec/src/spec/VConcat.ts rename packages/spec/src/{ast/VSpaceNode.d.ts => spec/VSpace.ts} (86%) create mode 100644 packages/spec/tsconfig.json diff --git a/packages/spec/jsconfig.json b/packages/spec/jsconfig.json index 4be2f3fd..22324029 100644 --- a/packages/spec/jsconfig.json +++ b/packages/spec/jsconfig.json @@ -1,14 +1,10 @@ { + "include": ["src/**/*", "../../specs/ts/*.ts"], "compilerOptions": { - "checkJs": false, + "checkJs": true, "noEmit": true, "noImplicitAny": false, - "module": "Node16", - "baseUrl": "./src", - "skipLibCheck": true, - "paths": { - "@uwdata/mosaic-spec": ["index"] - } - }, - "include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.ts", "../../specs/ts/*.ts"] + "module": "node16", + "skipLibCheck": true + } } \ No newline at end of file diff --git a/packages/spec/package.json b/packages/spec/package.json index 69f7a705..1debe001 100644 --- a/packages/spec/package.json +++ b/packages/spec/package.json @@ -16,22 +16,22 @@ "module": "src/index.js", "jsdelivr": "dist/mosaic-spec.min.js", "unpkg": "dist/mosaic-spec.min.js", - "types": "src/index.d.ts", + "types": "dist/types/index.d.ts", "repository": { "type": "git", "url": "https://github.com/uwdata/mosaic.git" }, "scripts": { "prebuild": "rimraf dist && mkdir dist", - "build": "node ../../esbuild.js mosaic-spec", + "build": "tsc && node ../../esbuild.js mosaic-spec", "lint": "eslint src test --ext .js", + "pretest": "tsc", "test": "mocha 'test/**/*-test.js' && tsc -p jsconfig.json", "prepublishOnly": "npm run test && npm run lint && npm run build" }, "dependencies": { "@uwdata/mosaic-core": "^0.7.1", "@uwdata/mosaic-sql": "^0.7.0", - "@uwdata/vgplot": "^0.7.1", - "isoformat": "^0.2.1" + "@uwdata/vgplot": "^0.7.1" } } diff --git a/packages/spec/src/ast-to-dom.d.ts b/packages/spec/src/ast-to-dom.d.ts deleted file mode 100644 index 174a7d81..00000000 --- a/packages/spec/src/ast-to-dom.d.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Param, Selection } from '@uwdata/mosaic-core'; -import { SpecNode } from './ast/SpecNode.js'; - -/** - * Generate running web application (DOM content) for a Mosaic spec AST. - * @param ast Mosaic AST root node. - * @param options Instantiation options. - * @returns An object with the resulting DOM element, and a map of named parameters (Param and Selection instances). - */ -export function astToDOM( - ast: SpecNode, - options?: AstToDOMOptions -) : Promise; - -/** - * Mosaic specification instantiation options. - */ -export interface AstToDOMOptions { - api?: object; - plotDefaults?: any[]; - params?: Map; - /** The base URL for loading data files. */ - baseURL?: string; -} - -export type AstToDOMResult = { - element: HTMLElement | SVGSVGElement, - params: Map -}; diff --git a/packages/spec/src/ast-to-dom.js b/packages/spec/src/ast-to-dom.js index 7f8975c0..b1774eb0 100644 --- a/packages/spec/src/ast-to-dom.js +++ b/packages/spec/src/ast-to-dom.js @@ -1,14 +1,21 @@ +import { Param, Selection } from '@uwdata/mosaic-core'; import { createAPIContext, loadExtension } from '@uwdata/vgplot'; +import { SpecNode } from './ast/SpecNode.js'; import { resolveExtensions } from './config/extensions.js'; import { error } from './util.js'; /** - * Generate running web application (DOM content) for a Mosaic spec AST. - * @param {import('ast/SpecNode.js').SpecNode)} ast Mosaic AST root node. + * Generate a running web application (DOM content) for a Mosaic spec AST. + * @param {SpecNode} ast Mosaic AST root node. * @param {object} [options] Instantiation options. * @param {string} [options.baseURL] The base URL for loading data files. - * @returns {Promise} An object with the resulting DOM element, and - * a map of named parameters (Param and Selection instances). + * @param {any[]} [options.plotDefaults] Array of default plot attributes. + * @param {Map} [options.params] A map of predefined Params/Selections. + * @returns {Promise<{ + * element: HTMLElement | SVGSVGElement; +* params: Map; +* }>} A Promise to an object with the resulting + * DOM element, and a map of named parameters (Param and Selection instances). */ export async function astToDOM(ast, options) { const { data, params, plotDefaults } = ast; diff --git a/packages/spec/src/ast-to-esm.d.ts b/packages/spec/src/ast-to-esm.d.ts deleted file mode 100644 index beb31891..00000000 --- a/packages/spec/src/ast-to-esm.d.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { SpecNode } from './ast/SpecNode.js'; - -/** - * Generate ESM (ECMAScript Module) code for a Mosaic specification AST. - * @param ast Mosaic AST root node. - * @param options Code generation options. - * @returns Generated ESM code using the vgplot API. - */ -export function astToESM( - ast: SpecNode, - options?: AstToESMOptions -) : string; - -/** - * Mosaic specification code generation options. - */ -export interface AstToESMOptions { - /** The base URL for loading data files. */ - baseURL?: string; - /** - * A database connector to initialize. - * Valid values are 'wasm', 'socket', and 'rest'. - * If undefined, no connector code is generated. - */ - connector?: 'wasm' | 'socket' | 'rest' | string; - /** The vgplot API namespace object (default 'vg'). */ - namespace?: string; - /** The starting indentation depth (default 0). */ - depth?: number; - /** - * A Map of imports to include in generated code. Keys are packages (e.g., - * '@uwdata/vgplot') and values indicate what to import (e.g., '* as vg'). - */ - imports?: Map; - /** Code to include after imports. */ - preamble?: string | string[]; -} diff --git a/packages/spec/src/ast-to-esm.js b/packages/spec/src/ast-to-esm.js index d91b293c..aab8de5b 100644 --- a/packages/spec/src/ast-to-esm.js +++ b/packages/spec/src/ast-to-esm.js @@ -12,9 +12,9 @@ import { error, isArray, isObject, isString, toArray, toParamRef } from './util. * If undefined, no connector code is generated. * @param {string} [options.namespace='vg'] The vgplot API namespace object. * @param {number} [options.depth=0] The starting indentation depth. - * @param {Map} [options.imports] A Map of ESM imports to - * include in generated code. Keys are packages (e.g., '@uwdata/vgplot') - * and values indicate what to import (e.g., '* as vg'). + * @param {Map} [options.imports] A Map of ESM + * imports to include in generated code. Keys are packages (e.g., + * '@uwdata/vgplot') and values indicate what to import (e.g., '* as vg'). * @param {string|string[]} [options.preamble] Code to include after imports. * @returns {string} Generated ESM code using the vgplot API. */ @@ -29,6 +29,7 @@ export function astToESM(ast, options = {}) { importsCode.push( isString(methods) ? `import ${methods} from "${pkg}";` + // @ts-ignore : `import { ${methods.join(', ')} } from "${pkg}";` ); } @@ -107,12 +108,26 @@ export function astToESM(ast, options = {}) { } export class CodegenContext { + /** + * Create a new code generator context. + * @param {object} [options] Code generation options. + * @param {*} [options.plotDefaults] Default attributes to apply to all plots. + * @param {string} [options.baseURL] The base URL for loading data files. + * @param {string} [options.connector] A database connector to initialize. + * Valid values are 'wasm', 'socket', and 'rest'. + * If undefined, no connector code is generated. + * @param {string} [options.namespace='vg'] The vgplot API namespace object. + * @param {number} [options.depth=0] The starting indentation depth. + * @param {Map} [options.imports] A Map of ESM + * imports to include in generated code. Keys are packages (e.g., + * '@uwdata/vgplot') and values indicate what to import (e.g., '* as vg'). + */ constructor({ - plotDefaults = null, + plotDefaults = undefined, namespace = 'vg', - connector = null, + connector = undefined, imports = new Map([['@uwdata/vgplot', '* as vg']]), - baseURL = null, + baseURL = undefined, depth = 0 } = {}) { this.plotDefaults = plotDefaults; @@ -125,9 +140,11 @@ export class CodegenContext { addImport(pkg, method) { if (!this.imports.has(pkg)) { - this.imports.set(pkg, []); + this.imports.set(pkg, [method]); + } else { + // @ts-ignore + this.imports.get(pkg).push(method); } - this.imports.get(pkg).push(method); } setImports(pkg, all) { diff --git a/packages/spec/src/ast/ASTNode.js b/packages/spec/src/ast/ASTNode.js index aea11ae8..6161291e 100644 --- a/packages/spec/src/ast/ASTNode.js +++ b/packages/spec/src/ast/ASTNode.js @@ -1,27 +1,36 @@ +/** + * Abstract base class for Mosaic spec AST nodes. + */ export class ASTNode { constructor(type, children = null) { + /** @type {string} */ this.type = type; + /** @type {ASTNode[] | null} */ this.children = children; } /** - * @returns {void} + * Instantiate this AST node to use in a live web application. + * @param {import('../ast-to-dom.js').InstantiateContext} ctx The instantiation context. + * @returns {*} The instantiated value of this node. */ - instantiate(/* ctx */) { + instantiate(ctx) { // eslint-disable-line no-unused-vars // @ts-ignore throw Error('instantiate not implemented'); } /** - * @returns {string} The for for the node. + * Generate ESM code for this AST node. + * @param {import('../ast-to-esm.js').CodegenContext} ctx The code generator context. + * @returns {string|void} The generated ESM code for the node. */ - codegen(/* ctx */) { + codegen(ctx) { // eslint-disable-line no-unused-vars // @ts-ignore return Error('codegen not implemented'); } /** - * @returns {object} The node as JSON. + * @returns {*} This AST node in JSON specification format. */ toJSON() { // @ts-ignore diff --git a/packages/spec/src/ast/DataNode.d.ts b/packages/spec/src/ast/DataNode.d.ts deleted file mode 100644 index 86dfbc20..00000000 --- a/packages/spec/src/ast/DataNode.d.ts +++ /dev/null @@ -1,63 +0,0 @@ -export interface SpecDataBaseOptions { - select?: string[]; - where?: string | string[]; - view?: boolean; - temp?: boolean; - replace?: boolean; -} - -export type SpecDataQuery = string; - -export type SpecDataArray = Array; - -export interface SpecDataFile { - /** - * The data file to load. If no type option is provided, - * the file suffix must be one of `.csv`, `.json`, or `.parquet`. - */ - file: `${string}.parquet` | `${string}.csv` | `${string}.json`; -} - -export interface SpecDataTable extends SpecDataBaseOptions { - type: 'table'; - query: string; -} - -export interface SpecDataParquet extends SpecDataBaseOptions { - type: 'parquet'; - file: string; -} - -export interface SpecDataCSV extends SpecDataBaseOptions { - type: 'csv'; - file: string; - delimiter?: string, - sample_size?: number; -} - -export interface SpecDataSpatial extends SpecDataBaseOptions { - type: 'spatial'; - file: string; - layer?: string; -} - -export interface SpecDataJSON extends SpecDataBaseOptions { - type: 'json'; - file: string; -} - -export interface SpecDataJSONObjects extends SpecDataBaseOptions { - type?: 'json'; - data: object[]; -} - -export type SpecDataDefinition = - | SpecDataQuery - | SpecDataArray - | SpecDataFile - | SpecDataTable - | SpecDataParquet - | SpecDataCSV - | SpecDataSpatial - | SpecDataJSON - | SpecDataJSONObjects; diff --git a/packages/spec/src/ast/DataNode.js b/packages/spec/src/ast/DataNode.js index abfef9b1..889b812b 100644 --- a/packages/spec/src/ast/DataNode.js +++ b/packages/spec/src/ast/DataNode.js @@ -10,6 +10,7 @@ export const CSV_DATA = 'csv'; export const JSON_DATA = 'json'; export const SPATIAL_DATA = 'spatial'; +// @ts-ignore const dataFormats = new Map([ [TABLE_DATA, parseTableData], [PARQUET_DATA, parseParquetData], @@ -18,16 +19,40 @@ const dataFormats = new Map([ [SPATIAL_DATA, parseSpatialData] ]); +/** + * Parse a data definition spec. + * @param {string} name The name of the dataset + * @param {import('../spec/Data.js').DataDefinition} spec The data definition spec. + * @param {import('../parse-spec.js').ParseContext} ctx The parser context. + * @returns {DataNode} a parsed data definition AST node + */ export function parseData(name, spec, ctx) { - spec = resolveDataSpec(spec); - if (dataFormats.has(spec.type)) { - const parse = dataFormats.get(spec.type); - return parse(name, spec, ctx); + const def = resolveDataSpec(spec); + if (dataFormats.has(def.type)) { + const parse = dataFormats.get(def.type); + return parse(name, def, ctx); } else { ctx.error(`Unrecognized data format type.`, spec); } } +function resolveDataSpec(spec) { + if (isArray(spec)) spec = { type: 'json', data: spec }; + if (isString(spec)) spec = { type: 'table', query: spec }; + return { ...spec, type: inferType(spec) }; +} + +function inferType(spec) { + return spec.type + || fileExtension(spec.file) + || 'table'; +} + +function fileExtension(file) { + const idx = file?.lastIndexOf('.'); + return idx > 0 ? file.slice(idx + 1) : null; +} + function parseTableData(name, spec, ctx) { // eslint-disable-next-line no-unused-vars const { query, type, ...options } = spec; @@ -61,23 +86,6 @@ function parseSpatialData(name, spec, ctx) { return new SpatialDataNode(name, file, parseOptions(options, ctx)); } -function resolveDataSpec(spec) { - if (isArray(spec)) spec = { type: 'json', data: spec }; - if (isString(spec)) spec = { type: 'table', query: spec }; - return { ...spec, type: inferType(spec) }; -} - -function inferType(spec) { - return spec.type - || fileExtension(spec.file) - || 'table'; -} - -function fileExtension(file) { - const idx = file?.lastIndexOf('.'); - return idx > 0 ? file.slice(idx + 1) : null; -} - function resolveFileURL(file, baseURL) { return baseURL ? new URL(file, baseURL).toString() : file; } @@ -100,19 +108,39 @@ export class QueryDataNode extends DataNode { super(name, format); } + /** + * Instantiate a table creation query. + * @param {import('../ast-to-dom.js').InstantiateContext} ctx The instantiation context. + * @returns {string|void} The instantiated query. + */ instantiateQuery(ctx) { ctx.error('instantiateQuery not implemented'); } + /** + * Code generate a table creation query. + * @param {import('../ast-to-esm.js').CodegenContext} ctx The code generator context. + * @returns {string|void} The generated query code. + */ codegenQuery(ctx) { ctx.error('codegenQuery not implemented'); } + /** + * Instantiate this AST node to use in a live web application. + * @param {import('../ast-to-dom.js').InstantiateContext} ctx The instantiation context. + * @returns {*} The instantiated value of this node. + */ instantiate(ctx) { const query = this.instantiateQuery(ctx); if (query) return query; } + /** + * Generate ESM code for this AST node. + * @param {import('../ast-to-esm.js').CodegenContext} ctx The code generator context. + * @returns {string|void} The generated ESM code for the node. + */ codegen(ctx) { const query = this.codegenQuery(ctx); if (query) return query; @@ -126,6 +154,11 @@ export class TableDataNode extends QueryDataNode { this.options = options; } + /** + * Instantiate a table creation query. + * @param {import('../ast-to-dom.js').InstantiateContext} ctx The instantiation context. + * @returns {string|void} The instantiated query. + */ instantiateQuery(ctx) { const { name, query, options } = this; if (query) { @@ -133,6 +166,11 @@ export class TableDataNode extends QueryDataNode { } } + /** + * Code generate a table creation query. + * @param {import('../ast-to-esm.js').CodegenContext} ctx The code generator context. + * @returns {string|void} The generated query code. + */ codegenQuery(ctx) { const { name, query, options } = this; if (query) { @@ -154,6 +192,11 @@ export class FileDataNode extends QueryDataNode { this.options = options; } + /** + * Instantiate a table creation query. + * @param {import('../ast-to-dom.js').InstantiateContext} ctx The instantiation context. + * @returns {string|void} The instantiated query. + */ instantiateQuery(ctx) { const { name, method, file, options } = this; const url = resolveFileURL(file, ctx.baseURL); @@ -161,6 +204,11 @@ export class FileDataNode extends QueryDataNode { return ctx.api[method](name, url, opt); } + /** + * Code generate a table creation query. + * @param {import('../ast-to-esm.js').CodegenContext} ctx The code generator context. + * @returns {string|void} The generated query code. + */ codegenQuery(ctx) { const { name, method, file, options } = this; const url = resolveFileURL(file, ctx.baseURL); @@ -205,11 +253,21 @@ export class LiteralJSONDataNode extends QueryDataNode { this.options = options; } + /** + * Instantiate a table creation query. + * @param {import('../ast-to-dom.js').InstantiateContext} ctx The instantiation context. + * @returns {string|void} The instantiated query. + */ instantiateQuery(ctx) { const { name, data, options } = this; return ctx.api.loadObjects(name, data, options.instantiate(ctx)); } + /** + * Code generate a table creation query. + * @param {import('../ast-to-esm.js').CodegenContext} ctx The code generator context. + * @returns {string|void} The generated query code. + */ codegenQuery(ctx) { const { name, data, options } = this; const opt = options ? ',' + options.codegen(ctx) : ''; diff --git a/packages/spec/src/ast/ExpressionNode.d.ts b/packages/spec/src/ast/ExpressionNode.d.ts deleted file mode 100644 index 1474a37a..00000000 --- a/packages/spec/src/ast/ExpressionNode.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -export type SpecExpression = - | { sql: string, label?: string } - | { agg: string, label?: string }; diff --git a/packages/spec/src/ast/HConcatNode.d.ts b/packages/spec/src/ast/HConcatNode.d.ts deleted file mode 100644 index 4aee8c55..00000000 --- a/packages/spec/src/ast/HConcatNode.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { SpecComponent } from './SpecNode.js'; - -export interface SpecHConcatNode { - /** - * Horizontally concatenate components in a row layout. - */ - hconcat: SpecComponent[]; -} diff --git a/packages/spec/src/ast/InputNode.d.ts b/packages/spec/src/ast/InputNode.d.ts deleted file mode 100644 index 6fe2fde8..00000000 --- a/packages/spec/src/ast/InputNode.d.ts +++ /dev/null @@ -1,140 +0,0 @@ -import { SpecParamRef } from './ParamRefNode.js'; - -export type SpecInput = - | SpecMenu - | SpecSearch - | SpecSlider - | SpecTable; - -export interface SpecMenu { - /** Create a menu input widget. */ - input: 'menu'; - /** A selection to which the current menu option is added. */ - as?: SpecParamRef; - /** - * A backing data table from which to pull menu options. - * Used in conjunction with the column property. - */ - from?: any; - /** - * A backing data column from which to pull menu options. - * Used in conjunction with the from property. - */ - column?: any; - /** - * A selection by which to filter a backing data table. - * Used in conjunction with the from and column properties. - */ - filterBy?: SpecParamRef; - /** A text label for this input. */ - label?: any; - /** - * An array of menu options, as literal values or option objects. - * Option objects have a `value` property and an optional `label` property. - * If no label is provided, the (string-coerced) value is used. - */ - options?: any; - /** The initial selected menu option. */ - value?: any; -} - -export interface SpecSearch { - /** Create a text search input widget. */ - input: 'search'; - /** A selection to which the text search query is added. */ - as?: SpecParamRef; - /** - * A backing data table from which to pull valid search results. - * Used in conjunction with the column property. - */ - from?: any; - /** - * A backing data column from which to pull valid search results. - * Used in conjunction with the from property. - */ - column?: any; - /** - * A selection by which to filter a backing data table. - * Used in conjunction with the from and column properties. - */ - filterBy?: SpecParamRef; - /** A text label for this input. */ - label?: any; - /** - * The type of text search to perform. - * One of: `"contains"` (default), `"prefix"`, `"suffix"`, or `"regexp"`. - */ - type?: any; -} - -export interface SpecSlider { - /** Create a slider input widget. */ - input: 'slider'; - /** A selection to which the current slider value is added. */ - as?: SpecParamRef; - /** - * A backing data table from which to determine the slider range. - * Used in conjunction with the column property. - */ - from?: any; - /** - * A backing data column from which to determine the slider range. - * Used in conjunction with the from property. - */ - column?: any; - /** - * A selection by which to filter a backing data table. - * Used in conjunction with the from and column properties. - */ - filterBy?: SpecParamRef; - /** The minumum slider value. */ - min?: any; - /** The maximum slider value. */ - max?: any; - /** The slider step, the increment between consecutive values. */ - step?: any; - /** A text label for this input. */ - label?: any; - /** The initial slider value. */ - value?: any; - /** The width of the slider in screen pixels. */ - width?: any; -} - -export interface SpecTable { - /** Create a table grid widget. */ - input: 'table'; - /** - * A backing data table from which to pull data for this widget. - */ - from?: any; - /** - * A list of column names to include in the table widget. - * If unspecified, all table columns are shown. - */ - columns?: any; - /** - * A selection by which to filter a backing data table. - * Used in conjunction with the from and column properties. - */ - filterBy?: SpecParamRef; - /** - * An object of per-column alignment values. - * Column names should be object keys, mapped to alignment values. - * Valid alignment values are: `"left"`, `"right"`, `"center"`, and `"justify"`. - * By default, numbers are right-aligned and other values are left-aligned. - */ - align?: any; - /** - * If a number, sets the total width of the table widget, in pixels. - * If an object, provides per-column pixel width values. - * Column names should be object keys, mapped to numeric width values. - */ - width?: any; - /** The maximum width of the table widget, in pixels. */ - maxWidth?: any; - /** The height of the table widget, in pixels. */ - height?: any; - /** The number of rows load in a new batch upon table scroll. */ - rowBatch?: any; -} diff --git a/packages/spec/src/ast/ParamNode.d.ts b/packages/spec/src/ast/ParamNode.d.ts deleted file mode 100644 index af7978e1..00000000 --- a/packages/spec/src/ast/ParamNode.d.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { SpecParamRef } from './ParamRefNode.js'; -import { SpecSelection } from './SelectionNode.js'; - -export interface SpecParamBase { - select?: 'value'; -} - -export interface SpecParam extends SpecParamBase { - value: SpecParamValue; -} - -export interface SpecParamDate extends SpecParamBase { - date: string; -} - -export type SpecParamLiteral = - | string - | number - | boolean; - -export type SpecParamValue = - | SpecParamLiteral - | Array; - - export type SpecParamDefinition = - | SpecParamValue - | SpecParam - | SpecParamDate - | SpecSelection; diff --git a/packages/spec/src/ast/ParamNode.js b/packages/spec/src/ast/ParamNode.js index de077279..ffcb3eaa 100644 --- a/packages/spec/src/ast/ParamNode.js +++ b/packages/spec/src/ast/ParamNode.js @@ -1,5 +1,4 @@ -import { parse as isoparse } from 'isoformat'; -import { isArray, isObject } from '../util.js'; +import { isArray, isObject, isoparse } from '../util.js'; import { ASTNode } from './ASTNode.js'; import { CROSSFILTER, INTERSECT, PARAM, SINGLE, UNION, VALUE } from '../constants.js'; import { SelectionNode } from './SelectionNode.js'; diff --git a/packages/spec/src/ast/ParamRefNode.d.ts b/packages/spec/src/ast/ParamRefNode.d.ts deleted file mode 100644 index cacc55c1..00000000 --- a/packages/spec/src/ast/ParamRefNode.d.ts +++ /dev/null @@ -1 +0,0 @@ -export type SpecParamRef = `$${string}`; diff --git a/packages/spec/src/ast/PlotAttributeNode.d.ts b/packages/spec/src/ast/PlotAttributeNode.d.ts deleted file mode 100644 index e05618ca..00000000 --- a/packages/spec/src/ast/PlotAttributeNode.d.ts +++ /dev/null @@ -1,240 +0,0 @@ -import { SpecParamRef } from './ParamRefNode.js'; - -export interface SpecPlotAttributes { - name?: string; - margins?: { - top?: number | SpecParamRef; - bottom?: number | SpecParamRef; - left?: number | SpecParamRef; - right?: number | SpecParamRef; - }; - margin?: number | SpecParamRef; - xyDomain?: any; - - // plot-level attributes - style?: any; - width?: any; - height?: any; - marginLeft?: any; - marginRight?: any; - marginTop?: any; - marginBottom?: any; - align?: any; - aspectRatio?: any; - axis?: any; - inset?: any; - grid?: any; - label?: any; - padding?: any; - round?: any; - - // x scale attributes - xScale?: any; - xDomain?: any; - xRange?: any; - xNice?: any; - xInset?: any; - xInsetLeft?: any; - xInsetRight?: any; - xClamp?: any; - xRound?: any; - xAlign?: any; - xPadding?: any; - xPaddingInner?: any; - xPaddingOuter?: any; - xAxis?: any; - xTicks?: any; - xTickSize?: any; - xTickSpacing?: any; - xTickPadding?: any; - xTickFormat?: any; - xTickRotate?: any; - xGrid?: any; - xLine?: any; - xLabel?: any; - xLabelAnchor?: any; - xLabelOffset?: any; - xFontVariant?: any; - xAriaLabel?: any; - xAriaDescription?: any; - xReverse?: any; - xZero?: any; - xBase?: any; - xExponent?: any; - xConstant?: any; - - // y scale attributes - yScale?: any; - yDomain?: any; - yRange?: any; - yNice?: any; - yInset?: any; - yInsetTop?: any; - yInsetBottom?: any; - yClamp?: any; - yRound?: any; - yAlign?: any; - yPadding?: any; - yPaddingInner?: any; - yPaddingOuter?: any; - yAxis?: any; - yTicks?: any; - yTickSize?: any; - yTickSpacing?: any; - yTickPadding?: any; - yTickFormat?: any; - yTickRotate?: any; - yGrid?: any; - yLine?: any; - yLabel?: any; - yLabelAnchor?: any; - yLabelOffset?: any; - yFontVariant?: any; - yAriaLabel?: any; - yAriaDescription?: any; - yReverse?: any; - yZero?: any; - yBase?: any; - yExponent?: any; - yConstant?: any; - - // facet attributes - facetMargin?: any; - facetMarginTop?: any; - facetMarginBottom?: any; - facetMarginLeft?: any; - facetMarginRight?: any; - facetGrid?: any; - facetLabel?: any; - - // fx scale attributes - fxDomain?: any; - fxRange?: any; - fxNice?: any; - fxInset?: any; - fxInsetLeft?: any; - fxInsetRight?: any; - fxRound?: any; - fxAlign?: any; - fxPadding?: any; - fxPaddingInner?: any; - fxPaddingOuter?: any; - fxAxis?: any; - fxTicks?: any; - fxTickSize?: any; - fxTickSpacing?: any; - fxTickPadding?: any; - fxTickFormat?: any; - fxTickRotate?: any; - fxGrid?: any; - fxLine?: any; - fxLabel?: any; - fxLabelAnchor?: any; - fxLabelOffset?: any; - fxFontVariant?: any; - fxAriaLabel?: any; - fxAriaDescription?: any; - fxReverse?: any; - - // fy scale attributes - fyDomain?: any; - fyRange?: any; - fyNice?: any; - fyInset?: any; - fyInsetTop?: any; - fyInsetBottom?: any; - fyRound?: any; - fyAlign?: any; - fyPadding?: any; - fyPaddingInner?: any; - fyPaddingOuter?: any; - fyAxis?: any; - fyTicks?: any; - fyTickSize?: any; - fyTickSpacing?: any; - fyTickPadding?: any; - fyTickFormat?: any; - fyTickRotate?: any; - fyGrid?: any; - fyLine?: any; - fyLabel?: any; - fyLabelAnchor?: any; - fyLabelOffset?: any; - fyFontVariant?: any; - fyAriaLabel?: any; - fyAriaDescription?: any; - fyReverse?: any; - - // color scale attributes - colorScale?: any; - colorDomain?: any; - colorRange?: any; - colorClamp?: any; - colorN?: any; - colorNice?: any; - colorScheme?: any; - colorInterpolate?: any; - colorPivot?: any; - colorSymmetric?: any; - colorLabel?: any; - colorReverse?: any; - colorZero?: any; - colorTickFormat?: any; - colorBase?: any; - colorExponent?: any; - colorConstant?: any; - - // opacity scale attributes - opacityScale?: any; - opacityDomain?: any; - opacityRange?: any; - opacityClamp?: any; - opacityNice?: any; - opacityLabel?: any; - opacityReverse?: any; - opacityZero?: any; - opacityTickFormat?: any; - opacityBase?: any; - opacityExponent?: any; - opacityConstant?: any; - - // symbol scale attributes - symbolScale?: any; - symbolDomain?: any; - symbolRange?: any; - - // r scale attributes - rScale?: any; - rDomain?: any; - rRange?: any; - rClamp?: any; - rNice?: any; - rZero?: any; - rBase?: any; - rExponent?: any; - rConstant?: any; - - // length scale attributes - lengthScale?: any; - lengthDomain?: any; - lengthRange?: any; - lengthClamp?: any; - lengthNice?: any; - lengthZero?: any; - lengthBase?: any; - lengthExponent?: any; - lengthConstant?: any; - - // projection attributes - projectionType?: any; - projectionParallels?: any; - projectionPrecision?: any; - projectionRotate?: any; - projectionDomain?: any; - projectionInset?: any; - projectionInsetLeft?: any; - projectionInsetRight?: any; - projectionInsetTop?: any; - projectionInsetBottom?: any; - projectionClip?: any; -}; diff --git a/packages/spec/src/ast/PlotFromNode.d.ts b/packages/spec/src/ast/PlotFromNode.d.ts deleted file mode 100644 index 19271eed..00000000 --- a/packages/spec/src/ast/PlotFromNode.d.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { SpecParamRef } from './ParamRefNode.js'; - -/** Input data for marks. */ -export type SpecPlotMarkData = - | SpecPlotDataInline - | SpecPlotFrom; - -/** - * An array of inline data values to visualize. As this data does not come - * from a database, it can not be filtered by interactive selections. - */ -export type SpecPlotDataInline = any[]; - -export interface SpecPlotFrom { - /** The name of the backing data table. */ - from: string; - /** A selection with which to filter the mark data. */ - filterBy?: SpecParamRef; - /** - * A flag (default `true`) to enable any mark-specific query optimizations. - * Set to `false` to disable optimizations to aid testing and debugging. - */ - optimize?: boolean; -}; \ No newline at end of file diff --git a/packages/spec/src/ast/PlotLegendNode.d.ts b/packages/spec/src/ast/PlotLegendNode.d.ts deleted file mode 100644 index fe28839b..00000000 --- a/packages/spec/src/ast/PlotLegendNode.d.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { SpecParamRef } from './ParamNode.js'; - -export type LegendType = - | 'color' - | 'opacity' - | 'symbol'; - -export interface SpecPlotLegend { - legend: LegendType; - as?: SpecParamRef; - field?: string; - tickSize?: number; - marginTop?: number; - marginBottom?: number; - marginLeft?: number; - marginRight?: number; - width?: number; - height?: number; - columns?: number; -} - -export interface SpecLegend extends SpecPlotLegend { - for: string; -} diff --git a/packages/spec/src/ast/PlotNode.d.ts b/packages/spec/src/ast/PlotNode.d.ts deleted file mode 100644 index 1441a544..00000000 --- a/packages/spec/src/ast/PlotNode.d.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { SpecPlotAttributes } from './PlotAttributeNode.js'; -import { SpecPlotInteractor } from './PlotInteractorNode.js'; -import { SpecPlotLegend } from './PlotLegendNode.js'; -import { SpecPlotMark } from './PlotMarkNode.js'; - -export type SpecPlotEntry = - | SpecPlotInteractor - | SpecPlotLegend - | SpecPlotMark; - -export interface SpecPlot extends SpecPlotAttributes { - /** - * An array of plot marks, interactors, or legends. - * Marks are graphical elements that make up plot layers. - * Unless otherwise configured, interactors will use the nearest - * previous mark as a basis for which data fields to select. - */ - plot: SpecPlotEntry[]; -} diff --git a/packages/spec/src/ast/SelectionNode.d.ts b/packages/spec/src/ast/SelectionNode.d.ts deleted file mode 100644 index 4f355bd6..00000000 --- a/packages/spec/src/ast/SelectionNode.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -export type SelectionType = - | 'crossfilter' - | 'intersect' - | 'single' - | 'union'; - -export interface SpecSelection { - select: SelectionType; - cross?: boolean; -} diff --git a/packages/spec/src/ast/SpecNode.d.ts b/packages/spec/src/ast/SpecNode.d.ts deleted file mode 100644 index 029471b9..00000000 --- a/packages/spec/src/ast/SpecNode.d.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { SpecDataDefinition } from './DataNode.js'; -import { SpecParamDefinition } from './ParamNode.js'; -import { SpecHConcatNode } from './HConcatNode.js'; -import { SpecVConcatNode } from './VConcatNode.js'; -import { SpecHSpace } from './HSpaceNode.js'; -import { SpecVSpace } from './VSpaceNode.js'; -import { SpecInput } from './InputNode.js'; -import { SpecPlot } from './PlotNode.js'; -import { SpecPlotMark } from './PlotMarkNode.js'; -import { SpecLegend } from './PlotLegendNode.js'; -import { SpecPlotAttributes } from './PlotAttributeNode.js'; -import { DataNode } from './DataNode.js'; -import { ParamNode } from './ParamNode.js'; -import { SelectionNode } from './SelectionNode.js'; -import { PlotAttributeNode } from './PlotAttributeNode.js'; -import { InputNode } from './InputNode.js'; -import { HConcatNode } from './HConcatNode.js'; -import { VConcatNode } from './VConcatNode.js'; -import { HSpaceNode } from './HSpaceNode.js'; -import { VSpaceNode } from './VSpaceNode.js'; -import { PlotNode } from './PlotNode.js'; -import { PlotMarkNode } from './PlotMarkNode.js'; -import { PlotLegendNode } from './PlotLegendNode.js'; - -/** Specification metadata. */ -export interface SpecMeta extends Record { - /** The specification title. */ - title?: string; - /** A description of the specification content. */ - description?: string; - /** Credits or other acknowledgements. */ - credit?: string; -} - -/** Configuration options. */ -export interface SpecConfig extends Record { - extensions?: string | string[]; -} - -/** Top-level dataset definitions. */ -export interface SpecData { - /** A dataset name and definition. */ - [name: string]: SpecDataDefinition; -} - -/** Top-level Param and Selection definitions. */ -export interface SpecData { - /** A parameter name and the Param or Selection definition. */ - [name: string]: SpecParamDefinition; -} - -/** A specifcation component such as a plot, input widget, or layout. */ -export type SpecComponent = - | SpecHConcatNode - | SpecVConcatNode - | SpecHSpace - | SpecVSpace - | SpecInput - | SpecPlot - | SpecPlotMark - | SpecLegend; - -/** A declarative Mosaic specification. */ -export type Spec = { - /** Specification metadata. */ - meta?: SpecMeta; - /** Configuration options. */ - config?: SpecConfig; - /** Dataset definitions. */ - data?: SpecData; - /** Param and Selection definitions. */ - params?: SpecParams; - /** A default set of attributes to apply to all plot components. */ - plotDefaults?: SpecPlotAttributes; -} & SpecComponent; diff --git a/packages/spec/src/ast/TransformNode.d.ts b/packages/spec/src/ast/TransformNode.d.ts deleted file mode 100644 index 381250f3..00000000 --- a/packages/spec/src/ast/TransformNode.d.ts +++ /dev/null @@ -1,191 +0,0 @@ -import { SpecParamRef } from './ParamRefNode.js'; - -export type SpecTransformField = - | string - | SpecParamRef; - -export interface SpecWindowOptions { - orderby?: SpecTransformField | SpecTransformField[]; - partitionby?: SpecTransformField | SpecTransformField[]; - rows?: SpecParamRef | (number | null)[]; - range?: SpecParamRef | (number | null)[]; -} - -export interface SpecAggregateOptions { - distinct?: boolean; -} - -export type SpecTransformArgument = - | string - | number - | boolean; - -export interface SpecBinTransform { - bin: SpecTransfromArgument | [SpecTransfromArgument]; -} - -export interface SpecDateMonthTransform { - dateMonth: SpecTransfromArgument | [SpecTransfromArgument]; -} - -export interface SpecDateMonthDayTransform { - dateMonthDay: SpecTransfromArgument | [SpecTransfromArgument]; -} - -export interface SpecDateDayTransform { - dateDay: SpecTransfromArgument | [SpecTransfromArgument]; -} - -export interface SpecCentroidTransform { - centroid: SpecTransfromArgument | [SpecTransfromArgument]; -} - -export interface SpecCentroidXTransform { - centroidX: SpecTransfromArgument | [SpecTransfromArgument]; -} - -export interface SpecCentroidYTransform { - centroidY: SpecTransfromArgument | [SpecTransfromArgument]; -} - -export interface SpecGeoJSONTransform { - geojson: SpecTransfromArgument | [SpecTransfromArgument]; -} - -export interface SpecArgmaxTransform extends SpecAggregateOptions, SpecWindowOptions { - argmax: [SpecTransfromArgument, SpecTransfromArgument]; -} - -export interface SpecArgminTransform extends SpecAggregateOptions, SpecWindowOptions { - argmin: [SpecTransfromArgument, SpecTransfromArgument]; -} - -export interface SpecAvgTransform extends SpecAggregateOptions, SpecWindowOptions { - avg: SpecTransfromArgument | [SpecTransfromArgument]; -} - -export interface SpecCountTransform extends SpecAggregateOptions, SpecWindowOptions { - count: []; -} - -export interface SpecFirstTransform extends SpecAggregateOptions, SpecWindowOptions { - first: SpecTransfromArgument | [SpecTransfromArgument]; -} - -export interface SpecLastTransform extends SpecAggregateOptions, SpecWindowOptions { - last: SpecTransfromArgument | [SpecTransfromArgument]; -} - -export interface SpecMaxTransform extends SpecAggregateOptions, SpecWindowOptions { - max: SpecTransfromArgument | [SpecTransfromArgument]; -} - -export interface SpecMinTransform extends SpecAggregateOptions, SpecWindowOptions { - min: SpecTransfromArgument | [SpecTransfromArgument]; -} - -export interface SpecMedianTransform extends SpecAggregateOptions, SpecWindowOptions { - median: SpecTransfromArgument | [SpecTransfromArgument]; -} - -export interface SpecModeTransform extends SpecAggregateOptions, SpecWindowOptions { - mode: SpecTransfromArgument | [SpecTransfromArgument]; -} - -export interface SpecProductTransform extends SpecAggregateOptions, SpecWindowOptions { - product: SpecTransfromArgument | [SpecTransfromArgument]; -} - -export interface SpecQuantileTransform extends SpecAggregateOptions, SpecWindowOptions { - quantile: [SpecTransfromArgument, SpecTransfromArgument]; -} - -export interface SpecSumTransform extends SpecAggregateOptions, SpecWindowOptions { - sum: SpecTransfromArgument | [SpecTransfromArgument]; -} - -export interface SpecRowNumberTransform extends SpecWindowOptions { - row_number: []; -} - -export interface SpecRankTransform extends SpecWindowOptions { - rank: []; -} - -export interface SpecDenseRankTransform extends SpecWindowOptions { - dense_rank: []; -} - -export interface SpecPercentRankTransform extends SpecWindowOptions { - percent_rank: []; -} - -export interface SpecCumeDistTransform extends SpecWindowOptions { - cume_dist: []; -} - -export interface SpecNTileTransform extends SpecWindowOptions { - ntile: SpecTransfromArgument | [SpecTransfromArgument]; -} - -export interface SpecRankTransform extends SpecWindowOptions { - rank: SpecTransfromArgument | [SpecTransfromArgument]; -} - -export interface SpecLagTransform extends SpecWindowOptions { - lag: SpecTransfromArgument | [SpecTransfromArgument, SpecTransfromArgument?, SpecTransfromArgument?]; -} - -export interface SpecLeadTransform extends SpecWindowOptions { - lag: SpecTransfromArgument | [SpecTransfromArgument, SpecTransfromArgument?, SpecTransfromArgument?]; -} - -export interface SpecFirstValueTransform extends SpecWindowOptions { - first_value: SpecTransfromArgument | [SpecTransfromArgument]; -} - -export interface SpecLastValueTransform extends SpecWindowOptions { - last_value: SpecTransfromArgument | [SpecTransfromArgument]; -} - -export interface SpecNthValueTransform extends SpecWindowOptions { - nth_value: SpecTransfromArgument | [SpecTransfromArgument, SpecTransfromArgument?]; -} - -export type SpecTransform = - | SpecBinTransform - | SpecDateMonthTransform - | SpecDateMonthDayTransform - | SpecDateDayTransform - | SpecCentroidTransform - | SpecCentroidXTransform - | SpecCentroidYTransform - | SpecGeoJSONTransform - | SpecArgmaxTransform - | SpecArgminTransform - | SpecAvgTransform - | SpecCountTransform - | SpecMaxTransform - | SpecMinTransform - | SpecFirstTransform - | SpecLastTransform - | SpecMaxTransform - | SpecMinTransform - | SpecMedianTransform - | SpecModeTransform - | SpecProductTransform - | SpecQuantileTransform - | SpecSumTransform - | SpecRowNumberTransform - | SpecRowNumberTransform - | SpecRankTransform - | SpecDenseRankTransform - | SpecPercentRankTransform - | SpecCumeDistTransform - | SpecNTileTransform - | SpecRankTransform - | SpecLagTransform - | SpecLeadTransform - | SpecFirstValueTransform - | SpecLastValueTransform - | SpecNthValueTransform; diff --git a/packages/spec/src/ast/VConcatNode.d.ts b/packages/spec/src/ast/VConcatNode.d.ts deleted file mode 100644 index 4d5bbab9..00000000 --- a/packages/spec/src/ast/VConcatNode.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { SpecComponent } from './SpecNode.js'; - -export interface SpecVConcatNode { - /** - * Vertically concatenate components in a column layout. - */ - vconcat: SpecComponent[]; -} diff --git a/packages/spec/src/config/inputs.js b/packages/spec/src/config/inputs.js index a606e269..de0995a0 100644 --- a/packages/spec/src/config/inputs.js +++ b/packages/spec/src/config/inputs.js @@ -1,5 +1,6 @@ /** * Set of input widget type names. + * @returns {Set} */ export function inputNames(overrides = []) { return new Set([ diff --git a/packages/spec/src/config/plots.js b/packages/spec/src/config/plots.js index 69abb9d9..4ea215c5 100644 --- a/packages/spec/src/config/plots.js +++ b/packages/spec/src/config/plots.js @@ -19,6 +19,7 @@ export function plotNames({ /** * Names of attribute directive functions. + * @returns {Set} */ export function plotAttributeNames(overrides = []) { return new Set([ @@ -29,6 +30,7 @@ export function plotAttributeNames(overrides = []) { /** * Names interactor directive functions. + * @returns {Set} */ export function plotInteractorNames(overrides = []) { return new Set([ @@ -39,6 +41,7 @@ export function plotInteractorNames(overrides = []) { /** * Names of legend directive functions. + * @returns {Set} */ export function plotLegendNames(overrides = []) { return new Set([ @@ -49,6 +52,7 @@ export function plotLegendNames(overrides = []) { /** * Names of mark directive functions. + * @returns {Set} */ export function plotMarkNames(overrides = []) { return new Set([ diff --git a/packages/spec/src/index.d.ts b/packages/spec/src/index.d.ts deleted file mode 100644 index 5856e727..00000000 --- a/packages/spec/src/index.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './ast/SpecNode.js'; -export * from './ast-to-dom.js'; -export * from './ast-to-esm.js'; -export * from './parse-spec.js'; diff --git a/packages/spec/src/index.js b/packages/spec/src/index.js index 1fdb3e45..84fab64c 100644 --- a/packages/spec/src/index.js +++ b/packages/spec/src/index.js @@ -1,3 +1,7 @@ +/** + * @typedef {import('./spec/Spec.js').Spec} Spec A Mosaic declarative specification. + */ + export { astToDOM, InstantiateContext } from './ast-to-dom.js'; export { astToESM, CodegenContext } from './ast-to-esm.js'; export { parseSpec } from './parse-spec.js'; diff --git a/packages/spec/src/parse-spec.d.ts b/packages/spec/src/parse-spec.d.ts deleted file mode 100644 index abc334d6..00000000 --- a/packages/spec/src/parse-spec.d.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Spec, SpecNode } from './ast/SpecNode.js'; - -/** - * Parse a Mosaic specification to an AST (abstract syntax tree). - */ -export function parseSpec( - spec: Spec | string, - options?: ParseSpecOptions -) : SpecNode; - -export interface ParseSpecOptions { - components?: Map, - transforms?: string[], - inputs?: string[], - plot?: string[], - params?: any[], - datasets?: any[] -} diff --git a/packages/spec/src/parse-spec.js b/packages/spec/src/parse-spec.js index 432dd571..d0e108cc 100644 --- a/packages/spec/src/parse-spec.js +++ b/packages/spec/src/parse-spec.js @@ -5,27 +5,52 @@ import { ParamRefNode } from './ast/ParamRefNode.js'; import { parseAttribute } from './ast/PlotAttributeNode.js'; import { SelectionNode } from './ast/SelectionNode.js'; import { SpecNode } from './ast/SpecNode.js'; - import { componentMap } from './config/components.js'; import { inputNames } from './config/inputs.js'; import { plotNames } from './config/plots.js'; import { transformNames } from './config/transforms.js'; +import { error, paramRef } from './util.js'; -import { error, isString, paramRef } from './util.js'; +/** + * @typedef {{ + * attributes: Set; + * interactors: Set; + * legends: Set; + * marks: Set; + * }} PlotNames names for supported plot elements + */ /** * Parse a Mosaic specification to an AST (abstract syntax tree). - * @param {object|string} spec The input specification as an object - * or JSON string. + * @param {import('./spec/Spec.js').Spec} spec The input specification. * @param {object} [options] Optional parse options object. + * @param {Map} [options.components] Map of component names to parse functions. + * @param {Set} [options.transforms] The names of allowed transform functions. + * @param {Set} [options.inputs] The names of supported input widgets. + * @param {PlotNames} [options.plot] The names of supported plot elements. + * @param {any[]} [options.params] An array of [name, node] pairs of pre-parsed + * Param or Selection AST nodes. + * @param {any[]} [options.datasets] An array of [name, node] pairs of pre-parsed + * dataset definition AST nodes. * @returns {SpecNode} The top-level AST spec node. */ export function parseSpec(spec, options) { - spec = isString(spec) ? JSON.parse(spec) : spec; return new ParseContext(options).parse(spec); } export class ParseContext { + /** + * Create a new parser context. + * @param {object} [options] + * @param {Map} [options.components] Map of component names to parse functions. + * @param {Set} [options.transforms] The names of allowed transform functions. + * @param {Set} [options.inputs] The names of supported input widgets. + * @param {PlotNames} [options.plot] The names of supported plot elements. + * @param {any[]} [options.params] An array of [name, node] pairs of pre-parsed + * Param or Selection AST nodes. + * @param {any[]} [options.datasets] An array of [name, node] pairs of pre-parsed + * dataset definition AST nodes. + */ constructor({ components = componentMap(), transforms = transformNames(), @@ -87,6 +112,14 @@ export class ParseContext { this.error(`Invalid specification.`, spec); } + /** + * Test if a value is param reference, if so, generate a paramter definition + * as needed and return a new ParamRefNode. Otherwise, return a LiteralNode. + * @param {*} value The value to test. + * @param {() => ParamNode | SelectionNode} [makeNode] A Param of Selection AST + * node constructor. + * @returns {ParamRefNode|LiteralNode} An AST node for the input value. + */ maybeParam(value, makeNode = () => new ParamNode) { const { params } = this; const name = paramRef(value); diff --git a/packages/spec/src/spec/Data.ts b/packages/spec/src/spec/Data.ts new file mode 100644 index 00000000..d83d5951 --- /dev/null +++ b/packages/spec/src/spec/Data.ts @@ -0,0 +1,69 @@ +export interface DataBaseOptions { + select?: string[]; + where?: string | string[]; + view?: boolean; + temp?: boolean; + replace?: boolean; +} + +/** + * A SQL query defining a new temporary database table. + */ +export type DataQuery = string; + +/** + * An inline array of data objects to treat as JSON data. + */ +export type DataArray = object[]; + +export interface DataFile { + /** + * The data file to load. If no type option is provided, + * the file suffix must be one of `.csv`, `.json`, or `.parquet`. + */ + file: `${string}.parquet` | `${string}.csv` | `${string}.json`; +} + +export interface DataTable extends DataBaseOptions { + type: 'table'; + query: string; +} + +export interface DataParquet extends DataBaseOptions { + type: 'parquet'; + file: string; +} + +export interface DataCSV extends DataBaseOptions { + type: 'csv'; + file: string; + delimiter?: string, + sample_size?: number; +} + +export interface DataSpatial extends DataBaseOptions { + type: 'spatial'; + file: string; + layer?: string; +} + +export interface DataJSON extends DataBaseOptions { + type: 'json'; + file: string; +} + +export interface DataJSONObjects extends DataBaseOptions { + type?: 'json'; + data: object[]; +} + +export type DataDefinition = + | DataQuery + | DataArray + | DataFile + | DataTable + | DataParquet + | DataCSV + | DataSpatial + | DataJSON + | DataJSONObjects; diff --git a/packages/spec/src/spec/Expression.ts b/packages/spec/src/spec/Expression.ts new file mode 100644 index 00000000..9e631aef --- /dev/null +++ b/packages/spec/src/spec/Expression.ts @@ -0,0 +1,13 @@ +export type Expression = + | SQLExpression + | AggregateExpression; + +export interface SQLExpression { + sql: string; + label?: string; +} + +export interface AggregateExpression { + agg: string; + label?: string; +} diff --git a/packages/spec/src/spec/HConcat.ts b/packages/spec/src/spec/HConcat.ts new file mode 100644 index 00000000..a91b9a48 --- /dev/null +++ b/packages/spec/src/spec/HConcat.ts @@ -0,0 +1,8 @@ +import { Component } from './Spec.js'; + +export interface HConcat { + /** + * Horizontally concatenate components in a row layout. + */ + hconcat: Component[]; +} diff --git a/packages/spec/src/ast/HSpaceNode.d.ts b/packages/spec/src/spec/HSpace.ts similarity index 86% rename from packages/spec/src/ast/HSpaceNode.d.ts rename to packages/spec/src/spec/HSpace.ts index 7cdbef43..a74302ed 100644 --- a/packages/spec/src/ast/HSpaceNode.d.ts +++ b/packages/spec/src/spec/HSpace.ts @@ -1,4 +1,4 @@ -export interface SpecHSpace { +export interface HSpace { /** * Horizontal space to place between components. * Number values indicate screen pixels. diff --git a/packages/spec/src/spec/Input.ts b/packages/spec/src/spec/Input.ts new file mode 100644 index 00000000..4ac4c38a --- /dev/null +++ b/packages/spec/src/spec/Input.ts @@ -0,0 +1,178 @@ +import { ParamRef } from './Param.js'; + +export interface Menu { + /** + * A menu input widget. + */ + input: 'menu'; + /** + * The output selection. A selection clause is added for the + * currently selected menu option. + */ + as?: ParamRef; + /** + * The name of a database table to use as a data source for this widget. + * Used in conjunction with the `column` property. + */ + from?: string; + /** + * The name of a database column from which to pull menu options. + * The unique column values are used as menu options. + * Used in conjunction with the `from` property. + */ + column?: string; + /** + * A selection to filter the database table indicated by the `from` property. + */ + filterBy?: ParamRef; + /** + * A text label for this input. + */ + label?: string; + /** + * An array of menu options, as literal values or option objects. + * Option objects have a `value` property and an optional `label` property. + * If no label is provided, the (string-coerced) value is used. + */ + options?: Array; + /** + * The initial selected menu option. + */ + value?: any; +} + +export interface Search { + /** + * A text search input widget. + */ + input: 'search'; + /** + * The output selection. A selection clause is added for the + * current text search query. + */ + as?: ParamRef; + /** + * The type of text search query to perform. One of: + * - `"contains"` (default): the query string may appear anywhere in the text + * - `"prefix"`: the query string must appear at the start of the text + * - `"suffix"`: the query string must appear at the end of the text + * - `"regexp"`: the query string is a regular expression the text must match + */ + type?: 'contains' | 'prefix' | 'suffix' | 'regexp'; + /** + * The name of a database table to use as an autocomplete data source + * for this widget. Used in conjunction with the `column` property. + */ + from?: string; + /** + * The name of a database column from which to pull valid search results. + * The unique column values are used as search autocomplete values. + * Used in conjunction with the `from` property. + */ + column?: string; + /** + * A selection to filter the database table indicated by the `from` property. + */ + filterBy?: ParamRef; + /** + * A text label for this input. + */ + label?: string; +} + +export interface Slider { + /** + * A slider input widget. + */ + input: 'slider'; + /** + * The output selection. A selection clause is added for the + * currently selected slider option. + */ + as?: ParamRef; + /** + * The name of a database table to use as a data source for this widget. + * Used in conjunction with the `column` property. + * The minimum and maximum values of the column determine the slider range. + */ + from?: string; + /** + * The name of a database column whose values determine the slider range. + * Used in conjunction with the `from` property. + * The minimum and maximum values of the column determine the slider range. + */ + column?: string; + /** + * A selection to filter the database table indicated by the `from` property. + */ + filterBy?: ParamRef; + /** + * The minumum slider value. + */ + min?: number; + /** + * The maximum slider value. + */ + max?: number; + /** + * The slider step, the amount to increment between consecutive values. + */ + step?: number; + /** + * A text label for this input. + */ + label?: string; + /** + * The initial slider value. + */ + value?: number; + /** + * The width of the slider in screen pixels. + */ + width?: number; +} + +export interface Table { + /** + * A table grid widget. + */ + input: 'table'; + /** + * The name of a database table to use as a data source for this widget. + */ + from: string; + /** + * A list of column names to include in the table grid. + * If unspecified, all table columns are included. + */ + columns?: string[]; + /** + * A selection to filter the database table indicated by the `from` property. + */ + filterBy?: ParamRef; + /** + * An object of per-column alignment values. + * Column names should be object keys, which map to alignment values. + * Valid alignment values are: `"left"`, `"right"`, `"center"`, and `"justify"`. + * By default, numbers are right-aligned and other values are left-aligned. + */ + align?: { [column: string]: 'left' | 'right' | 'center' | 'justify' }; + /** + * If a number, sets the total width of the table widget, in pixels. + * If an object, provides per-column pixel width values. + * Column names should be object keys, mapped to numeric width values. + */ + width?: number | { [column : string]: number }; + /** + * The maximum width of the table widget, in pixels. + */ + maxWidth?: number; + /** + * The height of the table widget, in pixels. + */ + height?: number; + /** + * The number of rows load in a new batch upon table scroll. + */ + rowBatch?: number; +} diff --git a/packages/spec/src/spec/Param.ts b/packages/spec/src/spec/Param.ts new file mode 100644 index 00000000..30df5513 --- /dev/null +++ b/packages/spec/src/spec/Param.ts @@ -0,0 +1,39 @@ +export type ParamRef = `$${string}`; + +export interface ParamBase { + select?: 'value'; +} + +export interface Param extends ParamBase { + value: ParamValue; +} + +export interface ParamDate extends ParamBase { + date: string; +} + +export type ParamLiteral = + | string + | number + | boolean; + +export type ParamValue = + | ParamLiteral + | Array; + +export type SelectionType = + | 'crossfilter' + | 'intersect' + | 'single' + | 'union'; + +export interface Selection { + select: SelectionType; + cross?: boolean; +} + +export type ParamDefinition = + | ParamValue + | Param + | ParamDate + | Selection; diff --git a/packages/spec/src/spec/Plot.ts b/packages/spec/src/spec/Plot.ts new file mode 100644 index 00000000..da0b0515 --- /dev/null +++ b/packages/spec/src/spec/Plot.ts @@ -0,0 +1,19 @@ +import { PlotAttributes } from './PlotAttribute.js'; +import { PlotInteractor } from './PlotInteractor.js'; +import { PlotLegend } from './PlotLegend.js'; +import { PlotMark } from './PlotMark.js'; + +export type PlotEntry = + | PlotInteractor + | PlotLegend + | PlotMark; + +export interface Plot extends PlotAttributes { + /** + * An array of plot marks, interactors, or legends. + * Marks are graphical elements that make up plot layers. + * Unless otherwise configured, interactors will use the nearest + * previous mark as a basis for which data fields to select. + */ + plot: PlotEntry[]; +} diff --git a/packages/spec/src/spec/PlotAttribute.ts b/packages/spec/src/spec/PlotAttribute.ts new file mode 100644 index 00000000..c74c22c8 --- /dev/null +++ b/packages/spec/src/spec/PlotAttribute.ts @@ -0,0 +1,383 @@ +import { ParamRef } from './Param.js'; + +export interface PlotAttributes { + /** + * A unique name for the plot. The name is used by standalone legend + * components to to lookup the plot and access scale mappings. + */ + name?: string; + + /** + * The outer width of the plot in pixels, including margins. Defaults to 640. + * On Observable, this can be set to the built-in [width][1] for full-width + * responsive plots. Note: the default style has a max-width of 100%; the plot + * will automatically shrink to fit even when a fixed width is specified. + * + * [1]: https://github.com/observablehq/stdlib/blob/main/README.md#width + */ + width?: number | ParamRef; + + /** + * The outer height of the plot in pixels, including margins. The default + * depends on the plot’s scales, and the plot’s width if an aspectRatio is + * specified. For example, if the *y* scale is linear and there is no *fy* + * scale, it might be 396. + */ + height?: number | ParamRef; + + /** + * The desired aspect ratio of the *x* and *y* scales, affecting the default + * height. Given an aspect ratio of *dx* / *dy*, and assuming that the *x* and + * *y* scales represent equivalent units (say, degrees Celsius or meters), + * computes a default height such that *dx* pixels along *x* represents the + * same variation as *dy* pixels along *y*. Note: when faceting, set the *fx* + * and *fy* scales’ **round** option to false for an exact aspect ratio. + */ + aspectRatio?: number | boolean | null | ParamRef; + + /** + * Shorthand to set the same default for all four margins: **marginTop**, + * **marginRight**, **marginBottom**, and **marginLeft**. Otherwise, the + * default margins depend on the maximum margins of the plot’s marks. While + * most marks default to zero margins (because they are drawn inside the chart + * area), Plot’s axis marks have non-zero default margins. + */ + margin?: number | ParamRef; + + /** + * The top margin; the distance in pixels between the top edges of the inner + * and outer plot area. Defaults to the maximum top margin of the plot’s + * marks. + */ + marginTop?: number | ParamRef; + + /** + * The right margin; the distance in pixels between the right edges of the + * inner and outer plot area. Defaults to the maximum right margin of the + * plot’s marks. + */ + marginRight?: number | ParamRef; + + /** + * The bottom margin; the distance in pixels between the bottom edges of the + * inner and outer plot area. Defaults to the maximum bottom margin of the + * plot’s marks. + */ + marginBottom?: number | ParamRef; + + /** + * The left margin; the distance in pixels between the left edges of the inner + * and outer plot area. Defaults to the maximum left margin of the plot’s + * marks. + */ + marginLeft?: number | ParamRef; + + /** + * A shorthand object notation for setting multiple margin values. + * The object keys are margin names (top, right, etc). + */ + margins?: { + top?: number | ParamRef; + right?: number | ParamRef; + bottom?: number | ParamRef; + left?: number | ParamRef; + }; + + /** + * Shorthand to set the same default for all four insets: **insetTop**, + * **insetRight**, **insetBottom**, and **insetLeft**. All insets typically + * default to zero, though not always (say when using bin transform). A + * positive inset reduces effective area, while a negative inset increases it. + */ + inset?: number | ParamRef; + + /** + * Custom styles to override Plot’s defaults. Styles may be specified either + * as a string of inline styles (*e.g.*, `"color: red;"`, in the same fashion + * as assigning [*element*.style][1]) or an object of properties (*e.g.*, + * `{color: "red"}`, in the same fashion as assigning [*element*.style + * properties][2]). Note that unitless numbers ([quirky lengths][3]) such as + * `{padding: 20}` may not supported by some browsers; you should instead + * specify a string with units such as `{padding: "20px"}`. By default, the + * returned plot has a max-width of 100%, and the system-ui font. Plot’s marks + * and axes default to [currentColor][4], meaning that they will inherit the + * surrounding content’s color. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style + * [2]: https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleDeclaration + * [3]: https://www.w3.org/TR/css-values-4/#deprecated-quirky-length + * [4]: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#currentcolor_keyword + */ + style?: string | Partial | null | ParamRef; + + /** + * How to distribute unused space in the **range** for *point* and *band* + * scales. A number in [0, 1], such as: + * + * - 0 - use the start of the range, putting unused space at the end + * - 0.5 (default) - use the middle, distributing unused space evenly + * - 1 use the end, putting unused space at the start + * + * For ordinal position scales only. + */ + align?: number | ParamRef; + + /** + * For *band* scales, how much of the **range** to reserve to separate + * adjacent bands; defaults to 0.1 (10%). For *point* scales, the amount of + * inset for the first and last value as a proportion of the bandwidth; + * defaults to 0.5 (50%). + * + * For ordinal position scales only. + */ + padding?: number | ParamRef; + + /** + * The side of the frame on which to place the implicit axis: *top* or + * *bottom* for *x* or *fx*, or *left* or *right* for *y* or *fy*. The default + * depends on the scale: + * + * - *x* - *bottom* + * - *y* - *left* + * - *fx* - *top* if there is a *bottom* *x* axis, and otherwise *bottom* + * - *fy* - *right* if there is a *left* *y* axis, and otherwise *right* + * + * If *both*, an implicit axis will be rendered on both sides of the plot + * (*top* and *bottom* for *x* or *fx*, or *left* and *right* for *y* or + * *fy*). If null, the implicit axis is suppressed. + * + * For position axes only. + */ + axis?: 'top' | 'right' | 'bottom' | 'left' | 'both' | boolean | null | ParamRef; + + /** + * Whether to show a grid aligned with the scale’s ticks. If true, show a grid + * with the currentColor stroke; if a string, show a grid with the specified + * stroke color; if an approximate number of ticks, an interval, or an array + * of tick values, show corresponding grid lines. See also the grid mark. + * + * For axes only. + */ + grid?: boolean | string | ParamRef; + + /** + * A textual label to show on the axis or legend; if null, show no label. By + * default the scale label is inferred from channel definitions, possibly with + * an arrow (↑, →, ↓, or ←) to indicate the direction of increasing value. + * + * For axes and legends only. + */ + label?: string | null | ParamRef; + + xyDomain?: any; + + // x scale attributes + xScale?: any; + xDomain?: any; + xRange?: any; + xNice?: any; + xInset?: any; + xInsetLeft?: any; + xInsetRight?: any; + xClamp?: any; + xRound?: any; + xAlign?: any; + xPadding?: any; + xPaddingInner?: any; + xPaddingOuter?: any; + xAxis?: any; + xTicks?: any; + xTickSize?: any; + xTickSpacing?: any; + xTickPadding?: any; + xTickFormat?: any; + xTickRotate?: any; + xGrid?: any; + xLine?: any; + xLabel?: any; + xLabelAnchor?: any; + xLabelOffset?: any; + xFontVariant?: any; + xAriaLabel?: any; + xAriaDescription?: any; + xReverse?: any; + xZero?: any; + xBase?: any; + xExponent?: any; + xConstant?: any; + + // y scale attributes + yScale?: any; + yDomain?: any; + yRange?: any; + yNice?: any; + yInset?: any; + yInsetTop?: any; + yInsetBottom?: any; + yClamp?: any; + yRound?: any; + yAlign?: any; + yPadding?: any; + yPaddingInner?: any; + yPaddingOuter?: any; + yAxis?: any; + yTicks?: any; + yTickSize?: any; + yTickSpacing?: any; + yTickPadding?: any; + yTickFormat?: any; + yTickRotate?: any; + yGrid?: any; + yLine?: any; + yLabel?: any; + yLabelAnchor?: any; + yLabelOffset?: any; + yFontVariant?: any; + yAriaLabel?: any; + yAriaDescription?: any; + yReverse?: any; + yZero?: any; + yBase?: any; + yExponent?: any; + yConstant?: any; + + // facet attributes + facetMargin?: any; + facetMarginTop?: any; + facetMarginBottom?: any; + facetMarginLeft?: any; + facetMarginRight?: any; + facetGrid?: any; + facetLabel?: any; + + // fx scale attributes + fxDomain?: any; + fxRange?: any; + fxNice?: any; + fxInset?: any; + fxInsetLeft?: any; + fxInsetRight?: any; + fxRound?: any; + fxAlign?: any; + fxPadding?: any; + fxPaddingInner?: any; + fxPaddingOuter?: any; + fxAxis?: any; + fxTicks?: any; + fxTickSize?: any; + fxTickSpacing?: any; + fxTickPadding?: any; + fxTickFormat?: any; + fxTickRotate?: any; + fxGrid?: any; + fxLine?: any; + fxLabel?: any; + fxLabelAnchor?: any; + fxLabelOffset?: any; + fxFontVariant?: any; + fxAriaLabel?: any; + fxAriaDescription?: any; + fxReverse?: any; + + // fy scale attributes + fyDomain?: any; + fyRange?: any; + fyNice?: any; + fyInset?: any; + fyInsetTop?: any; + fyInsetBottom?: any; + fyRound?: any; + fyAlign?: any; + fyPadding?: any; + fyPaddingInner?: any; + fyPaddingOuter?: any; + fyAxis?: any; + fyTicks?: any; + fyTickSize?: any; + fyTickSpacing?: any; + fyTickPadding?: any; + fyTickFormat?: any; + fyTickRotate?: any; + fyGrid?: any; + fyLine?: any; + fyLabel?: any; + fyLabelAnchor?: any; + fyLabelOffset?: any; + fyFontVariant?: any; + fyAriaLabel?: any; + fyAriaDescription?: any; + fyReverse?: any; + + // color scale attributes + colorScale?: any; + colorDomain?: any; + colorRange?: any; + colorClamp?: any; + colorN?: any; + colorNice?: any; + colorScheme?: any; + colorInterpolate?: any; + colorPivot?: any; + colorSymmetric?: any; + colorLabel?: any; + colorReverse?: any; + colorZero?: any; + colorTickFormat?: any; + colorBase?: any; + colorExponent?: any; + colorConstant?: any; + + // opacity scale attributes + opacityScale?: any; + opacityDomain?: any; + opacityRange?: any; + opacityClamp?: any; + opacityNice?: any; + opacityLabel?: any; + opacityReverse?: any; + opacityZero?: any; + opacityTickFormat?: any; + opacityBase?: any; + opacityExponent?: any; + opacityConstant?: any; + + // symbol scale attributes + symbolScale?: any; + symbolDomain?: any; + symbolRange?: any; + + // r scale attributes + rScale?: any; + rDomain?: any; + rRange?: any; + rClamp?: any; + rNice?: any; + rZero?: any; + rBase?: any; + rExponent?: any; + rConstant?: any; + + // length scale attributes + lengthScale?: any; + lengthDomain?: any; + lengthRange?: any; + lengthClamp?: any; + lengthNice?: any; + lengthZero?: any; + lengthBase?: any; + lengthExponent?: any; + lengthConstant?: any; + + // projection attributes + projectionType?: any; + projectionParallels?: any; + projectionPrecision?: any; + projectionRotate?: any; + projectionDomain?: any; + projectionInset?: any; + projectionInsetLeft?: any; + projectionInsetRight?: any; + projectionInsetTop?: any; + projectionInsetBottom?: any; + projectionClip?: any; +} diff --git a/packages/spec/src/spec/PlotFrom.ts b/packages/spec/src/spec/PlotFrom.ts new file mode 100644 index 00000000..00755e83 --- /dev/null +++ b/packages/spec/src/spec/PlotFrom.ts @@ -0,0 +1,28 @@ +import { ParamRef } from './Param.js'; + +/** Input data for marks. */ +export type PlotMarkData = + | PlotDataInline + | PlotFrom; + +/** + * An array of inline data values to visualize. As this data does not come + * from a database, it can not be filtered by interactive selections. + */ +export type PlotDataInline = any[]; + +export interface PlotFrom { + /** + * The name of the backing data table. + */ + from: string; + /** + * A selection that filters the mark data. + */ + filterBy?: ParamRef; + /** + * A flag (default `true`) to enable any mark-specific query optimizations. + * If `false`, optimizations are disabled to aid testing and debugging. + */ + optimize?: boolean; +} diff --git a/packages/spec/src/ast/PlotInteractorNode.d.ts b/packages/spec/src/spec/PlotInteractor.ts similarity index 69% rename from packages/spec/src/ast/PlotInteractorNode.d.ts rename to packages/spec/src/spec/PlotInteractor.ts index 54ed54ad..425e1fc4 100644 --- a/packages/spec/src/ast/PlotInteractorNode.d.ts +++ b/packages/spec/src/spec/PlotInteractor.ts @@ -1,4 +1,4 @@ -export type SpecPlotInteractor = { +export type PlotInteractor = { select: string; } & { // todo: specific interactors diff --git a/packages/spec/src/spec/PlotLegend.ts b/packages/spec/src/spec/PlotLegend.ts new file mode 100644 index 00000000..b41c76f9 --- /dev/null +++ b/packages/spec/src/spec/PlotLegend.ts @@ -0,0 +1,19 @@ +import { ParamRef } from './Param.js'; + +export interface PlotLegend { + legend: 'color' | 'opacity' | 'symbol'; + as?: ParamRef; + field?: string; + tickSize?: number; + marginTop?: number; + marginBottom?: number; + marginLeft?: number; + marginRight?: number; + width?: number; + height?: number; + columns?: number; +} + +export interface Legend extends PlotLegend { + for: string; +} diff --git a/packages/spec/src/ast/PlotMarkNode.d.ts b/packages/spec/src/spec/PlotMark.ts similarity index 87% rename from packages/spec/src/ast/PlotMarkNode.d.ts rename to packages/spec/src/spec/PlotMark.ts index 33c9a2dd..14091443 100644 --- a/packages/spec/src/ast/PlotMarkNode.d.ts +++ b/packages/spec/src/spec/PlotMark.ts @@ -1,7 +1,7 @@ -import { SpecExpression } from './ExpressionNode.js'; -import { SpecParamRef } from './ParamRefNode.js'; -import { SpecPlotMarkData } from './PlotFromNode.js'; -import { SpecTransform } from './TransformNode.js'; +import { Expression } from './Expression.js'; +import { ParamRef } from './Param.js'; +import { PlotMarkData } from './PlotFrom.js'; +import { Transform } from './Transform.js'; export type MarkType = | 'area' @@ -67,22 +67,22 @@ export type MarkType = | 'graticule'; export type MarkOption = - | SpecParamRef + | ParamRef | number | string | boolean - | SpecExpression - | SpecTransform + | Expression + | Transform | any[]; /** * A graphical mark (layer) for a plot. */ -export interface SpecPlotMark { +export interface PlotMark { /** The mark type. */ mark: MarkType; /** The data the mark should visualize. */ - data?: SpecPlotMarkData; + data?: PlotMarkData; filter?: MarkOption; sort?: any; diff --git a/packages/spec/src/spec/Spec.ts b/packages/spec/src/spec/Spec.ts new file mode 100644 index 00000000..cf4c3f63 --- /dev/null +++ b/packages/spec/src/spec/Spec.ts @@ -0,0 +1,60 @@ +import { DataDefinition } from './Data.js'; +import { ParamDefinition } from './Param.js'; +import { HConcat } from './HConcat.js'; +import { VConcat } from './VConcat.js'; +import { HSpace } from './HSpace.js'; +import { VSpace } from './VSpace.js'; +import { Menu, Search, Slider, Table } from './Input.js'; +import { Plot } from './Plot.js'; +import { PlotMark } from './PlotMark.js'; +import { Legend } from './PlotLegend.js'; +import { PlotAttributes } from './PlotAttribute.js'; + +/** Specification metadata. */ +export interface Meta extends Record { + /** The specification title. */ + title?: string; + /** A description of the specification content. */ + description?: string; + /** Credits or other acknowledgements. */ + credit?: string; +} + +/** Configuration options. */ +export interface Config extends Record { + extensions?: string | string[]; +} + +/** Top-level dataset definitions. */ +export type Data = Record; + +/** Top-level Param and Selection definitions. */ +export type Params = Record; + +/** A specifcation component such as a plot, input widget, or layout. */ +export type Component = + | HConcat + | VConcat + | HSpace + | VSpace + | Menu + | Search + | Slider + | Table + | Plot + | PlotMark + | Legend; + +/** A declarative Mosaic specification. */ +export type Spec = { + /** Specification metadata. */ + meta?: Meta; + /** Configuration options. */ + config?: Config; + /** Dataset definitions. */ + data?: Data; + /** Param and Selection definitions. */ + params?: Params; + /** A default set of attributes to apply to all plot components. */ + plotDefaults?: PlotAttributes; +} & Component; diff --git a/packages/spec/src/spec/Transform.ts b/packages/spec/src/spec/Transform.ts new file mode 100644 index 00000000..0dcd6829 --- /dev/null +++ b/packages/spec/src/spec/Transform.ts @@ -0,0 +1,187 @@ +import { ParamRef } from './Param.js'; + +export type TransformField = + | string + | ParamRef; + +export interface WindowOptions { + orderby?: TransformField | TransformField[]; + partitionby?: TransformField | TransformField[]; + rows?: ParamRef | (number | null)[]; + range?: ParamRef | (number | null)[]; +} + +export interface AggregateOptions { + distinct?: boolean; +} + +type TransformArgument = string | number | boolean; +type Arg1 = TransformArgument | [TransformArgument]; +type Arg2 = [TransformArgument, TransformArgument]; +type Arg2Opt = TransformArgument | [TransformArgument, TransformArgument?]; +type Arg3Opt = TransformArgument | [TransformArgument, TransformArgument?, TransformArgument?]; + +export interface Bin { + bin: Arg1; +} + +export interface DateMonth { + dateMonth: Arg1; +} + +export interface DateMonthDay { + dateMonthDay: Arg1; +} + +export interface DateDay { + dateDay: Arg1; +} + +export interface Centroid { + centroid: Arg1; +} + +export interface CentroidX { + centroidX: Arg1; +} + +export interface CentroidY { + centroidY: Arg1; +} + +export interface GeoJSON { + geojson: Arg1; +} + +export interface Argmax extends AggregateOptions, WindowOptions { + argmax: Arg2; +} + +export interface Argmin extends AggregateOptions, WindowOptions { + argmin: Arg2; +} + +export interface Avg extends AggregateOptions, WindowOptions { + avg: Arg1; +} + +export interface Count extends AggregateOptions, WindowOptions { + count: [] | Arg1; +} + +export interface First extends AggregateOptions, WindowOptions { + first: Arg1; +} + +export interface Last extends AggregateOptions, WindowOptions { + last: Arg1; +} + +export interface Max extends AggregateOptions, WindowOptions { + max: Arg1; +} + +export interface Min extends AggregateOptions, WindowOptions { + min: Arg1; +} + +export interface Median extends AggregateOptions, WindowOptions { + median: Arg1; +} + +export interface Mode extends AggregateOptions, WindowOptions { + mode: Arg1; +} + +export interface Product extends AggregateOptions, WindowOptions { + product: Arg1; +} + +export interface Quantile extends AggregateOptions, WindowOptions { + quantile: Arg2; +} + +export interface Sum extends AggregateOptions, WindowOptions { + sum: Arg1; +} + +export interface RowNumber extends WindowOptions { + row_number: []; +} + +export interface Rank extends WindowOptions { + rank: []; +} + +export interface DenseRank extends WindowOptions { + dense_rank: []; +} + +export interface PercentRank extends WindowOptions { + percent_rank: []; +} + +export interface CumeDist extends WindowOptions { + cume_dist: []; +} + +export interface NTile extends WindowOptions { + ntile: Arg1; +} + +export interface Lag extends WindowOptions { + lag: Arg3Opt; +} + +export interface Lead extends WindowOptions { + lag: Arg3Opt; +} + +export interface FirstValue extends WindowOptions { + first_value: Arg1; +} + +export interface LastValue extends WindowOptions { + last_value: Arg1; +} + +export interface NthValue extends WindowOptions { + nth_value: Arg2Opt; +} + +export type Transform = + | Bin + | DateMonth + | DateMonthDay + | DateDay + | Centroid + | CentroidX + | CentroidY + | GeoJSON + | Argmax + | Argmin + | Avg + | Count + | Max + | Min + | First + | Last + | Max + | Min + | Median + | Mode + | Product + | Quantile + | Sum + | RowNumber + | Rank + | DenseRank + | PercentRank + | CumeDist + | NTile + | Rank + | Lag + | Lead + | FirstValue + | LastValue + | NthValue; diff --git a/packages/spec/src/spec/VConcat.ts b/packages/spec/src/spec/VConcat.ts new file mode 100644 index 00000000..ccd2af70 --- /dev/null +++ b/packages/spec/src/spec/VConcat.ts @@ -0,0 +1,8 @@ +import { Component } from './Spec.js'; + +export interface VConcat { + /** + * Vertically concatenate components in a column layout. + */ + vconcat: Component[]; +} diff --git a/packages/spec/src/ast/VSpaceNode.d.ts b/packages/spec/src/spec/VSpace.ts similarity index 86% rename from packages/spec/src/ast/VSpaceNode.d.ts rename to packages/spec/src/spec/VSpace.ts index 8a6c53bf..2fa0b4e3 100644 --- a/packages/spec/src/ast/VSpaceNode.d.ts +++ b/packages/spec/src/spec/VSpace.ts @@ -1,4 +1,4 @@ -export interface SpecVSpace { +export interface VSpace { /** * Vertical space to place between components. * Number values indicate screen pixels. diff --git a/packages/spec/src/util.js b/packages/spec/src/util.js index 844af67e..f45b5089 100644 --- a/packages/spec/src/util.js +++ b/packages/spec/src/util.js @@ -45,3 +45,11 @@ export function isFunction(value) { export function error(message, data) { throw Object.assign(Error(message), { data }); } + +const re = /^(?:[-+]\d{2})?\d{4}(?:-\d{2}(?:-\d{2})?)?(?:T\d{2}:\d{2}(?::\d{2}(?:\.\d{3})?)?(?:Z|[-+]\d{2}:?\d{2})?)?$/; + +// adapted from https://github.com/mbostock/isoformat/ +export function isoparse(string, fallback) { + if (!re.test(string += '')) return fallback; + return new Date(string); +} diff --git a/packages/spec/tsconfig.json b/packages/spec/tsconfig.json new file mode 100644 index 00000000..d6eaae60 --- /dev/null +++ b/packages/spec/tsconfig.json @@ -0,0 +1,11 @@ +{ + "include": ["src/**/*.js", "src/**/*.ts"], + "compilerOptions": { + "allowJs": true, + "declaration": true, + "emitDeclarationOnly": true, + "outDir": "dist/types", + "module": "node16", + "skipLibCheck": true + } +} \ No newline at end of file diff --git a/packages/widget/jsconfig.json b/packages/widget/jsconfig.json index 377c9f13..a2e4f75e 100644 --- a/packages/widget/jsconfig.json +++ b/packages/widget/jsconfig.json @@ -1,14 +1,10 @@ { + "files": ["src/index.js"], "compilerOptions": { "noEmit": true, "checkJs": true, "noImplicitAny": false, - "module": "Node16", - "baseUrl": "./src", - "skipLibCheck": true, - "paths": { - "@uwdata/mosaic-widget": ["index"] - } - }, - "files": ["src/index.js"] + "module": "node16", + "skipLibCheck": true + } } diff --git a/packages/widget/package.json b/packages/widget/package.json index c519c7a5..cb4d796d 100644 --- a/packages/widget/package.json +++ b/packages/widget/package.json @@ -18,6 +18,7 @@ "publish": "hatch publish" }, "dependencies": { + "@uwdata/mosaic-spec": "0.7.1", "@uwdata/vgplot": "^0.7.1", "apache-arrow": "^15.0.0", "uuid": "^9.0.1" diff --git a/packages/widget/src/index.js b/packages/widget/src/index.js index a765e715..ad8e440b 100644 --- a/packages/widget/src/index.js +++ b/packages/widget/src/index.js @@ -9,7 +9,7 @@ import { v4 as uuidv4 } from 'uuid'; * @typedef {Record} Params * * @typedef Model - * @prop {Record} spec the current specification + * @prop {import('@uwdata/mosaic-spec').Spec} spec the current specification * @prop {boolean} temp_indexes whether data cube indexes should be created as temp tables * @prop {Params} params the current params */ From bbd54f8fc3b7a8db7f0666e76ae18458c432105d Mon Sep 17 00:00:00 2001 From: jheer Date: Wed, 3 Apr 2024 09:38:33 -0700 Subject: [PATCH 12/25] feat: Add spec interactor typings. --- packages/spec/src/spec/PlotInteractor.ts | 30 +++++++-- .../spec/src/spec/interactors/Highlight.ts | 37 +++++++++++ .../spec/src/spec/interactors/Interval1D.ts | 63 +++++++++++++++++++ .../spec/src/spec/interactors/Interval2D.ts | 46 ++++++++++++++ packages/spec/src/spec/interactors/Nearest.ts | 25 ++++++++ packages/spec/src/spec/interactors/PanZoom.ts | 58 +++++++++++++++++ packages/spec/src/spec/interactors/Toggle.ts | 51 +++++++++++++++ 7 files changed, 304 insertions(+), 6 deletions(-) create mode 100644 packages/spec/src/spec/interactors/Highlight.ts create mode 100644 packages/spec/src/spec/interactors/Interval1D.ts create mode 100644 packages/spec/src/spec/interactors/Interval2D.ts create mode 100644 packages/spec/src/spec/interactors/Nearest.ts create mode 100644 packages/spec/src/spec/interactors/PanZoom.ts create mode 100644 packages/spec/src/spec/interactors/Toggle.ts diff --git a/packages/spec/src/spec/PlotInteractor.ts b/packages/spec/src/spec/PlotInteractor.ts index 425e1fc4..5292263c 100644 --- a/packages/spec/src/spec/PlotInteractor.ts +++ b/packages/spec/src/spec/PlotInteractor.ts @@ -1,6 +1,24 @@ -export type PlotInteractor = { - select: string; -} & { - // todo: specific interactors - [key: string]: any; -}; +import { Highlight } from './interactors/Highlight.js'; +import { IntervalX, IntervalY } from './interactors/Interval1D.js'; +import { IntervalXY } from './interactors/Interval2D.js'; +import { NearestX, NearestY } from './interactors/Nearest.js'; +import { Pan, PanX, PanY, PanZoom, PanZoomX, PanZoomY } from './interactors/PanZoom.js'; +import { Toggle, ToggleColor, ToggleX, ToggleY } from './interactors/Toggle.js'; + +export type PlotInteractor = + | Highlight + | IntervalX + | IntervalY + | IntervalXY + | NearestX + | NearestY + | Toggle + | ToggleX + | ToggleY + | ToggleColor + | Pan + | PanX + | PanY + | PanZoom + | PanZoomX + | PanZoomY; diff --git a/packages/spec/src/spec/interactors/Highlight.ts b/packages/spec/src/spec/interactors/Highlight.ts new file mode 100644 index 00000000..08e325bd --- /dev/null +++ b/packages/spec/src/spec/interactors/Highlight.ts @@ -0,0 +1,37 @@ +import { ParamRef } from '../Param.js'; + +export interface Highlight { + /** + * Highlight selected marks by deemphasizing the others. + */ + select: 'highlight'; + /** + * The input selection. Unselected marks are deemphasized. + */ + by: ParamRef; + /** + * The overall opacity of deemphasized marks. + * By default the opacity is set to 0.2. + */ + opacity?: number; + /** + * The fill opacity of deemphasized marks. + * By default the fill opacity is unchanged. + */ + fillOpacity?: number; + /** + * The stroke opacity of deemphasized marks. + * By default the stroke opacity is unchanged. + */ + strokeOpacity?: number; + /** + * The fill color of deemphasized marks. + * By default the fill is unchanged. + */ + fill?: string; + /** + * The stroke color of deemphasized marks. + * By default the stroke is unchanged. + */ + stroke?: string; +} diff --git a/packages/spec/src/spec/interactors/Interval1D.ts b/packages/spec/src/spec/interactors/Interval1D.ts new file mode 100644 index 00000000..0c9c41ae --- /dev/null +++ b/packages/spec/src/spec/interactors/Interval1D.ts @@ -0,0 +1,63 @@ +import { ParamRef } from '../Param.js'; + +export interface BrushStyles { + /** + * The overall opacity of the brush rectangle. + */ + opacity?: number; + /** + * The fill opacity of the brush rectangle. + */ + fillOpacity?: number; + /** + * The stroke opacity of the brush rectangle. + */ + strokeOpacity?: number; + /** + * The fill color of the brush rectangle. + */ + fill?: string; + /** + * The stroke color of the brush rectangle. + */ + stroke?: string; +} + +export interface Interval1DOptions { + /** + * The output selection. A clause of the form `field BETWEEN lo AND hi` + * is added for the currently selected interval [lo, hi]. + */ + as: ParamRef; + /** + * The name of the field (database column) over which the interval + * selection should be defined. If unspecified, the channel field of the + * first valid prior mark definition is used. + */ + field?: string; + /** + * The size of an interative pixel (default `1`). Larger pixel sizes reduce + * the brush resolution, which can reduce the size of data cube indexes. + */ + pixelSize?: number; + /** + * A flag indicating if peer (sibling) marks are when cross-filtering + * (default `true`). If set, peer marks will not be filtered by this + * interactor's selection in cross-filtering setups. + */ + peers?: boolean; + /** + * CSS styles for the brush (SVG `rect`) element. + */ + brush?: BrushStyles; +} + +export interface IntervalX extends Interval1DOptions { + /** Select a continuous 1D interval selection over the `x` scale domain. */ + select: 'intervalX'; +} + +export interface IntervalY extends Interval1DOptions { + /** Select a continuous 1D interval selection over the `y` scale domain. */ + select: 'intervalY'; +} diff --git a/packages/spec/src/spec/interactors/Interval2D.ts b/packages/spec/src/spec/interactors/Interval2D.ts new file mode 100644 index 00000000..f8aa5a1a --- /dev/null +++ b/packages/spec/src/spec/interactors/Interval2D.ts @@ -0,0 +1,46 @@ +import { ParamRef } from '../Param.js'; +import { BrushStyles } from './Interval1D.js'; + +export interface Interval2DOptions { + /** + * The output selection. A clause of the form + * `(xfield BETWEEN x1 AND x2) AND (yfield BETWEEN y1 AND y2)` + * is added for the currently selected intervals. + */ + as: ParamRef; + /** + * The name of the field (database column) over which the `x`-component + * of the interval selection should be defined. If unspecified, the `x` + * channel field of the first valid prior mark definition is used. + */ + xfield?: string; + /** + * The name of the field (database column) over which the `y`-component + * of the interval selection should be defined. If unspecified, the `y` + * channel field of the first valid prior mark definition is used. + */ + yfield?: string; + /** + * The size of an interative pixel (default `1`). Larger pixel sizes reduce + * the brush resolution, which can reduce the size of data cube indexes. + */ + pixelSize?: number; + /** + * A flag indicating if peer (sibling) marks are when cross-filtering + * (default `true`). If set, peer marks will not be filtered by this + * interactor's selection in cross-filtering setups. + */ + peers?: boolean; + /** + * CSS styles for the brush (SVG `rect`) element. + */ + brush?: BrushStyles; +} + +export interface IntervalXY extends Interval2DOptions { + /** + * Select a continuous 2D interval selection + * over the `x` and `y` scale domains. + */ + select: 'intervalXY'; +} diff --git a/packages/spec/src/spec/interactors/Nearest.ts b/packages/spec/src/spec/interactors/Nearest.ts new file mode 100644 index 00000000..e6c00961 --- /dev/null +++ b/packages/spec/src/spec/interactors/Nearest.ts @@ -0,0 +1,25 @@ +import { ParamRef } from '../Param.js'; + +export interface NearestOptions { + /** + * The output selection. A clause of the form `field = value` + * is added for the currently nearest value. + */ + as: ParamRef; + /** + * The name of the field (database column) over which the nearest + * selection should be defined. If unspecified, the channel field of the + * first valid prior mark definition is used. + */ + field?: string; +} + +export interface NearestX extends NearestOptions { + /** Select the `x` domain value of the mark closest to the pointer. */ + select: 'nearestX'; +} + +export interface NearestY extends NearestOptions { + /** Select the `y` domain value of the mark closest to the pointer. */ + select: 'nearestY'; +} diff --git a/packages/spec/src/spec/interactors/PanZoom.ts b/packages/spec/src/spec/interactors/PanZoom.ts new file mode 100644 index 00000000..61b249ea --- /dev/null +++ b/packages/spec/src/spec/interactors/PanZoom.ts @@ -0,0 +1,58 @@ +import { ParamRef } from '../Param.js'; + +export interface PanZoomOptions { + /** + * The output selection for the `x` domain. + * A clause of the form `field BETWEEN x1 AND x2` is added for the + * current pan/zom interval [x1, x2]. + */ + x?: ParamRef; + /** + * The output selection for the `y` domain. + * A clause of the form `field BETWEEN y1 AND y2` is added for the + * current pan/zom interval [y1, y2]. + */ + y?: ParamRef; + /** + * The name of the field (database column) over which the `x`-component + * of the pan/zoom interval should be defined. If unspecified, the `x` + * channel field of the first valid prior mark definition is used. + */ + xfield?: string; + /** + * The name of the field (database column) over which the `y`-component + * of the pan/zoom interval should be defined. If unspecified, the `y` + * channel field of the first valid prior mark definition is used. + */ + yfield?: string; +} + +export interface Pan extends PanZoomOptions { + /** Pan a plot along both the `x` and `y` scales. */ + select: 'pan'; +} + +export interface PanX extends PanZoomOptions { + /** Pan a plot along the `x` scale only. */ + select: 'panX'; +} + +export interface PanY extends PanZoomOptions { + /** Pan a plot along the `y` scale only. */ + select: 'panY'; +} + +export interface PanZoom extends PanZoomOptions { + /** Pan and zoom a plot along both the `x` and `y` scales. */ + select: 'panZoom'; +} + +export interface PanZoomX extends PanZoomOptions { + /** Pan and zoom a plot along the `x` scale only. */ + select: 'panZoomX'; +} + +export interface PanZoomY extends PanZoomOptions { + /** Pan and zoom a plot along the `y` scale only. */ + select: 'panZoomY'; +} diff --git a/packages/spec/src/spec/interactors/Toggle.ts b/packages/spec/src/spec/interactors/Toggle.ts new file mode 100644 index 00000000..a3d3cddd --- /dev/null +++ b/packages/spec/src/spec/interactors/Toggle.ts @@ -0,0 +1,51 @@ +import { ParamRef } from '../Param.js'; + +export interface ToggleOptions { + /** + * The output selection. A clause of the form + * `(field = value1) OR (field = value2) ...` + * is added for the currently selected values. + */ + as: ParamRef; + /** + * A flag indicating if peer (sibling) marks are when cross-filtering + * (default `true`). If set, peer marks will not be filtered by this + * interactor's selection in cross-filtering setups. + */ + peers?: boolean; +} + +export interface Toggle extends ToggleOptions { + /** Select individal values. */ + select: 'toggle'; + /** + * The encoding channels over which to select values. + * For a selected mark, selection clauses will cover + * the backing data fields for each channel. + */ + channels: string[]; +} + +export interface ToggleX extends ToggleOptions { + /** + * Select individal values in the `x` scale domain. + * Clicking or touching a mark toggles its selection status. + */ + select: 'toggleX'; +} + +export interface ToggleY extends ToggleOptions { + /** + * Select individal values in the `y` scale domain. + * Clicking or touching a mark toggles its selection status. + */ + select: 'toggleY'; +} + +export interface ToggleColor extends ToggleOptions { + /** + * Select individal values in the `color` scale domain. + * Clicking or touching a mark toggles its selection status. + */ + select: 'toggleColor'; +} From 93ed3c64a67e02e399865f5a8999b6d04da3459c Mon Sep 17 00:00:00 2001 From: jheer Date: Wed, 3 Apr 2024 13:00:34 -0700 Subject: [PATCH 13/25] feat: Document spec transform types. --- packages/spec/src/spec/Transform.ts | 153 ++++++++++++++++++++++++++-- 1 file changed, 147 insertions(+), 6 deletions(-) diff --git a/packages/spec/src/spec/Transform.ts b/packages/spec/src/spec/Transform.ts index 0dcd6829..8c02391f 100644 --- a/packages/spec/src/spec/Transform.ts +++ b/packages/spec/src/spec/Transform.ts @@ -15,137 +15,278 @@ export interface AggregateOptions { distinct?: boolean; } -type TransformArgument = string | number | boolean; -type Arg1 = TransformArgument | [TransformArgument]; -type Arg2 = [TransformArgument, TransformArgument]; -type Arg2Opt = TransformArgument | [TransformArgument, TransformArgument?]; -type Arg3Opt = TransformArgument | [TransformArgument, TransformArgument?, TransformArgument?]; +type Arg = string | number | boolean; +type Arg1 = Arg | [Arg]; +type Arg2 = [Arg, Arg]; +type Arg2Opt = Arg | [Arg, Arg?]; +type Arg3Opt = Arg | [Arg, Arg?, Arg?]; + +export interface BinOptions { + /** + * The target number of binning steps to use. To accommodate human-friendly + * bin boundaries, the actual number of bins may diverge from this exact number. + */ + steps?: number; + /** + * The minimum allowed bin step size (default `0`). + * For example, a setting of `1` will prevent step sizes less than 1. + */ + minstep?: number; + /** + * A flag requesting "nice" human-friendly step sizes (default `true`). + */ + nice?: true; + /** + * Offset for computed bins (default `0`). For example, a value of `1` will + * result in using the next consecutive bin boundary. + */ + offset?: number; +} export interface Bin { - bin: Arg1; + /** + * Bin a continuous variable into discrete intervals. This transform accepts + * a data column to bin over as well as an optional bin options object. + */ + bin: Arg | [Arg] | [Arg, BinOptions]; } export interface DateMonth { + /** + * Transform a Date value to a month boundary for cyclic comparison. + * Year values are collapsed to enable comparison over months only. + */ dateMonth: Arg1; } export interface DateMonthDay { + /** + * Transform a Date value to a month and day boundary for cyclic comparison. + * Year values are collapsed to enable comparison over months and days only. + */ dateMonthDay: Arg1; } export interface DateDay { + /** + * Transform a Date value to a day of the month for cyclic comparison. + * Year and month values are collapsed to enable comparison over days only. + */ dateDay: Arg1; } export interface Centroid { + /** + * Compute the 2D centroid of geometry-typed data. + * This transform requires the DuckDB `spatial` extension. + */ centroid: Arg1; } export interface CentroidX { + /** + * Compute the centroid x-coordinate of geometry-typed data. + * This transform requires the DuckDB `spatial` extension. + */ centroidX: Arg1; } export interface CentroidY { + /** + * Compute the centroid y-coordinate of geometry-typed data. + * This transform requires the DuckDB `spatial` extension. + */ centroidY: Arg1; } export interface GeoJSON { + /** + * Compute a GeoJSON-formatted string from geometry-typed data. + * This transform requires the DuckDB `spatial` extension. + */ geojson: Arg1; } export interface Argmax extends AggregateOptions, WindowOptions { + /** + * Find a value of the first column that maximizes the second column. + */ argmax: Arg2; } export interface Argmin extends AggregateOptions, WindowOptions { + /** + * Find a value of the first column that minimizes the second column. + */ argmin: Arg2; } export interface Avg extends AggregateOptions, WindowOptions { + /** + * Compute the average (mean) value of the given column. + */ avg: Arg1; } export interface Count extends AggregateOptions, WindowOptions { + /** + * Compute the count of records in an aggregation group. + */ count: [] | Arg1; } export interface First extends AggregateOptions, WindowOptions { + /** + * Return the first column value found in an aggregation group. + */ first: Arg1; } export interface Last extends AggregateOptions, WindowOptions { + /** + * Return the last column value found in an aggregation group. + */ last: Arg1; } export interface Max extends AggregateOptions, WindowOptions { + /** + * Compute the maximum value of the given column. + */ max: Arg1; } export interface Min extends AggregateOptions, WindowOptions { + /** + * Compute the minimum value of the given column. + */ min: Arg1; } export interface Median extends AggregateOptions, WindowOptions { + /** + * Compute the median value of the given column. + */ median: Arg1; } export interface Mode extends AggregateOptions, WindowOptions { + /** + * Compute the mode value of the given column. + */ mode: Arg1; } export interface Product extends AggregateOptions, WindowOptions { + /** + * Compute the product of the given column. + */ product: Arg1; } export interface Quantile extends AggregateOptions, WindowOptions { + /** + * Compute the quantile value of the given column at the provided + * probability threshold. For example, 0.5 is the median. + */ quantile: Arg2; } export interface Sum extends AggregateOptions, WindowOptions { + /** + * Compute the sum of the given column. + */ sum: Arg1; } export interface RowNumber extends WindowOptions { + /** + * Compute the 1-based row number over an ordered window partition. + */ row_number: []; } export interface Rank extends WindowOptions { + /** + * Compute the row rank over an ordered window partition. + * Sorting ties result in gaps in the rank numbers ([1, 1, 3, ...]). + */ rank: []; } export interface DenseRank extends WindowOptions { + /** + * Compute the dense row rank (no gaps) over an ordered window partition. + * Sorting ties do not result in gaps in the rank numbers ( [1, 1, 2, ...]). + */ dense_rank: []; } export interface PercentRank extends WindowOptions { + /** + * Compute the percetange rank over an ordered window partition. + */ percent_rank: []; } export interface CumeDist extends WindowOptions { + /** + * Compute the cumulative distribution value over an ordered window + * partition. Equals the number of partition rows preceding or peer with + * the current row, divided by the total number of partition rows. + */ cume_dist: []; } export interface NTile extends WindowOptions { + /** + * Compute an n-tile integer ranging from 1 to the provided argument + * (num_buckets), dividing the partition as equally as possible. + */ ntile: Arg1; } export interface Lag extends WindowOptions { + /** + * Compute lagging values in a column. Returns the value at the row that is + * `offset` (second argument, default `1`) rows before the current row within + * the window frame. If there is no such row, instead return `default` (third + * argument, default `null`). Both offset and default are evaluated with + * respect to the current row. + */ lag: Arg3Opt; } export interface Lead extends WindowOptions { + /** + * Compute leading values in a column. Returns the value at the row that is + * `offset` (second argument, default `1`) rows after the current row within + * the window frame. If there is no such row, instead return `default` (third + * argument, default `null`). Both offset and default are evaluated with + * respect to the current row. + */ lag: Arg3Opt; } export interface FirstValue extends WindowOptions { + /** + * Get the first value of the given column in the current window frame. + */ first_value: Arg1; } export interface LastValue extends WindowOptions { + /** + * Get the last value of the given column in the current window frame. + */ last_value: Arg1; } export interface NthValue extends WindowOptions { + /** + * Get the nth value of the given column in the current window frame, + * counting from one. The second argument is the offset for the nth row. + */ nth_value: Arg2Opt; } From f94eb5fe261ddc44c483d41fab29560cd9ea3d3f Mon Sep 17 00:00:00 2001 From: jheer Date: Wed, 3 Apr 2024 13:37:45 -0700 Subject: [PATCH 14/25] feat: Document more spec typings. --- packages/spec/src/spec/Data.ts | 97 ++++++++++++++++++++++++++++ packages/spec/src/spec/Expression.ts | 16 +++++ packages/spec/src/spec/Param.ts | 27 ++++++++ packages/spec/src/spec/PlotLegend.ts | 53 ++++++++++++++- packages/spec/src/spec/Transform.ts | 13 ++-- 5 files changed, 199 insertions(+), 7 deletions(-) diff --git a/packages/spec/src/spec/Data.ts b/packages/spec/src/spec/Data.ts index d83d5951..4724f58c 100644 --- a/packages/spec/src/spec/Data.ts +++ b/packages/spec/src/spec/Data.ts @@ -1,8 +1,26 @@ export interface DataBaseOptions { + /** + * A list of column names to extract upon load. + * Any other columns are omitted. + */ select?: string[]; + /** + * A filter (WHERE clause) to apply upon load. + * Only rows that pass the filted are included. + */ where?: string | string[]; + /** + * Flag (default `false`) to generate a view instead of a table. + */ view?: boolean; + /** + * Flag (default `true`) to generate a temporary view or table. + */ temp?: boolean; + /** + * Flag (default `true`) to replace an existing table of the same name. + * If `false`, creating a new table with an existing name raises an error. + */ replace?: boolean; } @@ -25,35 +43,114 @@ export interface DataFile { } export interface DataTable extends DataBaseOptions { + /** + * The data source type. One of: + * - `"table"`: Define a new table based on a SQL query. + * - `"csv"`: Load a comma-separated values (CSV) file. + * - `"json"`: Load JavaScript Object Notation (json) data. + * - `"parquet"`: Load a Parquet file. + * - `"spatial"`: Load a spatial data file format via `ST_Read`. + */ type: 'table'; + /** + * A SQL query string for the desired table data. + */ query: string; } export interface DataParquet extends DataBaseOptions { + /** + * The data source type. One of: + * - `"table"`: Define a new table based on a SQL query. + * - `"csv"`: Load a comma-separated values (CSV) file. + * - `"json"`: Load JavaScript Object Notation (json) data. + * - `"parquet"`: Load a Parquet file. + * - `"spatial"`: Load a spatial data file format via `ST_Read`. + */ type: 'parquet'; + /** + * The file path for the dataset to load. + */ file: string; } export interface DataCSV extends DataBaseOptions { + /** + * The data source type. One of: + * - `"table"`: Define a new table based on a SQL query. + * - `"csv"`: Load a comma-separated values (CSV) file. + * - `"json"`: Load JavaScript Object Notation (json) data. + * - `"parquet"`: Load a Parquet file. + * - `"spatial"`: Load a spatial data file format via `ST_Read`. + */ type: 'csv'; + /** + * The file path for the dataset to load. + */ file: string; + /** + * The column delimiter string. If not specified, DuckDB will try to infer + * the delimiter automatically. + */ delimiter?: string, + /** + * The sample size, in table rows, to consult for type inference. + * Set to `-1` to process all rows in the dataset. + */ sample_size?: number; } export interface DataSpatial extends DataBaseOptions { + /** + * The data source type. One of: + * - `"table"`: Define a new table based on a SQL query. + * - `"csv"`: Load a comma-separated values (CSV) file. + * - `"json"`: Load JavaScript Object Notation (json) data. + * - `"parquet"`: Load a Parquet file. + * - `"spatial"`: Load a spatial data file format via `ST_Read`. + */ type: 'spatial'; + /** + * The file path for the dataset to load. + */ file: string; + /** + * The named layer to load from the file. For example, in a TopoJSON file + * the layer is the named object to extract. For Excel spreadsheet files, + * the layer is the name of the worksheet to extract. + */ layer?: string; } export interface DataJSON extends DataBaseOptions { + /** + * The data source type. One of: + * - `"table"`: Define a new table based on a SQL query. + * - `"csv"`: Load a comma-separated values (CSV) file. + * - `"json"`: Load JavaScript Object Notation (json) data. + * - `"parquet"`: Load a Parquet file. + * - `"spatial"`: Load a spatial data file format via `ST_Read`. + */ type: 'json'; + /** + * The file path for the dataset to load. + */ file: string; } export interface DataJSONObjects extends DataBaseOptions { + /** + * The data source type. One of: + * - `"table"`: Define a new table based on a SQL query. + * - `"csv"`: Load a comma-separated values (CSV) file. + * - `"json"`: Load JavaScript Object Notation (json) data. + * - `"parquet"`: Load a Parquet file. + * - `"spatial"`: Load a spatial data file format via `ST_Read`. + */ type?: 'json'; + /** + * An array of inline objects in JSON-style format. + */ data: object[]; } diff --git a/packages/spec/src/spec/Expression.ts b/packages/spec/src/spec/Expression.ts index 9e631aef..0c37e8a1 100644 --- a/packages/spec/src/spec/Expression.ts +++ b/packages/spec/src/spec/Expression.ts @@ -3,11 +3,27 @@ export type Expression = | AggregateExpression; export interface SQLExpression { + /** + * A SQL expression string to derive a new column value. + * Embedded Param refrences, such as `$param + 1`, are supported. + * For expressions with aggregate functions, use `agg` instead. + */ sql: string; + /** + * A label for this expression, for example to label a plot axis. + */ label?: string; } export interface AggregateExpression { + /** + * A SQL expression string to calculate an aggregate value. + * Embedded Param references, such as `SUM($param + 1)`, are supported. + * For expressions without aggregate functions, use `sql` instead. + */ agg: string; + /** + * A label for this expression, for example to label a plot axis. + */ label?: string; } diff --git a/packages/spec/src/spec/Param.ts b/packages/spec/src/spec/Param.ts index 30df5513..46e1ab18 100644 --- a/packages/spec/src/spec/Param.ts +++ b/packages/spec/src/spec/Param.ts @@ -1,14 +1,29 @@ export type ParamRef = `$${string}`; export interface ParamBase { + /** + * The type of reactive parameter. One of: + * - `"value"` (default) for a standard `Param` + * - `"intersect"` for a `Selection` that intersects clauses (logical "and") + * - `"union"` for a `Selection` that unions clauses (logical "or") + * - `"single"` for a `Selection` that retains a single clause only + * - `"crossfilter"` for a cross-filtered intersection `Selection` + */ select?: 'value'; } export interface Param extends ParamBase { + /** + * The initial parameter value. + */ value: ParamValue; } export interface ParamDate extends ParamBase { + /** + * The initial parameter value as an ISO date/time + * string to be parsed to a Date object. + */ date: string; } @@ -28,7 +43,19 @@ export type SelectionType = | 'union'; export interface Selection { + /** + * The type of reactive parameter. One of: + * - `"value"` (default) for a standard `Param` + * - `"intersect"` for a `Selection` that intersects clauses (logical "and") + * - `"union"` for a `Selection` that unions clauses (logical "or") + * - `"single"` for a `Selection` that retains a single clause only + * - `"crossfilter"` for a cross-filtered intersection `Selection` + */ select: SelectionType; + /** + * A flag for cross-filtering, where selections made in a plot filter others + * but not oneself (default `false`, except for `crossfilter` selections). + */ cross?: boolean; } diff --git a/packages/spec/src/spec/PlotLegend.ts b/packages/spec/src/spec/PlotLegend.ts index b41c76f9..e960ebea 100644 --- a/packages/spec/src/spec/PlotLegend.ts +++ b/packages/spec/src/spec/PlotLegend.ts @@ -1,19 +1,70 @@ import { ParamRef } from './Param.js'; +/** + * A legend defined as an entry within a plot. + */ export interface PlotLegend { + /** + * A legend of the given type. + * The valid types are `"color"`, `"opacity"`, and `"symbol"`. + */ legend: 'color' | 'opacity' | 'symbol'; + /** + * The output selection. If specified, the legend is interactive, using a + * `toggle` interaction for discrete legends or an `intervalX` interaction + * for continuous legends. + */ as?: ParamRef; + /** + * The data field over which to generate output selection clauses. If + * unspecified, a matching field is retrieved from existing plot marks. + */ field?: string; + /** + * The legend label. + */ + label?: string; + /** + * The size of legend ticks in a continuous legend, in pixels. + */ tickSize?: number; + /** + * The top margin of the legend component, in pixels. + */ marginTop?: number; + /** + * The right margin of the legend component, in pixels. + */ + marginRight?: number; + /** + * The bottom margin of the legend component, in pixels. + */ marginBottom?: number; + /** + * The left margin of the legend component, in pixels. + */ marginLeft?: number; - marginRight?: number; + /** + * The width of a continuous legend, in pixels. + */ width?: number; + /** + * The height of a continuous legend, in pixels. + */ height?: number; + /** + * The number of columns to use to layout a discrete legend. + */ columns?: number; } +/** + * A legend defined as a top-level spec component. + */ export interface Legend extends PlotLegend { + /** + * The name of the plot this legend applies to. + * A plot must include a `name` attribute to be referenced. + */ for: string; } diff --git a/packages/spec/src/spec/Transform.ts b/packages/spec/src/spec/Transform.ts index 8c02391f..64176044 100644 --- a/packages/spec/src/spec/Transform.ts +++ b/packages/spec/src/spec/Transform.ts @@ -16,6 +16,7 @@ export interface AggregateOptions { } type Arg = string | number | boolean; +type Arg0 = null | []; type Arg1 = Arg | [Arg]; type Arg2 = [Arg, Arg]; type Arg2Opt = Arg | [Arg, Arg?]; @@ -132,7 +133,7 @@ export interface Count extends AggregateOptions, WindowOptions { /** * Compute the count of records in an aggregation group. */ - count: [] | Arg1; + count: Arg0 | Arg1; } export interface First extends AggregateOptions, WindowOptions { @@ -203,7 +204,7 @@ export interface RowNumber extends WindowOptions { /** * Compute the 1-based row number over an ordered window partition. */ - row_number: []; + row_number: Arg0; } export interface Rank extends WindowOptions { @@ -211,7 +212,7 @@ export interface Rank extends WindowOptions { * Compute the row rank over an ordered window partition. * Sorting ties result in gaps in the rank numbers ([1, 1, 3, ...]). */ - rank: []; + rank: Arg0; } export interface DenseRank extends WindowOptions { @@ -219,14 +220,14 @@ export interface DenseRank extends WindowOptions { * Compute the dense row rank (no gaps) over an ordered window partition. * Sorting ties do not result in gaps in the rank numbers ( [1, 1, 2, ...]). */ - dense_rank: []; + dense_rank: Arg0; } export interface PercentRank extends WindowOptions { /** * Compute the percetange rank over an ordered window partition. */ - percent_rank: []; + percent_rank: Arg0; } export interface CumeDist extends WindowOptions { @@ -235,7 +236,7 @@ export interface CumeDist extends WindowOptions { * partition. Equals the number of partition rows preceding or peer with * the current row, divided by the total number of partition rows. */ - cume_dist: []; + cume_dist: Arg0; } export interface NTile extends WindowOptions { From 875bae2d214f3c967f8254b0b20b3090e9a119b5 Mon Sep 17 00:00:00 2001 From: jheer Date: Thu, 4 Apr 2024 08:41:21 -0700 Subject: [PATCH 15/25] feat: Types and documentation for plot attributes. --- packages/spec/src/spec/PlotAttribute.ts | 1717 ++++++++++++++++++++--- packages/spec/src/spec/PlotTypes.ts | 384 +++++ 2 files changed, 1911 insertions(+), 190 deletions(-) create mode 100644 packages/spec/src/spec/PlotTypes.ts diff --git a/packages/spec/src/spec/PlotAttribute.ts b/packages/spec/src/spec/PlotAttribute.ts index c74c22c8..a232cc0f 100644 --- a/packages/spec/src/spec/PlotAttribute.ts +++ b/packages/spec/src/spec/PlotAttribute.ts @@ -1,4 +1,8 @@ import { ParamRef } from './Param.js'; +import { + ColorScaleType, ColorScheme, ContinuousScaleType, DiscreteScaleType, + Fixed, Interpolate, Interval, PositionScaleType, ProjectionName +} from './PlotTypes.js'; export interface PlotAttributes { /** @@ -169,215 +173,1548 @@ export interface PlotAttributes { */ label?: string | null | ParamRef; - xyDomain?: any; // x scale attributes - xScale?: any; - xDomain?: any; - xRange?: any; - xNice?: any; - xInset?: any; - xInsetLeft?: any; - xInsetRight?: any; - xClamp?: any; - xRound?: any; - xAlign?: any; - xPadding?: any; - xPaddingInner?: any; - xPaddingOuter?: any; - xAxis?: any; - xTicks?: any; - xTickSize?: any; - xTickSpacing?: any; - xTickPadding?: any; - xTickFormat?: any; - xTickRotate?: any; - xGrid?: any; - xLine?: any; - xLabel?: any; - xLabelAnchor?: any; - xLabelOffset?: any; - xFontVariant?: any; - xAriaLabel?: any; - xAriaDescription?: any; - xReverse?: any; - xZero?: any; - xBase?: any; - xExponent?: any; - xConstant?: any; + + /** + * The *x* scale type, affecting how the scale encodes abstract data, say by + * applying a mathematical transformation. If null, the scale is disabled. + * + * For quantitative data (numbers), defaults to *linear*; for temporal data + * (dates), defaults to *utc*; for ordinal data (strings or booleans), + * defaults to *point* for position scales, *categorical* for color scales, + * and otherwise *ordinal*. However, the radius scale defaults to *sqrt*, and + * the length and opacity scales default to *linear*; these scales are + * intended for quantitative data. The plot’s marks may also impose a scale + * type; for example, the barY mark requires that *x* is a *band* scale. + */ + xScale?: PositionScaleType | null | ParamRef; + + /** + * The extent of the scale’s inputs (abstract values). By default inferred + * from channel values. For continuous data (numbers and dates), it is + * typically [*min*, *max*]; it can be [*max*, *min*] to reverse the scale. + * For ordinal data (strings or booleans), it is an array (or iterable) of + * values is the desired order, defaulting to natural ascending order. + * + * Linear scales have a default domain of [0, 1]. Log scales have a default + * domain of [1, 10] and cannot include zero. Radius scales have a default + * domain from 0 to the median first quartile of associated channels. Length + * have a default domain from 0 to the median median of associated channels. + * Opacity scales have a default domain from 0 to the maximum value of + * associated channels. + */ + xDomain?: any[] | Fixed | ParamRef; + + /** + * The extent of the scale’s outputs (visual values). By default inferred from + * the scale’s **type** and **domain**, and for position scales, the plot’s + * dimensions. For continuous data (numbers and dates), and for ordinal + * position scales (*point* and *band*), it is typically [*min*, *max*]; it + * can be [*max*, *min*] to reverse the scale. + */ + xRange?: any[] | Fixed | ParamRef; + + /** + * If true, or a tick count or interval, extend the domain to nice round + * values. Defaults to 1, 2 or 5 times a power of 10 for *linear* scales, and + * nice time intervals for *utc* and *time* scales. Pass an interval such as + * *minute*, *wednesday* or *month* to specify what constitutes a nice + * interval. + * + * For continuous scales only. + */ + xNice?: boolean | number | Interval | ParamRef; + + /** + * Shorthand to set the same default for all four insets: **insetTop**, + * **insetRight**, **insetBottom**, and **insetLeft**. All insets typically + * default to zero, though not always (say when using bin transform). A + * positive inset reduces effective area, while a negative inset increases it. + */ + xInset?: number | ParamRef; + + /** + * Insets the right edge by the specified number of pixels. A positive value + * insets towards the left edge (reducing effective area), while a negative + * value insets away from the left edge (increasing it). + */ + xInsetRight?: number | ParamRef; + + /** + * Insets the left edge by the specified number of pixels. A positive value + * insets towards the right edge (reducing effective area), while a negative + * value insets away from the right edge (increasing it). + */ + xInsetLeft?: number | ParamRef; + + /** + * If true, values below the domain minimum are treated as the domain minimum, + * and values above the domain maximum are treated as the domain maximum. + * + * Clamping is useful for focusing on a subset of the data while ensuring that + * extreme values remain visible, but use caution: clamped values may need an + * annotation to avoid misinterpretation. Clamping typically requires setting + * an explicit **domain** since if the domain is inferred, no values will be + * outside the domain. + * + * For continuous scales only. + */ + xClamp?: boolean | ParamRef; + + /** + * If true, round the output value to the nearest integer (pixel); useful for + * crisp edges when rendering. + * + * For position scales only. + */ + xRound?: boolean | ParamRef; + + /** + * How to distribute unused space in the **range** for *point* and *band* + * scales. A number in [0, 1], such as: + * + * - 0 - use the start of the range, putting unused space at the end + * - 0.5 (default) - use the middle, distributing unused space evenly + * - 1 use the end, putting unused space at the start + * + * For ordinal position scales only. + */ + xAlign?: number | ParamRef; + + /** + * For *band* scales, how much of the **range** to reserve to separate + * adjacent bands; defaults to 0.1 (10%). For *point* scales, the amount of + * inset for the first and last value as a proportion of the bandwidth; + * defaults to 0.5 (50%). + * + * For ordinal position scales only. + */ + xPadding?: number | ParamRef; + + /** + * For a *band* scale, how much of the range to reserve to separate + * adjacent bands. + */ + xPaddingInner?: number | ParamRef; + + /** + * For a *band* scale, how much of the range to reserve to inset first and + * last bands. + */ + xPaddingOuter?: number | ParamRef; + + /** + * The side of the frame on which to place the implicit axis: *top* or + * *bottom* for *x*. Defaults to *bottom* for an *x* scale. + * + * If *both*, an implicit axis will be rendered on both sides of the plot + * (*top* and *bottom* for *x*). If null, the implicit axis is suppressed. + */ + xAxis?: 'top' | 'bottom' | 'both' | boolean | null | ParamRef; + + /** + * The desired approximate number of axis ticks, or an explicit array of tick + * values, or an interval such as *day* or *month*. + */ + xTicks?: number | Interval | any[] | ParamRef; + + /** + * The length of axis tick marks in pixels; negative values extend in the + * opposite direction. Defaults to 6 for *x* and *y* axes and *color* and + * *opacity* *ramp* legends, and 0 for *fx* and *fy* axes. + */ + xTickSize?: number | ParamRef; + + /** + * The desired approximate spacing between adjacent axis ticks, affecting the + * default **ticks**; defaults to 80 pixels for *x* and *fx*, and 35 pixels + * for *y* and *fy*. + */ + xTickSpacing?: number | ParamRef; + + /** + * The distance between an axis tick mark and its associated text label (in + * pixels); often defaults to 3, but may be affected by **xTickSize** and + * **xTickRotate**. + */ + xTickPadding?: number | ParamRef; + + /** + * How to format inputs (abstract values) for axis tick labels; one of: + * + * - a [d3-format][1] string for numeric scales + * - a [d3-time-format][2] string for temporal scales + * + * [1]: https://d3js.org/d3-time + * [2]: https://d3js.org/d3-time-format + */ + xTickFormat?: string | null | ParamRef; + + /** + * The rotation angle of axis tick labels in degrees clocksize; defaults to 0. + */ + xTickRotate?: number | ParamRef; + + /** + * Whether to show a grid aligned with the scale’s ticks. If true, show a grid + * with the currentColor stroke; if a string, show a grid with the specified + * stroke color; if an approximate number of ticks, an interval, or an array + * of tick values, show corresponding grid lines. See also the grid mark. + * + * For axes only. + */ + xGrid?: boolean | string | Interval | any[] | ParamRef; + + /** + * If true, draw a line along the axis; if false (default), do not. + */ + xLine?: boolean | ParamRef; + + /** + * A textual label to show on the axis or legend; if null, show no label. By + * default the scale label is inferred from channel definitions, possibly with + * an arrow (↑, →, ↓, or ←) to indicate the direction of increasing value. + * + * For axes and legends only. + */ + xLabel?: string | null | ParamRef; + + /** + * Where to place the axis **label** relative to the plot’s frame. For + * vertical position scales (*y* and *fy*), may be *top*, *bottom*, or + * *center*; for horizontal position scales (*x* and *fx*), may be *left*, + * *right*, or *center*. Defaults to *center* for ordinal scales (including + * *fx* and *fy*), and otherwise *top* for *y*, and *right* for *x*. + */ + xLabelAnchor?: 'top' | 'right' | 'bottom' | 'left' | 'center' | ParamRef; + + /** + * The axis **label** position offset (in pixels); default depends on margins + * and orientation. + */ + xLabelOffset?: number | ParamRef; + + /** + * The font-variant attribute for axis ticks; defaults to *tabular-nums* for + * quantitative axes. + */ + xFontVariant?: string | ParamRef; + + /** + * A short label representing the axis in the accessibility tree. + */ + xAriaLabel?: string | ParamRef; + + /** + * A textual description for the axis in the accessibility tree. + */ + xAriaDescription?: string | ParamRef; + + /** + * Whether to reverse the scale’s encoding; equivalent to reversing either the + * **domain** or **range**. + */ + xReverse?: boolean | ParamRef; + + /** + * Whether the **domain** must include zero. If the domain minimum is + * positive, it will be set to zero; otherwise if the domain maximum is + * negative, it will be set to zero. + * + * For quantitative scales only. + */ + xZero?: boolean | ParamRef; + + /** + * A power scale’s exponent (*e.g.*, 0.5 for sqrt); defaults to 1 for a + * linear scale. For *pow* scales only. + */ + xExponent?: number | ParamRef; + + /** + * A log scale’s base; defaults to 10. Does not affect the scale’s encoding, + * but rather the default ticks. For *log* scales only. + */ + xBase?: number | ParamRef; + + /** + * A symlog scale’s constant, expressing the magnitude of the linear region + * around the origin; defaults to 1. For *symlog* scales only. + */ + xConstant?: number | ParamRef; + // y scale attributes - yScale?: any; - yDomain?: any; - yRange?: any; - yNice?: any; - yInset?: any; - yInsetTop?: any; - yInsetBottom?: any; - yClamp?: any; - yRound?: any; - yAlign?: any; - yPadding?: any; - yPaddingInner?: any; - yPaddingOuter?: any; - yAxis?: any; - yTicks?: any; - yTickSize?: any; - yTickSpacing?: any; - yTickPadding?: any; - yTickFormat?: any; - yTickRotate?: any; - yGrid?: any; - yLine?: any; - yLabel?: any; - yLabelAnchor?: any; - yLabelOffset?: any; - yFontVariant?: any; - yAriaLabel?: any; - yAriaDescription?: any; - yReverse?: any; - yZero?: any; - yBase?: any; - yExponent?: any; - yConstant?: any; - // facet attributes - facetMargin?: any; - facetMarginTop?: any; - facetMarginBottom?: any; - facetMarginLeft?: any; - facetMarginRight?: any; - facetGrid?: any; - facetLabel?: any; + /** + * The *y* scale type, affecting how the scale encodes abstract data, say by + * applying a mathematical transformation. If null, the scale is disabled. + * + * For quantitative data (numbers), defaults to *linear*; for temporal data + * (dates), defaults to *utc*; for ordinal data (strings or booleans), + * defaults to *point* for position scales, The plot’s marks may also impose + * a scale type; for example, the barY mark requires that *x* is a *band* + * scale. + */ + yScale?: PositionScaleType | null | ParamRef; + + /** + * The extent of the scale’s inputs (abstract values). By default inferred + * from channel values. For continuous data (numbers and dates), it is + * typically [*min*, *max*]; it can be [*max*, *min*] to reverse the scale. + * For ordinal data (strings or booleans), it is an array (or iterable) of + * values is the desired order, defaulting to natural ascending order. + * + * Linear scales have a default domain of [0, 1]. Log scales have a default + * domain of [1, 10] and cannot include zero. + */ + yDomain?: any[] | Fixed | ParamRef; + + /** + * The extent of the scale’s outputs (visual values). By default inferred + * from the scale’s **type** and **domain**, and for position scales, the + * plot’s dimensions. For continuous data (numbers and dates), and for + * ordinal position scales (*point* and *band*), it is typically [*min*, + * *max*]; it can be [*max*, *min*] to reverse the scale. + */ + yRange?: any[] | Fixed | ParamRef; + + /** + * If true, or a tick count or interval, extend the domain to nice round + * values. Defaults to 1, 2 or 5 times a power of 10 for *linear* scales, and + * nice time intervals for *utc* and *time* scales. Pass an interval such as + * *minute*, *wednesday* or *month* to specify what constitutes a nice + * interval. + * + * For continuous scales only. + */ + yNice?: boolean | number | Interval | ParamRef; + + /** + * Shorthand to set the same default for all four insets: **insetTop**, + * **insetRight**, **insetBottom**, and **insetLeft**. All insets typically + * default to zero, though not always (say when using bin transform). A + * positive inset reduces effective area, while a negative inset increases it. + */ + yInset?: number | ParamRef; + + /** + * Insets the top edge by the specified number of pixels. A positive value + * insets towards the bottom edge (reducing effective area), while a negative + * value insets away from the bottom edge (increasing it). + */ + yInsetTop?: number | ParamRef; + + /** + * Insets the bottom edge by the specified number of pixels. A positive value + * insets towards the top edge (reducing effective area), while a negative + * value insets away from the top edge (increasing it). + */ + yInsetBottom?: number | ParamRef; + + /** + * If true, values below the domain minimum are treated as the domain minimum, + * and values above the domain maximum are treated as the domain maximum. + * + * Clamping is useful for focusing on a subset of the data while ensuring that + * extreme values remain visible, but use caution: clamped values may need an + * annotation to avoid misinterpretation. Clamping typically requires setting + * an explicit **domain** since if the domain is inferred, no values will be + * outside the domain. + * + * For continuous scales only. + */ + yClamp?: boolean | ParamRef; + + /** + * If true, round the output value to the nearest integer (pixel); useful for + * crisp edges when rendering. + * + * For position scales only. + */ + yRound?: boolean | ParamRef; + + /** + * How to distribute unused space in the **range** for *point* and *band* + * scales. A number in [0, 1], such as: + * + * - 0 - use the start of the range, putting unused space at the end + * - 0.5 (default) - use the middle, distributing unused space evenly + * - 1 use the end, putting unused space at the start + * + * For ordinal position scales only. + */ + yAlign?: number | ParamRef; + + /** + * For *band* scales, how much of the **range** to reserve to separate + * adjacent bands; defaults to 0.1 (10%). For *point* scales, the amount of + * inset for the first and last value as a proportion of the bandwidth; + * defaults to 0.5 (50%). + * + * For ordinal position scales only. + */ + yPadding?: number | ParamRef; + + /** + * For a *band* scale, how much of the range to reserve to separate + * adjacent bands. + */ + yPaddingInner?: number | ParamRef; + + /** + * For a *band* scale, how much of the range to reserve to inset first and + * last bands. + */ + yPaddingOuter?: number | ParamRef; + + /** + * The side of the frame on which to place the implicit axis: *left* or + * *right* for *y*. Defaults to *left* for a *y* scale. + * + * If *both*, an implicit axis will be rendered on both sides of the plot + * (*left* and *right* for *y*). If null, the implicit axis is suppressed. + */ + yAxis?: 'left' | 'right' | 'both' | boolean | null | ParamRef; + + /** + * The desired approximate number of axis ticks, or an explicit array of tick + * values, or an interval such as *day* or *month*. + */ + yTicks?: number | Interval | any[] | ParamRef; + + /** + * The length of axis tick marks in pixels; negative values extend in the + * opposite direction. Defaults to 6 for *x* and *y* axes and *color* and + * *opacity* *ramp* legends, and 0 for *fx* and *fy* axes. + */ + yTickSize?: number | ParamRef; + + /** + * The desired approximate spacing between adjacent axis ticks, affecting the + * default **ticks**; defaults to 80 pixels for *x* and *fx*, and 35 pixels + * for *y* and *fy*. + */ + yTickSpacing?: number | ParamRef; + + /** + * The distance between an axis tick mark and its associated text label (in + * pixels); often defaults to 3, but may be affected by **yTickSize** and + * **yTickRotate**. + */ + yTickPadding?: number | ParamRef; + + /** + * How to format inputs (abstract values) for axis tick labels; one of: + * + * - a [d3-format][1] string for numeric scales + * - a [d3-time-format][2] string for temporal scales + * + * [1]: https://d3js.org/d3-time + * [2]: https://d3js.org/d3-time-format + */ + yTickFormat?: string | null | ParamRef; + + /** + * The rotation angle of axis tick labels in degrees clocksize; defaults to 0. + */ + yTickRotate?: number | ParamRef; + + /** + * Whether to show a grid aligned with the scale’s ticks. If true, show a grid + * with the currentColor stroke; if a string, show a grid with the specified + * stroke color; if an approximate number of ticks, an interval, or an array + * of tick values, show corresponding grid lines. See also the grid mark. + * + * For axes only. + */ + yGrid?: boolean | string | Interval | any[] | ParamRef; + + /** + * If true, draw a line along the axis; if false (default), do not. + */ + yLine?: boolean | ParamRef; + + /** + * A textual label to show on the axis or legend; if null, show no label. By + * default the scale label is inferred from channel definitions, possibly with + * an arrow (↑, →, ↓, or ←) to indicate the direction of increasing value. + * + * For axes and legends only. + */ + yLabel?: string | null | ParamRef; + + /** + * Where to place the axis **label** relative to the plot’s frame. For + * vertical position scales (*y* and *fy*), may be *top*, *bottom*, or + * *center*; for horizontal position scales (*x* and *fx*), may be *left*, + * *right*, or *center*. Defaults to *center* for ordinal scales (including + * *fx* and *fy*), and otherwise *top* for *y*, and *right* for *x*. + */ + yLabelAnchor?: 'top' | 'right' | 'bottom' | 'left' | 'center' | ParamRef; + + /** + * The axis **label** position offset (in pixels); default depends on margins + * and orientation. + */ + yLabelOffset?: number | ParamRef; + + /** + * The font-variant attribute for axis ticks; defaults to *tabular-nums* for + * quantitative axes. + */ + yFontVariant?: string | ParamRef; + + /** + * A short label representing the axis in the accessibility tree. + */ + yAriaLabel?: string | ParamRef; + + /** + * A textual description for the axis in the accessibility tree. + */ + yAriaDescription?: string | ParamRef; + + /** + * Whether to reverse the scale’s encoding; equivalent to reversing either the + * **domain** or **range**. Note that by default, when the *y* scale is + * continuous, the *max* value points to the top of the screen, whereas + * ordinal values are ranked from top to bottom. + */ + yReverse?: boolean | ParamRef; + + /** + * Whether the **domain** must include zero. If the domain minimum is + * positive, it will be set to zero; otherwise if the domain maximum is + * negative, it will be set to zero. + * + * For quantitative scales only. + */ + yZero?: boolean | ParamRef; + + /** + * A power scale’s exponent (*e.g.*, 0.5 for sqrt); defaults to 1 for a + * linear scale. For *pow* scales only. + */ + yExponent?: number | ParamRef; + + /** + * A log scale’s base; defaults to 10. Does not affect the scale’s encoding, + * but rather the default ticks. For *log* scales only. + */ + yBase?: number | ParamRef; + + /** + * A symlog scale’s constant, expressing the magnitude of the linear region + * around the origin; defaults to 1. For *symlog* scales only. + */ + yConstant?: number | ParamRef; + + + /** + * Set the *x* and *y* scale domains. + */ + xyDomain?: any[] | Fixed | ParamRef; + + + // facet attributes + + /** + * Shorthand to set the same default for all four facet margins: marginTop, + * marginRight, marginBottom, and marginLeft. + */ + facetMargin?: number | ParamRef; + + /** + * The top facet margin; the (minimum) distance in pixels between the top + * edges of the inner and outer plot area. + */ + facetMarginTop?: number | ParamRef; + + /** + * The right facet margin; the (minimum) distance in pixels between the right + * edges of the inner and outer plot area. + */ + facetMarginBottom?: number | ParamRef; + + /** + * The bottom facet margin; the (minimum) distance in pixels between the + * bottom edges of the inner and outer plot area. + */ + facetMarginLeft?: number | ParamRef; + + /** + * The left facet margin; the (minimum) distance in pixels between the left + * edges of the inner and outer plot area. + */ + facetMarginRight?: number | ParamRef; + + /** + * Default axis grid for fx and fy scales; typically set to true to enable. + */ + facetGrid?: boolean | string | Interval | any[] | ParamRef; + + /** + * Default axis label for fx and fy scales; typically set to null to disable. + */ + facetLabel?: string | null | ParamRef; + + + // fx scale attributes + + /** + * The extent of the scale’s inputs (abstract values). By default inferred + * from channel values. For ordinal data (strings or booleans), it is an + * array (or iterable) of values is the desired order, defaulting to natural + * ascending order. + */ + fxDomain?: any[] | Fixed | ParamRef; + + /** + * The extent of the scale’s outputs (visual values). By default inferred from + * the scale’s **type** and **domain**, and the plot’s dimensions. For ordinal + * position scales (*point* and *band*), it is typically [*min*, *max*]; it + * can be [*max*, *min*] to reverse the scale. + */ + fxRange?: any[] | Fixed | ParamRef; + + /** + * Shorthand to set the same default for all four insets: **insetTop**, + * **insetRight**, **insetBottom**, and **insetLeft**. All insets typically + * default to zero, though not always (say when using bin transform). A + * positive inset reduces effective area, while a negative inset increases it. + */ + fxInset?: number | ParamRef; + + /** + * Insets the right edge by the specified number of pixels. A positive value + * insets towards the left edge (reducing effective area), while a negative + * value insets away from the left edge (increasing it). + */ + fxInsetRight?: number | ParamRef; + + /** + * Insets the left edge by the specified number of pixels. A positive value + * insets towards the right edge (reducing effective area), while a negative + * value insets away from the right edge (increasing it). + */ + fxInsetLeft?: number | ParamRef; + + /** + * If true, round the output value to the nearest integer (pixel); useful for + * crisp edges when rendering. + * + * For position scales only. + */ + fxRound?: boolean | ParamRef; + + /** + * How to distribute unused space in the **range** for *point* and *band* + * scales. A number in [0, 1], such as: + * + * - 0 - use the start of the range, putting unused space at the end + * - 0.5 (default) - use the middle, distributing unused space evenly + * - 1 use the end, putting unused space at the start + * + * For ordinal position scales only. + */ + fxAlign?: number | ParamRef; + + /** + * For *band* scales, how much of the **range** to reserve to separate + * adjacent bands; defaults to 0.1 (10%). For *point* scales, the amount of + * inset for the first and last value as a proportion of the bandwidth; + * defaults to 0.5 (50%). + * + * For ordinal position scales only. + */ + fxPadding?: number | ParamRef; + + /** + * For a *band* scale, how much of the range to reserve to separate + * adjacent bands. + */ + fxPaddingInner?: number | ParamRef; + + /** + * For a *band* scale, how much of the range to reserve to inset first and + * last bands. + */ + fxPaddingOuter?: number | ParamRef; + + /** + * The side of the frame on which to place the implicit axis: *top* or + * *bottom* for *fx*. Defaults to *top* if there is a *bottom* *x* axis, + * and otherwise *bottom*. + * + * If *both*, an implicit axis will be rendered on both sides of the plot + * (*top* and *bottom* for *fx*). If null, the implicit axis is suppressed. + */ + fxAxis?: 'top' | 'bottom' | 'both' | boolean | null | ParamRef; + + /** + * The desired approximate number of axis ticks, or an explicit array of tick + * values, or an interval such as *day* or *month*. + */ + fxTicks?: number | Interval | any[] | ParamRef; + + /** + * The length of axis tick marks in pixels; negative values extend in the + * opposite direction. Defaults to 6 for *x* and *y* axes and *color* and + * *opacity* *ramp* legends, and 0 for *fx* and *fy* axes. + */ + fxTickSize?: number | ParamRef; + + /** + * The desired approximate spacing between adjacent axis ticks, affecting the + * default **ticks**; defaults to 80 pixels for *x* and *fx*, and 35 pixels + * for *y* and *fy*. + */ + fxTickSpacing?: number | ParamRef; + + /** + * The distance between an axis tick mark and its associated text label (in + * pixels); often defaults to 3, but may be affected by **fxTickSize** and + * **fxTickRotate**. + */ + fxTickPadding?: number | ParamRef; + + /** + * How to format inputs (abstract values) for axis tick labels; one of: + * + * - a [d3-format][1] string for numeric scales + * - a [d3-time-format][2] string for temporal scales + * + * [1]: https://d3js.org/d3-time + * [2]: https://d3js.org/d3-time-format + */ + fxTickFormat?: string | null | ParamRef; + + /** + * The rotation angle of axis tick labels in degrees clocksize; defaults to 0. + */ + fxTickRotate?: number | ParamRef; + + /** + * Whether to show a grid aligned with the scale’s ticks. If true, show a grid + * with the currentColor stroke; if a string, show a grid with the specified + * stroke color; if an approximate number of ticks, an interval, or an array + * of tick values, show corresponding grid lines. See also the grid mark. + * + * For axes only. + */ + fxGrid?: boolean | string | Interval | any[] | ParamRef; + + /** + * If true, draw a line along the axis; if false (default), do not. + */ + fxLine?: boolean | ParamRef; + + /** + * A textual label to show on the axis or legend; if null, show no label. By + * default the scale label is inferred from channel definitions, possibly with + * an arrow (↑, →, ↓, or ←) to indicate the direction of increasing value. + * + * For axes and legends only. + */ + fxLabel?: string | null | ParamRef; + + /** + * Where to place the axis **label** relative to the plot’s frame. For + * vertical position scales (*y* and *fy*), may be *top*, *bottom*, or + * *center*; for horizontal position scales (*x* and *fx*), may be *left*, + * *right*, or *center*. Defaults to *center* for ordinal scales (including + * *fx* and *fy*), and otherwise *top* for *y*, and *right* for *x*. + */ + fxLabelAnchor?: 'top' | 'right' | 'bottom' | 'left' | 'center' | ParamRef; + + /** + * The axis **label** position offset (in pixels); default depends on margins + * and orientation. + */ + fxLabelOffset?: number | ParamRef; + + /** + * The font-variant attribute for axis ticks; defaults to *tabular-nums* for + * quantitative axes. + */ + fxFontVariant?: string | ParamRef; + + /** + * A short label representing the axis in the accessibility tree. + */ + fxAriaLabel?: string | ParamRef; + + /** + * A textual description for the axis in the accessibility tree. + */ + fxAriaDescription?: string | ParamRef; + + /** + * Whether to reverse the scale’s encoding; equivalent to reversing either the + * **domain** or **range**. + */ + fxReverse?: boolean | ParamRef; + + + // fy scale attributes + + /** + * The extent of the scale’s inputs (abstract values). By default inferred + * from channel values. For ordinal data (strings or booleans), it is an + * array (or iterable) of values is the desired order, defaulting to natural + * ascending order. + */ + fyDomain?: any[] | Fixed | ParamRef; + + /** + * The extent of the scale’s outputs (visual values). By default inferred from + * the scale’s **type** and **domain**, and the plot’s dimensions. For ordinal + * position scales (*point* and *band*), it is typically [*min*, *max*]; it + * can be [*max*, *min*] to reverse the scale. + */ + fyRange?: any[] | Fixed | ParamRef; + + /** + * Shorthand to set the same default for all four insets: **insetTop**, + * **insetRight**, **insetBottom**, and **insetLeft**. All insets typically + * default to zero, though not always (say when using bin transform). A + * positive inset reduces effective area, while a negative inset increases it. + */ + fyInset?: number | ParamRef; + + /** + * Insets the top edge by the specified number of pixels. A positive value + * insets towards the bottom edge (reducing effective area), while a negative + * value insets away from the bottom edge (increasing it). + */ + fyInsetTop?: number | ParamRef; + + /** + * Insets the bottom edge by the specified number of pixels. A positive value + * insets towards the top edge (reducing effective area), while a negative + * value insets away from the top edge (increasing it). + */ + fyInsetBottom?: number | ParamRef; + + /** + * If true, round the output value to the nearest integer (pixel); useful for + * crisp edges when rendering. + * + * For position scales only. + */ + fyRound?: boolean | ParamRef; + + /** + * How to distribute unused space in the **range** for *point* and *band* + * scales. A number in [0, 1], such as: + * + * - 0 - use the start of the range, putting unused space at the end + * - 0.5 (default) - use the middle, distributing unused space evenly + * - 1 use the end, putting unused space at the start + * + * For ordinal position scales only. + */ + fyAlign?: number | ParamRef; + + /** + * For *band* scales, how much of the **range** to reserve to separate + * adjacent bands; defaults to 0.1 (10%). For *point* scales, the amount of + * inset for the first and last value as a proportion of the bandwidth; + * defaults to 0.5 (50%). + * + * For ordinal position scales only. + */ + fyPadding?: number | ParamRef; + + /** + * For a *band* scale, how much of the range to reserve to separate + * adjacent bands. + */ + fyPaddingInner?: number | ParamRef; + + /** + * For a *band* scale, how much of the range to reserve to inset first and + * last bands. + */ + fyPaddingOuter?: number | ParamRef; + + /** + * The side of the frame on which to place the implicit axis: *left* or + * *right* for *fy*. Defaults to *left* for an *fy* scale. + * + * If *both*, an implicit axis will be rendered on both sides of the plot + * (*left* and *right* for *fy*). If null, the implicit axis is suppressed. + */ + fyAxis?: 'left' | 'right' | 'both' | boolean | null | ParamRef; + + /** + * The desired approximate number of axis ticks, or an explicit array of tick + * values, or an interval such as *day* or *month*. + */ + fyTicks?: number | Interval | any[] | ParamRef; + + /** + * The length of axis tick marks in pixels; negative values extend in the + * opposite direction. Defaults to 6 for *x* and *y* axes and *color* and + * *opacity* *ramp* legends, and 0 for *fx* and *fy* axes. + */ + fyTickSize?: number | ParamRef; + + /** + * The desired approximate spacing between adjacent axis ticks, affecting the + * default **ticks**; defaults to 80 pixels for *x* and *fx*, and 35 pixels + * for *y* and *fy*. + */ + fyTickSpacing?: number | ParamRef; + + /** + * The distance between an axis tick mark and its associated text label (in + * pixels); often defaults to 3, but may be affected by **fyTickSize** and + * **fyTickRotate**. + */ + fyTickPadding?: number | ParamRef; + + /** + * How to format inputs (abstract values) for axis tick labels; one of: + * + * - a [d3-format][1] string for numeric scales + * - a [d3-time-format][2] string for temporal scales + * + * [1]: https://d3js.org/d3-time + * [2]: https://d3js.org/d3-time-format + */ + fyTickFormat?: string | null | ParamRef; + + /** + * The rotation angle of axis tick labels in degrees clocksize; defaults to 0. + */ + fyTickRotate?: number | ParamRef; + + /** + * Whether to show a grid aligned with the scale’s ticks. If true, show a grid + * with the currentColor stroke; if a string, show a grid with the specified + * stroke color; if an approximate number of ticks, an interval, or an array + * of tick values, show corresponding grid lines. See also the grid mark. + * + * For axes only. + */ + fyGrid?: boolean | string | Interval | any[] | ParamRef; + + /** + * If true, draw a line along the axis; if false (default), do not. + */ + fyLine?: boolean | ParamRef; + + /** + * A textual label to show on the axis or legend; if null, show no label. By + * default the scale label is inferred from channel definitions, possibly with + * an arrow (↑, →, ↓, or ←) to indicate the direction of increasing value. + * + * For axes and legends only. + */ + fyLabel?: string | null | ParamRef; + + /** + * Where to place the axis **label** relative to the plot’s frame. For + * vertical position scales (*y* and *fy*), may be *top*, *bottom*, or + * *center*; for horizontal position scales (*x* and *fx*), may be *left*, + * *right*, or *center*. Defaults to *center* for ordinal scales (including + * *fx* and *fy*), and otherwise *top* for *y*, and *right* for *x*. + */ + fyLabelAnchor?: 'top' | 'right' | 'bottom' | 'left' | 'center' | ParamRef; - // fx scale attributes - fxDomain?: any; - fxRange?: any; - fxNice?: any; - fxInset?: any; - fxInsetLeft?: any; - fxInsetRight?: any; - fxRound?: any; - fxAlign?: any; - fxPadding?: any; - fxPaddingInner?: any; - fxPaddingOuter?: any; - fxAxis?: any; - fxTicks?: any; - fxTickSize?: any; - fxTickSpacing?: any; - fxTickPadding?: any; - fxTickFormat?: any; - fxTickRotate?: any; - fxGrid?: any; - fxLine?: any; - fxLabel?: any; - fxLabelAnchor?: any; - fxLabelOffset?: any; - fxFontVariant?: any; - fxAriaLabel?: any; - fxAriaDescription?: any; - fxReverse?: any; + /** + * The axis **label** position offset (in pixels); default depends on margins + * and orientation. + */ + fyLabelOffset?: number | ParamRef; + + /** + * The font-variant attribute for axis ticks; defaults to *tabular-nums* for + * quantitative axes. + */ + fyFontVariant?: string | ParamRef; + + /** + * A short label representing the axis in the accessibility tree. + */ + fyAriaLabel?: string | ParamRef; + + /** + * A textual description for the axis in the accessibility tree. + */ + fyAriaDescription?: string | ParamRef; + + /** + * Whether to reverse the scale’s encoding; equivalent to reversing either the + * **domain** or **range**. + */ + fyReverse?: boolean | ParamRef; - // fy scale attributes - fyDomain?: any; - fyRange?: any; - fyNice?: any; - fyInset?: any; - fyInsetTop?: any; - fyInsetBottom?: any; - fyRound?: any; - fyAlign?: any; - fyPadding?: any; - fyPaddingInner?: any; - fyPaddingOuter?: any; - fyAxis?: any; - fyTicks?: any; - fyTickSize?: any; - fyTickSpacing?: any; - fyTickPadding?: any; - fyTickFormat?: any; - fyTickRotate?: any; - fyGrid?: any; - fyLine?: any; - fyLabel?: any; - fyLabelAnchor?: any; - fyLabelOffset?: any; - fyFontVariant?: any; - fyAriaLabel?: any; - fyAriaDescription?: any; - fyReverse?: any; // color scale attributes - colorScale?: any; - colorDomain?: any; - colorRange?: any; - colorClamp?: any; - colorN?: any; - colorNice?: any; - colorScheme?: any; - colorInterpolate?: any; - colorPivot?: any; - colorSymmetric?: any; - colorLabel?: any; - colorReverse?: any; - colorZero?: any; - colorTickFormat?: any; - colorBase?: any; - colorExponent?: any; - colorConstant?: any; + + /** + * The *color* scale type, affecting how the scale encodes abstract data, say + * by applying a mathematical transformation. If null, the scale is disabled. + * + * For quantitative data (numbers), defaults to *linear*; for temporal data + * (dates), defaults to *utc*; for ordinal data (strings or booleans), + * defaults to *point* for position scales, *categorical* for color scales, + * and otherwise *ordinal*. + */ + colorScale?: ColorScaleType | null | ParamRef; + + /** + * The extent of the scale’s inputs (abstract values). By default inferred + * from channel values. For continuous data (numbers and dates), it is + * typically [*min*, *max*]; it can be [*max*, *min*] to reverse the scale. + * For ordinal data (strings or booleans), it is an array (or iterable) of + * values is the desired order, defaulting to natural ascending order. + */ + colorDomain?: any[] | Fixed | ParamRef; + + /** + * The extent of the scale’s outputs (visual values). By default inferred from + * the scale’s **type** and **domain**. For other ordinal data, it is an array + * (or iterable) of output values in the same order as the **domain**. + */ + colorRange?: any[] | Fixed | ParamRef; + + /** + * If true, values below the domain minimum are treated as the domain minimum, + * and values above the domain maximum are treated as the domain maximum. + * + * Clamping is useful for focusing on a subset of the data while ensuring that + * extreme values remain visible, but use caution: clamped values may need an + * annotation to avoid misinterpretation. Clamping typically requires setting + * an explicit **domain** since if the domain is inferred, no values will be + * outside the domain. + * + * For continuous scales only. + */ + colorClamp?: boolean | ParamRef; + + /** + * For a *quantile* scale, the number of quantiles (creates *n* - 1 + * thresholds); for a *quantize* scale, the approximate number of thresholds; + * defaults to 5. + */ + colorN?: number | ParamRef; + + /** + * If true, or a tick count or interval, extend the domain to nice round + * values. Defaults to 1, 2 or 5 times a power of 10 for *linear* scales, and + * nice time intervals for *utc* and *time* scales. Pass an interval such as + * *minute*, *wednesday* or *month* to specify what constitutes a nice + * interval. + * + * For continuous scales only. + */ + colorNice?: boolean | number | Interval | ParamRef; + + /** + * If specified, shorthand for setting the **colorRange** + * or **colorInterpolate** option of a *color* scale. + */ + colorScheme?: ColorScheme | ParamRef; + + /** + * How to interpolate color range values. For quantitative scales only. + * This attribute can be used to specify a color space for interpolating + * colors specified in the **colorRange**. + */ + colorInterpolate?: Interpolate | ParamRef; + + /** + * For a diverging color scale, the input value (abstract value) that divides + * the domain into two parts; defaults to 0 for *diverging* scales, dividing + * the domain into negative and positive parts; defaults to 1 for + * *diverging-log* scales. By default, diverging scales are symmetric around + * the pivot; see the **symmetric** option. + */ + colorPivot?: any | ParamRef; + + /** + * For a diverging color scale, if true (the default), extend the domain to + * ensure that the lower part of the domain (below the **pivot**) is + * commensurate with the upper part of the domain (above the **pivot**). + * + * A symmetric diverging color scale may not use all of its output **range**; + * this reduces contrast but ensures that deviations both below and above the + * **pivot** are represented proportionally. Otherwise if false, the full + * output **range** will be used; this increases contrast but values on + * opposite sides of the **pivot** may not be meaningfully compared. + */ + colorSymmetric?: boolean | ParamRef; + + /** + * A textual label to show on the axis or legend; if null, show no label. By + * default the scale label is inferred from channel definitions, possibly with + * an arrow (↑, →, ↓, or ←) to indicate the direction of increasing value. + * + * For axes and legends only. + */ + colorLabel?: string | null | ParamRef; + + /** + * Whether to reverse the scale’s encoding; equivalent to reversing either the + * **domain** or **range**. + */ + colorReverse?: boolean | ParamRef; + + /** + * Whether the **domain** must include zero. If the domain minimum is + * positive, it will be set to zero; otherwise if the domain maximum is + * negative, it will be set to zero. + * + * For quantitative scales only. + */ + colorZero?: boolean | ParamRef; + + /** + * How to format inputs (abstract values) for axis tick labels; one of: + * + * - a [d3-format][1] string for numeric scales + * - a [d3-time-format][2] string for temporal scales + * + * [1]: https://d3js.org/d3-time + * [2]: https://d3js.org/d3-time-format + */ + colorTickFormat?: string | null | ParamRef; + + /** + * A power scale’s exponent (*e.g.*, 0.5 for sqrt); defaults to 1 for a + * linear scale. For *pow* and *diverging-pow* scales only. + */ + colorExponent?: number | ParamRef; + + /** + * A log scale’s base; defaults to 10. Does not affect the scale’s encoding, + * but rather the default ticks. For *log* and *diverging-log* scales only. + */ + colorBase?: number | ParamRef; + + /** + * A symlog scale’s constant, expressing the magnitude of the linear region + * around the origin; defaults to 1. For *symlog* and *diverging-symlog* + * scales only. + */ + colorConstant?: number | ParamRef; + // opacity scale attributes - opacityScale?: any; - opacityDomain?: any; - opacityRange?: any; - opacityClamp?: any; - opacityNice?: any; - opacityLabel?: any; - opacityReverse?: any; - opacityZero?: any; - opacityTickFormat?: any; - opacityBase?: any; - opacityExponent?: any; - opacityConstant?: any; + + /** + * The *opacity* scale type, affecting how the scale encodes abstract data, + * say by applying a mathematical transformation. If null, the scale is + * disabled. The opacity scale defaults to *linear*; this scales is intended + * for quantitative data. + */ + opacityScale?: ContinuousScaleType | null | ParamRef; + + /** + * The extent of the scale’s inputs (abstract values). By default inferred + * from channel values. For continuous data (numbers and dates), it is + * typically [*min*, *max*]; it can be [*max*, *min*] to reverse the scale. + * For ordinal data (strings or booleans), it is an array (or iterable) of + * values is the desired order, defaulting to natural ascending order. + * + * Opacity scales have a default domain from 0 to the maximum value of + * associated channels. + */ + opacityDomain?: any[] | Fixed | ParamRef; + + /** + * The extent of the scale’s outputs (visual values). + * + * Opacity scales have a default range of [0, 1]. + */ + opacityRange?: any[] | Fixed | ParamRef; + + /** + * If true, values below the domain minimum are treated as the domain minimum, + * and values above the domain maximum are treated as the domain maximum. + * + * Clamping is useful for focusing on a subset of the data while ensuring that + * extreme values remain visible, but use caution: clamped values may need an + * annotation to avoid misinterpretation. Clamping typically requires setting + * an explicit **domain** since if the domain is inferred, no values will be + * outside the domain. + * + * For continuous scales only. + */ + opacityClamp?: boolean | ParamRef; + + /** + * If true, or a tick count or interval, extend the domain to nice round + * values. Defaults to 1, 2 or 5 times a power of 10 for *linear* scales, and + * nice time intervals for *utc* and *time* scales. Pass an interval such as + * *minute*, *wednesday* or *month* to specify what constitutes a nice + * interval. + * + * For continuous scales only. + */ + opacityNice?: boolean | number| Interval | ParamRef; + + /** + * A textual label to show on the axis or legend; if null, show no label. By + * default the scale label is inferred from channel definitions, possibly with + * an arrow (↑, →, ↓, or ←) to indicate the direction of increasing value. + * + * For axes and legends only. + */ + opacityLabel?: string | null | ParamRef; + + /** + * Whether to reverse the scale’s encoding; equivalent to reversing either the + * **domain** or **range**. + */ + opacityReverse?: boolean | ParamRef; + + /** + * Whether the **domain** must include zero. If the domain minimum is + * positive, it will be set to zero; otherwise if the domain maximum is + * negative, it will be set to zero. + * + * For quantitative scales only. + */ + opacityZero?: boolean | ParamRef; + + /** + * How to format inputs (abstract values) for axis tick labels; one of: + * + * - a [d3-format][1] string for numeric scales + * - a [d3-time-format][2] string for temporal scales + * + * [1]: https://d3js.org/d3-time + * [2]: https://d3js.org/d3-time-format + */ + opacityTickFormat?: string | null | ParamRef; + + /** + * A power scale’s exponent (*e.g.*, 0.5 for sqrt); defaults to 1 for a + * linear scale. For *pow* scales only. + */ + opacityExponent?: number | ParamRef; + + /** + * A log scale’s base; defaults to 10. Does not affect the scale’s encoding, + * but rather the default ticks. For *log* scales only. + */ + opacityBase?: number | ParamRef; + + /** + * A symlog scale’s constant, expressing the magnitude of the linear region + * around the origin; defaults to 1. For *symlog* scales only. + */ + opacityConstant?: number | ParamRef; + // symbol scale attributes - symbolScale?: any; - symbolDomain?: any; - symbolRange?: any; + + /** + * The *symbol* scale type, affecting how the scale encodes abstract data, + * say by applying a mathematical transformation. If null, the scale is + * disabled. Defaults to an *ordinal* scale type. + */ + symbolScale?: DiscreteScaleType | null | ParamRef; + + /** + * The extent of the scale’s inputs (abstract values). By default inferred + * from channel values. As symbol scales are discrete, the domain is an array + * (or iterable) of values is the desired order, defaulting to natural + * ascending order. + */ + symbolDomain?: any[] | Fixed | ParamRef; + + /** + * The extent of the scale’s outputs (visual values). By default inferred from + * the scale’s **type** and **domain**, and for position scales, the plot’s + * dimensions. For continuous data (numbers and dates), and for ordinal + * position scales (*point* and *band*), it is typically [*min*, *max*]; it + * can be [*max*, *min*] to reverse the scale. For other ordinal data, such as + * for a *color* scale, it is an array (or iterable) of output values in the + * same order as the **domain**. + * + * Symbol scales have a default range of categorical symbols; the choice of + * symbols depends on whether the associated dot mark is filled or stroked. + */ + symbolRange?: any[] | Fixed | ParamRef; + // r scale attributes - rScale?: any; - rDomain?: any; - rRange?: any; + + /** + * The *r* (radius) scale type, affecting how the scale encodes abstract + * data, say by applying a mathematical transformation. If null, the scale + * is disabled. The radius scale defaults to *sqrt*; this scale is intended + * for quantitative data. + */ + rScale?: ContinuousScaleType | null | ParamRef; + + /** + * The extent of the scale’s inputs (abstract values). By default inferred + * from channel values. For continuous data (numbers and dates), it is + * typically [*min*, *max*]; it can be [*max*, *min*] to reverse the scale. + * For ordinal data (strings or booleans), it is an array (or iterable) of + * values is the desired order, defaulting to natural ascending order. + * + * Radius scales have a default domain from 0 to the median first quartile + * of associated channels. + */ + rDomain?: any[] | Fixed | ParamRef; + + /** + * The extent of the scale’s outputs (visual values). By default inferred from + * the scale’s **type** and **domain**, and for position scales, the plot’s + * dimensions. For continuous data (numbers and dates), and for ordinal + * position scales (*point* and *band*), it is typically [*min*, *max*]; it + * can be [*max*, *min*] to reverse the scale. For other ordinal data, such as + * for a *color* scale, it is an array (or iterable) of output values in the + * same order as the **domain**. + * + * Radius scales have a default range of [0, 3]. + */ + rRange?: any[] | Fixed | ParamRef; + + /** + * If true, values below the domain minimum are treated as the domain minimum, + * and values above the domain maximum are treated as the domain maximum. + * + * Clamping is useful for focusing on a subset of the data while ensuring that + * extreme values remain visible, but use caution: clamped values may need an + * annotation to avoid misinterpretation. Clamping typically requires setting + * an explicit **domain** since if the domain is inferred, no values will be + * outside the domain. + * + * For continuous scales only. + */ rClamp?: any; - rNice?: any; - rZero?: any; - rBase?: any; - rExponent?: any; - rConstant?: any; + + /** + * If true, or a tick count or interval, extend the domain to nice round + * values. Defaults to 1, 2 or 5 times a power of 10 for *linear* scales, and + * nice time intervals for *utc* and *time* scales. Pass an interval such as + * *minute*, *wednesday* or *month* to specify what constitutes a nice + * interval. + * + * For continuous scales only. + */ + rNice?: boolean | number| Interval | ParamRef; + + /** + * Whether the **domain** must include zero. If the domain minimum is + * positive, it will be set to zero; otherwise if the domain maximum is + * negative, it will be set to zero. + * + * For quantitative scales only. + */ + rZero?: boolean | ParamRef; + + /** + * A power scale’s exponent (*e.g.*, 0.5 for sqrt); defaults to 1 for a + * linear scale. For *pow* scales only. + */ + rExponent?: number | ParamRef; + + /** + * A log scale’s base; defaults to 10. Does not affect the scale’s encoding, + * but rather the default ticks. For *log* scales only. + */ + rBase?: number | ParamRef; + + /** + * A symlog scale’s constant, expressing the magnitude of the linear region + * around the origin; defaults to 1. For *symlog* scales only. + */ + rConstant?: number | ParamRef; + // length scale attributes - lengthScale?: any; - lengthDomain?: any; - lengthRange?: any; + + /** + * The *length* scale type, affecting how the scale encodes abstract data, + * say by applying a mathematical transformation. If null, the scale is + * disabled. The length scale defaults to *linear*, as this scale is intended + * for quantitative data. + */ + lengthScale?: ContinuousScaleType | null | ParamRef; + + /** + * The extent of the scale’s inputs (abstract values). By default inferred + * from channel values. For continuous data (numbers and dates), it is + * typically [*min*, *max*]; it can be [*max*, *min*] to reverse the scale. + * For ordinal data (strings or booleans), it is an array (or iterable) of + * values is the desired order, defaulting to natural ascending order. + * + * Linear scales have a default domain of [0, 1]. Log scales have a default + * domain of [1, 10] and cannot include zero. Radius scales have a default + * domain from 0 to the median first quartile of associated channels. Length + * have a default domain from 0 to the median median of associated channels. + * Opacity scales have a default domain from 0 to the maximum value of + * associated channels. + */ + lengthDomain?: any[] | Fixed | ParamRef; + + /** + * The extent of the scale’s outputs (visual values). By default inferred from + * the scale’s **type** and **domain**, and for position scales, the plot’s + * dimensions. For continuous data (numbers and dates), and for ordinal + * position scales (*point* and *band*), it is typically [*min*, *max*]; it + * can be [*max*, *min*] to reverse the scale. For other ordinal data, such as + * for a *color* scale, it is an array (or iterable) of output values in the + * same order as the **domain**. + * + * Length scales have a default range of [0, 12]. + */ + lengthRange?: any[] | Fixed | ParamRef; + + /** + * If true, values below the domain minimum are treated as the domain minimum, + * and values above the domain maximum are treated as the domain maximum. + * + * Clamping is useful for focusing on a subset of the data while ensuring that + * extreme values remain visible, but use caution: clamped values may need an + * annotation to avoid misinterpretation. Clamping typically requires setting + * an explicit **domain** since if the domain is inferred, no values will be + * outside the domain. + * + * For continuous scales only. + */ lengthClamp?: any; - lengthNice?: any; - lengthZero?: any; - lengthBase?: any; - lengthExponent?: any; - lengthConstant?: any; + + /** + * If true, or a tick count or interval, extend the domain to nice round + * values. Defaults to 1, 2 or 5 times a power of 10 for *linear* scales, and + * nice time intervals for *utc* and *time* scales. Pass an interval such as + * *minute*, *wednesday* or *month* to specify what constitutes a nice + * interval. + * + * For continuous scales only. + */ + lengthNice?: boolean | number| Interval | ParamRef; + + /** + * Whether the **domain** must include zero. If the domain minimum is + * positive, it will be set to zero; otherwise if the domain maximum is + * negative, it will be set to zero. + * + * For quantitative scales only. + */ + lengthZero?: boolean | ParamRef; + + /** + * A power scale’s exponent (*e.g.*, 0.5 for sqrt); defaults to 1 for a + * linear scale. For *pow* scales only. + */ + lengthExponent?: number | ParamRef; + + /** + * A log scale’s base; defaults to 10. Does not affect the scale’s encoding, + * but rather the default ticks. For *log* scales only. + */ + lengthBase?: number | ParamRef; + + /** + * A symlog scale’s constant, expressing the magnitude of the linear region + * around the origin; defaults to 1. For *symlog* scales only. + */ + lengthConstant?: number | ParamRef; + // projection attributes - projectionType?: any; - projectionParallels?: any; - projectionPrecision?: any; - projectionRotate?: any; - projectionDomain?: any; - projectionInset?: any; - projectionInsetLeft?: any; - projectionInsetRight?: any; - projectionInsetTop?: any; - projectionInsetBottom?: any; - projectionClip?: any; + + /** + * The desired projection; one of: + * + * - a named built-in projection such as *albers-usa* + * - null, for no projection + * + * Named projections are scaled and translated to fit + * the **domain** to the plot’s frame (minus insets). + */ + projectionType?: ProjectionName | null | ParamRef; + + /** + * A GeoJSON object to fit to the plot’s frame (minus insets); defaults to a + * Sphere for spherical projections (outline of the the whole globe). + */ + projectionDomain?: object | ParamRef; + + /** + * A rotation of the sphere before projection; defaults to [0, 0, 0]. + * Specified as Euler angles λ (yaw, or reference longitude), φ (pitch, or + * reference latitude), and optionally γ (roll), in degrees. + */ + projectionRotate?: + | [x: number | ParamRef, y: number | ParamRef, z?: number | ParamRef] + | ParamRef; + + /** + * The [standard parallels][1]. For conic projections only. + * + * [1]: https://d3js.org/d3-geo/conic#conic_parallels + */ + projectionParallels?: [y1: number | ParamRef, y2: number | ParamRef] | ParamRef; + + /** + * The projection’s [sampling threshold][1]. + * + * [1]: https://d3js.org/d3-geo/projection#projection_precision + */ + projectionPrecision?: number | ParamRef; + + /** + * The projection’s clipping method; one of: + * + * - *frame* or true (default) - clip to the plot’s frame (including margins but not insets) + * - a number - clip to a circle of the given radius in degrees centered around the origin + * - null or false - do not clip + * + * Some projections (such as [*armadillo*][1] and [*berghaus*][2]) require + * spherical clipping: in that case set the marks’ **clip** option to + * *sphere*. + * + * [1]: https://observablehq.com/@d3/armadillo + * [2]: https://observablehq.com/@d3/berghaus-star + */ + projectionClip?: boolean | number | 'frame' | null | ParamRef; + + /** + * Shorthand to set the same default for all four projection insets. + * All insets typically default to zero, though not always. A positive + * inset reduces effective area, while a negative inset increases it. + */ + projectionInset?: number | ParamRef; + + /** + * Insets the top edge of the projection by the specified number of pixels. + * A positive value insets towards the bottom edge (reducing effective area), + * while a negative value insets away from the bottom edge (increasing it). + */ + projectionInsetTop?: number | ParamRef; + + /** + * Insets the right edge of the projection by the specified number of pixels. + * A positive value insets towards the left edge (reducing effective area), + * while a negative value insets away from the left edge (increasing it). + */ + projectionInsetRight?: number | ParamRef; + + /** + * Insets the bottom edge of the projection by the specified number of pixels. + * A positive value insets towards the top edge (reducing effective area), + * while a negative value insets away from the top edge (increasing it). + */ + projectionInsetBottom?: number | ParamRef; + + /** + * Insets the left edge of the projection by the specified number of pixels. + * A positive value insets towards the right edge (reducing effective area), + * while a negative value insets away from the right edge (increasing it). + */ + projectionInsetLeft?: number | ParamRef; } diff --git a/packages/spec/src/spec/PlotTypes.ts b/packages/spec/src/spec/PlotTypes.ts new file mode 100644 index 00000000..5b60704a --- /dev/null +++ b/packages/spec/src/spec/PlotTypes.ts @@ -0,0 +1,384 @@ + +/** + * A symbol indicating a fixed scale domain. A fixed domain is initially + * determined from data as usual, but subsequently "fixed" so that it does not + * change over subsequent interactive filtering, ensring stable comparisons. + */ +export type Fixed = 'Fixed'; + +// For internal use. +export type LiteralTimeInterval = + | "3 months" + | "10 years" + | TimeIntervalName + | (`${TimeIntervalName}s` & Record) + | (`${number} ${TimeIntervalName}` & Record) + | (`${number} ${TimeIntervalName}s` & Record); + +/** + * The built-in time intervals; UTC or local time, depending on context. The + * *week* interval is an alias for *sunday*. The *quarter* interval is every + * three months, and the *half* interval is every six months, aligned at the + * start of the year. + */ +export type TimeIntervalName = + | "second" + | "minute" + | "hour" + | "day" + | "week" + | "month" + | "quarter" // 3 months + | "half" // 6 months + | "year" + | "monday" + | "tuesday" + | "wednesday" + | "thursday" + | "friday" + | "saturday" + | "sunday"; + +/** + * How to partition a continuous range into discrete intervals; one of: + * + * - a named time interval such as *day* (for date intervals) + * - a number (for number intervals), defining intervals at integer multiples of *n* + */ +export type Interval = T extends Date ? LiteralTimeInterval + : T extends number ? number + : never; + +/** + * The built-in scale names; one of: + * + * - *x* - horizontal position + * - *y* - vertical position + * - *fx* - horizontal facet position + * - *fy* - vertical facet position + * - *r* - radius (for dots and point geos) + * - *color* - color + * - *opacity* - opacity + * - *symbol* - categorical symbol (for dots) + * - *length* - length (for vectors) + * + * Position scales may have associated axes. Color, opacity, and symbol scales + * may have an associated legend. + */ +export type ScaleName = 'x' | 'y' | 'fx' | 'fy' | 'r' | 'color' | 'opacity' | 'symbol' | 'length'; + +/** + * The supported scale types for *x* and *y* position encodings. + * + * For quantitative data, one of: + * + * - *linear* (default) - linear transform (translate and scale) + * - *pow* - power (exponential) transform + * - *sqrt* - square-root transform; *pow* with *exponent* = 0.5 + * - *log* - logarithmic transform + * - *symlog* - bi-symmetric logarithmic transform per Webber et al. + * + * For temporal data, one of: + * + * - *utc* (default, recommended) - UTC time + * - *time* - local time + * + * For ordinal data, one of: + * + * - *point* (for position only) - divide a continuous range into discrete points + * - *band* (for position only) - divide a continuous range into discrete points + * + * Other scale types: + * + * - *identity* - do not transform values when encoding + */ +export type PositionScaleType = + | 'linear' + | 'pow' + | 'sqrt' + | 'log' + | 'symlog' + | 'utc' + | 'time' + | 'point' + | 'band' + | 'threshold' + | 'quantile' + | 'quantize' + | 'identity'; + +/** + * The supported scale types for *color* encodings. + * + * For quantitative data, one of: + * + * - *linear* (default) - linear transform (translate and scale) + * - *pow* - power (exponential) transform + * - *sqrt* - square-root transform; *pow* with *exponent* = 0.5 + * - *log* - logarithmic transform + * - *symlog* - bi-symmetric logarithmic transform per Webber et al. + * + * For temporal data, one of: + * + * - *utc* (default, recommended) - UTC time + * - *time* - local time + * + * For ordinal data, one of: + * + * - *ordinal* - from discrete inputs to discrete outputs + * + * For color, one of: + * + * - *categorical* - equivalent to *ordinal*; defaults to *observable10* + * - *sequential* - equivalent to *linear*; defaults to *turbo* + * - *cyclical* - equivalent to *linear*; defaults to *rainbow* + * - *threshold* - encodes using discrete thresholds; defaults to *rdylbu* + * - *quantile* - encodes using quantile thresholds; defaults to *rdylbu* + * - *quantize* - uniformly quantizes a continuous domain; defaults to *rdylbu* + * - *diverging* - *linear*, but with a pivot; defaults to *rdbu* + * - *diverging-log* - *log*, but with a pivot; defaults to *rdbu* + * - *diverging-pow* - *pow*, but with a pivot; defaults to *rdbu* + * - *diverging-sqrt* - *sqrt*, but with a pivot; defaults to *rdbu* + * - *diverging-symlog* - *symlog*, but with a pivot; defaults to *rdbu* + * + * Other scale types: + * + * - *identity* - do not transform values when encoding + */ +export type ColorScaleType = + | 'linear' + | 'pow' + | 'sqrt' + | 'log' + | 'symlog' + | 'utc' + | 'time' + | 'point' + | 'band' + | 'ordinal' + | 'sequential' + | 'cyclical' + | 'diverging' + | 'diverging-log' + | 'diverging-pow' + | 'diverging-sqrt' + | 'diverging-symlog' + | 'categorical' + | 'threshold' + | 'quantile' + | 'quantize' + | 'identity'; + +/** + * The supported scale types for continuous encoding channels. + * + * For quantitative data, one of: + * + * - *linear* (default) - linear transform (translate and scale) + * - *pow* - power (exponential) transform + * - *sqrt* - square-root transform; *pow* with *exponent* = 0.5 + * - *log* - logarithmic transform + * - *symlog* - bi-symmetric logarithmic transform per Webber et al. + * + * For temporal data, one of: + * + * - *utc* (default, recommended) - UTC time + * - *time* - local time + * + * Other scale types: + * + * - *identity* - do not transform values when encoding + */ +export type ContinuousScaleType = + | 'linear' + | 'pow' + | 'sqrt' + | 'log' + | 'symlog' + | 'utc' + | 'time' + | 'identity'; + +/** + * The supported scale types for discrete encoding channels. One of: + * + * - *ordinal* - from discrete inputs to discrete outputs + * - *identity* - do not transform values when encoding + */ +export type DiscreteScaleType = + | 'ordinal' + | 'identity'; + +/** + * The built-in projection implementations; one of: + * + * - *albers-usa* - a U.S.-centric composite projection with insets for Alaska and Hawaii + * - *albers* - a U.S.-centric *conic-equal-area* projection + * - *azimuthal-equal-area* - the azimuthal equal-area projection + * - *azimuthal-equidistant* - the azimuthal equidistant projection + * - *conic-conformal* - the conic conformal projection + * - *conic-equal-area* - the conic equal-area projection + * - *conic-equidistant* - the conic equidistant projection + * - *equal-earth* - the Equal Earth projection Šavrič et al., 2018 + * - *equirectangular* - the equirectangular (plate carrée) projection + * - *gnomonic* - the gnomonic projection + * - *identity* - the identity projection + * - *reflect-y* - the identity projection, but flipping *y* + * - *mercator* - the spherical Mercator projection + * - *orthographic* - the orthographic projection + * - *stereographic* - the stereographic projection + * - *transverse-mercator* - the transverse spherical Mercator projection + */ +export type ProjectionName = + | 'albers-usa' + | 'albers' + | 'azimuthal-equal-area' + | 'azimuthal-equidistant' + | 'conic-conformal' + | 'conic-equal-area' + | 'conic-equidistant' + | 'equal-earth' + | 'equirectangular' + | 'gnomonic' + | 'identity' + | 'reflect-y' + | 'mercator' + | 'orthographic' + | 'stereographic' + | 'transverse-mercator'; + +/** + * How to interpolate range (output) values for continuous scales; one of: + * + * - *number* - linear numeric interpolation + * - *rgb* - red, green, blue (sRGB) + * - *hsl* - hue, saturation, lightness (HSL; cylindrical sRGB) + * - *hcl* - hue, chroma, perceptual lightness (CIELCh_ab; cylindrical CIELAB) + * - *lab* - perceptual lightness and opponent colors (L\*a\*b\*, CIELAB) + */ +export type Interpolate = + | 'number' + | 'rgb' + | 'hsl' + | 'hcl' + | 'lab'; + +/** The built-in color schemes, cased. */ +type ColorSchemeCase = + | 'Accent' + | 'Category10' + | 'Dark2' + | 'Observable10' + | 'Paired' + | 'Pastel1' + | 'Pastel2' + | 'Set1' + | 'Set2' + | 'Set3' + | 'Tableau10' + | 'BrBG' + | 'PRGn' + | 'PiYG' + | 'PuOr' + | 'RdBu' + | 'RdGy' + | 'RdYlBu' + | 'RdYlGn' + | 'Spectral' + | 'BuRd' + | 'BuYlRd' + | 'Blues' + | 'Greens' + | 'Greys' + | 'Oranges' + | 'Purples' + | 'Reds' + | 'Turbo' + | 'Viridis' + | 'Magma' + | 'Inferno' + | 'Plasma' + | 'Cividis' + | 'Cubehelix' + | 'Warm' + | 'Cool' + | 'BuGn' + | 'BuPu' + | 'GnBu' + | 'OrRd' + | 'PuBu' + | 'PuBuGn' + | 'PuRd' + | 'RdPu' + | 'YlGn' + | 'YlGnBu' + | 'YlOrBr' + | 'YlOrRd' + | 'Rainbow' + | 'Sinebow'; + +/** + * The built-in color schemes. For categorical data, one of: + * + * - *Accent* - eight colors + * - *Category10* - ten colors + * - *Dark2* - eight colors + * - *Observable10* (default) - ten colors + * - *Paired* - twelve paired colors + * - *Pastel1* - nine colors + * - *Pastel2* - eight colors + * - *Set1* - nine colors + * - *Set2* - eight colors + * - *Set3* - twelve colors + * - *Tableau10* - ten colors + * + * For diverging data, one of: + * + * - *BrBG* - from brown to white to blue-green + * - *PRGn* - from purple to white to green + * - *PiYG* - from pink to white to yellow-green + * - *PuOr* - from purple to white to orange + * - *RdBu* (default) - from red to white to blue + * - *RdGy* - from red to white to gray + * - *RdYlBu* - from red to yellow to blue + * - *RdYlGn* - from red to yellow to green + * - *Spectral* - from red to blue, through the spectrum + * - *BuRd* - from blue to white to red + * - *BuYlRd* - from blue to yellow to red + * + * For sequential data, one of: + * + * - *Blues* - from white to blue + * - *Greens* - from white to green + * - *Greys* - from white to gray + * - *Oranges* - from white to orange + * - *Purples* - from white to purple + * - *Reds* - from white to red + * - *Turbo* (default) - from blue to red, through the spectrum + * - *Viridis* - from blue to green to yellow + * - *Magma* - from purple to orange to yellow + * - *Inferno* - from purple to orange to yellow + * - *Plasma* - from purple to orange to yellow + * - *Cividis* - from blue to yellow + * - *Cubehelix* - from black to white, rotating hue + * - *Warm* - from purple to green, through warm hues + * - *Cool* - from green to to purple, through cool hues + * - *BuGn* - from light blue to dark green + * - *BuPu* - from light blue to dark purple + * - *GnBu* - from light green to dark blue + * - *OrRd* - from light orange to dark red + * - *PuBu* - from light purple to dark blue + * - *PuBuGn* - from light purple to blue to dark green + * - *PuRd* - from light purple to dark red + * - *RdPu* - from light red to dark purple + * - *YlGn* - from light yellow to dark green + * - *YlGnBu* - from light yellow to green to dark blue + * - *YlOrBr* - from light yellow to orange to dark brown + * - *YlOrRd* - from light yellow to orange to dark red + * + * For cyclical data, one of: + * + * - *Rainbow* (default) - the less-angry rainbow color scheme + * - *Sinebow* - Bumgardner and Loyd’s “sinebow” scheme + */ +export type ColorScheme = ColorSchemeCase | (Lowercase & Record); From b2670eb3342481a9836ab4ff082e2ceb0b025e11 Mon Sep 17 00:00:00 2001 From: jheer Date: Thu, 4 Apr 2024 13:29:09 -0700 Subject: [PATCH 16/25] feat: Initial progress towards spec mark types. --- packages/spec/src/spec/PlotMark.ts | 18 +- packages/spec/src/spec/PlotTypes.ts | 113 ++++ packages/spec/src/spec/marks/Dot.ts | 142 +++++ packages/spec/src/spec/marks/Marks.ts | 772 ++++++++++++++++++++++++++ 4 files changed, 1039 insertions(+), 6 deletions(-) create mode 100644 packages/spec/src/spec/marks/Dot.ts create mode 100644 packages/spec/src/spec/marks/Marks.ts diff --git a/packages/spec/src/spec/PlotMark.ts b/packages/spec/src/spec/PlotMark.ts index 14091443..dfb37182 100644 --- a/packages/spec/src/spec/PlotMark.ts +++ b/packages/spec/src/spec/PlotMark.ts @@ -2,6 +2,7 @@ import { Expression } from './Expression.js'; import { ParamRef } from './Param.js'; import { PlotMarkData } from './PlotFrom.js'; import { Transform } from './Transform.js'; +import { Circle, Dot, DotX, DotY, Hexagon } from './marks/Dot.js'; export type MarkType = | 'area' @@ -18,11 +19,11 @@ export type MarkType = | 'rect' | 'rectX' | 'rectY' - | 'dot' - | 'dotX' - | 'dotY' - | 'circle' - | 'hexagon' + // | 'dot' + // | 'dotX' + // | 'dotY' + // | 'circle' + // | 'hexagon' | 'text' | 'textX' | 'textY' @@ -75,12 +76,17 @@ export type MarkOption = | Transform | any[]; +export type PlotMark = + | Dot | DotX | DotY | Circle | Hexagon + | GenericPlotMark; + /** * A graphical mark (layer) for a plot. */ -export interface PlotMark { +export interface GenericPlotMark { /** The mark type. */ mark: MarkType; + /** The data the mark should visualize. */ data?: PlotMarkData; diff --git a/packages/spec/src/spec/PlotTypes.ts b/packages/spec/src/spec/PlotTypes.ts index 5b60704a..a5e50473 100644 --- a/packages/spec/src/spec/PlotTypes.ts +++ b/packages/spec/src/spec/PlotTypes.ts @@ -382,3 +382,116 @@ type ColorSchemeCase = * - *Sinebow* - Bumgardner and Loyd’s “sinebow” scheme */ export type ColorScheme = ColorSchemeCase | (Lowercase & Record); + +/** + * The built-in symbol implementations. For fill, one of: + * + * - *circle* - a circle + * - *cross* - a Greek cross with arms of equal length + * - *diamond* - a rhombus + * - *square* - a square + * - *star* - a pentagonal star (pentagram) + * - *triangle* - an up-pointing triangle + * - *wye* - a Y with arms of equal length + * + * For stroke (based on [Heman Robinson’s research][1]), one of: + * + * - *circle* - a circle + * - *plus* - a plus sign + * - *times* - an X with arms of equal length + * - *triangle2* - an (alternate) up-pointing triangle + * - *asterisk* - an asterisk + * - *square2* - a (alternate) square + * - *diamond2* - a rotated square + * + * The *hexagon* symbol is also supported. + * + * [1]: https://www.tandfonline.com/doi/abs/10.1080/10618600.2019.1637746 + */ +export type SymbolType = + | "asterisk" + | "circle" + | "cross" + | "diamond" + | "diamond2" + | "hexagon" + | "plus" + | "square" + | "square2" + | "star" + | "times" + | "triangle" + | "triangle2" + | "wye"; + +/** + * How to anchor a mark relative to the plot’s frame; one of: + * + * - *middle* - centered in the middle + * - in the middle of one of the edges: *top*, *right*, *bottom*, *left* + * - in one of the corners: *top-left*, *top-right*, *bottom-right*, *bottom-left* + */ +export type FrameAnchor = + | "middle" + | "top-left" + | "top" + | "top-right" + | "right" + | "bottom-right" + | "bottom" + | "bottom-left" + | "left"; + +// from https://github.com/observablehq/plot/blob/main/src/reducer.d.ts +type Digit = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9; + +// For internal use. +export type ReducerPercentile = + | (`p${Digit}${Digit}` & Record) // see https://github.com/microsoft/TypeScript/issues/29729 + | "p25" + | "p50" + | "p75"; + +/** + * How to reduce aggregated (binned or grouped) values; one of: + * + * - *first* - the first value, in input order + * - *last* - the last value, in input order + * - *count* - the number of elements (frequency) + * - *distinct* - the number of distinct values + * - *sum* - the sum of values + * - *proportion* - the sum proportional to the overall total (weighted frequency) + * - *proportion-facet* - the sum proportional to the facet total + * - *deviation* - the standard deviation + * - *min* - the minimum value + * - *min-index* - the zero-based index of the minimum value + * - *max* - the maximum value + * - *max-index* - the zero-based index of the maximum value + * - *mean* - the mean value (average) + * - *median* - the median value + * - *variance* - the variance per [Welford’s algorithm][1] + * - *mode* - the value with the most occurrences + * - *pXX* - the percentile value, where XX is a number in [00,99] + * - *identity* - the array of values + * + * [1]: https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm + */ +export type Reducer = + | "first" + | "last" + | "identity" + | "count" + | "distinct" + | "sum" + | "proportion" + | "proportion-facet" + | "deviation" + | "min" + | "min-index" + | "max" + | "max-index" + | "mean" + | "median" + | "variance" + | "mode" + | ReducerPercentile; diff --git a/packages/spec/src/spec/marks/Dot.ts b/packages/spec/src/spec/marks/Dot.ts new file mode 100644 index 00000000..7b00cd7c --- /dev/null +++ b/packages/spec/src/spec/marks/Dot.ts @@ -0,0 +1,142 @@ +import { ParamRef } from '../Param.js'; +import { FrameAnchor, Interval, SymbolType } from '../PlotTypes.js'; +import { + ChannelValue, ChannelValueIntervalSpec, + ChannelValueSpec, MarkData, MarkOptions +} from './Marks.js'; + +/** Options for the dot mark. */ +export interface DotOptions extends MarkData, MarkOptions { + /** + * The horizontal position channel specifying the dot’s center, typically + * bound to the *x* scale. + */ + x?: ChannelValueSpec; + + /** + * The vertical position channel specifying the dot’s center, typically bound + * to the *y* scale. + */ + y?: ChannelValueSpec; + + /** + * The radius of dots; either a channel or constant. When a number, it is + * interpreted as a constant radius in pixels. Otherwise it is interpreted as + * a channel, typically bound to the *r* channel, which defaults to the *sqrt* + * type for proportional symbols. The radius defaults to 4.5 pixels when using + * the **symbol** channel, and otherwise 3 pixels. Dots with a nonpositive + * radius are not drawn. + */ + r?: ChannelValueSpec | number | ParamRef; + + /** + * The rotation angle of dots in degrees clockwise; either a channel or a + * constant. When a number, it is interpreted as a constant; otherwise it is + * interpreted as a channel. Defaults to 0°, pointing up. + */ + rotate?: ChannelValue | number | ParamRef; + + /** + * The categorical symbol; either a channel or a constant. A constant symbol + * can be specified by a valid symbol name such as *star*, or a symbol object + * (implementing the draw method); otherwise it is interpreted as a channel. + * Defaults to *circle* for the **dot** mark, and *hexagon* for the + * **hexagon** mark. + * + * If the **symbol** channel’s values are all symbols, symbol names, or + * nullish, the channel is unscaled (values are interpreted literally); + * otherwise, the channel is bound to the *symbol* scale. + */ + symbol?: ChannelValueSpec | SymbolType | ParamRef; + + /** + * The frame anchor specifies defaults for **x** and **y** based on the plot’s + * frame; it may be one of the four sides (*top*, *right*, *bottom*, *left*), + * one of the four corners (*top-left*, *top-right*, *bottom-right*, + * *bottom-left*), or the *middle* of the frame. For example, for dots + * distributed horizontally at the top of the frame: + * + * ```js + * Plot.dot(data, {x: "date", frameAnchor: "top"}) + * ``` + */ + frameAnchor?: FrameAnchor | ParamRef; +} + +/** Options for the dotX mark. */ +export interface DotXOptions extends Omit { + /** + * The vertical position of the dot’s center, typically bound to the *y* + * scale. + */ + y?: ChannelValueIntervalSpec; + + /** + * An interval (such as *day* or a number), to transform **y** values to the + * middle of the interval. + */ + interval?: Interval | ParamRef; +} + +/** Options for the dotY mark. */ +export interface DotYOptions extends Omit { + /** + * The horizontal position of the dot’s center, typically bound to the *x* + * scale. + */ + x?: ChannelValueIntervalSpec; + + /** + * An interval (such as *day* or a number), to transform **x** values to the + * middle of the interval. + */ + interval?: Interval | ParamRef; +} + +export interface Dot extends DotOptions { + /** + * A dot mark that draws circles, or other symbols, as in a scatterplot. + * + * If either **x** or **y** is not specified, the default is determined by the + * **frameAnchor** option. If none of **x**, **y**, and **frameAnchor** are + * specified, *data* is assumed to be an array of pairs [[*x₀*, *y₀*], [*x₁*, + * *y₁*], [*x₂*, *y₂*], …] such that **x** = [*x₀*, *x₁*, *x₂*, …] and **y** = + * [*y₀*, *y₁*, *y₂*, …]. + * + * Dots are sorted by descending radius **r** by default to mitigate + * overplotting; set the **sort** option to null to draw them in input order. + */ + mark: 'dot'; +} + +export interface DotX extends DotXOptions { + /** + * Like dot, except that **x** defaults to the identity function, assuming that + * *data* = [*x₀*, *x₁*, *x₂*, …]. + * + * If an **interval** is specified, such as *day*, **y** is transformed to the + * middle of the interval. + */ + mark: 'dotX'; +} + +export interface DotY extends DotYOptions { + /** + * Like dot, except that **y** defaults to the identity function, assuming that + * *data* = [*y₀*, *y₁*, *y₂*, …]. + * + * If an **interval** is specified, such as *day*, **x** is transformed to the + * middle of the interval. + */ + mark: 'dotY'; +} + +export interface Circle extends Exclude { + /** Like dot, except that the **symbol** option is set to *circle*. */ + mark: 'circle'; +} + +export interface Hexagon extends Exclude { + /** Like dot, except that the **symbol** option is set to *hexagon*. */ + mark: 'hexagon'; +} diff --git a/packages/spec/src/spec/marks/Marks.ts b/packages/spec/src/spec/marks/Marks.ts new file mode 100644 index 00000000..6eff486e --- /dev/null +++ b/packages/spec/src/spec/marks/Marks.ts @@ -0,0 +1,772 @@ +import { AggregateExpression, SQLExpression } from '../Expression.js'; +import { ParamRef } from '../Param.js'; +import { PlotMarkData } from '../PlotFrom.js'; +import { FrameAnchor, Interval, Reducer, ScaleName } from '../PlotTypes.js'; +import { Transform } from '../Transform.js'; + +/** + * The set of known channel names. Channels in custom marks may use other names; + * these known names are enumerated for convenient autocomplete. + */ +export type ChannelName = + | 'ariaLabel' + | 'fill' + | 'fillOpacity' + | 'fontSize' + | 'fx' + | 'fy' + | 'geometry' + | 'height' + | 'href' + | 'length' + | 'opacity' + | 'path' + | 'r' + | 'rotate' + | 'src' + | 'stroke' + | 'strokeOpacity' + | 'strokeWidth' + | 'symbol' + | 'text' + | 'title' + | 'weight' + | 'width' + | 'x' + | 'x1' + | 'x2' + | 'y' + | 'y1' + | 'y2' + | 'z' + | (string & Record); // custom channel; see also https://github.com/microsoft/TypeScript/issues/29729 + +type ChannelScale = ScaleName | "auto" | boolean | null; + +/** + * A channel’s values may be expressed as: + * + * - a field name, to extract the corresponding value for each datum + * - an iterable of values, typically of the same length as the data + * - a channel transform or SQL expression + * - a constant date, number, or boolean + * - null to represent no value + */ +export type ChannelValue = + | any[] // column of values + | (string & Record) // field or literal color; see also https://github.com/microsoft/TypeScript/issues/29729 + | Date // constant + | number // constant + | boolean // constant + | null // constant + | Transform + | SQLExpression + | AggregateExpression; + +/** + * When specifying a mark channel’s value, you can provide a {value, scale} + * object to override the scale that would normally be associated with the + * channel. + */ +export type ChannelValueSpec = + | ChannelValue + | { + value: ChannelValue; + label?: string; + scale?: ScaleName | 'auto' | boolean | null; + }; + +/** + * In some contexts, when specifying a mark channel’s value, you can provide a + * {value, interval} object to specify an associated interval. + */ +export type ChannelValueIntervalSpec = + | ChannelValueSpec + | { value: ChannelValue; interval?: Interval }; + +/** A channel name, or an implied one for domain sorting. */ +type ChannelDomainName = ChannelName | 'data' | 'width' | 'height'; + +/** + * The available inputs for imputing scale domains. In addition to a named + * channel, an input may be specified as: + * + * - *data* - impute from mark data + * - *width* - impute from |*x2* - *x1*| + * - *height* - impute from |*y2* - *y1*| + * - null - impute from input order + * + * If the *x* channel is not defined, the *x2* channel will be used instead if + * available, and similarly for *y* and *y2*; this is useful for marks that + * implicitly stack. The *data* input is typically used in conjunction with a + * custom **reduce** function, as when the built-in single-channel reducers are + * insufficient. + */ +export type ChannelDomainValue = ChannelDomainName | `-${ChannelDomainName}` | null; + +/** Options for imputing scale domains from channel values. */ +export interface ChannelDomainOptions { + /** + * How to produce a singular value (for subsequent sorting) from aggregated + * channel values; one of: + * + * - true (default) - alias for *max* + * - false or null - disabled; don’t impute the scale domain + * - a named reducer implementation such as *count* or *sum* + * - a function that takes an array of values and returns the reduced value + * - an object that implements the *reduceIndex* method + */ + reduce?: Reducer | boolean | null; + + /** How to order reduced values. */ + order?: 'ascending' | 'descending' | null; + + /** If true, reverse the order after sorting. */ + reverse?: boolean; + + /** + * If a positive number, limit the domain to the first *n* sorted values. If a + * negative number, limit the domain to the last *-n* sorted values. Hence, a + * positive **limit** with **reverse** true will return the top *n* values in + * descending order. + * + * If an array [*lo*, *hi*], slices the sorted domain from *lo* (inclusive) to + * *hi* (exclusive). As with [*array*.slice][1], if either *lo* or *hi* are + * negative, it indicates an offset from the end of the array; if *lo* is + * undefined it defaults to 0, and if *hi* is undefined it defaults to + * Infinity. + * + * Note: limiting the imputed domain of one scale, say *x*, does not affect + * the imputed domain of another scale, say *y*; each scale domain is imputed + * independently. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice + */ + limit?: number | [lo?: number, hi?: number]; +} + +/** How to derive a scale’s domain from a channel’s values. */ +export type ChannelDomainValueSpec = + | ChannelDomainValue + | ({ value: ChannelDomainValue } & ChannelDomainOptions); + +/** How to impute scale domains from channel values. */ +export type ChannelDomainSort = { + [key in ScaleName]?: ChannelDomainValueSpec +} & ChannelDomainOptions; + +/** + * Output channels for aggregating transforms, such as bin and group. Each + * declared output channel has an associated reducer, and typically a + * corresponding input channel in *options*. Non-grouping channels declared in + * *options* but not *outputs* are computed on reduced data after grouping, + * which defaults to the array of data for the current group. + * + * If **title** is in *options* but not *outputs*, the reducer defaults to + * summarizing the most common values. If **href** is in *options* but not + * *outputs*, the reducer defaults to *first*. When **x1** or **x2** is in + * *outputs*, reads the input channel **x** if **x1** or **x2** is not in + * *options*; likewise for **y1** or **y2**, reads the input channel **y** if + * **y1** or **y2** is not in *options*. + */ +export type ChannelReducers = { + [key in ChannelName]?: T | { reduce: T; scale?: ChannelScale } | null +}; + +/** Abstract (unscaled) values, and associated scale, per channel. */ +export type ChannelStates = { + [key in ChannelName]?: { value: any[]; scale: ScaleName | null } +}; + +/** Possibly-scaled values for each channel. */ +export type ChannelValues = { + [key in ChannelName]?: any[] +} & { channels: ChannelStates }; + +/** + * How to order values; one of: + * + * - a function for comparing data, returning a signed number + * - a channel value definition for sorting given values in ascending order + * - a {value, order} object for sorting given values + * - a {channel, order} object for sorting the named channel’s values + */ +export type SortOrder = + | ChannelValue + | { value?: ChannelValue; order?: 'ascending' | 'descending' } + | { channel?: ChannelName | `-${ChannelName}`; order?: 'ascending' | 'descending' }; + + +/** The pointer mode for the tip; corresponds to pointerX, pointerY, and pointer. */ +export type TipPointer = 'x' | 'y' | 'xy'; + +export interface MarkData { + /** + * The data source for the mark. + */ + data: PlotMarkData; +} + +/** Shared options for all marks. */ +export interface MarkOptions { + /** + * Applies a transform to filter the mark’s index according to the given + * channel values; only truthy values are retained. + * + * Note that filtering only affects the rendered mark index, not the + * associated channel values, and thus has no effect on imputed scale domains. + */ + filter?: ChannelValue; + + /** + * Applies a transform to reverse the order of the mark’s index, say for + * reverse input order. + */ + reverse?: boolean | ParamRef; + + /** + * Either applies a transform to sort the mark’s index by the specified + * channel values, or imputes ordinal scale domains from this mark’s channels. + * + * When imputing ordinal scale domains from channel values, the **sort** + * option is an object whose keys are ordinal scale names such as *x* or *fx*, + * and whose values are channel names such as *y*, *y1*, or *y2*. For example, + * to impute the *y* scale’s domain from the associated *x* channel values in + * ascending order: + * + * ```js + * sort: {y: "x"} + * ``` + * + * For different sort options for different scales, replace the channel name + * with a *value* object and per-scale options: + * + * ```js + * sort: {y: {value: "-x"}} + * ``` + * + * When sorting the mark’s index, the **sort** option is instead one of: + * + * - a channel value definition for sorting given values in ascending order + * - a {value, order} object for sorting given values + * - a {channel, order} object for sorting the named channel’s values + */ + sort?: SortOrder | ChannelDomainSort; + + /** + * The horizontal facet position channel, for mark-level faceting, bound to + * the *fx* scale. + */ + fx?: ChannelValue; + + /** + * The vertical facet position channel, for mark-level faceting, bound to the + * *fy* scale. + */ + fy?: ChannelValue; + + /** + * Whether to enable or disable faceting; one of: + * + * - *auto* (default) - automatically determine if this mark should be faceted + * - *include* (or true) - draw the subset of the mark’s data in the current facet + * - *exclude* - draw the subset of the mark’s data *not* in the current facet + * - *super* - draw this mark in a single frame that covers all facets + * - null (or false) - repeat this mark’s data across all facets (*i.e.*, no faceting) + * + * When a mark uses *super* faceting, it is not allowed to use position scales + * (*x*, *y*, *fx*, or *fy*); *super* faceting is intended for decorations, + * such as labels and legends. + * + * When top-level faceting is used, the default *auto* setting is equivalent + * to *include* when the mark data is strictly equal to the top-level facet + * data; otherwise it is equivalent to null. When the *include* or *exclude* + * facet mode is chosen, the mark data must be parallel to the top-level facet + * data: the data must have the same length and order. If the data are not + * parallel, then the wrong data may be shown in each facet. The default + * *auto* therefore requires strict equality (`===`) for safety, and using the + * facet data as mark data is recommended when using the *exclude* facet mode. + * (To construct parallel data safely, consider using [*array*.map][1] on the + * facet data.) + * + * When mark-level faceting is used, the default *auto* setting is equivalent + * to *include*: the mark will be faceted if either the **fx** or **fy** + * channel option (or both) is specified. The null or false option will + * disable faceting, while *exclude* draws the subset of the mark’s data *not* + * in the current facet. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map + */ + facet?: 'auto' | 'include' | 'exclude' | 'super' | boolean | null | ParamRef; + + /** + * How to place the mark with respect to facets; one of: + * + * - null (default for most marks) - display the mark in each non-empty facet + * - *top*, *right*, *bottom*, or *left* - display the mark only in facets on + * the given side + * - *top-empty*, *right-empty*, *bottom-empty*, or *left-empty* (default for + * axis marks) - display the mark only in facets that have empty space on + * the given side: either the margin, or an empty facet + * - *empty* - display the mark in empty facets only + */ + facetAnchor?: + | 'top' + | 'right' + | 'bottom' + | 'left' + | 'top-left' + | 'top-right' + | 'bottom-left' + | 'bottom-right' + | 'top-empty' + | 'right-empty' + | 'bottom-empty' + | 'left-empty' + | 'empty' + | null + | ParamRef; + + /** + * Shorthand to set the same default for all four mark margins: **marginTop**, + * **marginRight**, **marginBottom**, and **marginLeft**; typically defaults + * to 0, except for axis marks. + */ + margin?: number | ParamRef; + + /** + * The mark’s top margin; the minimum distance in pixels between the top edges + * of the inner and outer plot area. + */ + marginTop?: number | ParamRef; + + /** + * The mark’s right margin; the minimum distance in pixels between the right + * edges of the mark’s inner and outer plot area. + */ + marginRight?: number | ParamRef; + + /** + * The mark’s bottom margin; the minimum distance in pixels between the bottom + * edges of the inner and outer plot area. + */ + marginBottom?: number | ParamRef; + + /** + * The mark’s left margin; the minimum distance in pixels between the left + * edges of the inner and outer plot area. + */ + marginLeft?: number | ParamRef; + + /** + * The [aria-description][1]; a constant textual description. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-description + */ + ariaDescription?: string | ParamRef; + + /** + * The [aria-hidden][1] state; a constant indicating whether the element is + * exposed to an accessibility API. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-hidden + */ + ariaHidden?: string | ParamRef; + + /** + * The [aria-label][1]; a channel specifying short textual labels representing + * the value in the accessibility tree. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-label + */ + ariaLabel?: ChannelValue; + + /** + * The [pointer-events][1] property; a constant string such as *none*. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events + */ + pointerEvents?: string | ParamRef; + + /** + * The title; a channel specifying accessible, short textual descriptions as + * strings (possibly with newlines). If the tip option is specified, the title + * will be displayed with an interactive tooltip instead of using the SVG + * [title element][1]. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/title + */ + title?: ChannelValue; + + /** Whether to generate a tooltip for this mark, and any tip options. */ + tip?: + | boolean + | TipPointer + | (TipOptions & { pointer?: TipPointer }) + | ParamRef; + + /** + * How to clip the mark; one of: + * + * - *frame* or true - clip to the plot’s frame (inner area) + * - *sphere* - clip to the projected sphere (*e.g.*, front hemisphere) + * - null or false - do not clip + * + * The *sphere* clip option requires a geographic projection. + */ + clip?: 'frame' | 'sphere' | boolean | null | ParamRef; + + /** + * The horizontal offset in pixels; a constant option. On low-density screens, + * an additional 0.5px offset may be applied for crisp edges. + */ + dx?: number | ParamRef; + + /** + * The vertical offset in pixels; a constant option. On low-density screens, + * an additional 0.5px offset may be applied for crisp edges. + */ + dy?: number | ParamRef; + + /** + * The [fill][1]; a constant CSS color string, or a channel typically bound to + * the *color* scale. If all channel values are valid CSS colors, by default + * the channel will not be bound to the *color* scale, interpreting the colors + * literally. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/fill + */ + fill?: ChannelValueSpec | ParamRef; + + /** + * The [fill-opacity][1]; a constant number between 0 and 1, or a channel + * typically bound to the *opacity* scale. If all channel values are numbers + * in [0, 1], by default the channel will not be bound to the *opacity* scale, + * interpreting the opacities literally. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/fill-opacity + */ + fillOpacity?: ChannelValueSpec | ParamRef; + + /** + * The [stroke][1]; a constant CSS color string, or a channel typically bound + * to the *color* scale. If all channel values are valid CSS colors, by + * default the channel will not be bound to the *color* scale, interpreting + * the colors literally. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke + */ + stroke?: ChannelValueSpec | ParamRef; + + /** + * The [stroke-dasharray][1]; a constant number indicating the length in + * pixels of alternating dashes and gaps, or a constant string of numbers + * separated by spaces or commas (_e.g._, *10 2* for dashes of 10 pixels + * separated by gaps of 2 pixels), or *none* (the default) for no dashing + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-dasharray + */ + strokeDasharray?: string | number | ParamRef; + + /** + * The [stroke-dashoffset][1]; a constant indicating the offset in pixels of + * the first dash along the stroke; defaults to zero. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-dashoffset + */ + strokeDashoffset?: string | number | ParamRef; + + /** + * The [stroke-linecap][1]; a constant specifying how to cap stroked paths, + * such as *butt*, *round*, or *square*. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-linecap + */ + strokeLinecap?: string | ParamRef; + + /** + * The [stroke-linejoin][1]; a constant specifying how to join stroked paths, + * such as *bevel*, *miter*, *miter-clip*, or *round*. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-linejoin + */ + strokeLinejoin?: string | ParamRef; + + /** + * The [stroke-miterlimit][1]; a constant number specifying how to limit the + * length of *miter* joins on stroked paths. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-miterlimit + */ + strokeMiterlimit?: number | ParamRef; + + /** + * The [stroke-opacity][1]; a constant between 0 and 1, or a channel typically + * bound to the *opacity* scale. If all channel values are numbers in [0, 1], + * by default the channel will not be bound to the *opacity* scale, + * interpreting the opacities literally. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-opacity + */ + strokeOpacity?: ChannelValueSpec; + + /** + * The [stroke-width][1]; a constant number in pixels, or a channel. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-width + */ + strokeWidth?: ChannelValueSpec; + + /** + * The [opacity][1]; a constant between 0 and 1, or a channel typically bound + * to the *opacity* scale. If all channel values are numbers in [0, 1], by + * default the channel will not be bound to the *opacity* scale, interpreting + * the opacities literally. For faster rendering, prefer the **strokeOpacity** + * or **fillOpacity** option. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/opacity + */ + opacity?: ChannelValueSpec; + + /** + * The [mix-blend-mode][1]; a constant string specifying how to blend content + * such as *multiply*. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode + */ + mixBlendMode?: string | ParamRef; + + /** + * A CSS [filter][1]; a constant string used to adjust the rendering of + * images, such as *blur(5px)*. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/CSS/filter + */ + imageFilter?: string | ParamRef; + + /** + * The [paint-order][1]; a constant string specifying the order in which the + * **fill**, **stroke**, and any markers are drawn; defaults to *normal*, + * which draws the fill, then stroke, then markers; defaults to *stroke* for + * the text mark to create a “halo” around text to improve legibility. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/CSS/paint-order + */ + paintOrder?: string | ParamRef; + + /** + * The [shape-rendering][1]; a constant string such as *crispEdges*. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/shape-rendering + */ + shapeRendering?: string | ParamRef; + + /** + * The [href][1]; a channel specifying URLs for clickable links. May be used + * in conjunction with the **target** option to open links in another window. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/href + */ + href?: ChannelValue; + + /** + * The [target][1]; a constant string specifying the target window (_e.g._, + * *_blank*) for clickable links; used in conjunction with the **href** + * option. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/target + */ + target?: string | ParamRef; +} + +/** Options for styling text (independent of anchor position). */ +export interface TextStyles { + /** + * The [text anchor][1] controls how text is aligned (typically horizontally) + * relative to its anchor point; it is one of *start*, *end*, or *middle*. If + * the frame anchor is *left*, *top-left*, or *bottom-left*, the default text + * anchor is *start*; if the frame anchor is *right*, *top-right*, or + * *bottom-right*, the default is *end*; otherwise it is *middle*. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/text-anchor + */ + textAnchor?: 'start' | 'middle' | 'end' | ParamRef; + + /** + * The line height in ems; defaults to 1. The line height affects the + * (typically vertical) separation between adjacent baselines of text, as well + * as the separation between the text and its anchor point. + */ + lineHeight?: number | ParamRef; + + /** + * The line width in ems (e.g., 10 for about 20 characters); defaults to + * infinity, disabling wrapping and clipping. + * + * If **textOverflow** is null, lines will be wrapped at the specified length. + * If a line is split at a soft hyphen (\xad), a hyphen (-) will be displayed + * at the end of the line. If **textOverflow** is not null, lines will be + * clipped according to the given strategy. + */ + lineWidth?: number | ParamRef; + + /** + * How truncate (or wrap) lines of text longer than the given **lineWidth**; + * one of: + * + * - null (default) - preserve overflowing characters (and wrap if needed) + * - *clip* or *clip-end* - remove characters from the end + * - *clip-start* - remove characters from the start + * - *ellipsis* or *ellipsis-end* - replace characters from the end with an ellipsis (…) + * - *ellipsis-start* - replace characters from the start with an ellipsis (…) + * - *ellipsis-middle* - replace characters from the middle with an ellipsis (…) + * + * If no **title** was specified, if text requires truncation, a title + * containing the non-truncated text will be implicitly added. + */ + textOverflow?: + | null + | 'clip' + | 'ellipsis' + | 'clip-start' + | 'clip-end' + | 'ellipsis-start' + | 'ellipsis-middle' + | 'ellipsis-end' + | ParamRef; + + /** + * If true, changes the default **fontFamily** to *monospace*, and uses + * simplified monospaced text metrics calculations. + */ + monospace?: boolean | ParamRef; + + /** + * The [font-family][1]; a constant; defaults to the plot’s font family, which + * is typically [*system-ui*][2]. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/CSS/font-family + * [2]: https://drafts.csswg.org/css-fonts-4/#valdef-font-family-system-ui + */ + fontFamily?: string | ParamRef; + + /** + * The [font size][1] in pixels; either a constant or a channel; defaults to + * the plot’s font size, which is typically 10. When a number, it is + * interpreted as a constant; otherwise it is interpreted as a channel. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/CSS/font-size + */ + fontSize?: ChannelValue | ParamRef; + + /** + * The [font style][1]; a constant; defaults to the plot’s font style, which + * is typically *normal*. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/CSS/font-style + */ + fontStyle?: string | ParamRef; + + /** + * The [font variant][1]; a constant; if the **text** channel contains numbers + * or dates, defaults to *tabular-nums* to facilitate comparing numbers; + * otherwise defaults to the plot’s font style, which is typically *normal*. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/CSS/font-variant + */ + fontVariant?: string | ParamRef; + + /** + * The [font weight][1]; a constant; defaults to the plot’s font weight, which + * is typically *normal*. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight + */ + fontWeight?: string | number | ParamRef; +} + +/** Options for the tip mark. */ +export interface TipOptions extends MarkOptions, TextStyles { + /** + * The horizontal position channel specifying the tip’s anchor, typically + * bound to the *x* scale. + */ + x?: ChannelValueSpec; + + /** + * The starting horizontal position channel specifying the tip’s anchor, + * typically bound to the *x* scale. + */ + x1?: ChannelValueSpec; + + /** + * The ending horizontal position channel specifying the tip’s anchor, + * typically bound to the *x* scale. + */ + x2?: ChannelValueSpec; + + /** + * The vertical position channel specifying the tip’s anchor, typically + * bound to the *y* scale. + */ + y?: ChannelValueSpec; + + /** + * The starting vertical position channel specifying the tip’s anchor, + * typically bound to the *y* scale. + */ + y1?: ChannelValueSpec; + + /** + * The ending vertical position channel specifying the tip’s anchor, typically + * bound to the *y* scale. + */ + y2?: ChannelValueSpec; + + /** + * The frame anchor specifies defaults for **x** and **y** based on the plot’s + * frame; it may be one of the four sides (*top*, *right*, *bottom*, *left*), + * one of the four corners (*top-left*, *top-right*, *bottom-right*, + * *bottom-left*), or the *middle* of the frame. For example, for tips + * distributed horizontally at the top of the frame: + * + * ```js + * Plot.tip(data, {x: "date", frameAnchor: "top"}) + * ``` + */ + frameAnchor?: FrameAnchor | ParamRef; + + /** + * The tip anchor specifies how to orient the tip box relative to its anchor + * position; it refers to the part of the tip box that is attached to the + * anchor point. For example, the *top-left* anchor places the top-left corner + * of tip box near the anchor position, hence placing the tip box below and to + * the right of the anchor position. + */ + anchor?: FrameAnchor | ParamRef; + + /** + * If an explicit tip anchor is not specified, an anchor is chosen + * automatically such that the tip fits within the plot’s frame; if the + * preferred anchor fits, it is chosen. + */ + preferredAnchor?: FrameAnchor | null | ParamRef; + + /** + * How channel values are formatted for display. If a format is a string, it + * is interpreted as a (UTC) time format for temporal channels, and otherwise + * a number format. + */ + format?: { + [name in ChannelName]?: boolean | string | ParamRef + }; + + /** The image filter for the tip’s box; defaults to a drop shadow. */ + pathFilter?: string | ParamRef; + + /** The size of the tip’s pointer in pixels; defaults to 12. */ + pointerSize?: number | ParamRef; + + /** The padding around the text in pixels; defaults to 8. */ + textPadding?: number | ParamRef; +} From f20b208e656b1f2033e8817cb309649cccfc4bb5 Mon Sep 17 00:00:00 2001 From: jheer Date: Thu, 4 Apr 2024 14:51:27 -0700 Subject: [PATCH 17/25] feat: Add Area and Line mark types. --- packages/spec/src/spec/PlotMark.ts | 16 +- packages/spec/src/spec/PlotTypes.ts | 146 ++++++++------- packages/spec/src/spec/marks/Area.ts | 151 +++++++++++++++ packages/spec/src/spec/marks/Line.ts | 90 +++++++++ packages/spec/src/spec/marks/Marks.ts | 258 +++++++++++++++++++++++++- 5 files changed, 591 insertions(+), 70 deletions(-) create mode 100644 packages/spec/src/spec/marks/Area.ts create mode 100644 packages/spec/src/spec/marks/Line.ts diff --git a/packages/spec/src/spec/PlotMark.ts b/packages/spec/src/spec/PlotMark.ts index dfb37182..d9f4b49d 100644 --- a/packages/spec/src/spec/PlotMark.ts +++ b/packages/spec/src/spec/PlotMark.ts @@ -2,15 +2,17 @@ import { Expression } from './Expression.js'; import { ParamRef } from './Param.js'; import { PlotMarkData } from './PlotFrom.js'; import { Transform } from './Transform.js'; +import { Area, AreaX, AreaY } from './marks/Area.js'; import { Circle, Dot, DotX, DotY, Hexagon } from './marks/Dot.js'; +import { Line, LineX, LineY } from './marks/Line.js'; export type MarkType = - | 'area' - | 'areaX' - | 'areaY' - | 'line' - | 'lineX' - | 'lineY' + // | 'area' + // | 'areaX' + // | 'areaY' + // | 'line' + // | 'lineX' + // | 'lineY' | 'barX' | 'barY' | 'cell' @@ -77,7 +79,9 @@ export type MarkOption = | any[]; export type PlotMark = + | Area | AreaX | AreaY | Dot | DotX | DotY | Circle | Hexagon + | Line | LineX | LineY | GenericPlotMark; /** diff --git a/packages/spec/src/spec/PlotTypes.ts b/packages/spec/src/spec/PlotTypes.ts index a5e50473..27342705 100644 --- a/packages/spec/src/spec/PlotTypes.ts +++ b/packages/spec/src/spec/PlotTypes.ts @@ -1,4 +1,3 @@ - /** * A symbol indicating a fixed scale domain. A fixed domain is initially * determined from data as usual, but subsequently "fixed" so that it does not @@ -8,8 +7,8 @@ export type Fixed = 'Fixed'; // For internal use. export type LiteralTimeInterval = - | "3 months" - | "10 years" + | '3 months' + | '10 years' | TimeIntervalName | (`${TimeIntervalName}s` & Record) | (`${number} ${TimeIntervalName}` & Record) @@ -22,22 +21,22 @@ export type LiteralTimeInterval = * start of the year. */ export type TimeIntervalName = - | "second" - | "minute" - | "hour" - | "day" - | "week" - | "month" - | "quarter" // 3 months - | "half" // 6 months - | "year" - | "monday" - | "tuesday" - | "wednesday" - | "thursday" - | "friday" - | "saturday" - | "sunday"; + | 'second' + | 'minute' + | 'hour' + | 'day' + | 'week' + | 'month' + | 'quarter' // 3 months + | 'half' // 6 months + | 'year' + | 'monday' + | 'tuesday' + | 'wednesday' + | 'thursday' + | 'friday' + | 'saturday' + | 'sunday'; /** * How to partition a continuous range into discrete intervals; one of: @@ -409,20 +408,20 @@ export type ColorScheme = ColorSchemeCase | (Lowercase & Record * [1]: https://www.tandfonline.com/doi/abs/10.1080/10618600.2019.1637746 */ export type SymbolType = - | "asterisk" - | "circle" - | "cross" - | "diamond" - | "diamond2" - | "hexagon" - | "plus" - | "square" - | "square2" - | "star" - | "times" - | "triangle" - | "triangle2" - | "wye"; + | 'asterisk' + | 'circle' + | 'cross' + | 'diamond' + | 'diamond2' + | 'hexagon' + | 'plus' + | 'square' + | 'square2' + | 'star' + | 'times' + | 'triangle' + | 'triangle2' + | 'wye'; /** * How to anchor a mark relative to the plot’s frame; one of: @@ -432,15 +431,15 @@ export type SymbolType = * - in one of the corners: *top-left*, *top-right*, *bottom-right*, *bottom-left* */ export type FrameAnchor = - | "middle" - | "top-left" - | "top" - | "top-right" - | "right" - | "bottom-right" - | "bottom" - | "bottom-left" - | "left"; + | 'middle' + | 'top-left' + | 'top' + | 'top-right' + | 'right' + | 'bottom-right' + | 'bottom' + | 'bottom-left' + | 'left'; // from https://github.com/observablehq/plot/blob/main/src/reducer.d.ts type Digit = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9; @@ -448,9 +447,9 @@ type Digit = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9; // For internal use. export type ReducerPercentile = | (`p${Digit}${Digit}` & Record) // see https://github.com/microsoft/TypeScript/issues/29729 - | "p25" - | "p50" - | "p75"; + | 'p25' + | 'p50' + | 'p75'; /** * How to reduce aggregated (binned or grouped) values; one of: @@ -477,21 +476,44 @@ export type ReducerPercentile = * [1]: https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm */ export type Reducer = - | "first" - | "last" - | "identity" - | "count" - | "distinct" - | "sum" - | "proportion" - | "proportion-facet" - | "deviation" - | "min" - | "min-index" - | "max" - | "max-index" - | "mean" - | "median" - | "variance" - | "mode" + | 'first' + | 'last' + | 'identity' + | 'count' + | 'distinct' + | 'sum' + | 'proportion' + | 'proportion-facet' + | 'deviation' + | 'min' + | 'min-index' + | 'max' + | 'max-index' + | 'mean' + | 'median' + | 'variance' + | 'mode' | ReducerPercentile; + +/** The built-in curve implementations. */ +export type CurveName = + | 'basis' + | 'basis-closed' + | 'basis-open' + | 'bundle' + | 'bump-x' + | 'bump-y' + | 'cardinal' + | 'cardinal-closed' + | 'cardinal-open' + | 'catmull-rom' + | 'catmull-rom-closed' + | 'catmull-rom-open' + | 'linear' + | 'linear-closed' + | 'monotone-x' + | 'monotone-y' + | 'natural' + | 'step' + | 'step-after' + | 'step-before'; diff --git a/packages/spec/src/spec/marks/Area.ts b/packages/spec/src/spec/marks/Area.ts new file mode 100644 index 00000000..96a9a7e1 --- /dev/null +++ b/packages/spec/src/spec/marks/Area.ts @@ -0,0 +1,151 @@ +import { + ChannelValue, ChannelValueSpec, CurveOptions, + MarkData, MarkOptions, StackOptions +} from './Marks.js'; + +/** Options for the area, areaX, and areaY marks. */ +export interface AreaOptions extends MarkData, MarkOptions, StackOptions, CurveOptions { + /** + * The required primary (starting, often left) horizontal position channel, + * representing the area’s baseline, typically bound to the *x* scale. For + * areaX, setting this option disables the implicit stackX transform. + */ + x1?: ChannelValueSpec; + + /** + * The optional secondary (ending, often right) horizontal position channel, + * representing the area’s topline, typically bound to the *x* scale; if not + * specified, **x1** is used. For areaX, setting this option disables the + * implicit stackX transform. + */ + x2?: ChannelValueSpec; + + /** + * The required primary (starting, often bottom) vertical position channel, + * representing the area’s baseline, typically bound to the *y* scale. For + * areaY, setting this option disables the implicit stackY transform. + */ + y1?: ChannelValueSpec; + + /** + * The optional secondary (ending, often top) vertical position channel, + * representing the area’s topline, typically bound to the *y* scale; if not + * specified, **y1** is used. For areaY, setting this option disables the + * implicit stackY transform. + */ + y2?: ChannelValueSpec; + + /** + * An optional ordinal channel for grouping data into (possibly stacked) + * series to be drawn as separate areas; defaults to **fill** if a channel, or + * **stroke** if a channel. + */ + z?: ChannelValue; +} + +/** Options for the areaX mark. */ +export interface AreaXOptions extends Omit { + /** + * The horizontal position (or length) channel, typically bound to the *x* + * scale. + * + * If neither **x1** nor **x2** is specified, an implicit stackX transform is + * applied and **x** defaults to the identity function, assuming that *data* = + * [*x₀*, *x₁*, *x₂*, …]. Otherwise, if only one of **x1** or **x2** is + * specified, the other defaults to **x**, which defaults to zero. + */ + x?: ChannelValueSpec; + + /** + * The vertical position channel, typically bound to the *y* scale; defaults + * to the zero-based index of the data [0, 1, 2, …]. + */ + y?: ChannelValueSpec; +} + +/** Options for the areaY mark. */ +export interface AreaYOptions extends Omit { + /** + * The horizontal position channel, typically bound to the *x* scale; defaults + * to the zero-based index of the data [0, 1, 2, …]. + */ + x?: ChannelValueSpec; + + /** + * The vertical position (or length) channel, typically bound to the *y* + * scale. + * + * If neither **y1** nor **y2** is specified, an implicit stackY transform is + * applied and **y** defaults to the identity function, assuming that *data* = + * [*y₀*, *y₁*, *y₂*, …]. Otherwise, if only one of **y1** or **y2** is + * specified, the other defaults to **y**, which defaults to zero. + */ + y?: ChannelValueSpec; +} + +export interface Area extends AreaOptions { + /** + * An area mark. The area mark is rarely used directly; it is only needed + * when the baseline and topline have neither *x* nor *y* values in common. + * Use areaY for a horizontal orientation where the baseline and topline + * share *x* values, or areaX for a vertical orientation where the baseline + * and topline share *y* values. + */ + mark: 'area'; +} + +export interface AreaX extends AreaXOptions { + /** + * A vertically-oriented area mark, where the baseline and topline share + * **y** values, as in a time-series area chart where time goes up↑. + * + * If neither **x1** nor **x2** is specified, an implicit stackX transform is + * applied and **x** defaults to the identity function, assuming that *data* = + * [*x₀*, *x₁*, *x₂*, …]. Otherwise, if only one of **x1** or **x2** is + * specified, the other defaults to **x**, which defaults to zero. + * + * If an **interval** is specified, **y** values are binned accordingly, + * allowing zeroes for empty bins instead of interpolating across gaps. This is + * recommended to “regularize” sampled data; for example, if your data + * represents timestamped observations and you expect one observation per day, + * use *day* as the **interval**. + * + * Variable aesthetic channels are supported: if the **fill** is defined as a + * channel, the area will be broken into contiguous overlapping sections when + * the fill color changes; the fill color will apply to the interval spanning + * the current data point and the following data point. This behavior also + * applies to the **fillOpacity**, **stroke**, **strokeOpacity**, + * **strokeWidth**, **opacity**, **href**, **title**, and **ariaLabel** + * channels. When any of these channels are used, setting an explicit **z** + * channel (possibly to null) is strongly recommended. + */ + mark: 'areaX'; +} + +export interface AreaY extends AreaYOptions { + /** + * A horizontally-oriented area mark, where the baseline and topline share + * **x** values, as in a time-series area chart where time goes right→. + * + * If neither **y1** nor **y2** is specified, an implicit stackY transform is + * applied and **y** defaults to the identity function, assuming that *data* = + * [*y₀*, *y₁*, *y₂*, …]. Otherwise, if only one of **y1** or **y2** is + * specified, the other defaults to **y**, which defaults to zero. + * + * If an **interval** is specified, **x** values are binned accordingly, + * allowing zeroes for empty bins instead of interpolating across gaps. This is + * recommended to “regularize” sampled data; for example, if your data + * represents timestamped observations and you expect one observation per day, + * use *day* as the **interval**. + * + * Variable aesthetic channels are supported: if the **fill** is defined as a + * channel, the area will be broken into contiguous overlapping sections when + * the fill color changes; the fill color will apply to the interval spanning + * the current data point and the following data point. This behavior also + * applies to the **fillOpacity**, **stroke**, **strokeOpacity**, + * **strokeWidth**, **opacity**, **href**, **title**, and **ariaLabel** + * channels. When any of these channels are used, setting an explicit **z** + * channel (possibly to null) is strongly recommended. + */ + mark: 'areaY'; +} diff --git a/packages/spec/src/spec/marks/Line.ts b/packages/spec/src/spec/marks/Line.ts new file mode 100644 index 00000000..2f90a79c --- /dev/null +++ b/packages/spec/src/spec/marks/Line.ts @@ -0,0 +1,90 @@ +import { + ChannelValue, ChannelValueSpec, CurveAutoOptions, + MarkData, MarkOptions, MarkerOptions +} from './Marks.js'; + +/** Options for the line mark. */ +export interface LineOptions extends MarkData, MarkOptions, MarkerOptions, CurveAutoOptions { + /** + * The required horizontal position channel, typically bound to the *x* + * scale. + */ + x?: ChannelValueSpec; + + /** + * The required vertical position channel, typically bound to the *y* scale. + */ + y?: ChannelValueSpec; + + /** + * An optional ordinal channel for grouping data into (possibly stacked) + * series to be drawn as separate lines. If not specified, it defaults to + * **fill** if a channel, or **stroke** if a channel. + */ + z?: ChannelValue; +} + +/** Options for the lineX mark. */ +export interface LineXOptions extends LineOptions { + /** + * The vertical position channel, typically bound to the *y* scale; + * defaults to the zero-based index of the data [0, 1, 2, …]. + */ + y?: ChannelValueSpec; +} + +/** Options for the lineY mark. */ +export interface LineYOptions extends LineOptions { + /** + * The horizontal position channel, typically bound to the *x* scale; + * defaults to the zero-based index of the data [0, 1, 2, …]. + */ + x?: ChannelValueSpec; +} + +export interface Line extends LineOptions { + /** + * A line mark that connects control points. + * + * Points along the line are connected in input order. If there are multiple + * series via the **z**, **fill**, or **stroke** channel, series are drawn in + * input order such that the last series is drawn on top. Typically *data* is + * already in sorted order, such as chronological for time series; if needed, + * consider a **sort** transform. + * + * If any **x** or **y** values are invalid (undefined, null, or NaN), the + * line will be interrupted, resulting in a break that divides the line shape + * into multiple segments. If a line segment consists of only a single point, + * it may appear invisible unless rendered with rounded or square line caps. + * In addition, some curves such as *cardinal-open* only render a visible + * segment if it contains multiple points. + * + * Variable aesthetic channels are supported: if the **stroke** is defined as + * a channel, the line will be broken into contiguous overlapping segments + * when the stroke color changes; the stroke color will apply to the interval + * spanning the current data point and the following data point. This + * behavior also applies to the **fill**, **fillOpacity**, **strokeOpacity**, + * **strokeWidth**, **opacity**, **href**, **title**, and **ariaLabel** + * channels. When any of these channels are used, setting an explicit **z** + * channel (possibly to null) is strongly recommended. + */ + mark: 'line'; +} + +export interface LineX extends LineXOptions { + /** + * Like line, except that **x** defaults to the identity function assuming + * that *data* = [*x₀*, *x₁*, *x₂*, …] and **y** defaults to the zero-based + * index [0, 1, 2, …]. + */ + mark: 'lineX' +} + +export interface LineY extends LineYOptions { + /** + * Like line, except **y** defaults to the identity function and assumes + * that *data* = [*y₀*, *y₁*, *y₂*, …] and **x** defaults to the zero-based + * index [0, 1, 2, …]. + */ + mark: 'lineY' +} diff --git a/packages/spec/src/spec/marks/Marks.ts b/packages/spec/src/spec/marks/Marks.ts index 6eff486e..176152b4 100644 --- a/packages/spec/src/spec/marks/Marks.ts +++ b/packages/spec/src/spec/marks/Marks.ts @@ -1,7 +1,7 @@ import { AggregateExpression, SQLExpression } from '../Expression.js'; import { ParamRef } from '../Param.js'; import { PlotMarkData } from '../PlotFrom.js'; -import { FrameAnchor, Interval, Reducer, ScaleName } from '../PlotTypes.js'; +import { CurveName, FrameAnchor, Interval, Reducer, ScaleName } from '../PlotTypes.js'; import { Transform } from '../Transform.js'; /** @@ -41,7 +41,7 @@ export type ChannelName = | 'z' | (string & Record); // custom channel; see also https://github.com/microsoft/TypeScript/issues/29729 -type ChannelScale = ScaleName | "auto" | boolean | null; +type ChannelScale = ScaleName | 'auto' | boolean | null; /** * A channel’s values may be expressed as: @@ -770,3 +770,257 @@ export interface TipOptions extends MarkOptions, TextStyles { /** The padding around the text in pixels; defaults to 8. */ textPadding?: number | ParamRef; } + +/** How to interpolate between control points. */ +export type Curve = CurveName; + +/** Options for marks that support curves, such as lines and areas. */ +export interface CurveOptions extends CurveAutoOptions { + /** + * The curve (interpolation) method for connecting adjacent points. One of: + * + * - *basis* - a cubic basis spline (repeating the end points) + * - *basis-open* - an open cubic basis spline + * - *basis-closed* - a closed cubic basis spline + * - *bump-x* - a Bézier curve with horizontal tangents + * - *bump-y* - a Bézier curve with vertical tangents + * - *bundle* - a straightened cubic basis spline (suitable for lines only, not areas) + * - *cardinal* - a cubic cardinal spline (with one-sided differences at the ends) + * - *cardinal-open* - an open cubic cardinal spline + * - *cardinal-closed* - an closed cubic cardinal spline + * - *catmull-rom* - a cubic Catmull–Rom spline (with one-sided differences at the ends) + * - *catmull-rom-open* - an open cubic Catmull–Rom spline + * - *catmull-rom-closed* - a closed cubic Catmull–Rom spline + * - *linear* - a piecewise linear curve (*i.e.*, straight line segments) + * - *linear-closed* - a closed piecewise linear curve (*i.e.*, straight line segments) + * - *monotone-x* - a cubic spline that preserves monotonicity in *x* + * - *monotone-y* - a cubic spline that preserves monotonicity in *y* + * - *natural* - a natural cubic spline + * - *step* - a piecewise constant function where *y* changes at the midpoint of *x* + * - *step-after* - a piecewise constant function where *y* changes after *x* + * - *step-before* - a piecewise constant function where *x* changes after *y* + */ + curve?: Curve | ParamRef; +} + +/** Options for marks that support possibly-projected curves. */ +export interface CurveAutoOptions { + /** + * The curve (interpolation) method for connecting adjacent points. One of: + * + * - *basis* - a cubic basis spline (repeating the end points) + * - *basis-open* - an open cubic basis spline + * - *basis-closed* - a closed cubic basis spline + * - *bump-x* - a Bézier curve with horizontal tangents + * - *bump-y* - a Bézier curve with vertical tangents + * - *bundle* - a straightened cubic basis spline (suitable for lines only, not areas) + * - *cardinal* - a cubic cardinal spline (with one-sided differences at the ends) + * - *cardinal-open* - an open cubic cardinal spline + * - *cardinal-closed* - an closed cubic cardinal spline + * - *catmull-rom* - a cubic Catmull–Rom spline (with one-sided differences at the ends) + * - *catmull-rom-open* - an open cubic Catmull–Rom spline + * - *catmull-rom-closed* - a closed cubic Catmull–Rom spline + * - *linear* - a piecewise linear curve (*i.e.*, straight line segments) + * - *linear-closed* - a closed piecewise linear curve (*i.e.*, straight line segments) + * - *monotone-x* - a cubic spline that preserves monotonicity in *x* + * - *monotone-y* - a cubic spline that preserves monotonicity in *y* + * - *natural* - a natural cubic spline + * - *step* - a piecewise constant function where *y* changes at the midpoint of *x* + * - *step-after* - a piecewise constant function where *y* changes after *x* + * - *step-before* - a piecewise constant function where *x* changes after *y* + * - *auto* (default) - like *linear*, but use the (possibly spherical) projection, if any + * + * The *auto* curve is typically used in conjunction with a spherical + * projection to interpolate along geodesics. + */ + curve?: Curve | 'auto' | ParamRef; + + /** + * The tension option only has an effect on bundle, cardinal and Catmull–Rom + * splines (*bundle*, *cardinal*, *cardinal-open*, *cardinal-closed*, + * *catmull-rom*, *catmull-rom-open*, and *catmull-rom-closed*). For bundle + * splines, it corresponds to [beta][1]; for cardinal splines, [tension][2]; + * for Catmull–Rom splines, [alpha][3]. + * + * [1]: https://d3js.org/d3-shape/curve#curveBundle_beta + * [2]: https://d3js.org/d3-shape/curve#curveCardinal_tension + * [3]: https://d3js.org/d3-shape/curve#curveCatmullRom_alpha + */ + tension?: number | ParamRef; +} + +/** + * A built-in stack offset method; one of: + * + * - *normalize* - rescale each stack to fill [0, 1] + * - *center* - align the centers of all stacks + * - *wiggle* - translate stacks to minimize apparent movement + * + * If a given stack has zero total value, the *normalize* offset will not adjust + * the stack’s position. Both the *center* and *wiggle* offsets ensure that the + * lowest element across stacks starts at zero for better default axes. The + * *wiggle* offset is recommended for streamgraphs in conjunction with the + * *inside-out* order. For more, see [Byron & Wattenberg][1]. + * + * [1]: https://leebyron.com/streamgraph/ + */ +export type StackOffsetName = + | 'center' + | 'normalize' + | 'wiggle' + | ('expand' & Record) // deprecated; use normalize + | ('silhouette' & Record); // deprecated; use center + +/** + * A stack offset method; one of: + * + * - *normalize* - rescale each stack to fill [0, 1] + * - *center* - align the centers of all stacks + * - *wiggle* - translate stacks to minimize apparent movement + * + * If a given stack has zero total value, the *normalize* offset will not adjust + * the stack’s position. Both the *center* and *wiggle* offsets ensure that the + * lowest element across stacks starts at zero for better default axes. The + * *wiggle* offset is recommended for streamgraphs in conjunction with the + * *inside-out* order. For more, see [Byron & Wattenberg][1]. + * + * [1]: https://leebyron.com/streamgraph/ + */ +export type StackOffset = StackOffsetName; + +/** + * The built-in stack order methods; one of: + * + * - *x* - alias of *value*; for stackX only + * - *y* - alias of *value*; for stackY only + * - *value* - ascending value (or descending with **reverse**) + * - *sum* - total value per series + * - *appearance* - position of maximum value per series + * - *inside-out* (default with *wiggle*) - order the earliest-appearing series on the inside + * + * The *inside-out* order is recommended for streamgraphs in conjunction with + * the *wiggle* offset. For more, see [Byron & Wattenberg][1]. + * + * [1]: https://leebyron.com/streamgraph/ + */ +export type StackOrderName = 'value' | 'x' | 'y' | 'z' | 'sum' | 'appearance' | 'inside-out'; + +/** + * How to order layers prior to stacking; one of: + * + * - a named stack order method such as *inside-out* or *sum* + * - a field name, for natural order of the corresponding values + * - an accessor function, for natural order of the corresponding values + * - a comparator function for ordering data + * - an array of explicit **z** values in the desired order + */ +export type StackOrder = + | StackOrderName + | `-${StackOrderName}` + | (string & Record) + | any[]; + +/** Options for the stack transform. */ +export interface StackOptions { + /** + * After stacking, an optional **offset** can be applied to translate and + * scale stacks, say to produce a streamgraph; defaults to null for a zero + * baseline (**y** = 0 for stackY, and **x** = 0 for stackX). If the *wiggle* + * offset is used, the default **order** changes to *inside-out*. + */ + offset?: StackOffset | null | ParamRef; + + /** + * The order in which stacks are layered; one of: + * + * - null (default) for input order + * - a named stack order method such as *inside-out* or *sum* + * - a field name, for natural order of the corresponding values + * - a function of data, for natural order of the corresponding values + * - an array of explicit **z** values in the desired order + * + * If the *wiggle* **offset** is used, as for a streamgraph, the default + * changes to *inside-out*. + */ + order?: StackOrder | null | ParamRef; + + /** If true, reverse the effective order of the stacks. */ + reverse?: boolean | ParamRef; + + /** + * The **z** channel defines the series of each value in the stack. Used when + * the **order** is *sum*, *appearance*, *inside-out*, or an explicit array of + * **z** values. + */ + z?: ChannelValue; +} + +/** + * The built-in marker implementations; one of: + * + * - *arrow* - an arrowhead with *auto* orientation + * - *arrow-reverse* - an arrowhead with *auto-start-reverse* orientation + * - *dot* - a filled *circle* with no stroke and 2.5px radius + * - *circle-fill* - a filled circle with a white stroke and 3px radius + * - *circle-stroke* - a stroked circle with a white fill and 3px radius + * - *circle* - alias for *circle-fill* + * - *tick* - a small opposing line + * - *tick-x* - a small horizontal line + * - *tick-y* - a small vertical line + */ +export type MarkerName = + | 'arrow' + | 'arrow-reverse' + | 'dot' + | 'circle' + | 'circle-fill' + | 'circle-stroke' + | 'tick' + | 'tick-x' + | 'tick-y'; + +/** Options for marks that support markers, such as lines and links. */ +export interface MarkerOptions { + /** + * Shorthand to set the same default for markerStart, markerMid, and + * markerEnd; one of: + * + * - a marker name such as *arrow* or *circle* + * - *none* (default) - no marker + * * true - alias for *circle-fill* + * * false or null - alias for *none* + */ + marker?: MarkerName | 'none' | boolean | null | ParamRef; + + /** + * The marker for the starting point of a line segment; one of: + * + * - a marker name such as *arrow* or *circle* + * * *none* (default) - no marker + * * true - alias for *circle-fill* + * * false or null - alias for *none* + */ + markerStart?: MarkerName | 'none' | boolean | null | ParamRef; + + /** + * The marker for any middle (interior) points of a line segment. If the line + * segment only has a start and end point, this option has no effect. One of: + * + * - a marker name such as *arrow* or *circle* + * * *none* (default) - no marker + * * true - alias for *circle-fill* + * * false or null - alias for *none* + * * a function - a custom marker function; see below + */ + markerMid?: MarkerName | 'none' | boolean | null | ParamRef; + + /** + * The marker for the ending point of a line segment; one of: + * + * - a marker name such as *arrow* or *circle* + * * *none* (default) - no marker + * * true - alias for *circle-fill* + * * false or null - alias for *none* + */ + markerEnd?: MarkerName | 'none' | boolean | null | ParamRef; +} From decc11229c6d916a6f991760ae4dbe3891594bad Mon Sep 17 00:00:00 2001 From: jheer Date: Fri, 5 Apr 2024 09:02:04 -0700 Subject: [PATCH 18/25] feat: Add more mark typings. --- docs/public/specs/esm/axes.js | 2 +- docs/public/specs/esm/seattle-temp.js | 2 +- docs/public/specs/json/axes.json | 5 +- docs/public/specs/json/seattle-temp.json | 5 +- docs/public/specs/yaml/axes.yaml | 2 +- docs/public/specs/yaml/seattle-temp.yaml | 2 +- packages/spec/src/spec/PlotMark.ts | 51 ++-- packages/spec/src/spec/marks/Axis.ts | 296 +++++++++++++++++++++++ packages/spec/src/spec/marks/Bar.ts | 160 ++++++++++++ packages/spec/src/spec/marks/Cell.ts | 59 +++++ packages/spec/src/spec/marks/Frame.ts | 22 ++ packages/spec/src/spec/marks/Marks.ts | 46 ++++ packages/spec/src/spec/marks/Rect.ts | 180 ++++++++++++++ packages/spec/src/spec/marks/Rule.ts | 108 +++++++++ packages/spec/src/spec/marks/Text.ts | 119 +++++++++ packages/spec/src/spec/marks/Tick.ts | 65 +++++ specs/esm/axes.js | 2 +- specs/esm/seattle-temp.js | 2 +- specs/json/axes.json | 5 +- specs/json/seattle-temp.json | 5 +- specs/ts/axes.ts | 5 +- specs/ts/seattle-temp.ts | 5 +- specs/yaml/axes.yaml | 2 +- specs/yaml/seattle-temp.yaml | 2 +- 24 files changed, 1085 insertions(+), 67 deletions(-) create mode 100644 packages/spec/src/spec/marks/Axis.ts create mode 100644 packages/spec/src/spec/marks/Bar.ts create mode 100644 packages/spec/src/spec/marks/Cell.ts create mode 100644 packages/spec/src/spec/marks/Frame.ts create mode 100644 packages/spec/src/spec/marks/Rect.ts create mode 100644 packages/spec/src/spec/marks/Rule.ts create mode 100644 packages/spec/src/spec/marks/Text.ts create mode 100644 packages/spec/src/spec/marks/Tick.ts diff --git a/docs/public/specs/esm/axes.js b/docs/public/specs/esm/axes.js index b2cee2bd..f268f001 100644 --- a/docs/public/specs/esm/axes.js +++ b/docs/public/specs/esm/axes.js @@ -1,7 +1,7 @@ import * as vg from "@uwdata/vgplot"; export default vg.plot( - vg.gridY({strokeDasharray: [0.75, 2], strokeOpacity: 1}), + vg.gridY({strokeDasharray: "0.75 2", strokeOpacity: 1}), vg.axisY({anchor: "left", tickSize: 0, dx: 38, dy: -4, lineAnchor: "bottom"}), vg.axisY({ anchor: "right", diff --git a/docs/public/specs/esm/seattle-temp.js b/docs/public/specs/esm/seattle-temp.js index f7bc2e82..3aade9b7 100644 --- a/docs/public/specs/esm/seattle-temp.js +++ b/docs/public/specs/esm/seattle-temp.js @@ -29,7 +29,7 @@ export default vg.plot( ), vg.ruleY( [15], - {strokeOpacity: 0.5, strokeDasharray: [5, 5]} + {strokeOpacity: 0.5, strokeDasharray: "5 5"} ), vg.xTickFormat("%b"), vg.yLabel("Temperature Range (°C)"), diff --git a/docs/public/specs/json/axes.json b/docs/public/specs/json/axes.json index 8dd85d9d..9078bba6 100644 --- a/docs/public/specs/json/axes.json +++ b/docs/public/specs/json/axes.json @@ -6,10 +6,7 @@ "plot": [ { "mark": "gridY", - "strokeDasharray": [ - 0.75, - 2 - ], + "strokeDasharray": "0.75 2", "strokeOpacity": 1 }, { diff --git a/docs/public/specs/json/seattle-temp.json b/docs/public/specs/json/seattle-temp.json index 2f6aac79..d90c9c6d 100644 --- a/docs/public/specs/json/seattle-temp.json +++ b/docs/public/specs/json/seattle-temp.json @@ -51,10 +51,7 @@ 15 ], "strokeOpacity": 0.5, - "strokeDasharray": [ - 5, - 5 - ] + "strokeDasharray": "5 5" } ], "xTickFormat": "%b", diff --git a/docs/public/specs/yaml/axes.yaml b/docs/public/specs/yaml/axes.yaml index 3514c2fa..9cdd0a08 100644 --- a/docs/public/specs/yaml/axes.yaml +++ b/docs/public/specs/yaml/axes.yaml @@ -5,7 +5,7 @@ meta: scale attributes such as `xAxis`, `yGrid`, etc. Just add data! plot: - mark: gridY - strokeDasharray: [0.75, 2] # dashed + strokeDasharray: 0.75 2 # dashed strokeOpacity: 1 # opaque - mark: axisY anchor: left diff --git a/docs/public/specs/yaml/seattle-temp.yaml b/docs/public/specs/yaml/seattle-temp.yaml index d9d013ad..43658195 100644 --- a/docs/public/specs/yaml/seattle-temp.yaml +++ b/docs/public/specs/yaml/seattle-temp.yaml @@ -26,7 +26,7 @@ plot: - mark: ruleY data: [15] strokeOpacity: 0.5 - strokeDasharray: [5, 5] + strokeDasharray: 5 5 xTickFormat: '%b' yLabel: Temperature Range (°C) width: 680 diff --git a/packages/spec/src/spec/PlotMark.ts b/packages/spec/src/spec/PlotMark.ts index d9f4b49d..37ee8cf3 100644 --- a/packages/spec/src/spec/PlotMark.ts +++ b/packages/spec/src/spec/PlotMark.ts @@ -3,36 +3,18 @@ import { ParamRef } from './Param.js'; import { PlotMarkData } from './PlotFrom.js'; import { Transform } from './Transform.js'; import { Area, AreaX, AreaY } from './marks/Area.js'; +import { AxisFx, AxisFy, AxisX, AxisY, GridFx, GridFy, GridX, GridY } from './marks/Axis.js'; +import { BarX, BarY } from './marks/Bar.js'; +import { Cell, CellX, CellY } from './marks/Cell.js'; import { Circle, Dot, DotX, DotY, Hexagon } from './marks/Dot.js'; +import { Frame } from './marks/Frame.js'; import { Line, LineX, LineY } from './marks/Line.js'; +import { Rect, RectX, RectY } from './marks/Rect.js'; +import { RuleX, RuleY } from './marks/Rule.js'; +import { Text,TextX, TextY } from './marks/Text.js'; +import { TickX, TickY } from './marks/Tick.js'; export type MarkType = - // | 'area' - // | 'areaX' - // | 'areaY' - // | 'line' - // | 'lineX' - // | 'lineY' - | 'barX' - | 'barY' - | 'cell' - | 'cellX' - | 'cellY' - | 'rect' - | 'rectX' - | 'rectY' - // | 'dot' - // | 'dotX' - // | 'dotY' - // | 'circle' - // | 'hexagon' - | 'text' - | 'textX' - | 'textY' - | 'ruleX' - | 'ruleY' - | 'tickX' - | 'tickY' | 'vector' | 'vectorX' | 'vectorY' @@ -56,15 +38,6 @@ export type MarkType = | 'hull' | 'arrow' | 'link' - | 'frame' - | 'axisX' - | 'axisY' - | 'axisFx' - | 'axisFy' - | 'gridX' - | 'gridY' - | 'gridFx' - | 'gridFy' | 'geo' | 'sphere' | 'graticule'; @@ -80,8 +53,16 @@ export type MarkOption = export type PlotMark = | Area | AreaX | AreaY + | AxisX | AxisY | AxisFx | AxisFy | GridX | GridY | GridFx | GridFy + | BarX | BarY + | Cell | CellX | CellY | Dot | DotX | DotY | Circle | Hexagon + | Frame | Line | LineX | LineY + | Rect | RectX | RectY + | RuleX | RuleY + | Text | TextX | TextY + | TickX | TickY | GenericPlotMark; /** diff --git a/packages/spec/src/spec/marks/Axis.ts b/packages/spec/src/spec/marks/Axis.ts new file mode 100644 index 00000000..0188e73d --- /dev/null +++ b/packages/spec/src/spec/marks/Axis.ts @@ -0,0 +1,296 @@ +import { ParamRef } from '../Param.js'; +import { Interval } from '../PlotTypes.js'; +import { MarkOptions } from './Marks.js'; +import { RuleXOptions, RuleYOptions } from './Rule.js'; +import { TextOptions } from './Text.js'; +import { TickXOptions, TickYOptions } from './Tick.js'; + +interface ScaleOptions { + /** + * Enforces uniformity for data at regular intervals, such as integer values + * or daily samples. The interval may be one of: + * + * - a named time interval such as *day* (for date intervals) + * - a number (for number intervals), defining intervals at integer multiples of *n* + * + * This option sets the internal transform to the given interval’s + * *interval*.floor function. In addition, the default **domain** will align + * with interval boundaries. + */ + interval?: Interval | ParamRef; + + /** + * The desired approximate number of axis ticks, or an explicit array of tick + * values, or an interval such as *day* or *month*. + */ + ticks?: number | Interval | any[] | ParamRef; + + /** + * The length of axis tick marks in pixels; negative values extend in the + * opposite direction. Defaults to 6 for *x* and *y* axes and *color* and + * *opacity* *ramp* legends, and 0 for *fx* and *fy* axes. + */ + tickSize?: number | ParamRef; + + /** + * The desired approximate spacing between adjacent axis ticks, affecting the + * default **ticks**; defaults to 80 pixels for *x* and *fx*, and 35 pixels + * for *y* and *fy*. + */ + tickSpacing?: number | ParamRef; + + /** + * The distance between an axis tick mark and its associated text label (in + * pixels); often defaults to 3, but may be affected by **xTickSize** and + * **xTickRotate**. + */ + tickPadding?: number | ParamRef; + + /** + * How to format inputs (abstract values) for axis tick labels; one of: + * + * - a [d3-format][1] string for numeric scales + * - a [d3-time-format][2] string for temporal scales + * + * [1]: https://d3js.org/d3-time + * [2]: https://d3js.org/d3-time-format + */ + tickFormat?: string | null | ParamRef; + + /** + * The rotation angle of axis tick labels in degrees clocksize; defaults to 0. + */ + tickRotate?: number | ParamRef; + + /** + * A textual label to show on the axis or legend; if null, show no label. By + * default the scale label is inferred from channel definitions, possibly with + * an arrow (↑, →, ↓, or ←) to indicate the direction of increasing value. + * + * For axes and legends only. + */ + label?: string | null | ParamRef; + + /** + * Where to place the axis **label** relative to the plot’s frame. For + * vertical position scales (*y* and *fy*), may be *top*, *bottom*, or + * *center*; for horizontal position scales (*x* and *fx*), may be *left*, + * *right*, or *center*. Defaults to *center* for ordinal scales (including + * *fx* and *fy*), and otherwise *top* for *y*, and *right* for *x*. + */ + labelAnchor?: 'top' | 'right' | 'bottom' | 'left' | 'center' | ParamRef; + + /** + * The axis **label** position offset (in pixels); default depends on margins + * and orientation. + */ + labelOffset?: number | ParamRef; + + /** + * Whether to apply a directional arrow such as → or ↑ to the scale label. If + * *auto* (the default), the presence of the arrow depends on whether the + * scale is ordinal. + */ + labelArrow?: 'auto' | 'up' | 'right' | 'down' | 'left' | 'none' | true | false | null | ParamRef; +} + +/** The subset of scale options for grids. */ +type GridScaleOptions = Pick; + +/** The subset of scale options for axes. */ +type AxisScaleOptions = Pick; + +/** Options for the grid marks. */ +export interface GridOptions extends GridScaleOptions { + /** + * The side of the frame on which to place the axis: *top* or *bottom* for + * horizontal axes (axisX and axisFx) and their associated vertical grids + * (gridX and gridFx), or *left* or *right* for vertical axes (axisY and + * axisFY) and their associated horizontal grids (gridY and gridFy). + * + * The default **anchor** depends on the associated scale: + * + * - *x* - *bottom* + * - *y* - *left* + * - *fx* - *top* if there is a *bottom* *x* axis, and otherwise *bottom* + * - *fy* - *right* if there is a *left* *y* axis, and otherwise *right* + * + * For grids, the **anchor** also affects the extent of grid lines when the + * opposite dimension is specified (**x** for gridY and **y** for gridX). + */ + anchor?: 'top' | 'right' | 'bottom' | 'left' | ParamRef; + + /** + * A shorthand for setting both **fill** and **stroke**; affects the stroke of + * tick vectors and grid rules, and the fill of tick texts and axis label + * texts; defaults to *currentColor*. + */ + color?: MarkOptions['stroke']; + + /** + * A shorthand for setting both **fillOpacity** and **strokeOpacity**; affects + * the stroke opacity of tick vectors and grid rules, and the fill opacity of + * tick texts and axis label texts; defaults to 1 for axes and 0.1 for grids. + */ + opacity?: MarkOptions['opacity']; +} + +/** Options for the axis marks. */ +export interface AxisOptions extends GridOptions, MarkOptions, TextOptions, AxisScaleOptions { + /** The tick text **stroke**, say for a *white* outline to improve legibility; defaults to null. */ + textStroke?: MarkOptions['stroke']; + /** The tick text **strokeOpacity**; defaults to 1; has no effect unless **textStroke** is set. */ + textStrokeOpacity?: MarkOptions['strokeOpacity']; + /** The tick text **strokeWidth**; defaults to 4; has no effect unless **textStroke** is set. */ + textStrokeWidth?: MarkOptions['strokeWidth']; +} + +/** Options for the axisX and axisFx marks. */ +export interface AxisXOptions extends AxisOptions, Omit {} + +/** Options for the axisY and axisFy marks. */ +export interface AxisYOptions extends AxisOptions, Omit {} + +/** Options for the gridX and gridFx marks. */ +export interface GridXOptions extends GridOptions, Omit {} + +/** Options for the gridY and gridFy marks. */ +export interface GridYOptions extends GridOptions, Omit {} + +export interface AxisX extends AxisXOptions { + /** + * An axis mark to document the visual encoding of the horizontal position + * *x* scale, comprised of (up to) three marks: a vector for ticks, a text + * for tick labels, and another text for an axis label. The data defaults to + * tick values sampled from the *x* scale’s domain; if desired, use one of + * the **ticks**, **tickSpacing**, or **interval** options. + * + * The **facetAnchor** option defaults to *bottom-empty* if **anchor** is + * *bottom*, and *top-empty* if **anchor** is *top*. The default margins + * likewise depend on **anchor** as follows; in order of **marginTop**, + * **marginRight**, **marginBottom**, and **marginLeft**, in pixels: + * + * - *top* - 30, 20, 0, 20 + * - *bottom* - 0, 20, 30, 20 + * + * For simplicity, and for consistent layout across plots, default axis margins + * are not affected by tick labels. If tick labels are too long, either increase + * the margin or shorten the labels: use the *k* SI-prefix tick format; use the + * **transform** *y*-scale option to show thousands or millions; or use the + * **textOverflow** and **lineWidth** options to clip. + */ + mark: 'axisX'; +} + +export interface AxisFx extends AxisXOptions { + /** + * An axis mark to document the visual encoding of the horizontal facet + * position *fx* scale, comprised of (up to) three marks: a vector for ticks, + * a text for tick labels, and another text for an axis label. The data + * defaults to the *fx* scale’s domain; if desired, use one of the **ticks**, + * **tickSpacing**, or **interval** options. + * + * The **facetAnchor** and **frameAnchor** options defaults to **anchor**. The + * default margins likewise depend on **anchor** as follows; in order of + * **marginTop**, **marginRight**, **marginBottom**, and **marginLeft**, in + * pixels: + * + * - *top* - 30, 20, 0, 20 + * - *bottom* - 0, 20, 30, 20 + * + * For simplicity, and for consistent layout across plots, default axis margins + * are not affected by tick labels. If tick labels are too long, either increase + * the margin or shorten the labels: use the *k* SI-prefix tick format; use the + * **transform** *y*-scale option to show thousands or millions; or use the + * **textOverflow** and **lineWidth** options to clip. + */ + mark: 'axisFx'; +} + +export interface AxisY extends AxisYOptions { + /** + * An axis mark to document the visual encoding of the vertical position *y* + * scale, comprised of (up to) three marks: a vector for ticks, a text for + * tick labels, and another text for an axis label. The data defaults to tick + * values sampled from the *y* scale’s domain; if desired, use one of the + * **ticks**, **tickSpacing**, or **interval** options. + * + * The **facetAnchor** option defaults to *right-empty* if **anchor** is + * *right*, and *left-empty* if **anchor** is *left*. The default margins + * likewise depend on **anchor** as follows; in order of **marginTop**, + * **marginRight**, **marginBottom**, and **marginLeft**, in pixels: + * + * - *right* - 20, 40, 20, 0 + * - *left* - 20, 0, 20, 40 + * + * For simplicity, and for consistent layout across plots, default axis + * margins are not affected by tick labels. If tick labels are too long, + * either increase the margin or shorten the labels: use the *k* SI-prefix + * tick format; or use the **textOverflow** and **lineWidth** options to + * clip. + */ + mark: 'axisY'; +} + +export interface AxisFy extends AxisYOptions { + /** + * An axis mark to document the visual encoding of the vertical facet + * position *fy* scale, comprised of (up to) three marks: a vector for ticks, + * a text for tick labels, and another text for an axis label. The data + * defaults to the *fy* scale’s domain; if desired, use one of the **ticks**, + * **tickSpacing**, or **interval** options. + * + * The **facetAnchor** option defaults to *right-empty* if **anchor** is + * *right*, and *left-empty* if **anchor** is *left*. The default margins + * likewise depend on **anchor** as follows; in order of **marginTop**, + * **marginRight**, **marginBottom**, and **marginLeft**, in pixels: + * + * - *right* - 20, 40, 20, 0 + * - *left* - 20, 0, 20, 40 + * + * For simplicity, and for consistent layout across plots, default axis + * margins are not affected by tick labels. If tick labels are too long, + * either increase the margin or shorten the labels: use the *k* SI-prefix + * tick format; or use the **textOverflow** and **lineWidth** options to + * clip. + */ + mark: 'axisFy'; +} + +export interface GridX extends GridXOptions { + /** + * A horizontally-positioned ruleX mark (a vertical line, |) that renders a + * grid for the *x* scale. The data defaults to tick values sampled from the + * *x* scale’s domain; if desired, use one of the **ticks**, **tickSpacing**, + * or **interval** options. + */ + mark: 'gridX'; +} + +export interface GridFx extends GridXOptions { + /** + * A horizontally-positioned ruleX mark (a vertical line, |) that renders a + * grid for the *fx* scale. The data defaults to the *fx* scale’s domain; + * if desired, use the **ticks** option. + */ + mark: 'gridFx'; +} + +export interface GridY extends GridYOptions { + /** + * A vertically-positioned ruleY mark (a horizontal line, —) that renders a + * grid for the *y* scale. The data defaults to tick values sampled from the + * *y* scale’s domain; if desired, use one of the **ticks**, **tickSpacing**, + * or **interval** options. + */ + mark: 'gridY'; +} + +export interface GridFy extends GridYOptions { + /** + * A vertically-positioned ruleY mark (a horizontal line, —) that renders a + * grid for the *fy* scale. The data defaults to the *fy* scale’s domain; + * if desired, use the **ticks** option. + */ + mark: 'gridFy'; +} diff --git a/packages/spec/src/spec/marks/Bar.ts b/packages/spec/src/spec/marks/Bar.ts new file mode 100644 index 00000000..c7c4f9eb --- /dev/null +++ b/packages/spec/src/spec/marks/Bar.ts @@ -0,0 +1,160 @@ +import { ParamRef } from '../Param.js'; +import { Interval } from '../PlotTypes.js'; +import { ChannelValueIntervalSpec, ChannelValueSpec, InsetOptions, MarkData, MarkOptions, StackOptions } from './Marks.js'; +import { RectCornerOptions } from './Rect.js'; + +// export interface AreaOptions extends MarkData, MarkOptions, StackOptions, CurveOptions { + +/** Options for the barX and barY marks. */ +interface BarOptions extends MarkData, MarkOptions, InsetOptions, RectCornerOptions, StackOptions { + /** + * How to convert a continuous value (**x** for barX, or **y** for barY) into + * an interval (**x1** and **x2** for barX, or **y1** and **y2** for barY); + * one of: + * + * - a named time interval such as *day* (for date intervals) + * - a number (for number intervals), defining intervals at integer multiples of *n* + * + * Setting this option disables the implicit stack transform (stackX for barX, + * or stackY for barY). + */ + interval?: Interval | ParamRef; +} + +/** Options for the barX mark. */ +export interface BarXOptions extends BarOptions { + /** + * The horizontal position (or length/width) channel, typically bound to the + * *x* scale. + * + * If neither **x1** nor **x2** nor **interval** is specified, an implicit + * stackX transform is applied and **x** defaults to the identity function, + * assuming that *data* = [*x₀*, *x₁*, *x₂*, …]. Otherwise if an **interval** + * is specified, then **x1** and **x2** are derived from **x**, representing + * the lower and upper bound of the containing interval, respectively. + * Otherwise, if only one of **x1** or **x2** is specified, the other + * defaults to **x**, which defaults to zero. + */ + x?: ChannelValueIntervalSpec; + + /** + * The required primary (starting, often left) horizontal position channel, + * typically bound to the *x* scale. Setting this option disables the + * implicit stackX transform. + * + * If *x* represents ordinal values, use a cell mark instead. + */ + x1?: ChannelValueSpec; + + /** + * The required secondary (ending, often right) horizontal position channel, + * typically bound to the *x* scale. Setting this option disables the + * implicit stackX transform. + * + * If *x* represents ordinal values, use a cell mark instead. + */ + x2?: ChannelValueSpec; + + /** + * The optional vertical position of the bar; a ordinal channel typically + * bound to the *y* scale. If not specified, the bar spans the vertical + * extent of the frame; otherwise the *y* scale must be a *band* scale. + * + * If *y* represents quantitative or temporal values, use a rectX mark + * instead. + */ + y?: ChannelValueSpec; +} + +/** Options for the barY mark. */ +export interface BarYOptions extends BarOptions { + /** + * The vertical position (or length/height) channel, typically bound to the + * *y* scale. + * + * If neither **y1** nor **y2** nor **interval** is specified, an implicit + * stackY transform is applied and **y** defaults to the identity function, + * assuming that *data* = [*y₀*, *y₁*, *y₂*, …]. Otherwise if an **interval** + * is specified, then **y1** and **y2** are derived from **y**, representing + * the lower and upper bound of the containing interval, respectively. + * Otherwise, if only one of **y1** or **y2** is specified, the other + * defaults to **y**, which defaults to zero. + */ + y?: ChannelValueIntervalSpec; + + /** + * The required primary (starting, often bottom) vertical position channel, + * typically bound to the *y* scale. Setting this option disables the + * implicit stackY transform. + * + * If *y* represents ordinal values, use a cell mark instead. + */ + y1?: ChannelValueSpec; + + /** + * The required secondary (ending, often top) horizontal position channel, + * typically bound to the *y* scale. Setting this option disables the + * implicit stackY transform. + * + * If *y* represents ordinal values, use a cell mark instead. + */ + y2?: ChannelValueSpec; + + /** + * The optional horizontal position of the bar; a ordinal channel typically + * bound to the *x* scale. If not specified, the bar spans the horizontal + * extent of the frame; otherwise the *x* scale must be a *band* scale. + * + * If *x* represents quantitative or temporal values, use a rectY mark + * instead. + */ + x?: ChannelValueSpec; +} + +export interface BarX extends BarXOptions { + /** + * A horizontal bar mark. The required *x* values should be quantitative or + * temporal, and the optional *y* values should be ordinal. + * + * If neither **x1** nor **x2** nor **interval** is specified, an implicit + * stackX transform is applied and **x** defaults to the identity function, + * assuming that *data* = [*x₀*, *x₁*, *x₂*, …]. Otherwise if an **interval** + * is specified, then **x1** and **x2** are derived from **x**, representing + * the lower and upper bound of the containing interval, respectively. + * Otherwise, if only one of **x1** or **x2** is specified, the other + * defaults to **x**, which defaults to zero. + * + * The optional **y** ordinal channel specifies the vertical position; it is + * typically bound to the *y* scale, which must be a *band* scale. If the + * **y** channel is not specified, the bar will span the vertical extent of + * the plot’s frame. + * + * If *y* is quantitative, use the rectX mark instead. + * If *x* is ordinal, use the cell mark instead. + */ + mark: 'barX'; +} + +export interface BarY extends BarYOptions { + /** + * A vertical bar mark. The required *y* values should be quantitative or + * temporal, and the optional *x* values should be ordinal. + * + * If neither **y1** nor **y2** nor **interval** is specified, an implicit + * stackY transform is applied and **y** defaults to the identity function, + * assuming that *data* = [*y₀*, *y₁*, *y₂*, …]. Otherwise if an **interval** + * is specified, then **y1** and **y2** are derived from **y**, representing + * the lower and upper bound of the containing interval, respectively. + * Otherwise, if only one of **y1** or **y2** is specified, the other + * defaults to **y**, which defaults to zero. + * + * The optional **x** ordinal channel specifies the horizontal position; it + * is typically bound to the *x* scale, which must be a *band* scale. If the + * **x** channel is not specified, the bar will span the horizontal extent of + * the plot’s frame. + * + * If *x* is quantitative, use the rectY mark instead. + * If *y* is ordinal, use the cell mark instead. + */ + mark: 'barY'; +} diff --git a/packages/spec/src/spec/marks/Cell.ts b/packages/spec/src/spec/marks/Cell.ts new file mode 100644 index 00000000..a339803d --- /dev/null +++ b/packages/spec/src/spec/marks/Cell.ts @@ -0,0 +1,59 @@ +import { ChannelValueSpec, InsetOptions, MarkData, MarkOptions } from './Marks.js'; +import { RectCornerOptions } from './Rect.js'; + +/** Options for the cell mark. */ +export interface CellOptions extends MarkData, MarkOptions, InsetOptions, RectCornerOptions { + /** + * The horizontal position of the cell; an optional ordinal channel typically + * bound to the *x* scale. If not specified, the cell spans the horizontal + * extent of the frame; otherwise the *x* scale must be a *band* scale. + * + * If *x* represents quantitative or temporal values, use a barX mark instead; + * if *y* is also quantitative or temporal, use a rect mark. + */ + x?: ChannelValueSpec; + + /** + * The vertical position of the cell; an optional ordinal channel typically + * bound to the *y* scale. If not specified, the cell spans the vertical + * extent of the frame; otherwise the *y* scale must be a *band* scale. + * + * If *y* represents quantitative or temporal values, use a barY mark instead; + * if *x* is also quantitative or temporal, use a rect mark. + */ + y?: ChannelValueSpec; +} + +export interface Cell extends CellOptions { + /** + * A rectangular cell mark. Along with **x** and/or **y**, a **fill** channel + * is typically specified to encode value as color. + * + * If neither **x** nor **y** are specified, *data* is assumed to be an array of + * pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that **x** = [*x₀*, + * *x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. + * + * Both **x** and **y** should be ordinal; if only **x** is quantitative (or + * temporal), use a barX mark; if only **y** is quantitative, use a barY mark; + * if both are quantitative, use a rect mark. + */ + mark: 'cell'; +} + +export interface CellX extends CellOptions { + /** + * Like cell, but **x** defaults to the zero-based index [0, 1, 2, …], and if + * **stroke** is not a channel, **fill** defaults to the identity function, + * assuming that *data* = [*x₀*, *x₁*, *x₂*, …]. + */ + mark: 'cellX'; +} + +export interface CellY extends CellOptions { + /** + * Like cell, but **y** defaults to the zero-based index [0, 1, 2, …], and if + * **stroke** is not a channel, **fill** defaults to the identity function, + * assuming that *data* = [*y₀*, *y₁*, *y₂*, …]. + */ + mark: 'cellY'; +} diff --git a/packages/spec/src/spec/marks/Frame.ts b/packages/spec/src/spec/marks/Frame.ts new file mode 100644 index 00000000..5a617f2e --- /dev/null +++ b/packages/spec/src/spec/marks/Frame.ts @@ -0,0 +1,22 @@ +import { ParamRef } from '../Param.js'; +import { InsetOptions, MarkOptions } from './Marks.js'; +import { RectCornerOptions } from './Rect.js'; + +/** Options for the frame decoration mark. */ +export interface FrameOptions extends MarkOptions, InsetOptions, RectCornerOptions { + /** + * If null (default), the rectangular outline of the frame is drawn; + * otherwise the frame is drawn as a line only on the given side, and the + * **rx**, **ry**, **fill**, and **fillOpacity** options are ignored. + */ + anchor?: 'top' | 'right' | 'bottom' | 'left' | null | ParamRef; +} + +export interface Frame extends FrameOptions { + /** + * Draws a rectangle around the plot’s frame, or if an **anchor** is given, + * a line on the given side. Useful for visual separation of facets, or in + * conjunction with axes and grids to fill the frame’s background. + */ + mark: 'frame'; +} diff --git a/packages/spec/src/spec/marks/Marks.ts b/packages/spec/src/spec/marks/Marks.ts index 176152b4..af62df39 100644 --- a/packages/spec/src/spec/marks/Marks.ts +++ b/packages/spec/src/spec/marks/Marks.ts @@ -207,6 +207,13 @@ export interface MarkData { data: PlotMarkData; } +export interface MarkDataOptional { + /** + * The data source for the mark. + */ + data?: PlotMarkData; +} + /** Shared options for all marks. */ export interface MarkOptions { /** @@ -579,6 +586,45 @@ export interface MarkOptions { target?: string | ParamRef; } +/** Options for insetting rectangular shapes. */ +export interface InsetOptions { + /** + * Shorthand to set the same default for all four insets: **insetTop**, + * **insetRight**, **insetBottom**, and **insetLeft**. All insets typically + * default to zero, though not always (say when using bin transform). A + * positive inset reduces effective area, while a negative inset increases it. + */ + inset?: number | ParamRef; + + /** + * Insets the top edge by the specified number of pixels. A positive value + * insets towards the bottom edge (reducing effective area), while a negative + * value insets away from the bottom edge (increasing it). + */ + insetTop?: number | ParamRef; + + /** + * Insets the right edge by the specified number of pixels. A positive value + * insets towards the left edge (reducing effective area), while a negative + * value insets away from the left edge (increasing it). + */ + insetRight?: number | ParamRef; + + /** + * Insets the bottom edge by the specified number of pixels. A positive value + * insets towards the top edge (reducing effective area), while a negative + * value insets away from the top edge (increasing it). + */ + insetBottom?: number | ParamRef; + + /** + * Insets the left edge by the specified number of pixels. A positive value + * insets towards the right edge (reducing effective area), while a negative + * value insets away from the right edge (increasing it). + */ + insetLeft?: number | ParamRef; +} + /** Options for styling text (independent of anchor position). */ export interface TextStyles { /** diff --git a/packages/spec/src/spec/marks/Rect.ts b/packages/spec/src/spec/marks/Rect.ts new file mode 100644 index 00000000..bb71dbb3 --- /dev/null +++ b/packages/spec/src/spec/marks/Rect.ts @@ -0,0 +1,180 @@ +import { ParamRef } from '../Param.js'; +import { Interval } from '../PlotTypes.js'; +import { + ChannelValueIntervalSpec, ChannelValueSpec, InsetOptions, + MarkData, MarkOptions, StackOptions +} from './Marks.js'; + +/** Options for marks that render rectangles, including bar, cell, and rect. */ +export interface RectCornerOptions { + /** + * The rounded corner [*x*-radius][1], either in pixels or as a percentage of + * the rect width. If **rx** is not specified, it defaults to **ry** if + * present, and otherwise draws square corners. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/rx + */ + rx?: number | string | ParamRef; + + /** + * The rounded corner [*y*-radius][1], either in pixels or as a percentage of + * the rect height. If **ry** is not specified, it defaults to **rx** if + * present, and otherwise draws square corners. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/ry + */ + ry?: number | string | ParamRef; +} + +/** Options for the rect mark. */ +export interface RectOptions extends MarkData, MarkOptions, InsetOptions, RectCornerOptions, StackOptions { + /** + * The horizontal position (or length/width) channel, typically bound to the + * *x* scale. + * + * If an **interval** is specified, then **x1** and **x2** are derived from + * **x**, representing the lower and upper bound of the containing interval, + * respectively. For example, for a vertical bar chart of items sold by day: + * + * ```js + * Plot.rectY(sales, {x: "date", interval: "day", y2: "items"}) + * ``` + * + * If *x* represents ordinal values, use a bar or cell mark instead. + */ + x?: ChannelValueIntervalSpec; + + /** + * The required primary (starting, often left) horizontal position channel, + * typically bound to the *x* scale. Setting this option disables the rectX + * mark’s implicit stackX transform. + * + * If *x* represents ordinal values, use a bar or cell mark instead. + */ + x1?: ChannelValueSpec; + + /** + * The required secondary (ending, often right) horizontal position channel, + * typically bound to the *x* scale. Setting this option disables the rectX + * mark’s implicit stackX transform. + * + * If *x* represents ordinal values, use a bar or cell mark instead. + */ + x2?: ChannelValueSpec; + + /** + * The vertical position (or length/height) channel, typically bound to the + * *y* scale. + * + * If an **interval** is specified, then **y1** and **y2** are derived from + * **y**, representing the lower and upper bound of the containing interval, + * respectively. For example, for a horizontal bar chart of items sold by day: + * + * ```js + * Plot.rectX(sales, {y: "date", interval: "day", x2: "items"}) + * ``` + * + * If *y* represents ordinal values, use a bar or cell mark instead. + */ + y?: ChannelValueIntervalSpec; + + /** + * The required primary (starting, often bottom) vertical position channel, + * typically bound to the *y* scale. Setting this option disables the rectY + * mark’s implicit stackY transform. + * + * If *y* represents ordinal values, use a bar or cell mark instead. + */ + y1?: ChannelValueSpec; + + /** + * The required secondary (ending, often top) vertical position channel, + * typically bound to the *y* scale. Setting this option disables the rectY + * mark’s implicit stackY transform. + * + * If *y* represents ordinal values, use a bar or cell mark instead. + */ + y2?: ChannelValueSpec; + + /** + * How to convert a continuous value (**x** for rectY, **y** for rectX, or + * both for rect) into an interval (**x1** and **x2** for rectY, or **y1** and + * **y2** for rectX, or both for rect); one of: + * + * - a named time interval such as *day* (for date intervals) + * - a number (for number intervals), defining intervals at integer multiples of *n* + * + * Setting this option disables the implicit stack transform (stackX for rectX, + * or stackY for rectY). + */ + interval?: Interval | ParamRef; +} + +/** Options for the rectX mark. */ +export interface RectXOptions extends RectOptions { + /** + * The horizontal position (or length/width) channel, typically bound to the + * *x* scale. + * + * If neither **x1** nor **x2** is specified, an implicit stackX transform is + * applied and **x** defaults to the identity function, assuming that *data* = + * [*x₀*, *x₁*, *x₂*, …]. Otherwise, if only one of **x1** or **x2** is + * specified, the other defaults to **x**, which defaults to zero. + */ + x?: ChannelValueSpec; // disallow x interval +} + +/** Options for the rectY mark. */ +export interface RectYOptions extends RectOptions { + /** + * The vertical position (or length/height) channel, typically bound to the + * *y* scale. + * + * If neither **y1** nor **y2** is specified, an implicit stackY transform is + * applied and **y** defaults to the identity function, assuming that *data* = + * [*y₀*, *y₁*, *y₂*, …]. Otherwise, if only one of **y1** or **y2** is + * specified, the other defaults to **y**, which defaults to zero. + */ + y?: ChannelValueSpec; // disallow y interval +} + +export interface Rect extends RectOptions { + /** + * A rect mark. The rectangle extends horizontally from **x1** to **x2**, + * and vertically from **y1** to **y2**. The position channels are often + * derived with a transform. + * + * When **y** extends from zero, for example for a histogram where the + * height of each rect reflects a count of values, use the rectY mark for an + * implicit stackY transform; similarly, if **x** extends from zero, use the + * rectX mark for an implicit stackX transform. + * + * If an **interval** is specified, then **x1** and **x2** are derived from + * **x**, and **y1** and **y2** are derived from **y**, each representing the + * lower and upper bound of the containing interval, respectively. + * + * Both *x* and *y* should be quantitative or temporal; otherwise, use a bar + * or cell mark. + */ + mark: 'rect'; +} + +export interface RectX extends RectXOptions { + /** + * Like rect, but if neither **x1** nor **x2** is specified, an implicit + * stackX transform is applied to **x**, and if **x** is not specified, it + * defaults to the identity function, assuming that *data* is an array of + * numbers [*x₀*, *x₁*, *x₂*, …]. + */ + mark: 'rectX'; +} + +export interface RectY extends RectYOptions { + /** + * Like rect, but if neither **y1** nor **y2** is specified, apply an + * implicit stackY transform is applied to **y**, and if **y** is not + * specified, it defaults to the identity function, assuming that *data* + * is an array of numbers [*y₀*, *y₁*, *y₂*, …]. + */ + mark: 'rectY'; +} diff --git a/packages/spec/src/spec/marks/Rule.ts b/packages/spec/src/spec/marks/Rule.ts new file mode 100644 index 00000000..26d1b987 --- /dev/null +++ b/packages/spec/src/spec/marks/Rule.ts @@ -0,0 +1,108 @@ +import { ParamRef } from '../Param.js'; +import { Interval } from '../PlotTypes.js'; +import { ChannelValueIntervalSpec, ChannelValueSpec, InsetOptions, MarkDataOptional, MarkOptions, MarkerOptions } from './Marks.js'; + +/** Options for the ruleX and ruleY marks. */ +interface RuleOptions extends MarkDataOptional, MarkOptions, MarkerOptions { + /** + * How to convert a continuous value (**y** for ruleX, or **x** for ruleY) + * into an interval (**y1** and **y2** for ruleX, or **x1** and **x2** for + * ruleY); one of: + * + * - a named time interval such as *day* (for date intervals) + * - a number (for number intervals), defining intervals at integer multiples of *n* + */ + interval?: Interval | ParamRef; +} + +/** Options for the ruleX mark. */ +export interface RuleXOptions extends RuleOptions, Omit { + /** + * The horizontal position of the tick; an optional channel bound to the *x* + * scale. If not specified, the rule will be horizontally centered in the + * plot’s frame. + */ + x?: ChannelValueSpec; + + /** + * Shorthand for specifying both the primary and secondary vertical position + * of the tick as the bounds of the containing interval; can only be used in + * conjunction with the **interval** option. + */ + y?: ChannelValueIntervalSpec; + + /** + * The primary (starting, often bottom) vertical position of the tick; a + * channel bound to the *y* scale. + * + * If *y* represents ordinal values, use a tickX mark instead. + */ + y1?: ChannelValueSpec; + + /** + * The secondary (ending, often top) vertical position of the tick; a channel + * bound to the *y* scale. + * + * If *y* represents ordinal values, use a tickX mark instead. + */ + y2?: ChannelValueSpec; +} + +/** Options for the ruleY mark. */ +export interface RuleYOptions extends RuleOptions, Omit { + /** + * Shorthand for specifying both the primary and secondary horizontal position + * of the tick as the bounds of the containing interval; can only be used in + * conjunction with the **interval** option. + */ + x?: ChannelValueIntervalSpec; + + /** + * The primary (starting, often left) horizontal position of the tick; a + * channel bound to the *x* scale. + * + * If *x* represents ordinal values, use a tickY mark instead. + */ + x1?: ChannelValueSpec; + + /** + * The secondary (ending, often right) horizontal position of the tick; a + * channel bound to the *x* scale. + * + * If *x* represents ordinal values, use a tickY mark instead. + */ + x2?: ChannelValueSpec; + + /** + * The vertical position of the tick; an optional channel bound to the *y* + * scale. If not specified, the rule will be vertically centered in the plot’s + * frame. + */ + y?: ChannelValueSpec; +} + +export interface RuleX extends RuleXOptions { + /** + * A horizontally-positioned ruleX mark (a vertical line, |). The **x** + * channel specifies the rule’s horizontal position and defaults to identity, + * assuming that *data* = [*x₀*, *x₁*, *x₂*, …]; the optional **y1** and + * **y2** channels specify its vertical extent. + * + * The ruleX mark is often used to highlight specific *x* values. + * If *y* represents ordinal values, use a tickX mark instead. + */ + mark: 'ruleX'; +} + +export interface RuleY extends RuleXOptions { + /** + * A vertically-positioned ruleY mark (a horizontal line, —). The **y** + * channel specifies the rule's vertical position and defaults to identity, + * assuming that *data* = [*y₀*, *y₁*, *y₂*, …]; the optional **x1** and + * **x2** channels specify its horizontal extent. + * + * The ruleY mark is often used to highlight specific *y* values. + * If *x* represents ordinal values, use a tickY mark instead. + */ + mark: 'ruleY'; +} diff --git a/packages/spec/src/spec/marks/Text.ts b/packages/spec/src/spec/marks/Text.ts new file mode 100644 index 00000000..45daf31a --- /dev/null +++ b/packages/spec/src/spec/marks/Text.ts @@ -0,0 +1,119 @@ +import { ParamRef } from '../Param.js'; +import { FrameAnchor, Interval } from '../PlotTypes.js'; +import { + ChannelValue, ChannelValueIntervalSpec, ChannelValueSpec, + MarkDataOptional, MarkOptions, TextStyles +} from './Marks.js'; + +/** Options for the text mark. */ +export interface TextOptions extends MarkDataOptional, MarkOptions, TextStyles { + /** + * The horizontal position channel specifying the text’s anchor point, + * typically bound to the *x* scale. + */ + x?: ChannelValueSpec; + + /** + * The vertical position channel specifying the text’s anchor point, typically + * bound to the *y* scale. + */ + y?: ChannelValueSpec; + + /** + * The text contents channel, possibly with line breaks (\n, \r\n, or \r). If + * not specified, defaults to the zero-based index [0, 1, 2, …]. + */ + text?: ChannelValue; + + /** + * The frame anchor specifies defaults for **x** and **y**, along with + * **textAnchor** and **lineAnchor**, based on the plot’s frame; it may be one + * of the four sides (*top*, *right*, *bottom*, *left*), one of the four + * corners (*top-left*, *top-right*, *bottom-right*, *bottom-left*), or the + * *middle* of the frame. + */ + frameAnchor?: FrameAnchor | ParamRef; + + /** + * The line anchor controls how text is aligned (typically vertically) + * relative to its anchor point; it is one of *top*, *bottom*, or *middle*. If + * the frame anchor is *top*, *top-left*, or *top-right*, the default line + * anchor is *top*; if the frame anchor is *bottom*, *bottom-right*, or + * *bottom-left*, the default is *bottom*; otherwise it is *middle*. + */ + lineAnchor?: 'top' | 'middle' | 'bottom' | ParamRef; + + /** + * The rotation angle in degrees clockwise; a constant or a channel; defaults + * to 0°. When a number, it is interpreted as a constant; otherwise it is + * interpreted as a channel. + */ + rotate?: ChannelValue | ParamRef; +} + +/** Options for the textX mark. */ +export interface TextXOptions extends Omit { + /** + * The vertical position of the text’s anchor point, typically bound to the + * *y* scale. + */ + y?: ChannelValueIntervalSpec; + + /** + * An interval (such as *day* or a number), to transform **y** values to the + * middle of the interval. + */ + interval?: Interval | ParamRef; +} + +/** Options for the textY mark. */ +export interface TextYOptions extends Omit { + /** + * The horizontal position of the text’s anchor point, typically bound to the + * *x* scale. + */ + x?: ChannelValueIntervalSpec; + + /** + * An interval (such as *day* or a number), to transform **x** values to the + * middle of the interval. + */ + interval?: Interval; +} + +export interface Text extends TextOptions { + /** + * A text mark. The **text** channel specifies the textual contents of the + * mark, which may be preformatted with line breaks (\n, \r\n, or \r), or + * wrapped or clipped using the **lineWidth** and **textOverflow** options. + * + * If **text** contains numbers or dates, a default formatter will be + * applied, and the **fontVariant** will default to *tabular-nums* instead + * of *normal*. If **text** is not specified, it defaults to the identity + * function for primitive data (such as numbers, dates, and strings), and to + * the zero-based index [0, 1, 2, …] for objects (so that something + * identifying is visible by default). + * + * If either **x** or **y** is not specified, the default is determined by + * the **frameAnchor** option. + */ + mark: 'text'; +} + +export interface TextX extends TextXOptions { + /** + * Like text, but **x** defaults to the identity function, assuming that + * *data* = [*x₀*, *x₁*, *x₂*, …]. If an **interval** is specified, such as + * *day*, **y** is transformed to the middle of the interval. + */ + mark: 'textX'; +} + +export interface TextY extends TextYOptions { + /** + * Like text, but **y** defaults to the identity function, assuming that + * *data* = [*y₀*, *y₁*, *y₂*, …]. If an **interval** is specified, such as + * *day*, **x** is transformed to the middle of the interval. + */ + mark: 'textY'; +} diff --git a/packages/spec/src/spec/marks/Tick.ts b/packages/spec/src/spec/marks/Tick.ts new file mode 100644 index 00000000..74ddaefa --- /dev/null +++ b/packages/spec/src/spec/marks/Tick.ts @@ -0,0 +1,65 @@ +import { ChannelValueSpec, InsetOptions, MarkData, MarkOptions, MarkerOptions } from './Marks.js'; + +/** Options for the tickX mark. */ +export interface TickXOptions extends MarkData, MarkOptions, MarkerOptions, Omit { + /** + * The required horizontal position of the tick; a channel typically bound to + * the *x* scale. + */ + x?: ChannelValueSpec; + + /** + * The optional vertical position of the tick; an ordinal channel typically + * bound to the *y* scale. If not specified, the tick spans the vertical + * extent of the frame; otherwise the *y* scale must be a *band* scale. + * + * If *y* represents quantitative or temporal values, use a ruleX mark + * instead. + */ + y?: ChannelValueSpec; +} + +/** Options for the tickY mark. */ +export interface TickYOptions extends MarkData, MarkOptions, MarkerOptions, Omit { + /** + * The required vertical position of the tick; a channel typically bound to + * the *y* scale. + */ + y?: ChannelValueSpec; + + /** + * The optional horizontal position of the tick; an ordinal channel typically + * bound to the *x* scale. If not specified, the tick spans the horizontal + * extent of the frame; otherwise the *x* scale must be a *band* scale. + * + * If *x* represents quantitative or temporal values, use a ruleY mark + * instead. + */ + x?: ChannelValueSpec; +} + +export interface TickX extends TickXOptions { + /** + * A horizontally-positioned tickX mark (a vertical line, |). The **x** + * channel specifies the tick’s horizontal position and defaults to identity, + * assuming that *data* = [*x₀*, *x₁*, *x₂*, …]; the optional **y** ordinal + * channel specifies its vertical position. + * + * If *y* represents quantitative or temporal values, use a ruleX mark + * instead. + */ + mark: 'tickX'; +} + +export interface TickY extends TickYOptions { + /** + * A vertically-positioned tickY mark (a horizontal line, —). The **y** + * channel specifies the tick's vertical position and defaults to identity, + * assuming that *data* = [*y₀*, *y₁*, *y₂*, …]; the optional **x** ordinal + * channel specifies its horizontal position. + * + * If *x* represents quantitative or temporal values, use a ruleY mark + * instead. + */ + mark: 'tickY'; +} diff --git a/specs/esm/axes.js b/specs/esm/axes.js index b2cee2bd..f268f001 100644 --- a/specs/esm/axes.js +++ b/specs/esm/axes.js @@ -1,7 +1,7 @@ import * as vg from "@uwdata/vgplot"; export default vg.plot( - vg.gridY({strokeDasharray: [0.75, 2], strokeOpacity: 1}), + vg.gridY({strokeDasharray: "0.75 2", strokeOpacity: 1}), vg.axisY({anchor: "left", tickSize: 0, dx: 38, dy: -4, lineAnchor: "bottom"}), vg.axisY({ anchor: "right", diff --git a/specs/esm/seattle-temp.js b/specs/esm/seattle-temp.js index f7bc2e82..3aade9b7 100644 --- a/specs/esm/seattle-temp.js +++ b/specs/esm/seattle-temp.js @@ -29,7 +29,7 @@ export default vg.plot( ), vg.ruleY( [15], - {strokeOpacity: 0.5, strokeDasharray: [5, 5]} + {strokeOpacity: 0.5, strokeDasharray: "5 5"} ), vg.xTickFormat("%b"), vg.yLabel("Temperature Range (°C)"), diff --git a/specs/json/axes.json b/specs/json/axes.json index 8dd85d9d..9078bba6 100644 --- a/specs/json/axes.json +++ b/specs/json/axes.json @@ -6,10 +6,7 @@ "plot": [ { "mark": "gridY", - "strokeDasharray": [ - 0.75, - 2 - ], + "strokeDasharray": "0.75 2", "strokeOpacity": 1 }, { diff --git a/specs/json/seattle-temp.json b/specs/json/seattle-temp.json index 59c7e2e6..6fa3ff73 100644 --- a/specs/json/seattle-temp.json +++ b/specs/json/seattle-temp.json @@ -52,10 +52,7 @@ 15 ], "strokeOpacity": 0.5, - "strokeDasharray": [ - 5, - 5 - ] + "strokeDasharray": "5 5" } ], "xTickFormat": "%b", diff --git a/specs/ts/axes.ts b/specs/ts/axes.ts index 7d74ac3d..9341bee4 100644 --- a/specs/ts/axes.ts +++ b/specs/ts/axes.ts @@ -8,10 +8,7 @@ export const spec : Spec = { "plot": [ { "mark": "gridY", - "strokeDasharray": [ - 0.75, - 2 - ], + "strokeDasharray": "0.75 2", "strokeOpacity": 1 }, { diff --git a/specs/ts/seattle-temp.ts b/specs/ts/seattle-temp.ts index 901f392e..12759abb 100644 --- a/specs/ts/seattle-temp.ts +++ b/specs/ts/seattle-temp.ts @@ -53,10 +53,7 @@ export const spec : Spec = { 15 ], "strokeOpacity": 0.5, - "strokeDasharray": [ - 5, - 5 - ] + "strokeDasharray": "5 5" } ], "xTickFormat": "%b", diff --git a/specs/yaml/axes.yaml b/specs/yaml/axes.yaml index 3514c2fa..9cdd0a08 100644 --- a/specs/yaml/axes.yaml +++ b/specs/yaml/axes.yaml @@ -5,7 +5,7 @@ meta: scale attributes such as `xAxis`, `yGrid`, etc. Just add data! plot: - mark: gridY - strokeDasharray: [0.75, 2] # dashed + strokeDasharray: 0.75 2 # dashed strokeOpacity: 1 # opaque - mark: axisY anchor: left diff --git a/specs/yaml/seattle-temp.yaml b/specs/yaml/seattle-temp.yaml index d9d013ad..43658195 100644 --- a/specs/yaml/seattle-temp.yaml +++ b/specs/yaml/seattle-temp.yaml @@ -26,7 +26,7 @@ plot: - mark: ruleY data: [15] strokeOpacity: 0.5 - strokeDasharray: [5, 5] + strokeDasharray: 5 5 xTickFormat: '%b' yLabel: Temperature Range (°C) width: 680 From 8c5f77c10fcbde82ed73c02803a20402ef2ad5c3 Mon Sep 17 00:00:00 2001 From: jheer Date: Fri, 5 Apr 2024 09:09:22 -0700 Subject: [PATCH 19/25] feat: Add vector and hexgrid mark typings. --- packages/spec/src/spec/PlotMark.ts | 9 +- packages/spec/src/spec/marks/Hexgrid.ts | 26 ++++++ packages/spec/src/spec/marks/Vector.ts | 109 ++++++++++++++++++++++++ 3 files changed, 139 insertions(+), 5 deletions(-) create mode 100644 packages/spec/src/spec/marks/Hexgrid.ts create mode 100644 packages/spec/src/spec/marks/Vector.ts diff --git a/packages/spec/src/spec/PlotMark.ts b/packages/spec/src/spec/PlotMark.ts index 37ee8cf3..3ee54f9c 100644 --- a/packages/spec/src/spec/PlotMark.ts +++ b/packages/spec/src/spec/PlotMark.ts @@ -8,17 +8,15 @@ import { BarX, BarY } from './marks/Bar.js'; import { Cell, CellX, CellY } from './marks/Cell.js'; import { Circle, Dot, DotX, DotY, Hexagon } from './marks/Dot.js'; import { Frame } from './marks/Frame.js'; +import { Hexgrid } from './marks/Hexgrid.js'; import { Line, LineX, LineY } from './marks/Line.js'; import { Rect, RectX, RectY } from './marks/Rect.js'; import { RuleX, RuleY } from './marks/Rule.js'; import { Text,TextX, TextY } from './marks/Text.js'; import { TickX, TickY } from './marks/Tick.js'; +import { Spike, Vector, VectorX, VectorY } from './marks/Vector.js'; export type MarkType = - | 'vector' - | 'vectorX' - | 'vectorY' - | 'spike' | 'image' | 'densityX' | 'densityY' @@ -29,7 +27,6 @@ export type MarkType = | 'raster' | 'rasterTile' | 'hexbin' - | 'hexgrid' | 'regressionY' | 'voronoi' | 'voronoiMesh' @@ -58,11 +55,13 @@ export type PlotMark = | Cell | CellX | CellY | Dot | DotX | DotY | Circle | Hexagon | Frame + | Hexgrid | Line | LineX | LineY | Rect | RectX | RectY | RuleX | RuleY | Text | TextX | TextY | TickX | TickY + | Vector | VectorX | VectorY | Spike | GenericPlotMark; /** diff --git a/packages/spec/src/spec/marks/Hexgrid.ts b/packages/spec/src/spec/marks/Hexgrid.ts new file mode 100644 index 00000000..f1a28524 --- /dev/null +++ b/packages/spec/src/spec/marks/Hexgrid.ts @@ -0,0 +1,26 @@ +import { ParamRef } from '../Param.js'; +import { MarkOptions } from './Marks.js'; + +/** Options for the hexgrid mark. */ +export interface HexgridOptions extends MarkOptions { + /** + * The distance between centers of neighboring hexagons, in pixels; defaults + * to 20. Should match the **binWidth** of the hexbin transform. + */ + binWidth?: number | ParamRef; +} + +export interface Hexgrid extends HexgridOptions { + /** + * The hexgrid decoration mark complements the hexbin mark, showing the + * outlines of all hexagons spanning the frame with a default **stroke** of + * *currentColor* and a default **strokeOpacity** of 0.1, similar to the + * default axis grids. + * + * Note that the **binWidth** option of the hexgrid mark should match that of + * the hexbin transform. The grid is clipped by the frame. This is a + * stroke-only mark, and **fill** is not supported; to fill the frame, + * use the frame mark. + */ + mark: 'hexgrid'; +} diff --git a/packages/spec/src/spec/marks/Vector.ts b/packages/spec/src/spec/marks/Vector.ts new file mode 100644 index 00000000..b64db5e6 --- /dev/null +++ b/packages/spec/src/spec/marks/Vector.ts @@ -0,0 +1,109 @@ +import { ParamRef } from '../Param.js'; +import { FrameAnchor } from '../PlotTypes.js'; +import { ChannelValue, ChannelValueSpec, MarkData, MarkOptions } from './Marks.js'; + +/** + * The built-in vector shape implementations; one of: + * + * - *arrow* - a straight line with an open arrowhead at the end (↑) + * - *spike* - an isosceles triangle with a flat base (▲) + */ +export type VectorShapeName = 'arrow' | 'spike'; + +/** How to draw a vector: either a named shape or a custom implementation. */ +export type VectorShape = VectorShapeName; + +/** Options for the vector mark. */ +export interface VectorOptions extends MarkData, MarkOptions { + /** + * The horizontal position of the vector’s anchor point; an optional channel + * bound to the *x* scale. Default depends on the **frameAnchor**. + */ + x?: ChannelValueSpec; + + /** + * The vertical position of the vector’s anchor point; an optional channel + * bound to the *y* scale. Default depends on the **frameAnchor**. + */ + y?: ChannelValueSpec; + + /** + * The vector shape’s radius, such as half the width of the *arrow*’s head or + * the *spike*’s base; a constant number in pixels. Defaults to 3.5 pixels. + */ + r?: number | ParamRef; + + /** + * The vector’s length; either an optional channel bound to the *length* scale + * or a constant number in pixels. Defaults to 12 pixels. + */ + length?: ChannelValueSpec; + + /** + * The vector’s orientation (rotation angle); either a constant number in + * degrees clockwise, or an optional channel (with no associated scale). + * Defaults to 0 degrees with the vector pointing up. + */ + rotate?: ChannelValue; + + /** The shape of the vector; a constant. Defaults to *arrow*. */ + shape?: VectorShape | ParamRef; + + /** + * The vector’s position along its orientation relative to its anchor point; a + * constant. Assuming a default **rotate** angle of 0°, one of: + * + * - *start* - from [*x*, *y*] to [*x*, *y* - *l*] + * - *middle* (default) - from [*x*, *y* + *l* / 2] to [*x*, *y* - *l* / 2] + * - *end* - from [*x*, *y* + *l*] to [*x*, *y*] + * + * where [*x*, *y*] is the vector’s anchor point and *l* is the vector’s + * (possibly scaled) length in pixels. + */ + anchor?: 'start' | 'middle' | 'end' | ParamRef; + + /** + * The vector’s frame anchor, to default **x** and **y** relative to the + * frame; a constant representing one of the frame corners (*top-left*, + * *top-right*, *bottom-right*, *bottom-left*), sides (*top*, *right*, + * *bottom*, *left*), or *middle* (default). Has no effect if both **x** + * and **y** are specified. + */ + frameAnchor?: FrameAnchor | ParamRef; +} + +export interface Vector extends VectorOptions { + /** + * A vector mark. + * + * If none of **frameAnchor**, **x**, and **y** are specified, then **x** and + * **y** default to accessors assuming that *data* contains tuples [[*x₀*, + * *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] + */ + mark: 'vector'; +} + +export interface VectorX extends VectorOptions { + /** + * Like vector, but **x** instead defaults to the identity function and **y** + * defaults to null, assuming that *data* is an array of numbers [*x₀*, *x₁*, + * *x₂*, …]. + */ + mark: 'vectorX'; +} + +export interface VectorY extends VectorOptions { + /** + * Like vector, but **y** instead defaults to the identity function and **x** + * defaults to null, assuming that *data* is an array of numbers [*y₀*, *y₁*, + * *y₂*, …]. + */ + mark: 'vectorY'; +} + +export interface Spike extends VectorOptions { + /** + * Like vector, but with default *options* suitable for drawing a spike map. + */ + mark: 'spike'; +} From f21548cfd1afcf7080624f5fe17d733c6ee5e8ee Mon Sep 17 00:00:00 2001 From: jheer Date: Fri, 5 Apr 2024 09:14:57 -0700 Subject: [PATCH 20/25] feat: Add geo mark typings. --- packages/spec/src/spec/PlotMark.ts | 7 ++-- packages/spec/src/spec/marks/Geo.ts | 55 +++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 packages/spec/src/spec/marks/Geo.ts diff --git a/packages/spec/src/spec/PlotMark.ts b/packages/spec/src/spec/PlotMark.ts index 3ee54f9c..7d72f9ca 100644 --- a/packages/spec/src/spec/PlotMark.ts +++ b/packages/spec/src/spec/PlotMark.ts @@ -8,6 +8,7 @@ import { BarX, BarY } from './marks/Bar.js'; import { Cell, CellX, CellY } from './marks/Cell.js'; import { Circle, Dot, DotX, DotY, Hexagon } from './marks/Dot.js'; import { Frame } from './marks/Frame.js'; +import { Geo, Graticule, Sphere } from './marks/Geo.js'; import { Hexgrid } from './marks/Hexgrid.js'; import { Line, LineX, LineY } from './marks/Line.js'; import { Rect, RectX, RectY } from './marks/Rect.js'; @@ -34,10 +35,7 @@ export type MarkType = | 'delaunayMesh' | 'hull' | 'arrow' - | 'link' - | 'geo' - | 'sphere' - | 'graticule'; + | 'link'; export type MarkOption = | ParamRef @@ -55,6 +53,7 @@ export type PlotMark = | Cell | CellX | CellY | Dot | DotX | DotY | Circle | Hexagon | Frame + | Geo | Graticule | Sphere | Hexgrid | Line | LineX | LineY | Rect | RectX | RectY diff --git a/packages/spec/src/spec/marks/Geo.ts b/packages/spec/src/spec/marks/Geo.ts new file mode 100644 index 00000000..3e511030 --- /dev/null +++ b/packages/spec/src/spec/marks/Geo.ts @@ -0,0 +1,55 @@ +import { ParamRef } from '../Param.js'; +import { ChannelValue, ChannelValueSpec, MarkData, MarkOptions } from './Marks.js'; + +/** Options for the geo mark. */ +export interface GeoOptions extends MarkData, MarkOptions { + /** + * A required channel for the geometry to render; defaults to identity, + * assuming *data* is a GeoJSON object or an iterable of GeoJSON objects. + */ + geometry?: ChannelValue; + + /** + * The size of Point and MultiPoint geometries, defaulting to a constant 3 + * pixels. If **r** is a number, it is interpreted as a constant radius in + * pixels; otherwise it is interpreted as a channel and the effective radius + * is controlled by the *r* scale, which defaults to a *sqrt* scale such that + * the visual area of a point is proportional to its associated value. + * + * If **r** is a channel, geometries will be sorted by descending radius by + * default, to limit occlusion; use the **sort** transform to control render + * order. Geometries with a nonpositive radius are not drawn. + */ + r?: ChannelValueSpec | ParamRef; +} + +export interface Geo extends GeoOptions { + /** + * A geo mark. The **geometry** channel, which defaults to the identity + * function assuming that *data* is a GeoJSON object or an iterable of + * GeoJSON objects, is projected to the plane using the plot’s top-level + * **projection**. + * + * If *data* is a GeoJSON feature collection, then the mark’s data is + * *data*.features; if *data* is a GeoJSON geometry collection, then the + * mark’s data is *data*.geometries; if *data* is some other GeoJSON + * object, then the mark’s data is the single-element array [*data*]. + */ + mark: 'geo'; +} + +export interface Sphere extends MarkOptions { + /** + * A geo mark whose *data* is the outline of the sphere on the + * projection’s plane. (For use with a spherical **projection** only.) + */ + mark: 'sphere'; +} + +export interface Graticule extends MarkOptions { + /** + * A geo mark whose *data* is a 10° global graticule. (For use with a + * spherical **projection** only.) + */ + mark: 'graticule'; +} From 0de518d1acf51fbbb97ecb77d56ee2ef9c61e692 Mon Sep 17 00:00:00 2001 From: jheer Date: Fri, 5 Apr 2024 10:02:50 -0700 Subject: [PATCH 21/25] feat: Add delaunay mark types. --- docs/public/specs/esm/voronoi.js | 1 - docs/public/specs/json/voronoi.json | 1 - docs/public/specs/yaml/voronoi.yaml | 1 - packages/spec/src/spec/PlotMark.ts | 7 +- packages/spec/src/spec/marks/Delaunay.ts | 87 ++++++++++++++++++++++++ specs/esm/voronoi.js | 1 - specs/json/voronoi.json | 1 - specs/ts/voronoi.ts | 1 - specs/yaml/voronoi.yaml | 1 - 9 files changed, 89 insertions(+), 12 deletions(-) create mode 100644 packages/spec/src/spec/marks/Delaunay.ts diff --git a/docs/public/specs/esm/voronoi.js b/docs/public/specs/esm/voronoi.js index 39d967c6..579e9cb9 100644 --- a/docs/public/specs/esm/voronoi.js +++ b/docs/public/specs/esm/voronoi.js @@ -17,7 +17,6 @@ export default vg.vconcat( stroke: "white", strokeWidth: 1, strokeOpacity: 0.5, - inset: 1, fill: "species", fillOpacity: 0.2 } diff --git a/docs/public/specs/json/voronoi.json b/docs/public/specs/json/voronoi.json index 7372723f..7730fc6b 100644 --- a/docs/public/specs/json/voronoi.json +++ b/docs/public/specs/json/voronoi.json @@ -26,7 +26,6 @@ "stroke": "white", "strokeWidth": 1, "strokeOpacity": 0.5, - "inset": 1, "fill": "species", "fillOpacity": 0.2 }, diff --git a/docs/public/specs/yaml/voronoi.yaml b/docs/public/specs/yaml/voronoi.yaml index 0f86eaa7..aa8b2ef0 100644 --- a/docs/public/specs/yaml/voronoi.yaml +++ b/docs/public/specs/yaml/voronoi.yaml @@ -19,7 +19,6 @@ vconcat: stroke: white strokeWidth: 1 strokeOpacity: 0.5 - inset: 1 fill: species fillOpacity: 0.2 - mark: hull diff --git a/packages/spec/src/spec/PlotMark.ts b/packages/spec/src/spec/PlotMark.ts index 7d72f9ca..144b6a65 100644 --- a/packages/spec/src/spec/PlotMark.ts +++ b/packages/spec/src/spec/PlotMark.ts @@ -6,6 +6,7 @@ import { Area, AreaX, AreaY } from './marks/Area.js'; import { AxisFx, AxisFy, AxisX, AxisY, GridFx, GridFy, GridX, GridY } from './marks/Axis.js'; import { BarX, BarY } from './marks/Bar.js'; import { Cell, CellX, CellY } from './marks/Cell.js'; +import { DelaunayLink, DelaunayMesh, Hull, Voronoi, VoronoiMesh } from './marks/Delaunay.js'; import { Circle, Dot, DotX, DotY, Hexagon } from './marks/Dot.js'; import { Frame } from './marks/Frame.js'; import { Geo, Graticule, Sphere } from './marks/Geo.js'; @@ -29,11 +30,6 @@ export type MarkType = | 'rasterTile' | 'hexbin' | 'regressionY' - | 'voronoi' - | 'voronoiMesh' - | 'delaunayLink' - | 'delaunayMesh' - | 'hull' | 'arrow' | 'link'; @@ -51,6 +47,7 @@ export type PlotMark = | AxisX | AxisY | AxisFx | AxisFy | GridX | GridY | GridFx | GridFy | BarX | BarY | Cell | CellX | CellY + | DelaunayLink | DelaunayMesh | Hull | Voronoi | VoronoiMesh | Dot | DotX | DotY | Circle | Hexagon | Frame | Geo | Graticule | Sphere diff --git a/packages/spec/src/spec/marks/Delaunay.ts b/packages/spec/src/spec/marks/Delaunay.ts new file mode 100644 index 00000000..a8ff49eb --- /dev/null +++ b/packages/spec/src/spec/marks/Delaunay.ts @@ -0,0 +1,87 @@ +import { + ChannelValue, ChannelValueSpec, CurveOptions, + MarkData, MarkOptions, MarkerOptions +} from './Marks.js'; + +/** Options for the Delaunay marks. */ +export interface DelaunayOptions extends MarkData, MarkOptions, MarkerOptions, CurveOptions { + /** The horizontal position channel, typically bound to the *x* scale. */ + x?: ChannelValueSpec; + /** The vertical position channel, typically bound to the *y* scale. */ + y?: ChannelValueSpec; + /** An optional ordinal channel for grouping to produce multiple (possibly overlapping) triangulations. */ + z?: ChannelValue; +} + +export interface DelaunayLink extends DelaunayOptions { + /** + *A mark that draws links for each edge of the Delaunay triangulation + * of points given by the **x** and **y** channels. Like the link mark, + * except that **x1**, **y1**, **x2**, and **y2** are derived automatically + * from **x** and **y**. When an aesthetic channel is specified (such as + * **stroke** or **strokeWidth**), the link inherits the corresponding + * channel value from one of its two endpoints arbitrarily. + * + * If **z** is specified, the input points are grouped by *z*, producing a + * separate Delaunay triangulation for each group. + */ + mark: 'delaunayLink'; +} + +export interface DelaunayMesh extends DelaunayOptions { + /** + * A mark that draws a mesh of the Delaunay triangulation of the points + * given by the **x** and **y** channels. The **stroke** option defaults to + * _currentColor_, and the **strokeOpacity** defaults to 0.2; the **fill** + * option is not supported. When an aesthetic channel is specified (such as + * **stroke** or **strokeWidth**), the mesh inherits the corresponding + * channel value from one of its constituent points arbitrarily. + * + * If **z** is specified, the input points are grouped by *z*, producing a + * separate Delaunay triangulation for each group. + */ + mark: 'delaunayMesh'; +} + +export interface Hull extends DelaunayOptions { + /** + * A mark that draws a convex hull around the points given by the **x** and + * **y** channels. The **stroke** option defaults to _currentColor_ and the + * **fill** option defaults to _none_. When an aesthetic channel is specified + * (such as **stroke** or **strokeWidth**), the hull inherits the + * corresponding channel value from one of its constituent points + * arbitrarily. + * + * If **z** is specified, the input points are grouped by *z*, producing a + * separate hull for each group. If **z** is not specified, it defaults to + * the **fill** channel, if any, or the **stroke** channel, if any. + */ + mark: 'hull'; +} + +export interface Voronoi extends DelaunayOptions { + /** + * A mark that draws polygons for each cell of the Voronoi tesselation + * of the points given by the **x** and **y** channels. + * + * If **z** is specified, the input points are grouped by *z*, producing a + * separate Voronoi tesselation for each group. + */ + mark: 'voronoi'; +} + +export interface VoronoiMesh extends DelaunayOptions { + /** + * A mark that draws a mesh for the cell boundaries of the Voronoi + * tesselation of the points given by the **x** and **y** channels. The + * **stroke** option defaults to _currentColor_, and the **strokeOpacity** + * defaults to 0.2. The **fill** option is not supported. When an aesthetic + * channel is specified (such as **stroke** or **strokeWidth**), the mesh + * inherits the corresponding channel value from one of its constituent + * points arbitrarily. + * + * If **z** is specified, the input points are grouped by *z*, producing a + * separate Voronoi tesselation for each group. + */ + mark: 'voronoiMesh'; +} diff --git a/specs/esm/voronoi.js b/specs/esm/voronoi.js index 39d967c6..579e9cb9 100644 --- a/specs/esm/voronoi.js +++ b/specs/esm/voronoi.js @@ -17,7 +17,6 @@ export default vg.vconcat( stroke: "white", strokeWidth: 1, strokeOpacity: 0.5, - inset: 1, fill: "species", fillOpacity: 0.2 } diff --git a/specs/json/voronoi.json b/specs/json/voronoi.json index 21f6d7e1..bc42e01a 100644 --- a/specs/json/voronoi.json +++ b/specs/json/voronoi.json @@ -27,7 +27,6 @@ "stroke": "white", "strokeWidth": 1, "strokeOpacity": 0.5, - "inset": 1, "fill": "species", "fillOpacity": 0.2 }, diff --git a/specs/ts/voronoi.ts b/specs/ts/voronoi.ts index 085acfc9..ddb448aa 100644 --- a/specs/ts/voronoi.ts +++ b/specs/ts/voronoi.ts @@ -28,7 +28,6 @@ export const spec : Spec = { "stroke": "white", "strokeWidth": 1, "strokeOpacity": 0.5, - "inset": 1, "fill": "species", "fillOpacity": 0.2 }, diff --git a/specs/yaml/voronoi.yaml b/specs/yaml/voronoi.yaml index 0f86eaa7..aa8b2ef0 100644 --- a/specs/yaml/voronoi.yaml +++ b/specs/yaml/voronoi.yaml @@ -19,7 +19,6 @@ vconcat: stroke: white strokeWidth: 1 strokeOpacity: 0.5 - inset: 1 fill: species fillOpacity: 0.2 - mark: hull From 71b3fd3b0b0ddddefffae264b9b2caff6ad3903c Mon Sep 17 00:00:00 2001 From: jheer Date: Fri, 5 Apr 2024 14:36:05 -0700 Subject: [PATCH 22/25] feat: Add arrow and link mark types. --- packages/spec/src/spec/PlotMark.ts | 8 +- packages/spec/src/spec/marks/Arrow.ts | 108 ++++++++++++++++++++++++++ packages/spec/src/spec/marks/Link.ts | 70 +++++++++++++++++ packages/spec/src/spec/marks/Marks.ts | 2 +- 4 files changed, 184 insertions(+), 4 deletions(-) create mode 100644 packages/spec/src/spec/marks/Arrow.ts create mode 100644 packages/spec/src/spec/marks/Link.ts diff --git a/packages/spec/src/spec/PlotMark.ts b/packages/spec/src/spec/PlotMark.ts index 144b6a65..91616f96 100644 --- a/packages/spec/src/spec/PlotMark.ts +++ b/packages/spec/src/spec/PlotMark.ts @@ -3,6 +3,7 @@ import { ParamRef } from './Param.js'; import { PlotMarkData } from './PlotFrom.js'; import { Transform } from './Transform.js'; import { Area, AreaX, AreaY } from './marks/Area.js'; +import { Arrow } from './marks/Arrow.js'; import { AxisFx, AxisFy, AxisX, AxisY, GridFx, GridFy, GridX, GridY } from './marks/Axis.js'; import { BarX, BarY } from './marks/Bar.js'; import { Cell, CellX, CellY } from './marks/Cell.js'; @@ -12,6 +13,7 @@ import { Frame } from './marks/Frame.js'; import { Geo, Graticule, Sphere } from './marks/Geo.js'; import { Hexgrid } from './marks/Hexgrid.js'; import { Line, LineX, LineY } from './marks/Line.js'; +import { Link } from './marks/Link.js'; import { Rect, RectX, RectY } from './marks/Rect.js'; import { RuleX, RuleY } from './marks/Rule.js'; import { Text,TextX, TextY } from './marks/Text.js'; @@ -29,9 +31,7 @@ export type MarkType = | 'raster' | 'rasterTile' | 'hexbin' - | 'regressionY' - | 'arrow' - | 'link'; + | 'regressionY'; export type MarkOption = | ParamRef @@ -44,6 +44,7 @@ export type MarkOption = export type PlotMark = | Area | AreaX | AreaY + | Arrow | AxisX | AxisY | AxisFx | AxisFy | GridX | GridY | GridFx | GridFy | BarX | BarY | Cell | CellX | CellY @@ -53,6 +54,7 @@ export type PlotMark = | Geo | Graticule | Sphere | Hexgrid | Line | LineX | LineY + | Link | Rect | RectX | RectY | RuleX | RuleY | Text | TextX | TextY diff --git a/packages/spec/src/spec/marks/Arrow.ts b/packages/spec/src/spec/marks/Arrow.ts new file mode 100644 index 00000000..aee860f9 --- /dev/null +++ b/packages/spec/src/spec/marks/Arrow.ts @@ -0,0 +1,108 @@ +import { ParamRef } from '../Param.js'; +import { ChannelValueSpec, MarkData, MarkOptions } from './Marks.js'; + +/** Options for the arrow mark. */ +export interface ArrowOptions extends MarkData, MarkOptions { + /** + * The horizontal position, for vertical arrows; typically bound to the *x* + * scale; shorthand for setting defaults for both **x1** and **x2**. + */ + x?: ChannelValueSpec; + + /** + * The vertical position, for horizontal arrows; typically bound to the *y* + * scale; shorthand for setting defaults for both **y1** and **y2**. + */ + y?: ChannelValueSpec; + + /** + * The starting horizontal position; typically bound to the *x* scale; also + * sets a default for **x2**. + */ + x1?: ChannelValueSpec; + + /** + * The starting vertical position; typically bound to the *y* scale; also + * sets a default for **y2**. + */ + y1?: ChannelValueSpec; + + /** + * The ending horizontal position; typically bound to the *x* scale; also + * sets a default for **x1**. + */ + x2?: ChannelValueSpec; + + /** + * The ending vertical position; typically bound to the *y* scale; also sets + * a default for **y1**. + */ + y2?: ChannelValueSpec; + + /** + * The angle, a constant in degrees, between the straight line intersecting + * the arrow’s two control points and the outgoing tangent direction of the + * arrow from the start point. The angle must be within ±90°; a positive + * angle will produce a clockwise curve, while a negative angle will produce + * a counterclockwise curve; zero (the default) will produce a straight line. + * Use true for 22.5°. + */ + bend?: number | boolean | ParamRef; + + /** + * How pointy the arrowhead is, in degrees; a constant typically between 0° + * and 180°, and defaults to 60°. + */ + headAngle?: number | ParamRef; + + /** + * The size of the arrowhead relative to the **strokeWidth**; a constant. + * Assuming the default of stroke width 1.5px, this is the length of the + * arrowhead’s side in pixels. + */ + headLength?: number | ParamRef; + + /** + * Shorthand to set the same default for **insetStart** and **insetEnd**. + */ + inset?: number | ParamRef; + + /** + * The starting inset, a constant in pixels; defaults to 0. A positive inset + * shortens the arrow by moving the starting point towards the endpoint + * point, while a negative inset extends it by moving the starting point in + * the opposite direction. A positive starting inset may be useful if the + * arrow emerges from a dot. + */ + insetStart?: number | ParamRef; + + /** + * The ending inset, a constant in pixels; defaults to 0. A positive inset + * shortens the arrow by moving the ending point towards the starting point, + * while a negative inset extends it by moving the ending point in the + * opposite direction. A positive ending inset may be useful if the arrow + * points to a dot. + */ + insetEnd?: number | ParamRef; + + /** + * The sweep order; defaults to 1 indicating a positive (clockwise) bend + * angle; -1 indicates a negative (anticlockwise) bend angle; 0 effectively + * clears the bend angle. If set to *-x*, the bend angle is flipped when the + * ending point is to the left of the starting point — ensuring all arrows + * bulge up (down if bend is negative); if set to *-y*, the bend angle is + * flipped when the ending point is above the starting point — ensuring all + * arrows bulge right (left if bend is negative); the sign is negated for + * *+x* and *+y*. + */ + sweep?: number | '+x' | '-x' | '+y' | '-y' | ParamRef; +} + +/** The arrow mark. */ +export interface Arrow extends ArrowOptions { + /** + * An arrow mark, drawing (possibly swoopy) arrows connecting pairs of + * points. + */ + mark: 'arrow'; +} diff --git a/packages/spec/src/spec/marks/Link.ts b/packages/spec/src/spec/marks/Link.ts new file mode 100644 index 00000000..fcba341d --- /dev/null +++ b/packages/spec/src/spec/marks/Link.ts @@ -0,0 +1,70 @@ +import { ParamRef } from '../Param.js'; +import { + ChannelValueSpec, CurveAutoOptions, MarkData, MarkOptions, MarkerOptions +} from './Marks.js'; + +/** Options for the link mark. */ +export interface LinkOptions extends MarkData, MarkOptions, MarkerOptions, CurveAutoOptions { + /** + * The horizontal position, for vertical links; typically bound to the *x* + * scale; shorthand for setting defaults for both **x1** and **x2**. + */ + x?: ChannelValueSpec; + + /** + * The vertical position, for horizontal links; typically bound to the *y* + * scale; shorthand for setting defaults for both **y1** and **y2**. + */ + y?: ChannelValueSpec; + + /** + * The starting horizontal position; typically bound to the *x* scale; also + * sets a default for **x2**. + */ + x1?: ChannelValueSpec; + + /** + * The starting vertical position; typically bound to the *y* scale; also sets + * a default for **y2**. + */ + y1?: ChannelValueSpec; + + /** + * The ending horizontal position; typically bound to the *x* scale; also sets + * a default for **x1**. + */ + x2?: ChannelValueSpec; + + /** + * The ending vertical position; typically bound to the *y* scale; also sets a + * default for **y1**. + */ + y2?: ChannelValueSpec; + + /** + * The curve (interpolation) method for connecting adjacent points. + * + * Since a link has exactly two points, only the following curves (or a custom + * curve) are recommended: *linear*, *step*, *step-after*, *step-before*, + * *bump-x*, or *bump-y*. Note that the *linear* curve is incapable of showing + * a fill since a straight line has zero area. For a curved link, use an arrow + * mark with the **bend** option. + * + * If the plot uses a spherical **projection**, the default *auto* **curve** + * will render links as geodesics; to draw a straight line instead, use the + * *linear* **curve**. + */ + curve?: CurveAutoOptions['curve'] | ParamRef; +} + +/** The link mark. */ +export interface Link extends LinkOptions { + /** + * A link mark, drawing line segments (curves) connecting pairs of points. + * + * If the plot uses a spherical **projection**, the default *auto* **curve** + * will render links as geodesics; to draw a straight line instead, use the + * *linear* **curve**. + */ + mark: 'link'; +} diff --git a/packages/spec/src/spec/marks/Marks.ts b/packages/spec/src/spec/marks/Marks.ts index af62df39..46e0e0b6 100644 --- a/packages/spec/src/spec/marks/Marks.ts +++ b/packages/spec/src/spec/marks/Marks.ts @@ -82,7 +82,7 @@ export type ChannelValueSpec = */ export type ChannelValueIntervalSpec = | ChannelValueSpec - | { value: ChannelValue; interval?: Interval }; + | { value: ChannelValue; interval: Interval }; /** A channel name, or an implied one for domain sorting. */ type ChannelDomainName = ChannelName | 'data' | 'width' | 'height'; From 845998055bcfcf1a88a723aafef46d531de2835c Mon Sep 17 00:00:00 2001 From: jheer Date: Fri, 5 Apr 2024 15:19:41 -0700 Subject: [PATCH 23/25] chore: Add missing outer mark doc comments. --- packages/spec/src/spec/marks/Area.ts | 3 +++ packages/spec/src/spec/marks/Axis.ts | 9 +++++++++ packages/spec/src/spec/marks/Bar.ts | 4 ++-- packages/spec/src/spec/marks/Cell.ts | 3 +++ packages/spec/src/spec/marks/Delaunay.ts | 5 +++++ packages/spec/src/spec/marks/Dot.ts | 5 +++++ packages/spec/src/spec/marks/Frame.ts | 1 + packages/spec/src/spec/marks/Geo.ts | 3 +++ packages/spec/src/spec/marks/Hexgrid.ts | 1 + packages/spec/src/spec/marks/Line.ts | 3 +++ packages/spec/src/spec/marks/Rect.ts | 3 +++ packages/spec/src/spec/marks/Rule.ts | 7 ++++++- packages/spec/src/spec/marks/Text.ts | 3 +++ packages/spec/src/spec/marks/Tick.ts | 6 +++++- packages/spec/src/spec/marks/Vector.ts | 4 ++++ 15 files changed, 56 insertions(+), 4 deletions(-) diff --git a/packages/spec/src/spec/marks/Area.ts b/packages/spec/src/spec/marks/Area.ts index 96a9a7e1..4fdb5d37 100644 --- a/packages/spec/src/spec/marks/Area.ts +++ b/packages/spec/src/spec/marks/Area.ts @@ -83,6 +83,7 @@ export interface AreaYOptions extends Omit { y?: ChannelValueSpec; } +/** The area mark. */ export interface Area extends AreaOptions { /** * An area mark. The area mark is rarely used directly; it is only needed @@ -94,6 +95,7 @@ export interface Area extends AreaOptions { mark: 'area'; } +/** The areaX mark. */ export interface AreaX extends AreaXOptions { /** * A vertically-oriented area mark, where the baseline and topline share @@ -122,6 +124,7 @@ export interface AreaX extends AreaXOptions { mark: 'areaX'; } +/** The areaY mark. */ export interface AreaY extends AreaYOptions { /** * A horizontally-oriented area mark, where the baseline and topline share diff --git a/packages/spec/src/spec/marks/Axis.ts b/packages/spec/src/spec/marks/Axis.ts index 0188e73d..cf09f93a 100644 --- a/packages/spec/src/spec/marks/Axis.ts +++ b/packages/spec/src/spec/marks/Axis.ts @@ -5,6 +5,7 @@ import { RuleXOptions, RuleYOptions } from './Rule.js'; import { TextOptions } from './Text.js'; import { TickXOptions, TickYOptions } from './Tick.js'; +/** The scale options used by axis and grid marks. */ interface ScaleOptions { /** * Enforces uniformity for data at regular intervals, such as integer values @@ -157,6 +158,7 @@ export interface GridXOptions extends GridOptions, Omit {} +/** The axisX mark. */ export interface AxisX extends AxisXOptions { /** * An axis mark to document the visual encoding of the horizontal position @@ -182,6 +184,7 @@ export interface AxisX extends AxisXOptions { mark: 'axisX'; } +/** The axisFx mark. */ export interface AxisFx extends AxisXOptions { /** * An axis mark to document the visual encoding of the horizontal facet @@ -207,6 +210,7 @@ export interface AxisFx extends AxisXOptions { mark: 'axisFx'; } +/** The axisY mark. */ export interface AxisY extends AxisYOptions { /** * An axis mark to document the visual encoding of the vertical position *y* @@ -232,6 +236,7 @@ export interface AxisY extends AxisYOptions { mark: 'axisY'; } +/** The axisFy mark. */ export interface AxisFy extends AxisYOptions { /** * An axis mark to document the visual encoding of the vertical facet @@ -257,6 +262,7 @@ export interface AxisFy extends AxisYOptions { mark: 'axisFy'; } +/** The gridX mark. */ export interface GridX extends GridXOptions { /** * A horizontally-positioned ruleX mark (a vertical line, |) that renders a @@ -267,6 +273,7 @@ export interface GridX extends GridXOptions { mark: 'gridX'; } +/** The gridFx mark. */ export interface GridFx extends GridXOptions { /** * A horizontally-positioned ruleX mark (a vertical line, |) that renders a @@ -276,6 +283,7 @@ export interface GridFx extends GridXOptions { mark: 'gridFx'; } +/** The gridY mark. */ export interface GridY extends GridYOptions { /** * A vertically-positioned ruleY mark (a horizontal line, —) that renders a @@ -286,6 +294,7 @@ export interface GridY extends GridYOptions { mark: 'gridY'; } +/** The gridFy mark. */ export interface GridFy extends GridYOptions { /** * A vertically-positioned ruleY mark (a horizontal line, —) that renders a diff --git a/packages/spec/src/spec/marks/Bar.ts b/packages/spec/src/spec/marks/Bar.ts index c7c4f9eb..4074f605 100644 --- a/packages/spec/src/spec/marks/Bar.ts +++ b/packages/spec/src/spec/marks/Bar.ts @@ -3,8 +3,6 @@ import { Interval } from '../PlotTypes.js'; import { ChannelValueIntervalSpec, ChannelValueSpec, InsetOptions, MarkData, MarkOptions, StackOptions } from './Marks.js'; import { RectCornerOptions } from './Rect.js'; -// export interface AreaOptions extends MarkData, MarkOptions, StackOptions, CurveOptions { - /** Options for the barX and barY marks. */ interface BarOptions extends MarkData, MarkOptions, InsetOptions, RectCornerOptions, StackOptions { /** @@ -111,6 +109,7 @@ export interface BarYOptions extends BarOptions { x?: ChannelValueSpec; } +/** The barX mark. */ export interface BarX extends BarXOptions { /** * A horizontal bar mark. The required *x* values should be quantitative or @@ -135,6 +134,7 @@ export interface BarX extends BarXOptions { mark: 'barX'; } +/** The barY mark. */ export interface BarY extends BarYOptions { /** * A vertical bar mark. The required *y* values should be quantitative or diff --git a/packages/spec/src/spec/marks/Cell.ts b/packages/spec/src/spec/marks/Cell.ts index a339803d..3a4e1cac 100644 --- a/packages/spec/src/spec/marks/Cell.ts +++ b/packages/spec/src/spec/marks/Cell.ts @@ -24,6 +24,7 @@ export interface CellOptions extends MarkData, MarkOptions, InsetOptions, RectCo y?: ChannelValueSpec; } +/** The cell mark. */ export interface Cell extends CellOptions { /** * A rectangular cell mark. Along with **x** and/or **y**, a **fill** channel @@ -40,6 +41,7 @@ export interface Cell extends CellOptions { mark: 'cell'; } +/** The cellX mark. */ export interface CellX extends CellOptions { /** * Like cell, but **x** defaults to the zero-based index [0, 1, 2, …], and if @@ -49,6 +51,7 @@ export interface CellX extends CellOptions { mark: 'cellX'; } +/** The cellY mark. */ export interface CellY extends CellOptions { /** * Like cell, but **y** defaults to the zero-based index [0, 1, 2, …], and if diff --git a/packages/spec/src/spec/marks/Delaunay.ts b/packages/spec/src/spec/marks/Delaunay.ts index a8ff49eb..7c3433ab 100644 --- a/packages/spec/src/spec/marks/Delaunay.ts +++ b/packages/spec/src/spec/marks/Delaunay.ts @@ -13,6 +13,7 @@ export interface DelaunayOptions extends MarkData, MarkOptions, MarkerOptions, C z?: ChannelValue; } +/** The delaunayLink mark. */ export interface DelaunayLink extends DelaunayOptions { /** *A mark that draws links for each edge of the Delaunay triangulation @@ -28,6 +29,7 @@ export interface DelaunayLink extends DelaunayOptions { mark: 'delaunayLink'; } +/** The delaunayMesh mark. */ export interface DelaunayMesh extends DelaunayOptions { /** * A mark that draws a mesh of the Delaunay triangulation of the points @@ -43,6 +45,7 @@ export interface DelaunayMesh extends DelaunayOptions { mark: 'delaunayMesh'; } +/** The hull mark. */ export interface Hull extends DelaunayOptions { /** * A mark that draws a convex hull around the points given by the **x** and @@ -59,6 +62,7 @@ export interface Hull extends DelaunayOptions { mark: 'hull'; } +/** The voronoi mark. */ export interface Voronoi extends DelaunayOptions { /** * A mark that draws polygons for each cell of the Voronoi tesselation @@ -70,6 +74,7 @@ export interface Voronoi extends DelaunayOptions { mark: 'voronoi'; } +/** The voronoiMesh mark. */ export interface VoronoiMesh extends DelaunayOptions { /** * A mark that draws a mesh for the cell boundaries of the Voronoi diff --git a/packages/spec/src/spec/marks/Dot.ts b/packages/spec/src/spec/marks/Dot.ts index 7b00cd7c..06e784e7 100644 --- a/packages/spec/src/spec/marks/Dot.ts +++ b/packages/spec/src/spec/marks/Dot.ts @@ -93,6 +93,7 @@ export interface DotYOptions extends Omit { interval?: Interval | ParamRef; } +/** The dot mark. */ export interface Dot extends DotOptions { /** * A dot mark that draws circles, or other symbols, as in a scatterplot. @@ -109,6 +110,7 @@ export interface Dot extends DotOptions { mark: 'dot'; } +/** The dotX mark. */ export interface DotX extends DotXOptions { /** * Like dot, except that **x** defaults to the identity function, assuming that @@ -120,6 +122,7 @@ export interface DotX extends DotXOptions { mark: 'dotX'; } +/** The dotY mark. */ export interface DotY extends DotYOptions { /** * Like dot, except that **y** defaults to the identity function, assuming that @@ -131,11 +134,13 @@ export interface DotY extends DotYOptions { mark: 'dotY'; } +/** The circle mark. */ export interface Circle extends Exclude { /** Like dot, except that the **symbol** option is set to *circle*. */ mark: 'circle'; } +/** The hexagon mark. */ export interface Hexagon extends Exclude { /** Like dot, except that the **symbol** option is set to *hexagon*. */ mark: 'hexagon'; diff --git a/packages/spec/src/spec/marks/Frame.ts b/packages/spec/src/spec/marks/Frame.ts index 5a617f2e..2e570086 100644 --- a/packages/spec/src/spec/marks/Frame.ts +++ b/packages/spec/src/spec/marks/Frame.ts @@ -12,6 +12,7 @@ export interface FrameOptions extends MarkOptions, InsetOptions, RectCornerOptio anchor?: 'top' | 'right' | 'bottom' | 'left' | null | ParamRef; } +/** The frame mark. */ export interface Frame extends FrameOptions { /** * Draws a rectangle around the plot’s frame, or if an **anchor** is given, diff --git a/packages/spec/src/spec/marks/Geo.ts b/packages/spec/src/spec/marks/Geo.ts index 3e511030..31cc4702 100644 --- a/packages/spec/src/spec/marks/Geo.ts +++ b/packages/spec/src/spec/marks/Geo.ts @@ -23,6 +23,7 @@ export interface GeoOptions extends MarkData, MarkOptions { r?: ChannelValueSpec | ParamRef; } +/** The geo mark. */ export interface Geo extends GeoOptions { /** * A geo mark. The **geometry** channel, which defaults to the identity @@ -38,6 +39,7 @@ export interface Geo extends GeoOptions { mark: 'geo'; } +/** The sphere mark. */ export interface Sphere extends MarkOptions { /** * A geo mark whose *data* is the outline of the sphere on the @@ -46,6 +48,7 @@ export interface Sphere extends MarkOptions { mark: 'sphere'; } +/** The graticule mark. */ export interface Graticule extends MarkOptions { /** * A geo mark whose *data* is a 10° global graticule. (For use with a diff --git a/packages/spec/src/spec/marks/Hexgrid.ts b/packages/spec/src/spec/marks/Hexgrid.ts index f1a28524..861adc12 100644 --- a/packages/spec/src/spec/marks/Hexgrid.ts +++ b/packages/spec/src/spec/marks/Hexgrid.ts @@ -10,6 +10,7 @@ export interface HexgridOptions extends MarkOptions { binWidth?: number | ParamRef; } +/** The hexgrid mark. */ export interface Hexgrid extends HexgridOptions { /** * The hexgrid decoration mark complements the hexbin mark, showing the diff --git a/packages/spec/src/spec/marks/Line.ts b/packages/spec/src/spec/marks/Line.ts index 2f90a79c..aaaaa47c 100644 --- a/packages/spec/src/spec/marks/Line.ts +++ b/packages/spec/src/spec/marks/Line.ts @@ -42,6 +42,7 @@ export interface LineYOptions extends LineOptions { x?: ChannelValueSpec; } +/** The line mark. */ export interface Line extends LineOptions { /** * A line mark that connects control points. @@ -71,6 +72,7 @@ export interface Line extends LineOptions { mark: 'line'; } +/** The linex mark. */ export interface LineX extends LineXOptions { /** * Like line, except that **x** defaults to the identity function assuming @@ -80,6 +82,7 @@ export interface LineX extends LineXOptions { mark: 'lineX' } +/** The lineY mark. */ export interface LineY extends LineYOptions { /** * Like line, except **y** defaults to the identity function and assumes diff --git a/packages/spec/src/spec/marks/Rect.ts b/packages/spec/src/spec/marks/Rect.ts index bb71dbb3..f2cef534 100644 --- a/packages/spec/src/spec/marks/Rect.ts +++ b/packages/spec/src/spec/marks/Rect.ts @@ -138,6 +138,7 @@ export interface RectYOptions extends RectOptions { y?: ChannelValueSpec; // disallow y interval } +/** The rect mark. */ export interface Rect extends RectOptions { /** * A rect mark. The rectangle extends horizontally from **x1** to **x2**, @@ -159,6 +160,7 @@ export interface Rect extends RectOptions { mark: 'rect'; } +/** The rectX mark. */ export interface RectX extends RectXOptions { /** * Like rect, but if neither **x1** nor **x2** is specified, an implicit @@ -169,6 +171,7 @@ export interface RectX extends RectXOptions { mark: 'rectX'; } +/** The rectY mark. */ export interface RectY extends RectYOptions { /** * Like rect, but if neither **y1** nor **y2** is specified, apply an diff --git a/packages/spec/src/spec/marks/Rule.ts b/packages/spec/src/spec/marks/Rule.ts index 26d1b987..449482f2 100644 --- a/packages/spec/src/spec/marks/Rule.ts +++ b/packages/spec/src/spec/marks/Rule.ts @@ -1,6 +1,9 @@ import { ParamRef } from '../Param.js'; import { Interval } from '../PlotTypes.js'; -import { ChannelValueIntervalSpec, ChannelValueSpec, InsetOptions, MarkDataOptional, MarkOptions, MarkerOptions } from './Marks.js'; +import { + ChannelValueIntervalSpec, ChannelValueSpec, InsetOptions, + MarkDataOptional, MarkOptions, MarkerOptions +} from './Marks.js'; /** Options for the ruleX and ruleY marks. */ interface RuleOptions extends MarkDataOptional, MarkOptions, MarkerOptions { @@ -81,6 +84,7 @@ export interface RuleYOptions extends RuleOptions, Omit { interval?: Interval; } +/** The text mark. */ export interface Text extends TextOptions { /** * A text mark. The **text** channel specifies the textual contents of the @@ -100,6 +101,7 @@ export interface Text extends TextOptions { mark: 'text'; } +/** The textX mark. */ export interface TextX extends TextXOptions { /** * Like text, but **x** defaults to the identity function, assuming that @@ -109,6 +111,7 @@ export interface TextX extends TextXOptions { mark: 'textX'; } +/** The textY mark. */ export interface TextY extends TextYOptions { /** * Like text, but **y** defaults to the identity function, assuming that diff --git a/packages/spec/src/spec/marks/Tick.ts b/packages/spec/src/spec/marks/Tick.ts index 74ddaefa..7205150c 100644 --- a/packages/spec/src/spec/marks/Tick.ts +++ b/packages/spec/src/spec/marks/Tick.ts @@ -1,4 +1,6 @@ -import { ChannelValueSpec, InsetOptions, MarkData, MarkOptions, MarkerOptions } from './Marks.js'; +import { + ChannelValueSpec, InsetOptions, MarkData, MarkOptions, MarkerOptions +} from './Marks.js'; /** Options for the tickX mark. */ export interface TickXOptions extends MarkData, MarkOptions, MarkerOptions, Omit { @@ -38,6 +40,7 @@ export interface TickYOptions extends MarkData, MarkOptions, MarkerOptions, Omit x?: ChannelValueSpec; } +/** The tickX mark. */ export interface TickX extends TickXOptions { /** * A horizontally-positioned tickX mark (a vertical line, |). The **x** @@ -51,6 +54,7 @@ export interface TickX extends TickXOptions { mark: 'tickX'; } +/** The tickY mark. */ export interface TickY extends TickYOptions { /** * A vertically-positioned tickY mark (a horizontal line, —). The **y** diff --git a/packages/spec/src/spec/marks/Vector.ts b/packages/spec/src/spec/marks/Vector.ts index b64db5e6..28aa05dc 100644 --- a/packages/spec/src/spec/marks/Vector.ts +++ b/packages/spec/src/spec/marks/Vector.ts @@ -72,6 +72,7 @@ export interface VectorOptions extends MarkData, MarkOptions { frameAnchor?: FrameAnchor | ParamRef; } +/** The vector mark. */ export interface Vector extends VectorOptions { /** * A vector mark. @@ -83,6 +84,7 @@ export interface Vector extends VectorOptions { mark: 'vector'; } +/** The vectorX mark. */ export interface VectorX extends VectorOptions { /** * Like vector, but **x** instead defaults to the identity function and **y** @@ -92,6 +94,7 @@ export interface VectorX extends VectorOptions { mark: 'vectorX'; } +/** The vectorY mark. */ export interface VectorY extends VectorOptions { /** * Like vector, but **y** instead defaults to the identity function and **x** @@ -101,6 +104,7 @@ export interface VectorY extends VectorOptions { mark: 'vectorY'; } +/** The spike mark. */ export interface Spike extends VectorOptions { /** * Like vector, but with default *options* suitable for drawing a spike map. From fd10c134ddffa8c104d60e1a50627af59863192b Mon Sep 17 00:00:00 2001 From: jheer Date: Fri, 5 Apr 2024 16:22:02 -0700 Subject: [PATCH 24/25] feat: Add spec types for image, raster, regression. --- packages/spec/src/spec/PlotMark.ts | 13 +- packages/spec/src/spec/marks/Image.ts | 101 +++++++++++++++ packages/spec/src/spec/marks/Raster.ts | 138 +++++++++++++++++++++ packages/spec/src/spec/marks/Regression.ts | 62 +++++++++ 4 files changed, 308 insertions(+), 6 deletions(-) create mode 100644 packages/spec/src/spec/marks/Image.ts create mode 100644 packages/spec/src/spec/marks/Raster.ts create mode 100644 packages/spec/src/spec/marks/Regression.ts diff --git a/packages/spec/src/spec/PlotMark.ts b/packages/spec/src/spec/PlotMark.ts index 91616f96..15712652 100644 --- a/packages/spec/src/spec/PlotMark.ts +++ b/packages/spec/src/spec/PlotMark.ts @@ -12,26 +12,24 @@ import { Circle, Dot, DotX, DotY, Hexagon } from './marks/Dot.js'; import { Frame } from './marks/Frame.js'; import { Geo, Graticule, Sphere } from './marks/Geo.js'; import { Hexgrid } from './marks/Hexgrid.js'; +import { Image } from './marks/Image.js'; import { Line, LineX, LineY } from './marks/Line.js'; import { Link } from './marks/Link.js'; +import { Heatmap, Raster, RasterTile } from './marks/Raster.js'; import { Rect, RectX, RectY } from './marks/Rect.js'; +import { RegressionY } from './marks/Regression.js'; import { RuleX, RuleY } from './marks/Rule.js'; import { Text,TextX, TextY } from './marks/Text.js'; import { TickX, TickY } from './marks/Tick.js'; import { Spike, Vector, VectorX, VectorY } from './marks/Vector.js'; export type MarkType = - | 'image' | 'densityX' | 'densityY' | 'density' | 'denseLine' | 'contour' - | 'heatmap' - | 'raster' - | 'rasterTile' - | 'hexbin' - | 'regressionY'; + | 'hexbin'; export type MarkOption = | ParamRef @@ -53,9 +51,12 @@ export type PlotMark = | Frame | Geo | Graticule | Sphere | Hexgrid + | Image | Line | LineX | LineY | Link + | Raster | Heatmap | RasterTile | Rect | RectX | RectY + | RegressionY | RuleX | RuleY | Text | TextX | TextY | TickX | TickY diff --git a/packages/spec/src/spec/marks/Image.ts b/packages/spec/src/spec/marks/Image.ts new file mode 100644 index 00000000..320cab6b --- /dev/null +++ b/packages/spec/src/spec/marks/Image.ts @@ -0,0 +1,101 @@ +import { ParamRef } from '../Param.js'; +import { FrameAnchor } from '../PlotTypes.js'; +import { ChannelValue, ChannelValueSpec, MarkData, MarkOptions } from './Marks.js'; + +/** Options for the image mark. */ +export interface ImageOptions extends MarkData, MarkOptions { + /** + * The horizontal position channel specifying the image’s center; typically + * bound to the *x* scale. + */ + x?: ChannelValueSpec; + + /** + * The vertical position channel specifying the image’s center; typically + * bound to the *y* scale. + */ + y?: ChannelValueSpec; + + /** + * The image width in pixels. When a number, it is interpreted as a constant + * radius in pixels; otherwise it is interpreted as a channel. Also sets the + * default **height**; if neither are set, defaults to 16. Images with a + * nonpositive width are not drawn. + */ + width?: ChannelValue | ParamRef; + + /** + * The image height in pixels. When a number, it is interpreted as a constant + * radius in pixels; otherwise it is interpreted as a channel. Also sets the + * default **height**; if neither are set, defaults to 16. Images with a + * nonpositive height are not drawn. + */ + height?: ChannelValue | ParamRef; + + /** + * The image clip radius, for circular images. If null (default), images are + * not clipped; when a number, it is interpreted as a constant in pixels; + * otherwise it is interpreted as a channel, typically bound to the *r* scale. + * Also defaults **height** and **width** to twice its value. + */ + r?: ChannelValue | ParamRef; + + /** + * The rotation angle, in degrees clockwise. When a number, it is interpreted + * as a constant; otherwise it is interpreted as a channel. + */ + rotate?: ChannelValue | ParamRef; + + /** + * The required image URL (or relative path). If a string that starts with a + * dot, slash, or URL protocol (*e.g.*, “https:”) it is assumed to be a + * constant; otherwise it is interpreted as a channel. + */ + src?: ChannelValue | ParamRef; + + /** + * The image [aspect ratio][1]; defaults to *xMidYMid meet*. To crop the image + * instead of scaling it to fit, use *xMidYMid slice*. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/preserveAspectRatio + */ + preserveAspectRatio?: string | ParamRef; + + /** + * The [cross-origin][1] behavior. See the [Plot.image notebook][2] for details. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/crossorigin + * [2]: https://observablehq.com/@observablehq/plot-image + */ + crossOrigin?: string | ParamRef; + + /** + * The frame anchor specifies defaults for **x** and **y** based on the plot’s + * frame; it may be one of the four sides (*top*, *right*, *bottom*, *left*), + * one of the four corners (*top-left*, *top-right*, *bottom-right*, + * *bottom-left*), or the *middle* of the frame. + */ + frameAnchor?: FrameAnchor | ParamRef; + + /** + * The [image-rendering attribute][1]; defaults to *auto* (bilinear). The + * option may be set to *pixelated* to disable bilinear interpolation for a + * sharper image; however, note that this is not supported in WebKit. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/image-rendering + */ + imageRendering?: string | ParamRef; +} + +export interface Image extends ImageOptions { + /** + * An image mark that draws images as in a scatterplot. + * + * If either **x** or **y** is not specified, the default is determined by + * the **frameAnchor** option. If none of **x**, **y**, and **frameAnchor** + * are specified, *data* is assumed to be an array of pairs [[*x₀*, *y₀*], + * [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that **x** = [*x₀*, *x₁*, *x₂*, …] + * and **y** = [*y₀*, *y₁*, *y₂*, …]. + */ + mark: 'image'; +} diff --git a/packages/spec/src/spec/marks/Raster.ts b/packages/spec/src/spec/marks/Raster.ts new file mode 100644 index 00000000..1fe772a3 --- /dev/null +++ b/packages/spec/src/spec/marks/Raster.ts @@ -0,0 +1,138 @@ +import { ParamRef } from '../Param.js'; +import { ChannelValueSpec, MarkData, MarkOptions } from './Marks.js'; + +/** + * A spatial interpolation method; one of: + * + * - *none* - do not perform interpolation (the default), maps samples to single bins + * - *linear* - apply proportional linear interpolation across adjacent bins + * - *nearest* - assign each pixel to the closest sample’s value (Voronoi diagram) + * - *barycentric* - apply barycentric interpolation over the Delaunay triangulation + * - *random-walk* - apply a random walk from each pixel, stopping when near a sample + */ +export type GridInterpolate = + | 'none' + | 'linear' + | 'nearest' + | 'barycentric' + | 'random-walk'; + +/** Options for grid2d marks. */ +export interface Grid2DOptions { + /** The horizontal position channel, typically bound to the *x* scale. */ + x?: ChannelValueSpec; + + /** The vertical position channel, typically bound to the *y* scale. */ + y?: ChannelValueSpec; + + /** The width (number of columns) of the grid, in actual pixels. */ + width?: number | ParamRef; + + /** The height (number of rows) of the grid, in actual pixels. */ + height?: number | ParamRef; + + /** + * The effective screen size of a raster pixel, used to determine the height + * and width of the raster from the frame’s dimensions; defaults to 1. + */ + pixelSize?: number | ParamRef; + + /** + * The bin padding, one of 1 (default) to include extra padding for the final + * bin, or 0 to make the bins flush with the maximum domain value. + */ + pad?: number | ParamRef; + + /** + * A non-negative pixel radius for smoothing; defaults to 0. Note that + * blurring is applied on the values (before the color scale is applied) if + * quantitative, and after (on the materialized pixels), if ordinal. + */ + bandwidth?: number | ParamRef; + + /** + * The spatial interpolation method; one of: + * + * - *none* - do not perform interpolation (the default), maps samples to single bins + * - *linear* - apply proportional linear interpolation across adjacent bins + * - *nearest* - assign each pixel to the closest sample’s value (Voronoi diagram) + * - *barycentric* - apply barycentric interpolation over the Delaunay triangulation + * - *random-walk* - apply a random walk from each pixel, stopping when near a sample + */ + interpolate?: GridInterpolate | null | ParamRef; +} + +/** Options for the raster mark. */ +export interface RasterOptions extends MarkData, Grid2DOptions, Omit { + /** + * The [image-rendering attribute][1]; defaults to *auto* (bilinear). The + * option may be set to *pixelated* to disable bilinear interpolation for a + * sharper image; however, note that this is not supported in WebKit. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/image-rendering + */ + imageRendering?: string | ParamRef; + + /** + * The fill, typically bound to the *color* scale. Can be specified as a + * constant or a channel based on the input data. Use the special value + * `"density"` to map computed density values to pixel colors. Use an + * aggregate expression to instead visualize an aggregate value per raster + * bin. If fill is set to a constant color or to a non-aggregate field, + * opacity will be used to convey densities. If a non-aggregate (group by) + * field is provided, multiple rasters are created with a unique categorical + * color per layer. + */ + fill?: ChannelValueSpec | ParamRef; + + /** + * The opacity, typically bound to the *opacity* scale. Can be specified as a + * constant or a channel based on the input data. Use the special value + * `"density"` to map computed density values to opacity. Use an aggregate + * expression to instead visualize an aggregate value per raster bin. + */ + fillOpacity?: ChannelValueSpec | ParamRef; +} + +export interface Raster extends RasterOptions { + /** + * A raster mark which renders a raster image from spatial samples. It + * represents discrete samples in abstract coordinates **x** and **y**; + * the **fill** and **fillOpacity** channels specify further abstract + * values (_e.g._, height in a topographic map) to be spatially + * interpolated to produce an image. + * + * The **x** and **y** data domains are binned into the cells ("pixels") + * of a raster grid, typically with an aggregate function evaluated over + * the binned data. The result can be optionally smoothed (blurred). + * + * To create a smoothed density heatmap, use the heatmap mark, which is + * a raster mark with different default options. + */ + mark: 'raster'; +} + +export interface Heatmap extends RasterOptions { + /** + * Like raster, but with default options for accurate density estimation + * via smoothing. The *bandwidth* (20), *interpolate* ("linear"), and + * *pixelSize* (2) options are set to produce smoothed density heatmaps. + */ + mark: 'heatmap'; +} + +export interface RasterTile extends RasterOptions { + /** + * An experimental raster mark which performs tiling and prefetching to + * support more scalable rasters upon panning the domain. Uses a tile + * size that matches with current width and height, and prefetches data + * from neighboring tile segments. + */ + mark: 'rasterTile'; + + /** + * The coordinates of the tile origin in the **x** and **y** data domains. + * Defaults to [0, 0]. + */ + origin?: [number, number] | ParamRef; +} diff --git a/packages/spec/src/spec/marks/Regression.ts b/packages/spec/src/spec/marks/Regression.ts new file mode 100644 index 00000000..1b1d395b --- /dev/null +++ b/packages/spec/src/spec/marks/Regression.ts @@ -0,0 +1,62 @@ +import { ParamRef } from '../Param.js'; +import { ChannelValue, ChannelValueSpec, MarkData, MarkOptions } from './Marks.js'; + +/** Options for regression marks. */ +interface RegressionOptions extends MarkData, MarkOptions { + /** + * The confidence interval in (0, 1), or 0 to hide bands; defaults to 0.95. + */ + ci?: number | ParamRef; + + /** + * The distance in pixels between samples of the confidence band; + * defaults to 4. + */ + precision?: number | ParamRef; + + /** + * An optional ordinal channel for grouping data into (possibly stacked) + * series, producing an independent regression for each group. If not + * specified, it defaults to **fill** if a channel, or **stroke** if a + * channel. + */ + z?: ChannelValue; +} + +/** Options for the regressionY mark. */ +export interface RegressionYOptions extends RegressionOptions { + /** + * The independent variable horizontal position channel, typically bound to + * the *x* scale; defaults to the zero-based index of the data [0, 1, 2, …]. + */ + x?: ChannelValueSpec; + + /** + * The dependent variable vertical position channel, typically bound to the + * *y* scale; defaults to identity, assuming that *data* = [*y₀*, *y₁*, *y₂*, + * …]. + */ + y?: ChannelValueSpec; +} + +export interface RegressionY extends RegressionYOptions { + /** + * A mark that draws [linear regression][1] lines with confidence bands, + * representing the estimated relation of a dependent variable (*y*) on an + * independent variable (*x*). + * + * The linear regression line is fit using the [least squares][2] approach. + * See Torben Jansen’s [“Linear regression with confidence bands”][3] and + * [this StatExchange question][4] for details on the confidence interval + * calculation. + * + * Multiple regressions can be produced by specifying a **z**, **fill**, or + * **stroke** channel. + * + * [1]: https://en.wikipedia.org/wiki/Linear_regression + * [2]: https://en.wikipedia.org/wiki/Least_squares + * [3]: https://observablehq.com/@toja/linear-regression-with-confidence-bands + * [4]: https://stats.stackexchange.com/questions/101318/understanding-shape-and-calculation-of-confidence-bands-in-linear-regression + */ + mark: 'regressionY'; +} From e4015ac7d7cb0133b3d9ad348e8be687a0541141 Mon Sep 17 00:00:00 2001 From: jheer Date: Sat, 6 Apr 2024 13:38:20 -0700 Subject: [PATCH 25/25] feat: Finish spec typings. --- packages/spec/src/spec/Data.ts | 20 ++- packages/spec/src/spec/Expression.ts | 6 +- packages/spec/src/spec/HConcat.ts | 1 + packages/spec/src/spec/HSpace.ts | 1 + packages/spec/src/spec/Input.ts | 4 + packages/spec/src/spec/Param.ts | 16 +- packages/spec/src/spec/Plot.ts | 8 +- packages/spec/src/spec/PlotAttribute.ts | 1 + packages/spec/src/spec/PlotFrom.ts | 17 +- packages/spec/src/spec/PlotInteractor.ts | 1 + packages/spec/src/spec/PlotMark.ts | 114 ++------------ packages/spec/src/spec/Spec.ts | 27 ++-- packages/spec/src/spec/Transform.ts | 81 +++++++++- packages/spec/src/spec/VConcat.ts | 1 + packages/spec/src/spec/VSpace.ts | 1 + .../spec/src/spec/interactors/Highlight.ts | 1 + .../spec/src/spec/interactors/Interval1D.ts | 4 + .../spec/src/spec/interactors/Interval2D.ts | 2 + packages/spec/src/spec/interactors/Nearest.ts | 7 +- packages/spec/src/spec/interactors/PanZoom.ts | 7 + packages/spec/src/spec/interactors/Toggle.ts | 5 + packages/spec/src/spec/marks/Area.ts | 8 +- packages/spec/src/spec/marks/Arrow.ts | 4 +- packages/spec/src/spec/marks/Bar.ts | 6 +- packages/spec/src/spec/marks/Cell.ts | 8 +- packages/spec/src/spec/marks/Contour.ts | 25 +++ packages/spec/src/spec/marks/Delaunay.ts | 19 ++- packages/spec/src/spec/marks/DenseLine.ts | 30 ++++ packages/spec/src/spec/marks/Density.ts | 145 ++++++++++++++++++ packages/spec/src/spec/marks/Dot.ts | 12 +- packages/spec/src/spec/marks/Geo.ts | 4 +- packages/spec/src/spec/marks/Hexbin.ts | 34 ++++ packages/spec/src/spec/marks/Hexgrid.ts | 2 +- packages/spec/src/spec/marks/Image.ts | 4 +- packages/spec/src/spec/marks/Line.ts | 10 +- packages/spec/src/spec/marks/Link.ts | 4 +- packages/spec/src/spec/marks/Marks.ts | 1 - packages/spec/src/spec/marks/Raster.ts | 25 +-- packages/spec/src/spec/marks/Rect.ts | 8 +- packages/spec/src/spec/marks/Regression.ts | 5 +- packages/spec/src/spec/marks/Rule.ts | 6 +- packages/spec/src/spec/marks/Text.ts | 8 +- packages/spec/src/spec/marks/Tick.ts | 8 +- packages/spec/src/spec/marks/Vector.ts | 10 +- 44 files changed, 487 insertions(+), 224 deletions(-) create mode 100644 packages/spec/src/spec/marks/Contour.ts create mode 100644 packages/spec/src/spec/marks/DenseLine.ts create mode 100644 packages/spec/src/spec/marks/Density.ts create mode 100644 packages/spec/src/spec/marks/Hexbin.ts diff --git a/packages/spec/src/spec/Data.ts b/packages/spec/src/spec/Data.ts index 4724f58c..bce90fd8 100644 --- a/packages/spec/src/spec/Data.ts +++ b/packages/spec/src/spec/Data.ts @@ -34,6 +34,9 @@ export type DataQuery = string; */ export type DataArray = object[]; +/** + * A data definition that loads an external data file. + */ export interface DataFile { /** * The data file to load. If no type option is provided, @@ -42,6 +45,9 @@ export interface DataFile { file: `${string}.parquet` | `${string}.csv` | `${string}.json`; } +/** + * A data definition that queries an existing table. + */ export interface DataTable extends DataBaseOptions { /** * The data source type. One of: @@ -58,6 +64,9 @@ export interface DataTable extends DataBaseOptions { query: string; } +/** + * A data definition that loads a parquet file. + */ export interface DataParquet extends DataBaseOptions { /** * The data source type. One of: @@ -74,6 +83,9 @@ export interface DataParquet extends DataBaseOptions { file: string; } +/** + * A data definition that loads a csv file. + */ export interface DataCSV extends DataBaseOptions { /** * The data source type. One of: @@ -100,6 +112,9 @@ export interface DataCSV extends DataBaseOptions { sample_size?: number; } +/** + * A data definition that loads a supported spatial data file format. + */ export interface DataSpatial extends DataBaseOptions { /** * The data source type. One of: @@ -111,7 +126,10 @@ export interface DataSpatial extends DataBaseOptions { */ type: 'spatial'; /** - * The file path for the dataset to load. + * The file path for the spatial dataset to load. See the DuckDB spatial + * documention [1] for more information on supported file types. + * + * [1] https://duckdb.org/docs/extensions/spatial.html#st_read--read-spatial-data-from-files */ file: string; /** diff --git a/packages/spec/src/spec/Expression.ts b/packages/spec/src/spec/Expression.ts index 0c37e8a1..c2ad8e0a 100644 --- a/packages/spec/src/spec/Expression.ts +++ b/packages/spec/src/spec/Expression.ts @@ -2,11 +2,12 @@ export type Expression = | SQLExpression | AggregateExpression; +/** A custom SQL expression. */ export interface SQLExpression { /** * A SQL expression string to derive a new column value. * Embedded Param refrences, such as `$param + 1`, are supported. - * For expressions with aggregate functions, use `agg` instead. + * For expressions with aggregate functions, use *agg* instead. */ sql: string; /** @@ -15,11 +16,12 @@ export interface SQLExpression { label?: string; } +/** A custom SQL aggregate expression. */ export interface AggregateExpression { /** * A SQL expression string to calculate an aggregate value. * Embedded Param references, such as `SUM($param + 1)`, are supported. - * For expressions without aggregate functions, use `sql` instead. + * For expressions without aggregate functions, use *sql* instead. */ agg: string; /** diff --git a/packages/spec/src/spec/HConcat.ts b/packages/spec/src/spec/HConcat.ts index a91b9a48..9819880d 100644 --- a/packages/spec/src/spec/HConcat.ts +++ b/packages/spec/src/spec/HConcat.ts @@ -1,5 +1,6 @@ import { Component } from './Spec.js'; +/** An hconcat component. */ export interface HConcat { /** * Horizontally concatenate components in a row layout. diff --git a/packages/spec/src/spec/HSpace.ts b/packages/spec/src/spec/HSpace.ts index a74302ed..fe743256 100644 --- a/packages/spec/src/spec/HSpace.ts +++ b/packages/spec/src/spec/HSpace.ts @@ -1,3 +1,4 @@ +/** An hspace component. */ export interface HSpace { /** * Horizontal space to place between components. diff --git a/packages/spec/src/spec/Input.ts b/packages/spec/src/spec/Input.ts index 4ac4c38a..e082523e 100644 --- a/packages/spec/src/spec/Input.ts +++ b/packages/spec/src/spec/Input.ts @@ -1,5 +1,6 @@ import { ParamRef } from './Param.js'; +/** A menu input component. */ export interface Menu { /** * A menu input widget. @@ -41,6 +42,7 @@ export interface Menu { value?: any; } +/** A search input component. */ export interface Search { /** * A text search input widget. @@ -80,6 +82,7 @@ export interface Search { label?: string; } +/** A slider input component. */ export interface Slider { /** * A slider input widget. @@ -132,6 +135,7 @@ export interface Slider { width?: number; } +/** A table grid view component. */ export interface Table { /** * A table grid widget. diff --git a/packages/spec/src/spec/Param.ts b/packages/spec/src/spec/Param.ts index 46e1ab18..7781e2c8 100644 --- a/packages/spec/src/spec/Param.ts +++ b/packages/spec/src/spec/Param.ts @@ -1,5 +1,6 @@ export type ParamRef = `$${string}`; +/** Base properties shared by Param definitions. */ export interface ParamBase { /** * The type of reactive parameter. One of: @@ -12,6 +13,7 @@ export interface ParamBase { select?: 'value'; } +/** A Param definition. */ export interface Param extends ParamBase { /** * The initial parameter value. @@ -19,6 +21,7 @@ export interface Param extends ParamBase { value: ParamValue; } +/** A Date-valued Param definition. */ export interface ParamDate extends ParamBase { /** * The initial parameter value as an ISO date/time @@ -27,21 +30,18 @@ export interface ParamDate extends ParamBase { date: string; } +/** Literal Param values. */ export type ParamLiteral = | string | number | boolean; +/** Valid Param values. */ export type ParamValue = | ParamLiteral | Array; -export type SelectionType = - | 'crossfilter' - | 'intersect' - | 'single' - | 'union'; - +/** A Selection definition. */ export interface Selection { /** * The type of reactive parameter. One of: @@ -51,7 +51,8 @@ export interface Selection { * - `"single"` for a `Selection` that retains a single clause only * - `"crossfilter"` for a cross-filtered intersection `Selection` */ - select: SelectionType; + select: 'crossfilter' | 'intersect' | 'single' | 'union'; + /** * A flag for cross-filtering, where selections made in a plot filter others * but not oneself (default `false`, except for `crossfilter` selections). @@ -59,6 +60,7 @@ export interface Selection { cross?: boolean; } +/** A Param or Selection definition. */ export type ParamDefinition = | ParamValue | Param diff --git a/packages/spec/src/spec/Plot.ts b/packages/spec/src/spec/Plot.ts index da0b0515..c37e0684 100644 --- a/packages/spec/src/spec/Plot.ts +++ b/packages/spec/src/spec/Plot.ts @@ -3,11 +3,7 @@ import { PlotInteractor } from './PlotInteractor.js'; import { PlotLegend } from './PlotLegend.js'; import { PlotMark } from './PlotMark.js'; -export type PlotEntry = - | PlotInteractor - | PlotLegend - | PlotMark; - +/** A plot component. */ export interface Plot extends PlotAttributes { /** * An array of plot marks, interactors, or legends. @@ -15,5 +11,5 @@ export interface Plot extends PlotAttributes { * Unless otherwise configured, interactors will use the nearest * previous mark as a basis for which data fields to select. */ - plot: PlotEntry[]; + plot: (PlotMark | PlotInteractor | PlotLegend)[]; } diff --git a/packages/spec/src/spec/PlotAttribute.ts b/packages/spec/src/spec/PlotAttribute.ts index a232cc0f..c114de8b 100644 --- a/packages/spec/src/spec/PlotAttribute.ts +++ b/packages/spec/src/spec/PlotAttribute.ts @@ -4,6 +4,7 @@ import { Fixed, Interpolate, Interval, PositionScaleType, ProjectionName } from './PlotTypes.js'; +/** Plot attributes. */ export interface PlotAttributes { /** * A unique name for the plot. The name is used by standalone legend diff --git a/packages/spec/src/spec/PlotFrom.ts b/packages/spec/src/spec/PlotFrom.ts index 00755e83..208622f1 100644 --- a/packages/spec/src/spec/PlotFrom.ts +++ b/packages/spec/src/spec/PlotFrom.ts @@ -1,24 +1,16 @@ import { ParamRef } from './Param.js'; -/** Input data for marks. */ -export type PlotMarkData = - | PlotDataInline - | PlotFrom; - /** * An array of inline data values to visualize. As this data does not come * from a database, it can not be filtered by interactive selections. */ export type PlotDataInline = any[]; +/** Input data specification for a plot mark. */ export interface PlotFrom { - /** - * The name of the backing data table. - */ + /** The name of the backing data table. */ from: string; - /** - * A selection that filters the mark data. - */ + /** A selection that filters the mark data. */ filterBy?: ParamRef; /** * A flag (default `true`) to enable any mark-specific query optimizations. @@ -26,3 +18,6 @@ export interface PlotFrom { */ optimize?: boolean; } + +/** Input data for a marks */ +export type PlotMarkData = PlotDataInline | PlotFrom; diff --git a/packages/spec/src/spec/PlotInteractor.ts b/packages/spec/src/spec/PlotInteractor.ts index 5292263c..bc60e472 100644 --- a/packages/spec/src/spec/PlotInteractor.ts +++ b/packages/spec/src/spec/PlotInteractor.ts @@ -5,6 +5,7 @@ import { NearestX, NearestY } from './interactors/Nearest.js'; import { Pan, PanX, PanY, PanZoom, PanZoomX, PanZoomY } from './interactors/PanZoom.js'; import { Toggle, ToggleColor, ToggleX, ToggleY } from './interactors/Toggle.js'; +/** A plot interactor entry. */ export type PlotInteractor = | Highlight | IntervalX diff --git a/packages/spec/src/spec/PlotMark.ts b/packages/spec/src/spec/PlotMark.ts index 15712652..bdc86a83 100644 --- a/packages/spec/src/spec/PlotMark.ts +++ b/packages/spec/src/spec/PlotMark.ts @@ -1,16 +1,16 @@ -import { Expression } from './Expression.js'; -import { ParamRef } from './Param.js'; -import { PlotMarkData } from './PlotFrom.js'; -import { Transform } from './Transform.js'; import { Area, AreaX, AreaY } from './marks/Area.js'; import { Arrow } from './marks/Arrow.js'; import { AxisFx, AxisFy, AxisX, AxisY, GridFx, GridFy, GridX, GridY } from './marks/Axis.js'; import { BarX, BarY } from './marks/Bar.js'; import { Cell, CellX, CellY } from './marks/Cell.js'; +import { Contour } from './marks/Contour.js'; import { DelaunayLink, DelaunayMesh, Hull, Voronoi, VoronoiMesh } from './marks/Delaunay.js'; +import { DenseLine } from './marks/DenseLine.js'; +import { Density, DensityX, DensityY } from './marks/Density.js'; import { Circle, Dot, DotX, DotY, Hexagon } from './marks/Dot.js'; import { Frame } from './marks/Frame.js'; import { Geo, Graticule, Sphere } from './marks/Geo.js'; +import { Hexbin } from './marks/Hexbin.js'; import { Hexgrid } from './marks/Hexgrid.js'; import { Image } from './marks/Image.js'; import { Line, LineX, LineY } from './marks/Line.js'; @@ -23,33 +23,21 @@ import { Text,TextX, TextY } from './marks/Text.js'; import { TickX, TickY } from './marks/Tick.js'; import { Spike, Vector, VectorX, VectorY } from './marks/Vector.js'; -export type MarkType = - | 'densityX' - | 'densityY' - | 'density' - | 'denseLine' - | 'contour' - | 'hexbin'; - -export type MarkOption = - | ParamRef - | number - | string - | boolean - | Expression - | Transform - | any[]; - +/** A plot mark entry. */ export type PlotMark = | Area | AreaX | AreaY | Arrow | AxisX | AxisY | AxisFx | AxisFy | GridX | GridY | GridFx | GridFy | BarX | BarY | Cell | CellX | CellY + | Contour | DelaunayLink | DelaunayMesh | Hull | Voronoi | VoronoiMesh + | DenseLine + | Density | DensityX | DensityY | Dot | DotX | DotY | Circle | Hexagon | Frame | Geo | Graticule | Sphere + | Hexbin | Hexgrid | Image | Line | LineX | LineY @@ -60,86 +48,4 @@ export type PlotMark = | RuleX | RuleY | Text | TextX | TextY | TickX | TickY - | Vector | VectorX | VectorY | Spike - | GenericPlotMark; - -/** - * A graphical mark (layer) for a plot. - */ -export interface GenericPlotMark { - /** The mark type. */ - mark: MarkType; - - /** The data the mark should visualize. */ - data?: PlotMarkData; - - filter?: MarkOption; - sort?: any; - clip?: MarkOption; - - title?: MarkOption; - tip?: MarkOption; - href?: MarkOption; - target?: MarkOption; - frameAnchor?: MarkOption; - - x?: MarkOption; - x1?: MarkOption; - x2?: MarkOption; - y?: MarkOption; - y1?: MarkOption; - y2?: MarkOption; - fx?: MarkOption; - fy?: MarkOption; - - z?: MarkOption; - r?: MarkOption; - length?: MarkOption; - rotate?: MarkOption; - symbol?: MarkOption; - opacity?: MarkOption; - fill?: MarkOption; - fillOpacity?: MarkOption; - stroke?: MarkOption; - strokeOpacity?: MarkOption; - strokeWidth?: MarkOption; - strokeDasharray?: MarkOption; - - geometry?: MarkOption; - - text?: MarkOption; - textAnchor?: MarkOption; - lineAnchor?: MarkOption; - fontSize?: MarkOption; - dx?: MarkOption; - dy?: MarkOption; - - anchor?: MarkOption; - label?: MarkOption; - labelAnchor?: MarkOption; - tickFormat?: MarkOption; - tickSize?: MarkOption; - tickPadding?: MarkOption; - - bandwidth?: MarkOption; - pixelSize?: MarkOption; - interpolate?: MarkOption; - pad?: MarkOption; - - width?: MarkOption; - height?: MarkOption; - - src?: MarkOption; - imageRendering?: MarkOption; - preserveAspectRatio?: MarkOption; - - thresholds?: MarkOption; - - normalize?: MarkOption; - - binWidth?: MarkOption; - inset?: MarkOption; - curve?: MarkOption; - marker?: MarkOption; - bend?: MarkOption; -} + | Vector | VectorX | VectorY | Spike; diff --git a/packages/spec/src/spec/Spec.ts b/packages/spec/src/spec/Spec.ts index cf4c3f63..ed59c322 100644 --- a/packages/spec/src/spec/Spec.ts +++ b/packages/spec/src/spec/Spec.ts @@ -31,6 +31,20 @@ export type Data = Record; /** Top-level Param and Selection definitions. */ export type Params = Record; +/** Top-level specification properties. */ +export interface SpecHead { + /** Specification metadata. */ + meta?: Meta; + /** Configuration options. */ + config?: Config; + /** Dataset definitions. */ + data?: Data; + /** Param and Selection definitions. */ + params?: Params; + /** A default set of attributes to apply to all plot components. */ + plotDefaults?: PlotAttributes; +} + /** A specifcation component such as a plot, input widget, or layout. */ export type Component = | HConcat @@ -46,15 +60,4 @@ export type Component = | Legend; /** A declarative Mosaic specification. */ -export type Spec = { - /** Specification metadata. */ - meta?: Meta; - /** Configuration options. */ - config?: Config; - /** Dataset definitions. */ - data?: Data; - /** Param and Selection definitions. */ - params?: Params; - /** A default set of attributes to apply to all plot components. */ - plotDefaults?: PlotAttributes; -} & Component; +export type Spec = SpecHead & Component; diff --git a/packages/spec/src/spec/Transform.ts b/packages/spec/src/spec/Transform.ts index 64176044..79b14d16 100644 --- a/packages/spec/src/spec/Transform.ts +++ b/packages/spec/src/spec/Transform.ts @@ -1,27 +1,47 @@ import { ParamRef } from './Param.js'; -export type TransformField = - | string - | ParamRef; +/** A field argument to a data transform. */ +export type TransformField = string | ParamRef; +/** Window transform options. */ export interface WindowOptions { orderby?: TransformField | TransformField[]; partitionby?: TransformField | TransformField[]; - rows?: ParamRef | (number | null)[]; - range?: ParamRef | (number | null)[]; + rows?: (number | null)[] | ParamRef; + range?: (number | null)[] | ParamRef; } +/** Aggregate transform options. */ export interface AggregateOptions { distinct?: boolean; } +/** A transform argument. */ type Arg = string | number | boolean; + +/** A zero argument transform signature. */ type Arg0 = null | []; + +/** A single argument transform signature. */ type Arg1 = Arg | [Arg]; + +/** + * A two argument transform signature; both arguments are required. + */ type Arg2 = [Arg, Arg]; + +/** + * A two argument transform signature; the second argument is optional. + */ type Arg2Opt = Arg | [Arg, Arg?]; + +/** + * A three argument transform signature; the + * second and third arguments are optional. + */ type Arg3Opt = Arg | [Arg, Arg?, Arg?]; +/** Bin transform options. */ export interface BinOptions { /** * The target number of binning steps to use. To accommodate human-friendly @@ -44,6 +64,7 @@ export interface BinOptions { offset?: number; } +/* A bin transform. */ export interface Bin { /** * Bin a continuous variable into discrete intervals. This transform accepts @@ -52,6 +73,7 @@ export interface Bin { bin: Arg | [Arg] | [Arg, BinOptions]; } +/* A dateMonth transform. */ export interface DateMonth { /** * Transform a Date value to a month boundary for cyclic comparison. @@ -60,6 +82,7 @@ export interface DateMonth { dateMonth: Arg1; } +/* A dateMonthDay transform. */ export interface DateMonthDay { /** * Transform a Date value to a month and day boundary for cyclic comparison. @@ -68,6 +91,7 @@ export interface DateMonthDay { dateMonthDay: Arg1; } +/* A dateDay transform. */ export interface DateDay { /** * Transform a Date value to a day of the month for cyclic comparison. @@ -76,6 +100,7 @@ export interface DateDay { dateDay: Arg1; } +/* A centroid transform. */ export interface Centroid { /** * Compute the 2D centroid of geometry-typed data. @@ -84,6 +109,7 @@ export interface Centroid { centroid: Arg1; } +/* A centroidX transform. */ export interface CentroidX { /** * Compute the centroid x-coordinate of geometry-typed data. @@ -92,6 +118,7 @@ export interface CentroidX { centroidX: Arg1; } +/* A centroidY transform. */ export interface CentroidY { /** * Compute the centroid y-coordinate of geometry-typed data. @@ -100,6 +127,7 @@ export interface CentroidY { centroidY: Arg1; } +/* A geojson transform. */ export interface GeoJSON { /** * Compute a GeoJSON-formatted string from geometry-typed data. @@ -108,6 +136,7 @@ export interface GeoJSON { geojson: Arg1; } +/* An argmax aggregate transform. */ export interface Argmax extends AggregateOptions, WindowOptions { /** * Find a value of the first column that maximizes the second column. @@ -115,6 +144,7 @@ export interface Argmax extends AggregateOptions, WindowOptions { argmax: Arg2; } +/* An argmin aggregate transform. */ export interface Argmin extends AggregateOptions, WindowOptions { /** * Find a value of the first column that minimizes the second column. @@ -122,6 +152,7 @@ export interface Argmin extends AggregateOptions, WindowOptions { argmin: Arg2; } +/* An avg (average, or mean) aggregate transform. */ export interface Avg extends AggregateOptions, WindowOptions { /** * Compute the average (mean) value of the given column. @@ -129,6 +160,7 @@ export interface Avg extends AggregateOptions, WindowOptions { avg: Arg1; } +/* A count aggregate transform. */ export interface Count extends AggregateOptions, WindowOptions { /** * Compute the count of records in an aggregation group. @@ -136,6 +168,7 @@ export interface Count extends AggregateOptions, WindowOptions { count: Arg0 | Arg1; } +/* A first aggregate transform. */ export interface First extends AggregateOptions, WindowOptions { /** * Return the first column value found in an aggregation group. @@ -143,6 +176,7 @@ export interface First extends AggregateOptions, WindowOptions { first: Arg1; } +/* A last aggregate transform. */ export interface Last extends AggregateOptions, WindowOptions { /** * Return the last column value found in an aggregation group. @@ -150,6 +184,7 @@ export interface Last extends AggregateOptions, WindowOptions { last: Arg1; } +/* A max aggregate transform. */ export interface Max extends AggregateOptions, WindowOptions { /** * Compute the maximum value of the given column. @@ -157,6 +192,7 @@ export interface Max extends AggregateOptions, WindowOptions { max: Arg1; } +/* A min aggregate transform. */ export interface Min extends AggregateOptions, WindowOptions { /** * Compute the minimum value of the given column. @@ -164,6 +200,7 @@ export interface Min extends AggregateOptions, WindowOptions { min: Arg1; } +/* A median aggregate transform. */ export interface Median extends AggregateOptions, WindowOptions { /** * Compute the median value of the given column. @@ -171,6 +208,7 @@ export interface Median extends AggregateOptions, WindowOptions { median: Arg1; } +/* A mode aggregate transform. */ export interface Mode extends AggregateOptions, WindowOptions { /** * Compute the mode value of the given column. @@ -178,6 +216,7 @@ export interface Mode extends AggregateOptions, WindowOptions { mode: Arg1; } +/* A product aggregate transform. */ export interface Product extends AggregateOptions, WindowOptions { /** * Compute the product of the given column. @@ -185,6 +224,7 @@ export interface Product extends AggregateOptions, WindowOptions { product: Arg1; } +/* A quantile aggregate transform. */ export interface Quantile extends AggregateOptions, WindowOptions { /** * Compute the quantile value of the given column at the provided @@ -193,6 +233,7 @@ export interface Quantile extends AggregateOptions, WindowOptions { quantile: Arg2; } +/* A sum aggregate transform. */ export interface Sum extends AggregateOptions, WindowOptions { /** * Compute the sum of the given column. @@ -200,6 +241,7 @@ export interface Sum extends AggregateOptions, WindowOptions { sum: Arg1; } +/* A row_number window transform. */ export interface RowNumber extends WindowOptions { /** * Compute the 1-based row number over an ordered window partition. @@ -207,6 +249,7 @@ export interface RowNumber extends WindowOptions { row_number: Arg0; } +/* A rank window transform. */ export interface Rank extends WindowOptions { /** * Compute the row rank over an ordered window partition. @@ -215,6 +258,7 @@ export interface Rank extends WindowOptions { rank: Arg0; } +/* A dense_rank window transform. */ export interface DenseRank extends WindowOptions { /** * Compute the dense row rank (no gaps) over an ordered window partition. @@ -223,6 +267,7 @@ export interface DenseRank extends WindowOptions { dense_rank: Arg0; } +/* A percent_rank window transform. */ export interface PercentRank extends WindowOptions { /** * Compute the percetange rank over an ordered window partition. @@ -230,6 +275,7 @@ export interface PercentRank extends WindowOptions { percent_rank: Arg0; } +/* A cume_dist window transform. */ export interface CumeDist extends WindowOptions { /** * Compute the cumulative distribution value over an ordered window @@ -239,6 +285,7 @@ export interface CumeDist extends WindowOptions { cume_dist: Arg0; } +/* An ntile window transform. */ export interface NTile extends WindowOptions { /** * Compute an n-tile integer ranging from 1 to the provided argument @@ -247,6 +294,7 @@ export interface NTile extends WindowOptions { ntile: Arg1; } +/* A lag window transform. */ export interface Lag extends WindowOptions { /** * Compute lagging values in a column. Returns the value at the row that is @@ -258,6 +306,7 @@ export interface Lag extends WindowOptions { lag: Arg3Opt; } +/* A lead window transform. */ export interface Lead extends WindowOptions { /** * Compute leading values in a column. Returns the value at the row that is @@ -269,6 +318,7 @@ export interface Lead extends WindowOptions { lag: Arg3Opt; } +/* A first_value window transform. */ export interface FirstValue extends WindowOptions { /** * Get the first value of the given column in the current window frame. @@ -276,6 +326,7 @@ export interface FirstValue extends WindowOptions { first_value: Arg1; } +/* A last_value window transform. */ export interface LastValue extends WindowOptions { /** * Get the last value of the given column in the current window frame. @@ -283,6 +334,7 @@ export interface LastValue extends WindowOptions { last_value: Arg1; } +/* An nth_value window transform. */ export interface NthValue extends WindowOptions { /** * Get the nth value of the given column in the current window frame, @@ -291,7 +343,8 @@ export interface NthValue extends WindowOptions { nth_value: Arg2Opt; } -export type Transform = +/** A data transform that maps one column value to another. */ +export type ColumnTransform = | Bin | DateMonth | DateMonthDay @@ -299,7 +352,10 @@ export type Transform = | Centroid | CentroidX | CentroidY - | GeoJSON + | GeoJSON; + +/** An aggregate transform that combines multiple values. */ +export type AggregateTransform = | Argmax | Argmin | Avg @@ -314,7 +370,10 @@ export type Transform = | Mode | Product | Quantile - | Sum + | Sum; + +/* A window transform that operates over a sorted domain. */ +export type WindowTransform = | RowNumber | Rank | DenseRank @@ -327,3 +386,9 @@ export type Transform = | FirstValue | LastValue | NthValue; + +/** A data transform. */ +export type Transform = + | ColumnTransform + | AggregateTransform + | WindowTransform; diff --git a/packages/spec/src/spec/VConcat.ts b/packages/spec/src/spec/VConcat.ts index ccd2af70..80a4c2bd 100644 --- a/packages/spec/src/spec/VConcat.ts +++ b/packages/spec/src/spec/VConcat.ts @@ -1,5 +1,6 @@ import { Component } from './Spec.js'; +/** A vconcat component. */ export interface VConcat { /** * Vertically concatenate components in a column layout. diff --git a/packages/spec/src/spec/VSpace.ts b/packages/spec/src/spec/VSpace.ts index 2fa0b4e3..32db577d 100644 --- a/packages/spec/src/spec/VSpace.ts +++ b/packages/spec/src/spec/VSpace.ts @@ -1,3 +1,4 @@ +/** A vspace component. */ export interface VSpace { /** * Vertical space to place between components. diff --git a/packages/spec/src/spec/interactors/Highlight.ts b/packages/spec/src/spec/interactors/Highlight.ts index 08e325bd..95eac6f5 100644 --- a/packages/spec/src/spec/interactors/Highlight.ts +++ b/packages/spec/src/spec/interactors/Highlight.ts @@ -1,5 +1,6 @@ import { ParamRef } from '../Param.js'; +/** A highlight interactor. */ export interface Highlight { /** * Highlight selected marks by deemphasizing the others. diff --git a/packages/spec/src/spec/interactors/Interval1D.ts b/packages/spec/src/spec/interactors/Interval1D.ts index 0c9c41ae..f164463f 100644 --- a/packages/spec/src/spec/interactors/Interval1D.ts +++ b/packages/spec/src/spec/interactors/Interval1D.ts @@ -1,5 +1,6 @@ import { ParamRef } from '../Param.js'; +/** Styles for rectangular selection brushes. */ export interface BrushStyles { /** * The overall opacity of the brush rectangle. @@ -23,6 +24,7 @@ export interface BrushStyles { stroke?: string; } +/** Options for 1D interval interactors. */ export interface Interval1DOptions { /** * The output selection. A clause of the form `field BETWEEN lo AND hi` @@ -52,11 +54,13 @@ export interface Interval1DOptions { brush?: BrushStyles; } +/** An intervalX interactor. */ export interface IntervalX extends Interval1DOptions { /** Select a continuous 1D interval selection over the `x` scale domain. */ select: 'intervalX'; } +/** An intervalY interactor. */ export interface IntervalY extends Interval1DOptions { /** Select a continuous 1D interval selection over the `y` scale domain. */ select: 'intervalY'; diff --git a/packages/spec/src/spec/interactors/Interval2D.ts b/packages/spec/src/spec/interactors/Interval2D.ts index f8aa5a1a..d4844520 100644 --- a/packages/spec/src/spec/interactors/Interval2D.ts +++ b/packages/spec/src/spec/interactors/Interval2D.ts @@ -1,6 +1,7 @@ import { ParamRef } from '../Param.js'; import { BrushStyles } from './Interval1D.js'; +/** Options for 2D interval interactors. */ export interface Interval2DOptions { /** * The output selection. A clause of the form @@ -37,6 +38,7 @@ export interface Interval2DOptions { brush?: BrushStyles; } +/** An intervalXY interactor. */ export interface IntervalXY extends Interval2DOptions { /** * Select a continuous 2D interval selection diff --git a/packages/spec/src/spec/interactors/Nearest.ts b/packages/spec/src/spec/interactors/Nearest.ts index e6c00961..83a1a12b 100644 --- a/packages/spec/src/spec/interactors/Nearest.ts +++ b/packages/spec/src/spec/interactors/Nearest.ts @@ -1,5 +1,6 @@ import { ParamRef } from '../Param.js'; +/** Options for nearest interactors. */ export interface NearestOptions { /** * The output selection. A clause of the form `field = value` @@ -14,12 +15,14 @@ export interface NearestOptions { field?: string; } +/** A nearestX interactor. */ export interface NearestX extends NearestOptions { - /** Select the `x` domain value of the mark closest to the pointer. */ + /** Select the **x** domain value of the mark closest to the pointer. */ select: 'nearestX'; } +/** A nearestY interactor. */ export interface NearestY extends NearestOptions { - /** Select the `y` domain value of the mark closest to the pointer. */ + /** Select the **y** domain value of the mark closest to the pointer. */ select: 'nearestY'; } diff --git a/packages/spec/src/spec/interactors/PanZoom.ts b/packages/spec/src/spec/interactors/PanZoom.ts index 61b249ea..3257253c 100644 --- a/packages/spec/src/spec/interactors/PanZoom.ts +++ b/packages/spec/src/spec/interactors/PanZoom.ts @@ -1,5 +1,6 @@ import { ParamRef } from '../Param.js'; +/** Options for pan/zoom interactors. */ export interface PanZoomOptions { /** * The output selection for the `x` domain. @@ -27,31 +28,37 @@ export interface PanZoomOptions { yfield?: string; } +/** A pan interactor. */ export interface Pan extends PanZoomOptions { /** Pan a plot along both the `x` and `y` scales. */ select: 'pan'; } +/** A panX interactor. */ export interface PanX extends PanZoomOptions { /** Pan a plot along the `x` scale only. */ select: 'panX'; } +/** A panY interactor. */ export interface PanY extends PanZoomOptions { /** Pan a plot along the `y` scale only. */ select: 'panY'; } +/** A panZoom interactor. */ export interface PanZoom extends PanZoomOptions { /** Pan and zoom a plot along both the `x` and `y` scales. */ select: 'panZoom'; } +/** A panZoomX interactor. */ export interface PanZoomX extends PanZoomOptions { /** Pan and zoom a plot along the `x` scale only. */ select: 'panZoomX'; } +/** A panZoomY interactor. */ export interface PanZoomY extends PanZoomOptions { /** Pan and zoom a plot along the `y` scale only. */ select: 'panZoomY'; diff --git a/packages/spec/src/spec/interactors/Toggle.ts b/packages/spec/src/spec/interactors/Toggle.ts index a3d3cddd..470ff915 100644 --- a/packages/spec/src/spec/interactors/Toggle.ts +++ b/packages/spec/src/spec/interactors/Toggle.ts @@ -1,5 +1,6 @@ import { ParamRef } from '../Param.js'; +/** Options for toggle interactors. */ export interface ToggleOptions { /** * The output selection. A clause of the form @@ -15,6 +16,7 @@ export interface ToggleOptions { peers?: boolean; } +/** A toggle interactor. */ export interface Toggle extends ToggleOptions { /** Select individal values. */ select: 'toggle'; @@ -26,6 +28,7 @@ export interface Toggle extends ToggleOptions { channels: string[]; } +/** A toggleX interactor. */ export interface ToggleX extends ToggleOptions { /** * Select individal values in the `x` scale domain. @@ -34,6 +37,7 @@ export interface ToggleX extends ToggleOptions { select: 'toggleX'; } +/** A toggleY interactor. */ export interface ToggleY extends ToggleOptions { /** * Select individal values in the `y` scale domain. @@ -42,6 +46,7 @@ export interface ToggleY extends ToggleOptions { select: 'toggleY'; } +/** A toggleColor interactor. */ export interface ToggleColor extends ToggleOptions { /** * Select individal values in the `color` scale domain. diff --git a/packages/spec/src/spec/marks/Area.ts b/packages/spec/src/spec/marks/Area.ts index 4fdb5d37..6b1ec0c5 100644 --- a/packages/spec/src/spec/marks/Area.ts +++ b/packages/spec/src/spec/marks/Area.ts @@ -4,7 +4,7 @@ import { } from './Marks.js'; /** Options for the area, areaX, and areaY marks. */ -export interface AreaOptions extends MarkData, MarkOptions, StackOptions, CurveOptions { +export interface AreaOptions extends MarkOptions, StackOptions, CurveOptions { /** * The required primary (starting, often left) horizontal position channel, * representing the area’s baseline, typically bound to the *x* scale. For @@ -84,7 +84,7 @@ export interface AreaYOptions extends Omit { } /** The area mark. */ -export interface Area extends AreaOptions { +export interface Area extends MarkData, AreaOptions { /** * An area mark. The area mark is rarely used directly; it is only needed * when the baseline and topline have neither *x* nor *y* values in common. @@ -96,7 +96,7 @@ export interface Area extends AreaOptions { } /** The areaX mark. */ -export interface AreaX extends AreaXOptions { +export interface AreaX extends MarkData, AreaXOptions { /** * A vertically-oriented area mark, where the baseline and topline share * **y** values, as in a time-series area chart where time goes up↑. @@ -125,7 +125,7 @@ export interface AreaX extends AreaXOptions { } /** The areaY mark. */ -export interface AreaY extends AreaYOptions { +export interface AreaY extends MarkData, AreaYOptions { /** * A horizontally-oriented area mark, where the baseline and topline share * **x** values, as in a time-series area chart where time goes right→. diff --git a/packages/spec/src/spec/marks/Arrow.ts b/packages/spec/src/spec/marks/Arrow.ts index aee860f9..1120c11b 100644 --- a/packages/spec/src/spec/marks/Arrow.ts +++ b/packages/spec/src/spec/marks/Arrow.ts @@ -2,7 +2,7 @@ import { ParamRef } from '../Param.js'; import { ChannelValueSpec, MarkData, MarkOptions } from './Marks.js'; /** Options for the arrow mark. */ -export interface ArrowOptions extends MarkData, MarkOptions { +export interface ArrowOptions extends MarkOptions { /** * The horizontal position, for vertical arrows; typically bound to the *x* * scale; shorthand for setting defaults for both **x1** and **x2**. @@ -99,7 +99,7 @@ export interface ArrowOptions extends MarkData, MarkOptions { } /** The arrow mark. */ -export interface Arrow extends ArrowOptions { +export interface Arrow extends MarkData, ArrowOptions { /** * An arrow mark, drawing (possibly swoopy) arrows connecting pairs of * points. diff --git a/packages/spec/src/spec/marks/Bar.ts b/packages/spec/src/spec/marks/Bar.ts index 4074f605..3bc839e7 100644 --- a/packages/spec/src/spec/marks/Bar.ts +++ b/packages/spec/src/spec/marks/Bar.ts @@ -4,7 +4,7 @@ import { ChannelValueIntervalSpec, ChannelValueSpec, InsetOptions, MarkData, Mar import { RectCornerOptions } from './Rect.js'; /** Options for the barX and barY marks. */ -interface BarOptions extends MarkData, MarkOptions, InsetOptions, RectCornerOptions, StackOptions { +interface BarOptions extends MarkOptions, InsetOptions, RectCornerOptions, StackOptions { /** * How to convert a continuous value (**x** for barX, or **y** for barY) into * an interval (**x1** and **x2** for barX, or **y1** and **y2** for barY); @@ -110,7 +110,7 @@ export interface BarYOptions extends BarOptions { } /** The barX mark. */ -export interface BarX extends BarXOptions { +export interface BarX extends MarkData, BarXOptions { /** * A horizontal bar mark. The required *x* values should be quantitative or * temporal, and the optional *y* values should be ordinal. @@ -135,7 +135,7 @@ export interface BarX extends BarXOptions { } /** The barY mark. */ -export interface BarY extends BarYOptions { +export interface BarY extends MarkData, BarYOptions { /** * A vertical bar mark. The required *y* values should be quantitative or * temporal, and the optional *x* values should be ordinal. diff --git a/packages/spec/src/spec/marks/Cell.ts b/packages/spec/src/spec/marks/Cell.ts index 3a4e1cac..20d7a84f 100644 --- a/packages/spec/src/spec/marks/Cell.ts +++ b/packages/spec/src/spec/marks/Cell.ts @@ -2,7 +2,7 @@ import { ChannelValueSpec, InsetOptions, MarkData, MarkOptions } from './Marks.j import { RectCornerOptions } from './Rect.js'; /** Options for the cell mark. */ -export interface CellOptions extends MarkData, MarkOptions, InsetOptions, RectCornerOptions { +export interface CellOptions extends MarkOptions, InsetOptions, RectCornerOptions { /** * The horizontal position of the cell; an optional ordinal channel typically * bound to the *x* scale. If not specified, the cell spans the horizontal @@ -25,7 +25,7 @@ export interface CellOptions extends MarkData, MarkOptions, InsetOptions, RectCo } /** The cell mark. */ -export interface Cell extends CellOptions { +export interface Cell extends MarkData, CellOptions { /** * A rectangular cell mark. Along with **x** and/or **y**, a **fill** channel * is typically specified to encode value as color. @@ -42,7 +42,7 @@ export interface Cell extends CellOptions { } /** The cellX mark. */ -export interface CellX extends CellOptions { +export interface CellX extends MarkData, CellOptions { /** * Like cell, but **x** defaults to the zero-based index [0, 1, 2, …], and if * **stroke** is not a channel, **fill** defaults to the identity function, @@ -52,7 +52,7 @@ export interface CellX extends CellOptions { } /** The cellY mark. */ -export interface CellY extends CellOptions { +export interface CellY extends MarkData, CellOptions { /** * Like cell, but **y** defaults to the zero-based index [0, 1, 2, …], and if * **stroke** is not a channel, **fill** defaults to the identity function, diff --git a/packages/spec/src/spec/marks/Contour.ts b/packages/spec/src/spec/marks/Contour.ts new file mode 100644 index 00000000..e65a1668 --- /dev/null +++ b/packages/spec/src/spec/marks/Contour.ts @@ -0,0 +1,25 @@ +import { ParamRef } from '../Param.js'; +import { MarkData, MarkOptions } from './Marks.js'; +import { Grid2DOptions } from './Raster.js'; + +export interface ContourOptions extends MarkOptions, Grid2DOptions { + /** + * The number of contour thresholds to subdivide the domain into discrete + * level sets; defaults to 10. One of: + * + * - a count representing the desired number of bins + * - an array of *n* threshold values for *n* - 1 bins + */ + thresholds?: number | number[] | ParamRef; +} + +/** The contour mark. */ +export interface Contour extends MarkData, ContourOptions { + /** + * A contour mark that draws isolines to delineate regions above and below a + * particular continuous value. It is often used to convey densities as a + * height field. The special column name "density" can be used to map density + * values to the fill or stroke options. + */ + mark: 'contour'; +} diff --git a/packages/spec/src/spec/marks/Delaunay.ts b/packages/spec/src/spec/marks/Delaunay.ts index 7c3433ab..9a02e51d 100644 --- a/packages/spec/src/spec/marks/Delaunay.ts +++ b/packages/spec/src/spec/marks/Delaunay.ts @@ -4,19 +4,22 @@ import { } from './Marks.js'; /** Options for the Delaunay marks. */ -export interface DelaunayOptions extends MarkData, MarkOptions, MarkerOptions, CurveOptions { +export interface DelaunayOptions extends MarkOptions, MarkerOptions, CurveOptions { /** The horizontal position channel, typically bound to the *x* scale. */ x?: ChannelValueSpec; /** The vertical position channel, typically bound to the *y* scale. */ y?: ChannelValueSpec; - /** An optional ordinal channel for grouping to produce multiple (possibly overlapping) triangulations. */ + /** + * An optional ordinal channel for grouping to produce multiple + * (possibly overlapping) triangulations. + */ z?: ChannelValue; } /** The delaunayLink mark. */ -export interface DelaunayLink extends DelaunayOptions { +export interface DelaunayLink extends MarkData, DelaunayOptions { /** - *A mark that draws links for each edge of the Delaunay triangulation + * A mark that draws links for each edge of the Delaunay triangulation * of points given by the **x** and **y** channels. Like the link mark, * except that **x1**, **y1**, **x2**, and **y2** are derived automatically * from **x** and **y**. When an aesthetic channel is specified (such as @@ -30,7 +33,7 @@ export interface DelaunayLink extends DelaunayOptions { } /** The delaunayMesh mark. */ -export interface DelaunayMesh extends DelaunayOptions { +export interface DelaunayMesh extends MarkData, DelaunayOptions { /** * A mark that draws a mesh of the Delaunay triangulation of the points * given by the **x** and **y** channels. The **stroke** option defaults to @@ -46,7 +49,7 @@ export interface DelaunayMesh extends DelaunayOptions { } /** The hull mark. */ -export interface Hull extends DelaunayOptions { +export interface Hull extends MarkData, DelaunayOptions { /** * A mark that draws a convex hull around the points given by the **x** and * **y** channels. The **stroke** option defaults to _currentColor_ and the @@ -63,7 +66,7 @@ export interface Hull extends DelaunayOptions { } /** The voronoi mark. */ -export interface Voronoi extends DelaunayOptions { +export interface Voronoi extends MarkData, DelaunayOptions { /** * A mark that draws polygons for each cell of the Voronoi tesselation * of the points given by the **x** and **y** channels. @@ -75,7 +78,7 @@ export interface Voronoi extends DelaunayOptions { } /** The voronoiMesh mark. */ -export interface VoronoiMesh extends DelaunayOptions { +export interface VoronoiMesh extends MarkData, DelaunayOptions { /** * A mark that draws a mesh for the cell boundaries of the Voronoi * tesselation of the points given by the **x** and **y** channels. The diff --git a/packages/spec/src/spec/marks/DenseLine.ts b/packages/spec/src/spec/marks/DenseLine.ts new file mode 100644 index 00000000..ad9d78fb --- /dev/null +++ b/packages/spec/src/spec/marks/DenseLine.ts @@ -0,0 +1,30 @@ +import { ParamRef } from '../Param.js'; +import { ChannelValue, MarkData } from './Marks.js'; +import { RasterOptions } from './Raster.js'; + +export interface DenseLineOptions extends RasterOptions { + /** + * Flag to perform approximate arc length normalization of line segments + * to prevent artifacts due to overcounting steep lines. Defaults to `true`. + */ + normalize?: boolean | ParamRef; + + /** + * A ordinal channel for grouping data into series to be drawn as separate + * lines. + */ + z?: ChannelValue; +} + +/** The denseLine mark. */ +export interface DenseLine extends MarkData, DenseLineOptions { + /** + * A denseLine mark that plots line densities rather than point densities. + * The mark forms a binned raster grid and "draws" straight lines into it. + * To avoid over-weighting steep lines, by default each drawn series is + * normalized on a per-column basis to approximate arc length normalization. + * The values for each series are aggregated to form the line density, which + * is then drawn as an image similar to the raster mark. + */ + mark: 'denseLine'; +} diff --git a/packages/spec/src/spec/marks/Density.ts b/packages/spec/src/spec/marks/Density.ts new file mode 100644 index 00000000..845ec2ed --- /dev/null +++ b/packages/spec/src/spec/marks/Density.ts @@ -0,0 +1,145 @@ +import { ParamRef } from "../Param.js"; +import { AreaXOptions, AreaYOptions } from "./Area.js"; +import { DotOptions } from "./Dot.js"; +import { LineXOptions, LineYOptions } from "./Line.js"; +import { MarkData, MarkOptions, TextStyles } from "./Marks.js"; +import { Grid2DOptions } from "./Raster.js"; +import { TextOptions } from "./Text.js"; + +// Density2D + +export interface Density2DOptions extends MarkOptions, Omit, TextStyles, Grid2DOptions { + /** + * The basic mark type to use to render 2D density values. + * Defaults to a dot mark; cell and text marks are also supported. + */ + type?: 'dot' | 'circle' | 'hexagon' | 'cell' | 'text' | ParamRef; +} + +/** The density mark for 2D densities. */ +export interface Density extends MarkData, Density2DOptions { + /** + * A 2D density mark that shows smoothed point cloud densities along two + * dimensions. The mark bins the data, counts the number of records that fall + * into each bin, and smooths the resulting counts, then plots the smoothed + * distribution, by default using a circular dot mark. The density mark + * calculates density values that can be mapped to encoding channels such as + * fill or r using the special field name "density". + * + * Set the *type* property to use a different base mark type. + */ + mark: 'density'; +} + +// Density1D + +export interface Density1DOptions { + /** + * The kernel density bandwidth for smoothing, in pixels. Defaults to 20. + */ + bandwidth?: number | ParamRef; + + /** + * The number of bins over which to discretize the data prior to smoothing. + * Defaults to 1024. + */ + bins?: number | ParamRef; +} + +export interface DensityAreaXOptions extends Omit { + /** + * The basic mark type to use to render 1D density values. Defaults to an + * areaX mark; lineX, dotX, and textX marks are also supported. + */ + type: 'areaX'; +} + +export interface DensityAreaYOptions extends Omit { + /** + * The basic mark type to use to render 1D density values. Defaults to an + * areaY mark; lineY, dot, and text marks are also supported. + */ + type?: 'areaY'; +} + +export interface DensityLineXOptions extends Omit { + /** + * The basic mark type to use to render 1D density values. Defaults to an + * areaX mark; lineX, dotX, and textX marks are also supported. + */ + type: 'lineX'; +} + +export interface DensityLineYOptions extends Omit { + /** + * The basic mark type to use to render 1D density values. Defaults to an + * areaY mark; lineY, dot, and text marks are also supported. + */ + type: 'lineY'; +} + +export interface DensityDotXOptions extends Omit { + /** + * The basic mark type to use to render 1D density values. Defaults to an + * areaX mark; lineX, dotX, and textX marks are also supported. + */ + type: 'dotX'; +} + +export interface DensityDotYOptions extends Omit { + /** + * The basic mark type to use to render 1D density values. Defaults to an + * areaY mark; lineY, dot, and text marks are also supported. + */ + type: 'dot' | 'dotY' | 'circle' | 'hexagon'; +} + +export interface DensityTextXOptions extends Omit { + /** + * The basic mark type to use to render 1D density values. Defaults to an + * areaX mark; lineX, dotX, and textX marks are also supported. + */ + type: 'textX'; +} + +export interface DensityTextYOptions extends Omit { + /** + * The basic mark type to use to render 1D density values. Defaults to an + * areaY mark; lineY, dot, and text marks are also supported. + */ + type: 'text' | 'textY'; +} + +export interface DensityXBase extends MarkData, Density1DOptions { + /** + * A densityX mark that visualizes smoothed point cloud densities along the + * **x** dimension. The mark bins the data, counts the number of records that + * fall into each bin, smooths the resulting counts, and then plots the + * smoothed distribution, by default using an areaX mark. + * + * Set the *type* property to use a different base mark type. + */ + mark: 'densityX'; +} + +export interface DensityYBase extends MarkData, Density1DOptions { + /** + * A densityY mark that visualizes smoothed point cloud densities along the + * **y** dimension. The mark bins the data, counts the number of records that + * fall into each bin, smooths the resulting counts, and then plots the + * smoothed distribution, by default using an areaY mark. + * + * Set the *type* property to use a different base mark type. + */ + mark: 'densityY'; +} + +/** The densityX mark. */ +export type DensityX = DensityXBase & ( + DensityAreaXOptions | DensityLineXOptions | DensityDotXOptions | DensityTextXOptions +); + +/** The densityY mark. */ +export type DensityY = DensityYBase & ( + DensityAreaYOptions | DensityLineYOptions | DensityDotYOptions | DensityTextYOptions +); diff --git a/packages/spec/src/spec/marks/Dot.ts b/packages/spec/src/spec/marks/Dot.ts index 06e784e7..2c76e9ef 100644 --- a/packages/spec/src/spec/marks/Dot.ts +++ b/packages/spec/src/spec/marks/Dot.ts @@ -6,7 +6,7 @@ import { } from './Marks.js'; /** Options for the dot mark. */ -export interface DotOptions extends MarkData, MarkOptions { +export interface DotOptions extends MarkOptions { /** * The horizontal position channel specifying the dot’s center, typically * bound to the *x* scale. @@ -94,7 +94,7 @@ export interface DotYOptions extends Omit { } /** The dot mark. */ -export interface Dot extends DotOptions { +export interface Dot extends MarkData, DotOptions { /** * A dot mark that draws circles, or other symbols, as in a scatterplot. * @@ -111,7 +111,7 @@ export interface Dot extends DotOptions { } /** The dotX mark. */ -export interface DotX extends DotXOptions { +export interface DotX extends MarkData, DotXOptions { /** * Like dot, except that **x** defaults to the identity function, assuming that * *data* = [*x₀*, *x₁*, *x₂*, …]. @@ -123,7 +123,7 @@ export interface DotX extends DotXOptions { } /** The dotY mark. */ -export interface DotY extends DotYOptions { +export interface DotY extends MarkData, DotYOptions { /** * Like dot, except that **y** defaults to the identity function, assuming that * *data* = [*y₀*, *y₁*, *y₂*, …]. @@ -135,13 +135,13 @@ export interface DotY extends DotYOptions { } /** The circle mark. */ -export interface Circle extends Exclude { +export interface Circle extends MarkData, Exclude { /** Like dot, except that the **symbol** option is set to *circle*. */ mark: 'circle'; } /** The hexagon mark. */ -export interface Hexagon extends Exclude { +export interface Hexagon extends MarkData, Exclude { /** Like dot, except that the **symbol** option is set to *hexagon*. */ mark: 'hexagon'; } diff --git a/packages/spec/src/spec/marks/Geo.ts b/packages/spec/src/spec/marks/Geo.ts index 31cc4702..33226a72 100644 --- a/packages/spec/src/spec/marks/Geo.ts +++ b/packages/spec/src/spec/marks/Geo.ts @@ -2,7 +2,7 @@ import { ParamRef } from '../Param.js'; import { ChannelValue, ChannelValueSpec, MarkData, MarkOptions } from './Marks.js'; /** Options for the geo mark. */ -export interface GeoOptions extends MarkData, MarkOptions { +export interface GeoOptions extends MarkOptions { /** * A required channel for the geometry to render; defaults to identity, * assuming *data* is a GeoJSON object or an iterable of GeoJSON objects. @@ -24,7 +24,7 @@ export interface GeoOptions extends MarkData, MarkOptions { } /** The geo mark. */ -export interface Geo extends GeoOptions { +export interface Geo extends MarkData, GeoOptions { /** * A geo mark. The **geometry** channel, which defaults to the identity * function assuming that *data* is a GeoJSON object or an iterable of diff --git a/packages/spec/src/spec/marks/Hexbin.ts b/packages/spec/src/spec/marks/Hexbin.ts new file mode 100644 index 00000000..2f66dbc6 --- /dev/null +++ b/packages/spec/src/spec/marks/Hexbin.ts @@ -0,0 +1,34 @@ +import { ParamRef } from '../Param.js'; +import { DotOptions } from './Dot.js'; +import { ChannelValue, MarkData, TextStyles } from './Marks.js'; + +export interface HexbinOptions extends DotOptions, TextStyles { + /** + * The basic mark type to use for hex-binned values. + * Defaults to a hexagon mark; dot and text marks are also supported. + */ + type?: 'dot' | 'circle' | 'hexagon' | 'text' | ParamRef; + + /** + * The distance between centers of neighboring hexagons, in pixels; defaults + * to 20. If also using a hexgrid mark, use matching **binWidth** values. + */ + binWidth?: number | ParamRef; + + /** + * How to subdivide bins. If not specified, defaults to the *fill* channel, + * if any, or the *stroke* channel, if any. If null, bins will not be + * subdivided. + */ + z?: ChannelValue; +} + +/** The hexbin mark. */ +export interface Hexbin extends MarkData, HexbinOptions { + /** + * A hexbin mark that bins **x** and **y** data into a hexagonal grid and + * visualizes aggregate functions per bin (e.g., count for binned density). + * Aggregate functions can be used for fill, stroke, or r (radius) options. + */ + mark: 'hexbin'; +} diff --git a/packages/spec/src/spec/marks/Hexgrid.ts b/packages/spec/src/spec/marks/Hexgrid.ts index 861adc12..e5846c66 100644 --- a/packages/spec/src/spec/marks/Hexgrid.ts +++ b/packages/spec/src/spec/marks/Hexgrid.ts @@ -5,7 +5,7 @@ import { MarkOptions } from './Marks.js'; export interface HexgridOptions extends MarkOptions { /** * The distance between centers of neighboring hexagons, in pixels; defaults - * to 20. Should match the **binWidth** of the hexbin transform. + * to 20. Should match the **binWidth** of the hexbin mark. */ binWidth?: number | ParamRef; } diff --git a/packages/spec/src/spec/marks/Image.ts b/packages/spec/src/spec/marks/Image.ts index 320cab6b..620781a2 100644 --- a/packages/spec/src/spec/marks/Image.ts +++ b/packages/spec/src/spec/marks/Image.ts @@ -3,7 +3,7 @@ import { FrameAnchor } from '../PlotTypes.js'; import { ChannelValue, ChannelValueSpec, MarkData, MarkOptions } from './Marks.js'; /** Options for the image mark. */ -export interface ImageOptions extends MarkData, MarkOptions { +export interface ImageOptions extends MarkOptions { /** * The horizontal position channel specifying the image’s center; typically * bound to the *x* scale. @@ -87,7 +87,7 @@ export interface ImageOptions extends MarkData, MarkOptions { imageRendering?: string | ParamRef; } -export interface Image extends ImageOptions { +export interface Image extends MarkData, ImageOptions { /** * An image mark that draws images as in a scatterplot. * diff --git a/packages/spec/src/spec/marks/Line.ts b/packages/spec/src/spec/marks/Line.ts index aaaaa47c..57f693f3 100644 --- a/packages/spec/src/spec/marks/Line.ts +++ b/packages/spec/src/spec/marks/Line.ts @@ -4,7 +4,7 @@ import { } from './Marks.js'; /** Options for the line mark. */ -export interface LineOptions extends MarkData, MarkOptions, MarkerOptions, CurveAutoOptions { +export interface LineOptions extends MarkOptions, MarkerOptions, CurveAutoOptions { /** * The required horizontal position channel, typically bound to the *x* * scale. @@ -43,7 +43,7 @@ export interface LineYOptions extends LineOptions { } /** The line mark. */ -export interface Line extends LineOptions { +export interface Line extends MarkData, LineOptions { /** * A line mark that connects control points. * @@ -72,8 +72,8 @@ export interface Line extends LineOptions { mark: 'line'; } -/** The linex mark. */ -export interface LineX extends LineXOptions { +/** The lineX mark. */ +export interface LineX extends MarkData, LineXOptions { /** * Like line, except that **x** defaults to the identity function assuming * that *data* = [*x₀*, *x₁*, *x₂*, …] and **y** defaults to the zero-based @@ -83,7 +83,7 @@ export interface LineX extends LineXOptions { } /** The lineY mark. */ -export interface LineY extends LineYOptions { +export interface LineY extends MarkData, LineYOptions { /** * Like line, except **y** defaults to the identity function and assumes * that *data* = [*y₀*, *y₁*, *y₂*, …] and **x** defaults to the zero-based diff --git a/packages/spec/src/spec/marks/Link.ts b/packages/spec/src/spec/marks/Link.ts index fcba341d..fba08070 100644 --- a/packages/spec/src/spec/marks/Link.ts +++ b/packages/spec/src/spec/marks/Link.ts @@ -4,7 +4,7 @@ import { } from './Marks.js'; /** Options for the link mark. */ -export interface LinkOptions extends MarkData, MarkOptions, MarkerOptions, CurveAutoOptions { +export interface LinkOptions extends MarkOptions, MarkerOptions, CurveAutoOptions { /** * The horizontal position, for vertical links; typically bound to the *x* * scale; shorthand for setting defaults for both **x1** and **x2**. @@ -58,7 +58,7 @@ export interface LinkOptions extends MarkData, MarkOptions, MarkerOptions, Curve } /** The link mark. */ -export interface Link extends LinkOptions { +export interface Link extends MarkData, LinkOptions { /** * A link mark, drawing line segments (curves) connecting pairs of points. * diff --git a/packages/spec/src/spec/marks/Marks.ts b/packages/spec/src/spec/marks/Marks.ts index 46e0e0b6..a853e9c4 100644 --- a/packages/spec/src/spec/marks/Marks.ts +++ b/packages/spec/src/spec/marks/Marks.ts @@ -196,7 +196,6 @@ export type SortOrder = | { value?: ChannelValue; order?: 'ascending' | 'descending' } | { channel?: ChannelName | `-${ChannelName}`; order?: 'ascending' | 'descending' }; - /** The pointer mode for the tip; corresponds to pointerX, pointerY, and pointer. */ export type TipPointer = 'x' | 'y' | 'xy'; diff --git a/packages/spec/src/spec/marks/Raster.ts b/packages/spec/src/spec/marks/Raster.ts index 1fe772a3..95e650ea 100644 --- a/packages/spec/src/spec/marks/Raster.ts +++ b/packages/spec/src/spec/marks/Raster.ts @@ -19,10 +19,16 @@ export type GridInterpolate = /** Options for grid2d marks. */ export interface Grid2DOptions { - /** The horizontal position channel, typically bound to the *x* scale. */ + /** + * The horizontal position channel, typically bound to the *x* scale. + * Domain values are binned into a grid with *width* horizontal bins. + */ x?: ChannelValueSpec; - /** The vertical position channel, typically bound to the *y* scale. */ + /** + * The vertical position channel, typically bound to the *y* scale. + * Domain values are binned into a grid with *height* vertical bins. + */ y?: ChannelValueSpec; /** The width (number of columns) of the grid, in actual pixels. */ @@ -44,9 +50,7 @@ export interface Grid2DOptions { pad?: number | ParamRef; /** - * A non-negative pixel radius for smoothing; defaults to 0. Note that - * blurring is applied on the values (before the color scale is applied) if - * quantitative, and after (on the materialized pixels), if ordinal. + * The kernel density bandwidth for smoothing, in pixels. */ bandwidth?: number | ParamRef; @@ -63,7 +67,7 @@ export interface Grid2DOptions { } /** Options for the raster mark. */ -export interface RasterOptions extends MarkData, Grid2DOptions, Omit { +export interface RasterOptions extends Grid2DOptions, Omit { /** * The [image-rendering attribute][1]; defaults to *auto* (bilinear). The * option may be set to *pixelated* to disable bilinear interpolation for a @@ -94,7 +98,8 @@ export interface RasterOptions extends MarkData, Grid2DOptions, Omit { } /** The text mark. */ -export interface Text extends TextOptions { +export interface Text extends MarkDataOptional, TextOptions { /** * A text mark. The **text** channel specifies the textual contents of the * mark, which may be preformatted with line breaks (\n, \r\n, or \r), or @@ -102,7 +102,7 @@ export interface Text extends TextOptions { } /** The textX mark. */ -export interface TextX extends TextXOptions { +export interface TextX extends MarkDataOptional, TextXOptions { /** * Like text, but **x** defaults to the identity function, assuming that * *data* = [*x₀*, *x₁*, *x₂*, …]. If an **interval** is specified, such as @@ -112,7 +112,7 @@ export interface TextX extends TextXOptions { } /** The textY mark. */ -export interface TextY extends TextYOptions { +export interface TextY extends MarkDataOptional, TextYOptions { /** * Like text, but **y** defaults to the identity function, assuming that * *data* = [*y₀*, *y₁*, *y₂*, …]. If an **interval** is specified, such as diff --git a/packages/spec/src/spec/marks/Tick.ts b/packages/spec/src/spec/marks/Tick.ts index 7205150c..7bce5157 100644 --- a/packages/spec/src/spec/marks/Tick.ts +++ b/packages/spec/src/spec/marks/Tick.ts @@ -3,7 +3,7 @@ import { } from './Marks.js'; /** Options for the tickX mark. */ -export interface TickXOptions extends MarkData, MarkOptions, MarkerOptions, Omit { +export interface TickXOptions extends MarkOptions, MarkerOptions, Omit { /** * The required horizontal position of the tick; a channel typically bound to * the *x* scale. @@ -22,7 +22,7 @@ export interface TickXOptions extends MarkData, MarkOptions, MarkerOptions, Omit } /** Options for the tickY mark. */ -export interface TickYOptions extends MarkData, MarkOptions, MarkerOptions, Omit { +export interface TickYOptions extends MarkOptions, MarkerOptions, Omit { /** * The required vertical position of the tick; a channel typically bound to * the *y* scale. @@ -41,7 +41,7 @@ export interface TickYOptions extends MarkData, MarkOptions, MarkerOptions, Omit } /** The tickX mark. */ -export interface TickX extends TickXOptions { +export interface TickX extends MarkData, TickXOptions { /** * A horizontally-positioned tickX mark (a vertical line, |). The **x** * channel specifies the tick’s horizontal position and defaults to identity, @@ -55,7 +55,7 @@ export interface TickX extends TickXOptions { } /** The tickY mark. */ -export interface TickY extends TickYOptions { +export interface TickY extends MarkData, TickYOptions { /** * A vertically-positioned tickY mark (a horizontal line, —). The **y** * channel specifies the tick's vertical position and defaults to identity, diff --git a/packages/spec/src/spec/marks/Vector.ts b/packages/spec/src/spec/marks/Vector.ts index 28aa05dc..1b6a66cc 100644 --- a/packages/spec/src/spec/marks/Vector.ts +++ b/packages/spec/src/spec/marks/Vector.ts @@ -14,7 +14,7 @@ export type VectorShapeName = 'arrow' | 'spike'; export type VectorShape = VectorShapeName; /** Options for the vector mark. */ -export interface VectorOptions extends MarkData, MarkOptions { +export interface VectorOptions extends MarkOptions { /** * The horizontal position of the vector’s anchor point; an optional channel * bound to the *x* scale. Default depends on the **frameAnchor**. @@ -73,7 +73,7 @@ export interface VectorOptions extends MarkData, MarkOptions { } /** The vector mark. */ -export interface Vector extends VectorOptions { +export interface Vector extends MarkData, VectorOptions { /** * A vector mark. * @@ -85,7 +85,7 @@ export interface Vector extends VectorOptions { } /** The vectorX mark. */ -export interface VectorX extends VectorOptions { +export interface VectorX extends MarkData, VectorOptions { /** * Like vector, but **x** instead defaults to the identity function and **y** * defaults to null, assuming that *data* is an array of numbers [*x₀*, *x₁*, @@ -95,7 +95,7 @@ export interface VectorX extends VectorOptions { } /** The vectorY mark. */ -export interface VectorY extends VectorOptions { +export interface VectorY extends MarkData, VectorOptions { /** * Like vector, but **y** instead defaults to the identity function and **x** * defaults to null, assuming that *data* is an array of numbers [*y₀*, *y₁*, @@ -105,7 +105,7 @@ export interface VectorY extends VectorOptions { } /** The spike mark. */ -export interface Spike extends VectorOptions { +export interface Spike extends MarkData, VectorOptions { /** * Like vector, but with default *options* suitable for drawing a spike map. */