@metabase/custom-viz 0.0.1-alpha.2 → 0.0.1-alpha.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,39 +1,100 @@
1
1
  #!/usr/bin/env node
2
- import { existsSync as e } from "node:fs";
3
- import { mkdir as t, writeFile as n } from "node:fs/promises";
4
- import { join as r } from "node:path";
5
- import { Command as i } from "commander";
2
+ import { existsSync as e, readFileSync as t } from "node:fs";
3
+ import { mkdir as n, writeFile as r } from "node:fs/promises";
4
+ import { dirname as i, join as a } from "node:path";
5
+ import { createInterface as o } from "node:readline";
6
+ import { Command as s } from "commander";
7
+ import { fileURLToPath as c } from "node:url";
6
8
  //#region package.json
7
- var a = "0.0.1-alpha.2", o = "node_modules/\n.DS_Store\n# dist must be committed\n", s = "import type {\n CreateCustomVisualization,\n CustomStaticVisualizationProps,\n CustomVisualizationProps,\n} from \"@metabase/custom-viz\";\n\ntype Settings = {\n threshold?: number;\n};\n\nconst createVisualization: CreateCustomVisualization<Settings> = () => {\n return {\n id: \"__CUSTOM_VIZ_NAME__\",\n getName: () => \"__CUSTOM_VIZ_NAME__\",\n minSize: { width: 1, height: 1 },\n defaultSize: { width: 2, height: 2 },\n isSensible({ cols, rows }) {\n return cols.length === 1 && rows.length === 1 && typeof rows[0][0] === \"number\";\n },\n checkRenderable(series, settings) {\n if (series.length !== 1) {\n throw new Error(\"Only 1 series is supported\");\n }\n\n const [\n {\n data: { cols, rows },\n },\n ] = series;\n\n if (cols.length !== 1) {\n throw new Error(\"Query results should only have 1 column\");\n }\n\n if (rows.length !== 1) {\n throw new Error(\"Query results should only have 1 row\");\n }\n\n if (typeof rows[0][0] !== \"number\") {\n throw new Error(\"Result is not a number\");\n }\n\n if (typeof settings.threshold !== \"number\") {\n throw new Error(\"Threshold setting is not set\");\n }\n },\n settings: {\n threshold: {\n id: \"1\",\n title: \"Threshold\",\n widget: \"number\",\n getDefault() {\n return 0;\n },\n getProps() {\n return {\n options: {\n isInteger: false,\n isNonNegative: false,\n },\n placeholder: \"Set threshold\",\n };\n },\n },\n },\n VisualizationComponent,\n StaticVisualizationComponent,\n };\n};\n\nconst VisualizationComponent = (props: CustomVisualizationProps<Settings>) => {\n const { height, series, settings, width } = props;\n const { threshold } = settings;\n const value = series[0].data.rows[0][0];\n\n if (typeof value !== \"number\" || typeof threshold !== \"number\") {\n throw new Error(\"Value and threshold need to be numbers\");\n }\n\n const emoji = value >= threshold ? \"👍\" : \"👎\";\n\n return (\n <div\n style={{\n display: \"flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n width,\n height,\n fontSize: \"10rem\",\n }}\n >\n {emoji}\n </div>\n );\n};\n\nconst StaticVisualizationComponent = (\n props: CustomStaticVisualizationProps<Settings>,\n) => {\n const width = 540;\n const height = 360;\n const { series, settings } = props;\n const { threshold } = settings;\n const value = series[0].data.rows[0][0];\n\n if (typeof value !== \"number\" || typeof threshold !== \"number\") {\n throw new Error(\"Value and threshold need to be numbers\");\n }\n\n const emoji =\n value >= threshold ? (\n <img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKAAAACgCAYAAACLz2ctAAAA0mVYSWZJSSoACAAAAAoAAAEEAAEAAACgAAAAAQEEAAEAAACgAAAAAgEDAAMAAACGAAAAEgEDAAEAAAABAAAAGgEFAAEAAACMAAAAGwEFAAEAAACUAAAAKAEDAAEAAAACAAAAMQECAA8AAACcAAAAMgECABQAAACsAAAAaYcEAAEAAADAAAAAAAAAAAgACAAIACwBAAABAAAALAEAAAEAAABHSU1QIDMuMC4wLVJDMQAAMjAyNjowMzoxOCAxNjoyNzo1OQABAAGgAwABAAAAAQAAAAAAAAD8VR8dAAABhWlDQ1BJQ0MgcHJvZmlsZQAAeJx9kb9Lw0AcxV9bpSoVQTuIPyBDdbKLijjWKhShQqgVWnUwufQXNGlIUlwcBdeCgz8Wqw4uzro6uAqC4A8Q/wBxUnSREr+XFFrEeHDch3f3HnfvAH+9zFSzIwaommWkEnEhk10Vgq8IoB/dGMaoxEx9ThST8Bxf9/Dx9S7Ks7zP/Tl6lZzJAJ9AHGO6YRFvEM9sWjrnfeIwK0oK8TnxhEEXJH7kuuzyG+eCw36eGTbSqXniMLFQaGO5jVnRUImniSOKqlG+P+OywnmLs1qusuY9+QtDOW1lmes0R5DAIpYgQoCMKkoow0KUVo0UEynaj3v4hxy/SC6ZXCUwciygAhWS4wf/g9/dmvmpSTcpFAc6X2z7YwwI7gKNmm1/H9t24wQIPANXWstfqQOzn6TXWlrkCOjbBi6uW5q8B1zuAINPumRIjhSg6c/ngfcz+qYsMHAL9Ky5vTX3cfoApKmr5A1wcAiMFyh73ePdXe29/Xum2d8PeZVyqWZhNiAAAA16aVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/Pgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA0LjQuMC1FeGl2MiI+CiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIKICAgIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiCiAgICB4bWxuczpHSU1QPSJodHRwOi8vd3d3LmdpbXAub3JnL3htcC8iCiAgICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgeG1wTU06RG9jdW1lbnRJRD0iZ2ltcDpkb2NpZDpnaW1wOjUyMGY3ZWIzLTNiNjktNDlkMy04NjZjLWIwOWI4YzQ4MzgwZSIKICAgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDozZDFlNjVmZS1kYWQ5LTQ4MjQtYmE5MS1hNGMyYzhmOWNjMmIiCiAgIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo4YzU1Zjg3MC02NjE4LTQ1MjAtYTE2Yy0wZmVlYWJjODE4ZTIiCiAgIEdJTVA6QVBJPSIzLjAiCiAgIEdJTVA6UGxhdGZvcm09IkxpbnV4IgogICBHSU1QOlRpbWVTdGFtcD0iMTc3MzgyNjA4MDI4Mzg3NSIKICAgR0lNUDpWZXJzaW9uPSIzLjAuMC1SQzEiCiAgIGRjOkZvcm1hdD0iaW1hZ2UvcG5nIgogICB0aWZmOk9yaWVudGF0aW9uPSIxIgogICB4bXA6Q3JlYXRvclRvb2w9IkdJTVAiCiAgIHhtcDpNZXRhZGF0YURhdGU9IjIwMjY6MDM6MThUMTY6Mjc6NTkrMDc6MDAiCiAgIHhtcDpNb2RpZnlEYXRlPSIyMDI2OjAzOjE4VDE2OjI3OjU5KzA3OjAwIj4KICAgPHhtcE1NOkhpc3Rvcnk+CiAgICA8cmRmOlNlcT4KICAgICA8cmRmOmxpCiAgICAgIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiCiAgICAgIHN0RXZ0OmNoYW5nZWQ9Ii8iCiAgICAgIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6YTc4M2Q4MzktMTY2Yy00MTM2LWI3ZDgtMWJlNGU1ZTcxNjFjIgogICAgICBzdEV2dDpzb2Z0d2FyZUFnZW50PSJHSU1QIDMuMC4wLVJDMSAoTGludXgpIgogICAgICBzdEV2dDp3aGVuPSIyMDI2LTAzLTE4VDE2OjI4OjAwKzA3OjAwIi8+CiAgICA8L3JkZjpTZXE+CiAgIDwveG1wTU06SGlzdG9yeT4KICA8L3JkZjpEZXNjcmlwdGlvbj4KIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAKPD94cGFja2V0IGVuZD0idyI/PlfBSK4AAAAGYktHRAAAAEgAkHfF9EMAAAAJcEhZcwAALiMAAC4jAXilP3YAAAAHdElNRQfqAxIJHAAvHRFSAAAAGXRFWHRDb21tZW50AENyZWF0ZWQgd2l0aCBHSU1QV4EOFwAACzdJREFUeNrtnXuQ1lUZxz/LLrHcZLnIgoYNN02oNGAyCUqUS2o6aRenZlKwEawxGa0ZJhswrZFwYsCKGqapyHEatYYIzUshTWqCFGCajFxWwEkEgWW5yHXh7Y/z21iWRZ7zvuf3vr/L9zNzZvjj8J7fec53z/08TxWiFGqBScBEYCQwCKgDCkAjsAFYC7wAPA/slMlECHoCDwC7IrFZ0olIjPcDQ2RCUSw3A7s9hNdeOg48BlwgcwornYDflii8tmk/MEWmFWejM/BcYPG1TrNlYnEmOgBLYhRfS5olU4v2uKcM4mtZpFwtc4vWXAwcKZMAC8A2oFueDFwtjb0viyIRlovuwHu4fUORcy6JhsVCmdO2PHUMHaSzMzIF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\" />\n ) : (\n <img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKAAAACgCAYAAACLz2ctAAAA0mVYSWZJSSoACAAAAAoAAAEEAAEAAACgAAAAAQEEAAEAAACgAAAAAgEDAAMAAACGAAAAEgEDAAEAAAABAAAAGgEFAAEAAACMAAAAGwEFAAEAAACUAAAAKAEDAAEAAAACAAAAMQECAA8AAACcAAAAMgECABQAAACsAAAAaYcEAAEAAADAAAAAAAAAAAgACAAIACwBAAABAAAALAEAAAEAAABHSU1QIDMuMC4wLVJDMQAAMjAyNjowMzoxOCAxNjoyODowOAABAAGgAwABAAAAAQAAAAAAAABiEXj2AAABhWlDQ1BJQ0MgcHJvZmlsZQAAeJx9kb9Lw0AcxV9bpSoVQTuIPyBDdbKLijjWKhShQqgVWnUwufQXNGlIUlwcBdeCgz8Wqw4uzro6uAqC4A8Q/wBxUnSREr+XFFrEeHDch3f3HnfvAH+9zFSzIwaommWkEnEhk10Vgq8IoB/dGMaoxEx9ThST8Bxf9/Dx9S7Ks7zP/Tl6lZzJAJ9AHGO6YRFvEM9sWjrnfeIwK0oK8TnxhEEXJH7kuuzyG+eCw36eGTbSqXniMLFQaGO5jVnRUImniSOKqlG+P+OywnmLs1qusuY9+QtDOW1lmes0R5DAIpYgQoCMKkoow0KUVo0UEynaj3v4hxy/SC6ZXCUwciygAhWS4wf/g9/dmvmpSTcpFAc6X2z7YwwI7gKNmm1/H9t24wQIPANXWstfqQOzn6TXWlrkCOjbBi6uW5q8B1zuAINPumRIjhSg6c/ngfcz+qYsMHAL9Ky5vTX3cfoApKmr5A1wcAiMFyh73ePdXe29/Xum2d8PeZVyqWZhNiAAAA16aVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/Pgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA0LjQuMC1FeGl2MiI+CiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIKICAgIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiCiAgICB4bWxuczpHSU1QPSJodHRwOi8vd3d3LmdpbXAub3JnL3htcC8iCiAgICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgeG1wTU06RG9jdW1lbnRJRD0iZ2ltcDpkb2NpZDpnaW1wOmI5OGQ1OTk1LTRkZjktNDdkMS1hMGRmLTJjYWJhOTU2NjFjZSIKICAgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo4MzNlNzUzYS0zZDhmLTRhNTAtODA1NC0wNmQ0N2JhNzQ1NWMiCiAgIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDoyYzJiM2I3Zi04OTU0LTQyZTgtOWNkOS1lOTc3Y2ExZmMwMGYiCiAgIEdJTVA6QVBJPSIzLjAiCiAgIEdJTVA6UGxhdGZvcm09IkxpbnV4IgogICBHSU1QOlRpbWVTdGFtcD0iMTc3MzgyNjA4OTM4NzIzMiIKICAgR0lNUDpWZXJzaW9uPSIzLjAuMC1SQzEiCiAgIGRjOkZvcm1hdD0iaW1hZ2UvcG5nIgogICB0aWZmOk9yaWVudGF0aW9uPSIxIgogICB4bXA6Q3JlYXRvclRvb2w9IkdJTVAiCiAgIHhtcDpNZXRhZGF0YURhdGU9IjIwMjY6MDM6MThUMTY6Mjg6MDgrMDc6MDAiCiAgIHhtcDpNb2RpZnlEYXRlPSIyMDI2OjAzOjE4VDE2OjI4OjA4KzA3OjAwIj4KICAgPHhtcE1NOkhpc3Rvcnk+CiAgICA8cmRmOlNlcT4KICAgICA8cmRmOmxpCiAgICAgIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiCiAgICAgIHN0RXZ0OmNoYW5nZWQ9Ii8iCiAgICAgIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6NDM0NWQ1MjktYzlmYi00NjQ5LTlmZDctYmVkMDNiYjMzNjUzIgogICAgICBzdEV2dDpzb2Z0d2FyZUFnZW50PSJHSU1QIDMuMC4wLVJDMSAoTGludXgpIgogICAgICBzdEV2dDp3aGVuPSIyMDI2LTAzLTE4VDE2OjI4OjA5KzA3OjAwIi8+CiAgICA8L3JkZjpTZXE+CiAgIDwveG1wTU06SGlzdG9yeT4KICA8L3JkZjpEZXNjcmlwdGlvbj4KIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAKPD94cGFja2V0IGVuZD0idyI/PkUgIFwAAAAGYktHRAAAAEgAkHfF9EMAAAAJcEhZcwAALiMAAC4jAXilP3YAAAAHdElNRQfqAxIJHAlWwan2AAAAGXRFWHRDb21tZW50AENyZWF0ZWQgd2l0aCBHSU1QV4EOFwAACwtJREFUeNrtnXuwVlUZh5+DAnI9ykWBJC9cdHAgRSUh1Kwhi6KG0TTorpjTDScNRm1Kx/5wqhHTAjRMssbRaaBxiDIgyUKHSASV0FIUUFFB4CBXhQNff6x1GOZ08Lzr+9be3778npk1wMzi23ut97fXfb1vA6ItOgBDgHP9nwOBk/2fXYDjfb4TMvbe7wI7gCZgC7AaeBpYAbwks2abYcB0YCmwE6gULD0L3AR8UKbODgOBHwHrCii4o6X9wL0SYn0ZAywAmkskvNbpPeAHwLGSQ3qMAh4rsejaSsuB0ySNZOnvu52DElyb6S1gpGQSn+P8wHuXRNZu2gmMlWTiMaFkk4sYaatfdhI1MBR4VGKqOv0X6CEZhdML+Jmf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\" />\n );\n\n return (\n <div\n style={{\n display: \"flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n width,\n height,\n fontSize: \"10rem\",\n }}\n >\n {emoji}\n </div>\n );\n};\n\nexport default createVisualization;\n", c = "{\n \"name\": \"__CUSTOM_VIZ_NAME__\",\n \"version\": \"0.0.1\",\n \"private\": true,\n \"type\": \"module\",\n \"scripts\": {\n \"build\": \"vite build\",\n \"dev\": \"vite build --watch & vite preview\",\n \"type-check\": \"tsc --noEmit\"\n },\n \"devDependencies\": {\n \"@metabase/custom-viz\": \"__CUSTOM_VIZ_VERSION__\",\n \"@types/react\": \"^19.2.14\",\n \"react\": \"^19.1.0\",\n \"typescript\": \"^5.9.3\",\n \"vite\": \"^8.0.0\"\n }\n}\n", l = "{\n \"compilerOptions\": {\n \"target\": \"ES2020\",\n \"module\": \"ESNext\",\n \"moduleResolution\": \"bundler\",\n \"jsx\": \"react-jsx\",\n \"strict\": true,\n \"esModuleInterop\": true,\n \"skipLibCheck\": true,\n \"outDir\": \"dist\",\n \"declaration\": true\n },\n \"include\": [\"src\"]\n}\n", u = "import { resolve } from \"path\";\nimport { createServer } from \"http\";\nimport { defineConfig } from \"vite\";\n\n/**\n * Vite plugin that replaces `react` and `react/jsx-runtime` imports with\n * virtual modules that read from Metabase's `window.__METABASE_VIZ_API__`.\n *\n * This is necessary because the ES module output format cannot use\n * `output.globals`, and bare `import 'react'` would fail in the browser.\n */\nfunction metabaseVizExternals() {\n const VIRTUAL_REACT = \"\\0virtual:react\";\n const VIRTUAL_JSX_RUNTIME = \"\\0virtual:react/jsx-runtime\";\n\n return {\n name: \"metabase-viz-externals\",\n enforce: \"pre\" as const,\n\n resolveId(source) {\n if (source === \"react\") {\n return VIRTUAL_REACT;\n }\n if (source === \"react/jsx-runtime\") {\n return VIRTUAL_JSX_RUNTIME;\n }\n return null;\n },\n\n load(id) {\n if (id === VIRTUAL_REACT) {\n return [\n \"const React = window.__METABASE_VIZ_API__.React;\",\n \"export default React;\",\n \"export const { useState, useEffect, useRef, useCallback, useMemo, useReducer, useContext, createElement, Fragment } = React;\",\n ].join(\"\\n\");\n }\n if (id === VIRTUAL_JSX_RUNTIME) {\n return [\n \"const jsxRuntime = window.__METABASE_VIZ_API__.jsxRuntime;\",\n \"export const { jsx, jsxs, Fragment } = jsxRuntime;\",\n ].join(\"\\n\");\n }\n return null;\n },\n };\n}\n\nconst NOTIFY_PORT = 5175;\n\n/**\n * Vite plugin that starts a tiny SSE server and sends a \"reload\" event\n * to all connected clients after each rebuild completes.\n * Metabase's frontend connects to this to live-reload the custom viz.\n */\nfunction metabaseNotifyReload() {\n const clients = new Set<import(\"http\").ServerResponse>();\n let server: ReturnType<typeof createServer> | null = null;\n\n return {\n name: \"metabase-notify-reload\",\n\n buildStart() {\n if (server) {\n return;\n }\n server = createServer((req, res) => {\n res.writeHead(200, {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n \"Access-Control-Allow-Origin\": \"*\",\n });\n clients.add(res);\n req.on(\"close\", () => clients.delete(res));\n });\n server.listen(NOTIFY_PORT, () => {\n console.log(\n `[metabase-notify] SSE server listening on http://localhost:${NOTIFY_PORT}`,\n );\n });\n },\n\n closeBundle() {\n for (const client of clients) {\n client.write(\"data: reload\\n\\n\");\n }\n console.log(\n `[metabase-notify] Build complete, notified ${clients.size} client(s)`,\n );\n },\n };\n}\n\nconst isWatch = process.argv.includes(\"--watch\");\n\nexport default defineConfig({\n plugins: [metabaseVizExternals(), ...(isWatch ? [metabaseNotifyReload()] : [])],\n build: {\n outDir: \"dist\",\n lib: {\n entry: resolve(__dirname, \"src/index.tsx\"),\n formats: [\"iife\"],\n fileName: () => \"index.js\",\n name: \"__customVizPlugin__\",\n },\n },\n preview: {\n port: 5174,\n host: true,\n cors: true,\n },\n});\n", d = "__CUSTOM_VIZ_NAME__", f = "__CUSTOM_VIZ_VERSION__";
8
- function p(e, t) {
9
- return e.split(d).join(t);
9
+ var l = "0.0.1-alpha.5", u = "node_modules/\n.DS_Store\n# dist must be committed\n", d = "<svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M1.79978 6.86661C1.65041 7.01583 1.53192 7.19302 1.45107 7.38806C1.37023 7.58309 1.32861 7.79215 1.32861 8.00328C1.32861 8.21441 1.37023 8.42347 1.45107 8.61851C1.53192 8.81354 1.65041 8.99073 1.79978 9.13995L6.85978 14.1999C7.00899 14.3493 7.18618 14.4678 7.38122 14.5487C7.57626 14.6295 7.78531 14.6711 7.99644 14.6711C8.20757 14.6711 8.41663 14.6295 8.61167 14.5487C8.80671 14.4678 8.9839 14.3493 9.13311 14.1999L14.1931 9.13995C14.3425 8.99073 14.461 8.81354 14.5418 8.61851C14.6227 8.42347 14.6643 8.21441 14.6643 8.00328C14.6643 7.79215 14.6227 7.58309 14.5418 7.38806C14.461 7.19302 14.3425 7.01583 14.1931 6.86661L9.13311 1.80661C8.9839 1.65725 8.80671 1.53875 8.61167 1.45791C8.41663 1.37706 8.20757 1.33545 7.99644 1.33545C7.78531 1.33545 7.57626 1.37706 7.38122 1.45791C7.18618 1.53875 7.00899 1.65725 6.85978 1.80661L1.79978 6.86661Z\" stroke=\"white\" stroke-width=\"1.33333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n</svg>\n", f = "import type {\n CreateCustomVisualization,\n CustomStaticVisualizationProps,\n CustomVisualizationProps,\n} from \"@metabase/custom-viz\";\n\ntype Settings = {\n threshold?: number;\n};\n\nconst createVisualization: CreateCustomVisualization<Settings> = ({\n getAssetUrl,\n}) => {\n return {\n id: \"__CUSTOM_VIZ_NAME__\",\n getName: () => \"__CUSTOM_VIZ_NAME__\",\n minSize: { width: 1, height: 1 },\n defaultSize: { width: 2, height: 2 },\n isSensible({ cols, rows }) {\n return cols.length === 1 && rows.length === 1 && typeof rows[0][0] === \"number\";\n },\n checkRenderable(series, settings) {\n if (series.length !== 1) {\n throw new Error(\"Only 1 series is supported\");\n }\n\n const [\n {\n data: { cols, rows },\n },\n ] = series;\n\n if (cols.length !== 1) {\n throw new Error(\"Query results should only have 1 column\");\n }\n\n if (rows.length !== 1) {\n throw new Error(\"Query results should only have 1 row\");\n }\n\n if (typeof rows[0][0] !== \"number\") {\n throw new Error(\"Result is not a number\");\n }\n\n if (typeof settings.threshold !== \"number\") {\n throw new Error(\"Threshold setting is not set\");\n }\n },\n settings: {\n threshold: {\n id: \"1\",\n title: \"Threshold\",\n widget: \"number\",\n getDefault() {\n return 0;\n },\n getProps() {\n return {\n options: {\n isInteger: false,\n isNonNegative: false,\n },\n placeholder: \"Set threshold\",\n };\n },\n },\n },\n VisualizationComponent: makeVisualizationComponent(getAssetUrl),\n StaticVisualizationComponent: makeStaticVisualizationComponent(getAssetUrl),\n };\n};\n\nconst makeVisualizationComponent = (getAssetUrl: (path: string) => string) => (props: CustomVisualizationProps<Settings>) => {\n const { height, series, settings, width } = props;\n const { threshold } = settings;\n const value = series[0].data.rows[0][0];\n\n if (typeof value !== \"number\" || typeof threshold !== \"number\") {\n throw new Error(\"Value and threshold need to be numbers\");\n }\n\n const emoji = value >= threshold ? \"👍\" : \"👎\";\n\n return (\n <div\n style={{\n display: \"flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n width,\n height,\n fontSize: \"10rem\",\n }}\n >\n {emoji}\n </div>\n );\n};\n\nconst makeStaticVisualizationComponent = (getAssetUrl: (path: string) => string) => (\n props: CustomStaticVisualizationProps<Settings>,\n) => {\n const width = 540;\n const height = 360;\n const { series, settings } = props;\n const { threshold } = settings;\n const value = series[0].data.rows[0][0];\n\n if (typeof value !== \"number\" || typeof threshold !== \"number\") {\n throw new Error(\"Value and threshold need to be numbers\");\n }\n\n const emoji =\n value >= threshold ? (\n <img src={getAssetUrl(\"thumbs-up.png\")} />\n ) : (\n <img src={getAssetUrl(\"thumbs-down.png\")} />\n );\n\n return (\n <div\n style={{\n display: \"flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n width,\n height,\n fontSize: \"10rem\",\n }}\n >\n {emoji}\n </div>\n );\n};\n\nexport default createVisualization;\n", p = "{\n \"name\": \"__CUSTOM_VIZ_NAME__\",\n \"icon\": \"icon.svg\",\n \"assets\": [\"icon.svg\", \"thumbs-up.png\", \"thumbs-down.png\"],\n \"metabase\": {\n \"version\": \">=59\"\n }\n}\n", m = "{\n \"name\": \"__CUSTOM_VIZ_NAME__\",\n \"version\": \"0.0.1\",\n \"private\": true,\n \"type\": \"module\",\n \"scripts\": {\n \"build\": \"vite build\",\n \"dev\": \"vite build --watch\",\n \"type-check\": \"tsc --noEmit\"\n },\n \"devDependencies\": {\n \"@metabase/custom-viz\": \"__CUSTOM_VIZ_VERSION__\",\n \"@types/react\": \"^19.2.14\",\n \"react\": \"^19.1.0\",\n \"typescript\": \"^5.9.3\",\n \"vite\": \"^8.0.0\"\n }\n}\n", h = "{\n \"compilerOptions\": {\n \"target\": \"ES2020\",\n \"module\": \"ESNext\",\n \"moduleResolution\": \"bundler\",\n \"jsx\": \"react-jsx\",\n \"strict\": true,\n \"esModuleInterop\": true,\n \"skipLibCheck\": true,\n \"outDir\": \"dist\",\n \"declaration\": true\n },\n \"include\": [\"src\"]\n}\n", g = "import { resolve } from \"path\";\nimport { createServer } from \"http\";\nimport { watch, cpSync } from \"fs\";\nimport { defineConfig } from \"vite\";\n\n/**\n * Vite plugin that replaces `react` and `react/jsx-runtime` imports with\n * virtual modules that read from Metabase's `window.__METABASE_VIZ_API__`.\n *\n * This is necessary because the ES module output format cannot use\n * `output.globals`, and bare `import 'react'` would fail in the browser.\n */\nfunction metabaseVizExternals() {\n const VIRTUAL_REACT = \"\\0virtual:react\";\n const VIRTUAL_JSX_RUNTIME = \"\\0virtual:react/jsx-runtime\";\n return {\n name: \"metabase-viz-externals\",\n enforce: \"pre\" as const,\n\n resolveId(source) {\n if (source === \"react\") {\n return VIRTUAL_REACT;\n }\n if (source === \"react/jsx-runtime\") {\n return VIRTUAL_JSX_RUNTIME;\n }\n return null;\n },\n\n load(id) {\n if (id === VIRTUAL_REACT) {\n return [\n \"const React = window.__METABASE_VIZ_API__.React;\",\n \"export default React;\",\n \"export const { useState, useEffect, useRef, useCallback, useMemo, useReducer, useContext, createElement, Fragment } = React;\",\n ].join(\"\\n\");\n }\n if (id === VIRTUAL_JSX_RUNTIME) {\n return [\n \"const jsxRuntime = window.__METABASE_VIZ_API__.jsxRuntime;\",\n \"export const { jsx, jsxs, Fragment } = jsxRuntime;\",\n ].join(\"\\n\");\n }\n return null;\n },\n };\n}\n\nconst DEV_PORT = 5174;\n\n/**\n * Vite plugin that starts a dev server serving both static files from dist/\n * and an SSE endpoint at /__sse for hot-reload notifications.\n * Metabase's frontend connects to /__sse on the same origin as dev_bundle_url.\n */\nfunction metabaseDevServer() {\n const clients = new Set<import(\"http\").ServerResponse>();\n let server: ReturnType<typeof createServer> | null = null;\n\n return {\n name: \"metabase-dev-server\",\n\n buildStart() {\n if (server) {\n return;\n }\n\n const distDir = resolve(__dirname, \"dist\");\n\n server = createServer((req, res) => {\n const url = req.url ?? \"/\";\n\n // SSE endpoint for hot-reload\n if (url === \"/__sse\") {\n res.writeHead(200, {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n \"Access-Control-Allow-Origin\": \"*\",\n });\n clients.add(res);\n req.on(\"close\", () => clients.delete(res));\n return;\n }\n\n // CORS headers for all static responses\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n\n // Serve static files from dist/\n const { readFile, stat } = require(\"fs\");\n const { join, extname } = require(\"path\");\n\n const filePath = url === \"/\" ? join(distDir, \"index.html\") : join(distDir, url);\n\n // Prevent directory traversal\n if (!filePath.startsWith(distDir)) {\n res.writeHead(403);\n res.end(\"Forbidden\");\n return;\n }\n\n stat(filePath, (err: NodeJS.ErrnoException | null) => {\n if (err) {\n res.writeHead(404);\n res.end(\"Not found\");\n return;\n }\n\n const mimeTypes: Record<string, string> = {\n \".html\": \"text/html\",\n \".js\": \"application/javascript\",\n \".css\": \"text/css\",\n \".json\": \"application/json\",\n \".png\": \"image/png\",\n \".jpg\": \"image/jpeg\",\n \".svg\": \"image/svg+xml\",\n \".ico\": \"image/x-icon\",\n };\n const contentType = mimeTypes[extname(filePath)] ?? \"application/octet-stream\";\n\n readFile(filePath, (readErr: NodeJS.ErrnoException | null, data: Buffer) => {\n if (readErr) {\n res.writeHead(500);\n res.end(\"Internal server error\");\n return;\n }\n res.writeHead(200, { \"Content-Type\": contentType });\n res.end(data);\n });\n });\n });\n\n server.listen(DEV_PORT, () => {\n console.log(\n `[custom-viz] Dev server listening on http://localhost:${DEV_PORT}`,\n );\n });\n\n // Watch public/assets/ for changes — copy to dist/assets/ and notify\n const assetsDir = resolve(__dirname, \"public/assets\");\n watch(assetsDir, { recursive: true }, (_event, filename) => {\n if (!filename) {\n return;\n }\n cpSync(resolve(assetsDir, filename), resolve(__dirname, \"dist/assets\", filename));\n for (const client of clients) {\n client.write(\"data: reload\\n\\n\");\n }\n console.log(`[custom-viz] Asset changed: ${filename}, notified ${clients.size} client(s)`);\n });\n },\n\n closeBundle() {\n for (const client of clients) {\n client.write(\"data: reload\\n\\n\");\n }\n console.log(\n `[custom-viz] Build complete, notified ${clients.size} client(s)`,\n );\n },\n };\n}\n\nconst isWatch = process.argv.includes(\"--watch\");\n\nexport default defineConfig({\n plugins: [metabaseVizExternals(), ...(isWatch ? [metabaseDevServer()] : [])],\n publicDir: \"public\",\n build: {\n outDir: \"dist\",\n lib: {\n entry: resolve(__dirname, \"src/index.tsx\"),\n formats: [\"iife\"],\n fileName: () => \"index.js\",\n name: \"__customVizPlugin__\",\n },\n },\n});\n", _ = "__CUSTOM_VIZ_NAME__", v = "__CUSTOM_VIZ_VERSION__";
10
+ function y(e, t) {
11
+ return e.split(_).join(t);
10
12
  }
11
- function m(e) {
12
- return p(c, e).split(f).join(a);
13
+ function b(e) {
14
+ return y(m, e).split(v).join(l);
13
15
  }
14
- function h() {
15
- return u;
16
+ function x() {
17
+ return g;
18
+ }
19
+ function S() {
20
+ return h;
16
21
  }
17
- function g() {
18
- return l;
22
+ function C(e) {
23
+ return y(f, e);
19
24
  }
20
- function _(e) {
21
- return p(s, e);
25
+ function w(e) {
26
+ return y(p, e);
22
27
  }
23
- function v() {
24
- return o;
28
+ function T() {
29
+ return d;
30
+ }
31
+ var E = a(i(c(import.meta.url)), "templates");
32
+ function D(e) {
33
+ return t(a(E, e));
34
+ }
35
+ function O() {
36
+ return u;
37
+ }
38
+ function k(e) {
39
+ let t = JSON.parse(e), n = JSON.parse(m.split(_).join("__placeholder__").split(v).join(l));
40
+ return t.devDependencies = {
41
+ ...t.devDependencies,
42
+ ...n.devDependencies
43
+ }, t.scripts = {
44
+ ...t.scripts,
45
+ ...n.scripts
46
+ }, JSON.stringify(t, null, 2) + "\n";
25
47
  }
26
48
  //#endregion
27
49
  //#region src/cli.ts
28
- var y = new i();
29
- y.name("metabase-custom-viz").description("CLI for creating custom visualizations for Metabase").version("0.0.1"), y.command("init").description("Scaffold a new custom visualization").argument("<name>", "Name of the custom visualization").action(async (i) => {
30
- let a = i.trim().replace(/\s+/g, "-").toLowerCase();
31
- (!a || !/^[a-z0-9@][a-z0-9._\-/]*$/.test(a)) && (console.error(`Error: "${i}" is not a valid project name. Use letters, numbers, hyphens, and dots.`), process.exit(1)), e(a) && (console.error(`Error: Directory "${a}" already exists.`), process.exit(1)), console.log(`Scaffolding custom visualization: ${a}\n`), await t(r(a, "src"), { recursive: !0 }), await Promise.all([
32
- n(r(a, "package.json"), m(a)),
33
- n(r(a, "vite.config.ts"), h()),
34
- n(r(a, "tsconfig.json"), g()),
35
- n(r(a, "src", "index.tsx"), _(a)),
36
- n(r(a, ".gitignore"), v())
37
- ]), console.log("Created files:"), console.log(` ${a}/package.json`), console.log(` ${a}/vite.config.ts`), console.log(` ${a}/tsconfig.json`), console.log(` ${a}/src/index.tsx`), console.log(` ${a}/.gitignore`), console.log(), console.log("Next steps:"), console.log(` cd ${a}`), console.log(" npm install"), console.log(" npm run dev # Watch mode"), console.log(" npm run build # Production build");
38
- }), y.parse();
50
+ var A = new s();
51
+ A.name("metabase-custom-viz").description("CLI for creating custom visualizations for Metabase").version(l), A.command("init").description("Scaffold a new custom visualization").argument("<name>", "Name of the custom visualization").action(async (t) => {
52
+ let i = t.trim().replace(/\s+/g, "-").toLowerCase();
53
+ (!i || !/^[a-z0-9@][a-z0-9._\-/]*$/.test(i)) && (console.error(`Error: "${t}" is not a valid project name. Use letters, numbers, hyphens, and dots.`), process.exit(1)), e(i) && (console.error(`Error: Directory "${i}" already exists.`), process.exit(1)), console.log(`Scaffolding custom visualization: ${i}\n`), await Promise.all([n(a(i, "src"), { recursive: !0 }), n(a(i, "public", "assets"), { recursive: !0 })]), await Promise.all([
54
+ r(a(i, "package.json"), b(i)),
55
+ r(a(i, "vite.config.ts"), x()),
56
+ r(a(i, "tsconfig.json"), S()),
57
+ r(a(i, "src", "index.tsx"), C(i)),
58
+ r(a(i, "metabase-plugin.json"), w(i)),
59
+ r(a(i, "public", "assets", "icon.svg"), T()),
60
+ r(a(i, "public", "assets", "thumbs-up.png"), D("thumbs-up.png")),
61
+ r(a(i, "public", "assets", "thumbs-down.png"), D("thumbs-down.png")),
62
+ r(a(i, ".gitignore"), O())
63
+ ]), console.log("Created files:"), console.log(` ${i}/package.json`), console.log(` ${i}/vite.config.ts`), console.log(` ${i}/tsconfig.json`), console.log(` ${i}/src/index.tsx`), console.log(` ${i}/metabase-plugin.json`), console.log(` ${i}/public/assets/icon.svg`), console.log(` ${i}/public/assets/thumbs-up.png`), console.log(` ${i}/public/assets/thumbs-down.png`), console.log(` ${i}/.gitignore`), console.log(), console.log("Next steps:"), console.log(` cd ${i}`), console.log(" npm install"), console.log(" npm run dev # Watch mode"), console.log(" npm run build # Production build");
64
+ }), A.command("upgrade").description("Upgrade an existing custom visualization to the latest template version").action(async () => {
65
+ let n = process.cwd(), i = a(n, "package.json");
66
+ e(i) || (console.error("Error: No package.json found. Run this command from the root of a custom visualization project."), process.exit(1));
67
+ let o;
68
+ try {
69
+ o = JSON.parse(t(i, "utf-8"));
70
+ } catch {
71
+ console.error("Error: Could not parse package.json."), process.exit(1);
72
+ }
73
+ let s = o.devDependencies?.["@metabase/custom-viz"];
74
+ if (s || (console.error("Error: @metabase/custom-viz not found in devDependencies. Is this a custom visualization project?"), process.exit(1)), s === "0.0.1-alpha.5") {
75
+ console.log(`Already up to date (v${l}). No changes needed.`);
76
+ return;
77
+ }
78
+ if (console.log(`Upgrading from @metabase/custom-viz ${s} → ${l}\n`), console.log("The following changes will be made:\n"), console.log(" vite.config.ts — Replace with the latest build configuration"), console.log(" tsconfig.json — Replace with the latest TypeScript configuration"), console.log(" .gitignore — Replace with the latest gitignore rules"), console.log(" package.json — Update devDependencies and scripts to latest versions"), console.log(), console.log("Your source code (src/), assets (public/), and metabase-plugin.json will NOT be modified.\n"), !await j("Proceed with upgrade?")) {
79
+ console.log("Upgrade cancelled.");
80
+ return;
81
+ }
82
+ console.log();
83
+ let c = [];
84
+ await r(a(n, "vite.config.ts"), x()), c.push("vite.config.ts"), await r(a(n, "tsconfig.json"), S()), c.push("tsconfig.json"), await r(a(n, ".gitignore"), O()), c.push(".gitignore"), await r(i, k(t(i, "utf-8"))), c.push("package.json"), console.log("Updated files:");
85
+ for (let e of c) console.log(` ${e}`);
86
+ console.log(), console.log("Next steps:"), console.log(" npm install # Install updated dependencies");
87
+ });
88
+ function j(e) {
89
+ let t = o({
90
+ input: process.stdin,
91
+ output: process.stdout
92
+ });
93
+ return new Promise((n) => {
94
+ t.question(`${e} (y/N) `, (e) => {
95
+ t.close(), n(e.trim().toLowerCase() === "y");
96
+ });
97
+ });
98
+ }
99
+ A.parse();
39
100
  //#endregion
@@ -0,0 +1,40 @@
1
+ import { ColumnPredicate, ColumnTypes } from './types/column-types';
2
+ import { Column } from './types/data';
3
+ export declare const isDate: ColumnPredicate;
4
+ export declare const isNumeric: ColumnPredicate;
5
+ export declare const isInteger: ColumnPredicate;
6
+ export declare const isBoolean: ColumnPredicate;
7
+ export declare const isString: ColumnPredicate;
8
+ export declare const isStringLike: ColumnPredicate;
9
+ export declare const isSummable: ColumnPredicate;
10
+ export declare const isNumericBaseType: ColumnPredicate;
11
+ export declare const isDateWithoutTime: ColumnPredicate;
12
+ export declare const isNumber: ColumnPredicate;
13
+ export declare const isFloat: ColumnPredicate;
14
+ export declare const isTime: ColumnPredicate;
15
+ export declare const isFK: ColumnPredicate;
16
+ export declare const isPK: ColumnPredicate;
17
+ export declare const isEntityName: ColumnPredicate;
18
+ export declare const isTitle: ColumnPredicate;
19
+ export declare const isProduct: ColumnPredicate;
20
+ export declare const isSource: ColumnPredicate;
21
+ export declare const isAddress: ColumnPredicate;
22
+ export declare const isScore: ColumnPredicate;
23
+ export declare const isQuantity: ColumnPredicate;
24
+ export declare const isCategory: ColumnPredicate;
25
+ export declare const isAny: ColumnPredicate;
26
+ export declare const isState: ColumnPredicate;
27
+ export declare const isCountry: ColumnPredicate;
28
+ export declare const isCoordinate: ColumnPredicate;
29
+ export declare const isLatitude: ColumnPredicate;
30
+ export declare const isLongitude: ColumnPredicate;
31
+ export declare const isCurrency: ColumnPredicate;
32
+ export declare const isPercentage: ColumnPredicate;
33
+ export declare const isID: ColumnPredicate;
34
+ export declare const isURL: ColumnPredicate;
35
+ export declare const isEmail: ColumnPredicate;
36
+ export declare const isAvatarURL: ColumnPredicate;
37
+ export declare const isImageURL: ColumnPredicate;
38
+ export declare const hasLatitudeAndLongitudeColumns: (cols: Column[]) => boolean;
39
+ export type { ColumnPredicate, ColumnTypes };
40
+ //# sourceMappingURL=column-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"column-types.d.ts","sourceRoot":"","sources":["../src/column-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACzE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAQ3C,eAAO,MAAM,MAAM,EAAE,eAA2C,CAAC;AACjE,eAAO,MAAM,SAAS,EAAE,eAA8C,CAAC;AACvE,eAAO,MAAM,SAAS,EAAE,eAA8C,CAAC;AACvE,eAAO,MAAM,SAAS,EAAE,eAA8C,CAAC;AACvE,eAAO,MAAM,QAAQ,EAAE,eAA6C,CAAC;AACrE,eAAO,MAAM,YAAY,EAAE,eAAiD,CAAC;AAC7E,eAAO,MAAM,UAAU,EAAE,eAA+C,CAAC;AACzE,eAAO,MAAM,iBAAiB,EAAE,eAAsD,CAAC;AACvF,eAAO,MAAM,iBAAiB,EAAE,eAAsD,CAAC;AACvF,eAAO,MAAM,QAAQ,EAAE,eAA6C,CAAC;AACrE,eAAO,MAAM,OAAO,EAAE,eAA4C,CAAC;AACnE,eAAO,MAAM,MAAM,EAAE,eAA2C,CAAC;AACjE,eAAO,MAAM,IAAI,EAAE,eAAyC,CAAC;AAC7D,eAAO,MAAM,IAAI,EAAE,eAAyC,CAAC;AAC7D,eAAO,MAAM,YAAY,EAAE,eAAiD,CAAC;AAC7E,eAAO,MAAM,OAAO,EAAE,eAA4C,CAAC;AACnE,eAAO,MAAM,SAAS,EAAE,eAA8C,CAAC;AACvE,eAAO,MAAM,QAAQ,EAAE,eAA6C,CAAC;AACrE,eAAO,MAAM,SAAS,EAAE,eAA8C,CAAC;AACvE,eAAO,MAAM,OAAO,EAAE,eAA4C,CAAC;AACnE,eAAO,MAAM,UAAU,EAAE,eAA+C,CAAC;AACzE,eAAO,MAAM,UAAU,EAAE,eAA+C,CAAC;AACzE,eAAO,MAAM,KAAK,EAAE,eAA0C,CAAC;AAC/D,eAAO,MAAM,OAAO,EAAE,eAA4C,CAAC;AACnE,eAAO,MAAM,SAAS,EAAE,eAA8C,CAAC;AACvE,eAAO,MAAM,YAAY,EAAE,eAAiD,CAAC;AAC7E,eAAO,MAAM,UAAU,EAAE,eAA+C,CAAC;AACzE,eAAO,MAAM,WAAW,EAAE,eAAgD,CAAC;AAC3E,eAAO,MAAM,UAAU,EAAE,eAA+C,CAAC;AACzE,eAAO,MAAM,YAAY,EAAE,eAAiD,CAAC;AAC7E,eAAO,MAAM,IAAI,EAAE,eAAyC,CAAC;AAC7D,eAAO,MAAM,KAAK,EAAE,eAA0C,CAAC;AAC/D,eAAO,MAAM,OAAO,EAAE,eAA4C,CAAC;AACnE,eAAO,MAAM,WAAW,EAAE,eAAgD,CAAC;AAC3E,eAAO,MAAM,UAAU,EAAE,eAA+C,CAAC;AACzE,eAAO,MAAM,8BAA8B,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,OACZ,CAAC;AAEtD,YAAY,EAAE,eAAe,EAAE,WAAW,EAAE,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { FormatValue, FormatValueOptions } from './types/format';
2
+ export declare const formatValue: FormatValue;
3
+ export type { FormatValue, FormatValueOptions };
4
+ //# sourceMappingURL=format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAItE,eAAO,MAAM,WAAW,EAAE,WACgC,CAAC;AAE3D,YAAY,EAAE,WAAW,EAAE,kBAAkB,EAAE,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,2 +1,6 @@
1
+ export * from './lib';
1
2
  export * from './types';
3
+ export * from './column-types';
4
+ export * from './format';
5
+ export * from './measure-text';
2
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,OAAO,CAAC;AACtB,cAAc,SAAS,CAAC;AACxB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,UAAU,CAAC;AACzB,cAAc,gBAAgB,CAAC"}
package/dist/index.js CHANGED
@@ -1,5 +1,10 @@
1
+ //#region src/lib/defineSetting.ts
2
+ function e(e) {
3
+ return e;
4
+ }
5
+ //#endregion
1
6
  //#region src/types/date-time.ts
2
- var e = [
7
+ var t = [
3
8
  "minute",
4
9
  "hour",
5
10
  "day",
@@ -7,7 +12,7 @@ var e = [
7
12
  "month",
8
13
  "quarter",
9
14
  "year"
10
- ], t = [
15
+ ], n = [
11
16
  "minute-of-hour",
12
17
  "hour-of-day",
13
18
  "day-of-week",
@@ -16,6 +21,15 @@ var e = [
16
21
  "week-of-year",
17
22
  "month-of-year",
18
23
  "quarter-of-year"
19
- ], n = [...e, ...t];
24
+ ], r = [...t, ...n];
25
+ //#endregion
26
+ //#region src/column-types.ts
27
+ function i() {
28
+ return window.__METABASE_VIZ_API__.columnTypes;
29
+ }
30
+ var a = (e) => i().isDate(e), o = (e) => i().isNumeric(e), s = (e) => i().isInteger(e), c = (e) => i().isBoolean(e), l = (e) => i().isString(e), u = (e) => i().isStringLike(e), d = (e) => i().isSummable(e), f = (e) => i().isNumericBaseType(e), p = (e) => i().isDateWithoutTime(e), m = (e) => i().isNumber(e), h = (e) => i().isFloat(e), g = (e) => i().isTime(e), _ = (e) => i().isFK(e), v = (e) => i().isPK(e), y = (e) => i().isEntityName(e), b = (e) => i().isTitle(e), x = (e) => i().isProduct(e), S = (e) => i().isSource(e), C = (e) => i().isAddress(e), w = (e) => i().isScore(e), T = (e) => i().isQuantity(e), E = (e) => i().isCategory(e), D = (e) => i().isAny(e), O = (e) => i().isState(e), k = (e) => i().isCountry(e), A = (e) => i().isCoordinate(e), j = (e) => i().isLatitude(e), M = (e) => i().isLongitude(e), N = (e) => i().isCurrency(e), P = (e) => i().isPercentage(e), F = (e) => i().isID(e), I = (e) => i().isURL(e), L = (e) => i().isEmail(e), R = (e) => i().isAvatarURL(e), z = (e) => i().isImageURL(e), B = (e) => i().hasLatitudeAndLongitudeColumns(e), V = (e, t) => window.__METABASE_VIZ_API__.formatValue(e, t), H = () => ({
31
+ width: 0,
32
+ height: 0
33
+ }), U = () => 0, W = () => 0;
20
34
  //#endregion
21
- export { e as dateTimeAbsoluteUnits, t as dateTimeRelativeUnits, n as dateTimeUnits };
35
+ export { t as dateTimeAbsoluteUnits, n as dateTimeRelativeUnits, r as dateTimeUnits, e as defineSetting, V as formatValue, B as hasLatitudeAndLongitudeColumns, C as isAddress, D as isAny, R as isAvatarURL, c as isBoolean, E as isCategory, A as isCoordinate, k as isCountry, N as isCurrency, a as isDate, p as isDateWithoutTime, L as isEmail, y as isEntityName, _ as isFK, h as isFloat, F as isID, z as isImageURL, s as isInteger, j as isLatitude, M as isLongitude, m as isNumber, o as isNumeric, f as isNumericBaseType, v as isPK, P as isPercentage, x as isProduct, T as isQuantity, w as isScore, S as isSource, O as isState, l as isString, u as isStringLike, d as isSummable, g as isTime, b as isTitle, I as isURL, H as measureText, W as measureTextHeight, U as measureTextWidth };
@@ -0,0 +1,22 @@
1
+ import { CustomVisualizationSettingDefinition, Series, WidgetName, Widgets } from '../types';
2
+ type PropsFromWidget<W> = W extends WidgetName ? Widgets[W] : W extends (props: infer P) => any ? P : never;
3
+ export declare function defineSetting<CustomVisualizationSettings extends Record<string, unknown>, TValue, W extends WidgetName | ((props: any) => any)>(settingDefinition: {
4
+ id: string;
5
+ section?: string;
6
+ title?: string;
7
+ group?: string;
8
+ index?: number;
9
+ inline?: boolean;
10
+ persistDefault?: boolean;
11
+ set?: boolean;
12
+ readDependencies?: string[];
13
+ writeDependencies?: string[];
14
+ eraseDependencies?: string[];
15
+ widget: W;
16
+ isValid?: (series: Series, settings: CustomVisualizationSettings) => boolean;
17
+ getDefault?: (series: Series, settings: CustomVisualizationSettings) => TValue;
18
+ getProps(object: Series, vizSettings: CustomVisualizationSettings): PropsFromWidget<W>;
19
+ getValue?: (series: Series, settings: CustomVisualizationSettings) => TValue;
20
+ }): CustomVisualizationSettingDefinition<CustomVisualizationSettings>;
21
+ export {};
22
+ //# sourceMappingURL=defineSetting.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defineSetting.d.ts","sourceRoot":"","sources":["../../src/lib/defineSetting.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,oCAAoC,EACpC,MAAM,EACN,UAAU,EACV,OAAO,EACR,MAAM,UAAU,CAAC;AAElB,KAAK,eAAe,CAAC,CAAC,IAAI,CAAC,SAAS,UAAU,GAC1C,OAAO,CAAC,CAAC,CAAC,GACV,CAAC,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,GAAG,GAC/B,CAAC,GACD,KAAK,CAAC;AAEZ,wBAAgB,aAAa,CAC3B,2BAA2B,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC3D,MAAM,EACN,CAAC,SAAS,UAAU,GAAG,CAAC,CAAC,KAAK,EAAE,GAAG,KAAK,GAAG,CAAC,EAC5C,iBAAiB,EAAE;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,GAAG,CAAC,EAAE,OAAO,CAAC;IAEd,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAE7B,MAAM,EAAE,CAAC,CAAC;IAEV,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,2BAA2B,KAAK,OAAO,CAAC;IAC7E,UAAU,CAAC,EAAE,CACX,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,2BAA2B,KAClC,MAAM,CAAC;IACZ,QAAQ,CACN,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,2BAA2B,GACvC,eAAe,CAAC,CAAC,CAAC,CAAC;IACtB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,2BAA2B,KAAK,MAAM,CAAC;CAC9E,GAAG,oCAAoC,CAAC,2BAA2B,CAAC,CAEpE"}
@@ -0,0 +1,2 @@
1
+ export * from './defineSetting';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC"}
@@ -0,0 +1,5 @@
1
+ import { TextHeightMeasurer, TextMeasurer, TextWidthMeasurer } from './types/measure-text';
2
+ export declare const measureText: TextMeasurer;
3
+ export declare const measureTextWidth: TextWidthMeasurer;
4
+ export declare const measureTextHeight: TextHeightMeasurer;
5
+ //# sourceMappingURL=measure-text.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"measure-text.d.ts","sourceRoot":"","sources":["../src/measure-text.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EACV,kBAAkB,EAClB,YAAY,EACZ,iBAAiB,EAClB,MAAM,sBAAsB,CAAC;AAE9B,eAAO,MAAM,WAAW,EAAE,YAA8C,CAAC;AACzE,eAAO,MAAM,gBAAgB,EAAE,iBAA2B,CAAC;AAC3D,eAAO,MAAM,iBAAiB,EAAE,kBAA4B,CAAC"}
@@ -0,0 +1,3 @@
1
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M1.79978 6.86661C1.65041 7.01583 1.53192 7.19302 1.45107 7.38806C1.37023 7.58309 1.32861 7.79215 1.32861 8.00328C1.32861 8.21441 1.37023 8.42347 1.45107 8.61851C1.53192 8.81354 1.65041 8.99073 1.79978 9.13995L6.85978 14.1999C7.00899 14.3493 7.18618 14.4678 7.38122 14.5487C7.57626 14.6295 7.78531 14.6711 7.99644 14.6711C8.20757 14.6711 8.41663 14.6295 8.61167 14.5487C8.80671 14.4678 8.9839 14.3493 9.13311 14.1999L14.1931 9.13995C14.3425 8.99073 14.461 8.81354 14.5418 8.61851C14.6227 8.42347 14.6643 8.21441 14.6643 8.00328C14.6643 7.79215 14.6227 7.58309 14.5418 7.38806C14.461 7.19302 14.3425 7.01583 14.1931 6.86661L9.13311 1.80661C8.9839 1.65725 8.80671 1.53875 8.61167 1.45791C8.41663 1.37706 8.20757 1.33545 7.99644 1.33545C7.78531 1.33545 7.57626 1.37706 7.38122 1.45791C7.18618 1.53875 7.00899 1.65725 6.85978 1.80661L1.79978 6.86661Z" stroke="white" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
3
+ </svg>
@@ -0,0 +1,136 @@
1
+ import type {
2
+ CreateCustomVisualization,
3
+ CustomStaticVisualizationProps,
4
+ CustomVisualizationProps,
5
+ } from "@metabase/custom-viz";
6
+
7
+ type Settings = {
8
+ threshold?: number;
9
+ };
10
+
11
+ const createVisualization: CreateCustomVisualization<Settings> = ({
12
+ getAssetUrl,
13
+ }) => {
14
+ return {
15
+ id: "__CUSTOM_VIZ_NAME__",
16
+ getName: () => "__CUSTOM_VIZ_NAME__",
17
+ minSize: { width: 1, height: 1 },
18
+ defaultSize: { width: 2, height: 2 },
19
+ isSensible({ cols, rows }) {
20
+ return cols.length === 1 && rows.length === 1 && typeof rows[0][0] === "number";
21
+ },
22
+ checkRenderable(series, settings) {
23
+ if (series.length !== 1) {
24
+ throw new Error("Only 1 series is supported");
25
+ }
26
+
27
+ const [
28
+ {
29
+ data: { cols, rows },
30
+ },
31
+ ] = series;
32
+
33
+ if (cols.length !== 1) {
34
+ throw new Error("Query results should only have 1 column");
35
+ }
36
+
37
+ if (rows.length !== 1) {
38
+ throw new Error("Query results should only have 1 row");
39
+ }
40
+
41
+ if (typeof rows[0][0] !== "number") {
42
+ throw new Error("Result is not a number");
43
+ }
44
+
45
+ if (typeof settings.threshold !== "number") {
46
+ throw new Error("Threshold setting is not set");
47
+ }
48
+ },
49
+ settings: {
50
+ threshold: {
51
+ id: "1",
52
+ title: "Threshold",
53
+ widget: "number",
54
+ getDefault() {
55
+ return 0;
56
+ },
57
+ getProps() {
58
+ return {
59
+ options: {
60
+ isInteger: false,
61
+ isNonNegative: false,
62
+ },
63
+ placeholder: "Set threshold",
64
+ };
65
+ },
66
+ },
67
+ },
68
+ VisualizationComponent: makeVisualizationComponent(getAssetUrl),
69
+ StaticVisualizationComponent: makeStaticVisualizationComponent(getAssetUrl),
70
+ };
71
+ };
72
+
73
+ const makeVisualizationComponent = (getAssetUrl: (path: string) => string) => (props: CustomVisualizationProps<Settings>) => {
74
+ const { height, series, settings, width } = props;
75
+ const { threshold } = settings;
76
+ const value = series[0].data.rows[0][0];
77
+
78
+ if (typeof value !== "number" || typeof threshold !== "number") {
79
+ throw new Error("Value and threshold need to be numbers");
80
+ }
81
+
82
+ const emoji = value >= threshold ? "👍" : "👎";
83
+
84
+ return (
85
+ <div
86
+ style={{
87
+ display: "flex",
88
+ justifyContent: "center",
89
+ alignItems: "center",
90
+ width,
91
+ height,
92
+ fontSize: "10rem",
93
+ }}
94
+ >
95
+ {emoji}
96
+ </div>
97
+ );
98
+ };
99
+
100
+ const makeStaticVisualizationComponent = (getAssetUrl: (path: string) => string) => (
101
+ props: CustomStaticVisualizationProps<Settings>,
102
+ ) => {
103
+ const width = 540;
104
+ const height = 360;
105
+ const { series, settings } = props;
106
+ const { threshold } = settings;
107
+ const value = series[0].data.rows[0][0];
108
+
109
+ if (typeof value !== "number" || typeof threshold !== "number") {
110
+ throw new Error("Value and threshold need to be numbers");
111
+ }
112
+
113
+ const emoji =
114
+ value >= threshold ? (
115
+ <img src={getAssetUrl("thumbs-up.png")} />
116
+ ) : (
117
+ <img src={getAssetUrl("thumbs-down.png")} />
118
+ );
119
+
120
+ return (
121
+ <div
122
+ style={{
123
+ display: "flex",
124
+ justifyContent: "center",
125
+ alignItems: "center",
126
+ width,
127
+ height,
128
+ fontSize: "10rem",
129
+ }}
130
+ >
131
+ {emoji}
132
+ </div>
133
+ );
134
+ };
135
+
136
+ export default createVisualization;
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "__CUSTOM_VIZ_NAME__",
3
+ "icon": "icon.svg",
4
+ "assets": ["icon.svg", "thumbs-up.png", "thumbs-down.png"],
5
+ "metabase": {
6
+ "version": ">=59"
7
+ }
8
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "__CUSTOM_VIZ_NAME__",
3
+ "version": "0.0.1",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "build": "vite build",
8
+ "dev": "vite build --watch",
9
+ "type-check": "tsc --noEmit"
10
+ },
11
+ "devDependencies": {
12
+ "@metabase/custom-viz": "__CUSTOM_VIZ_VERSION__",
13
+ "@types/react": "^19.2.14",
14
+ "react": "^19.1.0",
15
+ "typescript": "^5.9.3",
16
+ "vite": "^8.0.0"
17
+ }
18
+ }
Binary file
Binary file
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "jsx": "react-jsx",
7
+ "strict": true,
8
+ "esModuleInterop": true,
9
+ "skipLibCheck": true,
10
+ "outDir": "dist",
11
+ "declaration": true
12
+ },
13
+ "include": ["src"]
14
+ }
@@ -0,0 +1,178 @@
1
+ import { resolve } from "path";
2
+ import { createServer } from "http";
3
+ import { watch, cpSync } from "fs";
4
+ import { defineConfig } from "vite";
5
+
6
+ /**
7
+ * Vite plugin that replaces `react` and `react/jsx-runtime` imports with
8
+ * virtual modules that read from Metabase's `window.__METABASE_VIZ_API__`.
9
+ *
10
+ * This is necessary because the ES module output format cannot use
11
+ * `output.globals`, and bare `import 'react'` would fail in the browser.
12
+ */
13
+ function metabaseVizExternals() {
14
+ const VIRTUAL_REACT = "\0virtual:react";
15
+ const VIRTUAL_JSX_RUNTIME = "\0virtual:react/jsx-runtime";
16
+ return {
17
+ name: "metabase-viz-externals",
18
+ enforce: "pre" as const,
19
+
20
+ resolveId(source) {
21
+ if (source === "react") {
22
+ return VIRTUAL_REACT;
23
+ }
24
+ if (source === "react/jsx-runtime") {
25
+ return VIRTUAL_JSX_RUNTIME;
26
+ }
27
+ return null;
28
+ },
29
+
30
+ load(id) {
31
+ if (id === VIRTUAL_REACT) {
32
+ return [
33
+ "const React = window.__METABASE_VIZ_API__.React;",
34
+ "export default React;",
35
+ "export const { useState, useEffect, useRef, useCallback, useMemo, useReducer, useContext, createElement, Fragment } = React;",
36
+ ].join("\n");
37
+ }
38
+ if (id === VIRTUAL_JSX_RUNTIME) {
39
+ return [
40
+ "const jsxRuntime = window.__METABASE_VIZ_API__.jsxRuntime;",
41
+ "export const { jsx, jsxs, Fragment } = jsxRuntime;",
42
+ ].join("\n");
43
+ }
44
+ return null;
45
+ },
46
+ };
47
+ }
48
+
49
+ const DEV_PORT = 5174;
50
+
51
+ /**
52
+ * Vite plugin that starts a dev server serving both static files from dist/
53
+ * and an SSE endpoint at /__sse for hot-reload notifications.
54
+ * Metabase's frontend connects to /__sse on the same origin as dev_bundle_url.
55
+ */
56
+ function metabaseDevServer() {
57
+ const clients = new Set<import("http").ServerResponse>();
58
+ let server: ReturnType<typeof createServer> | null = null;
59
+
60
+ return {
61
+ name: "metabase-dev-server",
62
+
63
+ buildStart() {
64
+ if (server) {
65
+ return;
66
+ }
67
+
68
+ const distDir = resolve(__dirname, "dist");
69
+
70
+ server = createServer((req, res) => {
71
+ const url = req.url ?? "/";
72
+
73
+ // SSE endpoint for hot-reload
74
+ if (url === "/__sse") {
75
+ res.writeHead(200, {
76
+ "Content-Type": "text/event-stream",
77
+ "Cache-Control": "no-cache",
78
+ Connection: "keep-alive",
79
+ "Access-Control-Allow-Origin": "*",
80
+ });
81
+ clients.add(res);
82
+ req.on("close", () => clients.delete(res));
83
+ return;
84
+ }
85
+
86
+ // CORS headers for all static responses
87
+ res.setHeader("Access-Control-Allow-Origin", "*");
88
+
89
+ // Serve static files from dist/
90
+ const { readFile, stat } = require("fs");
91
+ const { join, extname } = require("path");
92
+
93
+ const filePath = url === "/" ? join(distDir, "index.html") : join(distDir, url);
94
+
95
+ // Prevent directory traversal
96
+ if (!filePath.startsWith(distDir)) {
97
+ res.writeHead(403);
98
+ res.end("Forbidden");
99
+ return;
100
+ }
101
+
102
+ stat(filePath, (err: NodeJS.ErrnoException | null) => {
103
+ if (err) {
104
+ res.writeHead(404);
105
+ res.end("Not found");
106
+ return;
107
+ }
108
+
109
+ const mimeTypes: Record<string, string> = {
110
+ ".html": "text/html",
111
+ ".js": "application/javascript",
112
+ ".css": "text/css",
113
+ ".json": "application/json",
114
+ ".png": "image/png",
115
+ ".jpg": "image/jpeg",
116
+ ".svg": "image/svg+xml",
117
+ ".ico": "image/x-icon",
118
+ };
119
+ const contentType = mimeTypes[extname(filePath)] ?? "application/octet-stream";
120
+
121
+ readFile(filePath, (readErr: NodeJS.ErrnoException | null, data: Buffer) => {
122
+ if (readErr) {
123
+ res.writeHead(500);
124
+ res.end("Internal server error");
125
+ return;
126
+ }
127
+ res.writeHead(200, { "Content-Type": contentType });
128
+ res.end(data);
129
+ });
130
+ });
131
+ });
132
+
133
+ server.listen(DEV_PORT, () => {
134
+ console.log(
135
+ `[custom-viz] Dev server listening on http://localhost:${DEV_PORT}`,
136
+ );
137
+ });
138
+
139
+ // Watch public/assets/ for changes — copy to dist/assets/ and notify
140
+ const assetsDir = resolve(__dirname, "public/assets");
141
+ watch(assetsDir, { recursive: true }, (_event, filename) => {
142
+ if (!filename) {
143
+ return;
144
+ }
145
+ cpSync(resolve(assetsDir, filename), resolve(__dirname, "dist/assets", filename));
146
+ for (const client of clients) {
147
+ client.write("data: reload\n\n");
148
+ }
149
+ console.log(`[custom-viz] Asset changed: ${filename}, notified ${clients.size} client(s)`);
150
+ });
151
+ },
152
+
153
+ closeBundle() {
154
+ for (const client of clients) {
155
+ client.write("data: reload\n\n");
156
+ }
157
+ console.log(
158
+ `[custom-viz] Build complete, notified ${clients.size} client(s)`,
159
+ );
160
+ },
161
+ };
162
+ }
163
+
164
+ const isWatch = process.argv.includes("--watch");
165
+
166
+ export default defineConfig({
167
+ plugins: [metabaseVizExternals(), ...(isWatch ? [metabaseDevServer()] : [])],
168
+ publicDir: "public",
169
+ build: {
170
+ outDir: "dist",
171
+ lib: {
172
+ entry: resolve(__dirname, "src/index.tsx"),
173
+ formats: ["iife"],
174
+ fileName: () => "index.js",
175
+ name: "__customVizPlugin__",
176
+ },
177
+ },
178
+ });
@@ -0,0 +1,41 @@
1
+ import { Column } from './data';
2
+ export type ColumnPredicate = (column: Column | null | undefined) => boolean;
3
+ export interface ColumnTypes {
4
+ isDate: ColumnPredicate;
5
+ isNumeric: ColumnPredicate;
6
+ isInteger: ColumnPredicate;
7
+ isBoolean: ColumnPredicate;
8
+ isString: ColumnPredicate;
9
+ isStringLike: ColumnPredicate;
10
+ isSummable: ColumnPredicate;
11
+ isNumericBaseType: ColumnPredicate;
12
+ isDateWithoutTime: ColumnPredicate;
13
+ isNumber: ColumnPredicate;
14
+ isFloat: ColumnPredicate;
15
+ isTime: ColumnPredicate;
16
+ isFK: ColumnPredicate;
17
+ isPK: ColumnPredicate;
18
+ isEntityName: ColumnPredicate;
19
+ isTitle: ColumnPredicate;
20
+ isProduct: ColumnPredicate;
21
+ isSource: ColumnPredicate;
22
+ isAddress: ColumnPredicate;
23
+ isScore: ColumnPredicate;
24
+ isQuantity: ColumnPredicate;
25
+ isCategory: ColumnPredicate;
26
+ isAny: ColumnPredicate;
27
+ isState: ColumnPredicate;
28
+ isCountry: ColumnPredicate;
29
+ isCoordinate: ColumnPredicate;
30
+ isLatitude: ColumnPredicate;
31
+ isLongitude: ColumnPredicate;
32
+ isCurrency: ColumnPredicate;
33
+ isPercentage: ColumnPredicate;
34
+ isID: ColumnPredicate;
35
+ isURL: ColumnPredicate;
36
+ isEmail: ColumnPredicate;
37
+ isAvatarURL: ColumnPredicate;
38
+ isImageURL: ColumnPredicate;
39
+ hasLatitudeAndLongitudeColumns: (cols: Column[]) => boolean;
40
+ }
41
+ //# sourceMappingURL=column-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"column-types.d.ts","sourceRoot":"","sources":["../../src/types/column-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAErC,MAAM,MAAM,eAAe,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,KAAK,OAAO,CAAC;AAE7E,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,eAAe,CAAC;IACxB,SAAS,EAAE,eAAe,CAAC;IAC3B,SAAS,EAAE,eAAe,CAAC;IAC3B,SAAS,EAAE,eAAe,CAAC;IAC3B,QAAQ,EAAE,eAAe,CAAC;IAC1B,YAAY,EAAE,eAAe,CAAC;IAC9B,UAAU,EAAE,eAAe,CAAC;IAC5B,iBAAiB,EAAE,eAAe,CAAC;IACnC,iBAAiB,EAAE,eAAe,CAAC;IACnC,QAAQ,EAAE,eAAe,CAAC;IAC1B,OAAO,EAAE,eAAe,CAAC;IACzB,MAAM,EAAE,eAAe,CAAC;IACxB,IAAI,EAAE,eAAe,CAAC;IACtB,IAAI,EAAE,eAAe,CAAC;IACtB,YAAY,EAAE,eAAe,CAAC;IAC9B,OAAO,EAAE,eAAe,CAAC;IACzB,SAAS,EAAE,eAAe,CAAC;IAC3B,QAAQ,EAAE,eAAe,CAAC;IAC1B,SAAS,EAAE,eAAe,CAAC;IAC3B,OAAO,EAAE,eAAe,CAAC;IACzB,UAAU,EAAE,eAAe,CAAC;IAC5B,UAAU,EAAE,eAAe,CAAC;IAC5B,KAAK,EAAE,eAAe,CAAC;IACvB,OAAO,EAAE,eAAe,CAAC;IACzB,SAAS,EAAE,eAAe,CAAC;IAC3B,YAAY,EAAE,eAAe,CAAC;IAC9B,UAAU,EAAE,eAAe,CAAC;IAC5B,WAAW,EAAE,eAAe,CAAC;IAC7B,UAAU,EAAE,eAAe,CAAC;IAC5B,YAAY,EAAE,eAAe,CAAC;IAC9B,IAAI,EAAE,eAAe,CAAC;IACtB,KAAK,EAAE,eAAe,CAAC;IACvB,OAAO,EAAE,eAAe,CAAC;IACzB,WAAW,EAAE,eAAe,CAAC;IAC7B,UAAU,EAAE,eAAe,CAAC;IAC5B,8BAA8B,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC;CAC7D"}
@@ -0,0 +1,22 @@
1
+ import { Column, RowValue } from './data';
2
+ export type FormatValueOptions = {
3
+ column?: Column;
4
+ compact?: boolean;
5
+ currency?: string;
6
+ currency_style?: string;
7
+ date_format?: string;
8
+ date_style?: string;
9
+ date_separator?: string;
10
+ date_abbreviate?: boolean;
11
+ decimals?: number;
12
+ number_style?: string;
13
+ number_separators?: string;
14
+ prefix?: string;
15
+ suffix?: string;
16
+ scale?: number;
17
+ negativeInParentheses?: boolean;
18
+ time_enabled?: "minutes" | "milliseconds" | "seconds" | null;
19
+ time_style?: string;
20
+ };
21
+ export type FormatValue = (value: RowValue, options?: FormatValueOptions) => string;
22
+ //# sourceMappingURL=format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/types/format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAE/C,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,YAAY,CAAC,EAAE,SAAS,GAAG,cAAc,GAAG,SAAS,GAAG,IAAI,CAAC;IAC7D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,CACxB,KAAK,EAAE,QAAQ,EACf,OAAO,CAAC,EAAE,kBAAkB,KACzB,MAAM,CAAC"}
@@ -1,5 +1,8 @@
1
+ export * from './column-types';
1
2
  export * from './data';
2
3
  export * from './date-time';
4
+ export * from './format';
5
+ export * from './measure-text';
3
6
  export * from './viz';
4
7
  export * from './viz-settings';
5
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC;AACvB,cAAc,aAAa,CAAC;AAC5B,cAAc,OAAO,CAAC;AACtB,cAAc,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,QAAQ,CAAC;AACvB,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,OAAO,CAAC;AACtB,cAAc,gBAAgB,CAAC"}
@@ -1,16 +1,29 @@
1
1
  import { ComponentType } from 'react';
2
2
  import { Column, DatasetData, RowValue, Series } from './data';
3
3
  import { TextHeightMeasurer, TextWidthMeasurer } from './measure-text';
4
- import { WidgetName, Widgets } from './viz-settings';
5
4
  /**
6
5
  * Export this function to define a custom visualization.
7
6
  */
8
7
  export type CreateCustomVisualization<CustomVisualizationSettings> = (props: CreateCustomVisualizationProps) => CustomVisualization<CustomVisualizationSettings>;
9
8
  export type CreateCustomVisualizationProps = {
9
+ /**
10
+ * Current user's locale (e.g., "de", "ja", "en").
11
+ */
12
+ locale: string;
10
13
  /**
11
14
  * Translates text using ttag function used in Metabase.
12
15
  */
13
16
  translate: (text: string) => string;
17
+ /**
18
+ * Returns a URL for a static asset declared in the plugin manifest.
19
+ * Use this to reference images and other static files from your plugin.
20
+ * @example getAssetUrl("icon.svg")
21
+ */
22
+ getAssetUrl: (assetPath: string) => string;
23
+ };
24
+ declare const SettingDefinitionSymbol: unique symbol;
25
+ export type CustomVisualizationSettingDefinition<_CustomVisualizationSettings> = {
26
+ readonly [SettingDefinitionSymbol]: never;
14
27
  };
15
28
  export type CustomVisualization<CustomVisualizationSettings> = {
16
29
  /**
@@ -40,7 +53,7 @@ export type CustomVisualization<CustomVisualizationSettings> = {
40
53
  /**
41
54
  * Visualization settings definitions.
42
55
  */
43
- settings?: CustomVisualizationSettingsDefinitions<CustomVisualizationSettings>;
56
+ settings?: Record<keyof CustomVisualizationSettings, CustomVisualizationSettingDefinition<CustomVisualizationSettings>>;
44
57
  /**
45
58
  * This function should return true if the data shape makes sense for this visualization.
46
59
  * TODO: should it get series: Series instead?
@@ -59,48 +72,12 @@ export type CustomVisualization<CustomVisualizationSettings> = {
59
72
  */
60
73
  StaticVisualizationComponent?: ComponentType<CustomStaticVisualizationProps<CustomVisualizationSettings>>;
61
74
  };
62
- export type CustomVisualizationSettingsDefinitions<CustomVisualizationSettings, K extends keyof CustomVisualizationSettings = keyof CustomVisualizationSettings> = {
63
- [Key in K]-?: VisualizationSettingDefinition<CustomVisualizationSettings[Key], Record<string, unknown>, CustomVisualizationSettings>;
64
- };
65
75
  export type BaseWidgetProps<TValue, CustomVisualizationSettings> = {
66
76
  id: string;
67
77
  value: TValue | undefined;
68
78
  onChange: (value?: TValue | null) => void;
69
79
  onChangeSettings: (settings: Partial<CustomVisualizationSettings>) => void;
70
80
  };
71
- type VisualizationSettingDefinitionBase<TValue, CustomVisualizationSettings> = {
72
- id: string;
73
- section?: string;
74
- title?: string;
75
- group?: string;
76
- index?: number;
77
- inline?: boolean;
78
- default?: TValue;
79
- persistDefault?: boolean;
80
- set?: boolean;
81
- value?: TValue;
82
- readDependencies?: string[];
83
- writeDependencies?: string[];
84
- eraseDependencies?: string[];
85
- isValid?: (series: Series, settings: CustomVisualizationSettings) => boolean;
86
- getDefault?: (series: Series, settings: CustomVisualizationSettings) => TValue;
87
- getValue?: (series: Series, settings: CustomVisualizationSettings) => TValue;
88
- };
89
- type VisualizationSettingDefinitionWithBuiltInWidget<TValue, CustomVisualizationSettings> = {
90
- [Key in WidgetName]: VisualizationSettingDefinitionBase<TValue, CustomVisualizationSettings> & {
91
- widget: Key;
92
- getProps?: (object: Series, vizSettings: CustomVisualizationSettings) => Widgets[Key];
93
- };
94
- }[WidgetName];
95
- type VisualizationSettingDefinitionWithCustomWidget<TValue, TProps, CustomVisualizationSettings> = VisualizationSettingDefinitionBase<TValue, CustomVisualizationSettings> & {
96
- widget: ComponentType<TProps & BaseWidgetProps<TValue, CustomVisualizationSettings>>;
97
- getProps?: (object: Series, vizSettings: CustomVisualizationSettings) => TProps;
98
- };
99
- type VisualizationSettingDefinitionWithoutWidget<TValue, CustomVisualizationSettings> = VisualizationSettingDefinitionBase<TValue, CustomVisualizationSettings> & {
100
- widget?: never;
101
- getProps?: never;
102
- };
103
- export type VisualizationSettingDefinition<TValue, TProps, CustomVisualizationSettings> = VisualizationSettingDefinitionWithBuiltInWidget<TValue, CustomVisualizationSettings> | VisualizationSettingDefinitionWithCustomWidget<TValue, TProps, CustomVisualizationSettings> | VisualizationSettingDefinitionWithoutWidget<TValue, CustomVisualizationSettings>;
104
81
  export type VisualizationGridSize = {
105
82
  /**
106
83
  * Number of grid columns in a Metabase dashboard.
@@ -116,7 +93,8 @@ export type CustomVisualizationProps<CustomVisualizationSettings> = {
116
93
  height: number;
117
94
  series: Series;
118
95
  settings: CustomVisualizationSettings;
119
- onClick: (clickObject: ClickObject<CustomVisualizationSettings> | null) => void;
96
+ onVisualizationClick: (clickObject: ClickObject<CustomVisualizationSettings> | null) => void;
97
+ onHoverChange: (hoverObject?: HoveredObject | null) => void;
120
98
  };
121
99
  export type ColorGetter = (colorName: string) => string;
122
100
  export interface RenderingContext {
@@ -132,7 +110,6 @@ export type CustomStaticVisualizationProps<CustomVisualizationSettings> = {
132
110
  settings: CustomVisualizationSettings;
133
111
  hasDevWatermark?: boolean;
134
112
  };
135
- export type CustomVisualizationSettingsProps = {};
136
113
  export type ClickObject<CustomVisualizationSettings> = {
137
114
  value?: RowValue;
138
115
  column?: Column;
@@ -149,5 +126,24 @@ export interface ClickObjectDimension {
149
126
  value: RowValue;
150
127
  column: Column;
151
128
  }
129
+ export type HoveredDataPoint = {
130
+ key: string;
131
+ value: RowValue;
132
+ col: Column;
133
+ };
134
+ export type HoveredDimension = {
135
+ value: RowValue;
136
+ column: Column;
137
+ };
138
+ export type HoveredObject = {
139
+ index?: number;
140
+ seriesIndex?: number;
141
+ value?: unknown;
142
+ column?: Column;
143
+ data?: HoveredDataPoint[];
144
+ dimensions?: HoveredDimension[];
145
+ element?: Element;
146
+ event?: MouseEvent;
147
+ };
152
148
  export {};
153
149
  //# sourceMappingURL=viz.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"viz.d.ts","sourceRoot":"","sources":["../../src/types/viz.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"viz.d.ts","sourceRoot":"","sources":["../../src/types/viz.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metabase/custom-viz",
3
- "version": "0.0.1-alpha.2",
3
+ "version": "0.0.1-alpha.5",
4
4
  "description": "Creating custom visualizations for Metabase",
5
5
  "type": "module",
6
6
  "bin": {
@@ -22,7 +22,7 @@
22
22
  ],
23
23
  "scripts": {
24
24
  "dev": "vite build --watch",
25
- "build": "vite build",
25
+ "build": "vite build && cp -r src/templates dist/templates",
26
26
  "lint": "oxlint src/",
27
27
  "format": "prettier --write .",
28
28
  "format:check": "prettier --check .",