@metabase/custom-viz 0.0.1-alpha.1 → 0.0.1-alpha.2
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/README.md +26 -29
- package/dist/cli.js +22 -22
- package/dist/types/viz.d.ts +15 -15
- package/dist/types/viz.d.ts.map +1 -1
- package/package.json +2 -3
package/README.md
CHANGED
|
@@ -1,53 +1,50 @@
|
|
|
1
1
|
# @metabase/custom-viz
|
|
2
2
|
|
|
3
|
-
CLI for creating
|
|
3
|
+
CLI and type definitions for creating custom visualizations for Metabase.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Getting Started
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- npm >= 10
|
|
9
|
-
|
|
10
|
-
## Development
|
|
7
|
+
### 1. Scaffold a new visualization
|
|
11
8
|
|
|
12
9
|
```bash
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
npx @metabase/custom-viz init my-viz
|
|
11
|
+
```
|
|
15
12
|
|
|
16
|
-
|
|
17
|
-
npm run dev
|
|
13
|
+
This creates a new directory with everything you need:
|
|
18
14
|
|
|
19
|
-
|
|
20
|
-
|
|
15
|
+
```
|
|
16
|
+
my-viz/
|
|
17
|
+
src/index.tsx # Your visualization code
|
|
18
|
+
package.json
|
|
19
|
+
vite.config.ts
|
|
20
|
+
tsconfig.json
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
### 2. Install dependencies and start developing
|
|
24
24
|
|
|
25
25
|
```bash
|
|
26
|
-
|
|
27
|
-
npm
|
|
28
|
-
|
|
29
|
-
# Format code
|
|
30
|
-
npm run format
|
|
31
|
-
|
|
32
|
-
# Check formatting
|
|
33
|
-
npm run format:check
|
|
26
|
+
cd my-viz
|
|
27
|
+
npm install
|
|
28
|
+
npm run dev # Watch mode — rebuilds on changes
|
|
34
29
|
```
|
|
35
30
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
This package uses [np](https://github.com/sindresorhus/np) for publishing.
|
|
31
|
+
### 3. Build for production
|
|
39
32
|
|
|
40
33
|
```bash
|
|
41
|
-
npm run
|
|
34
|
+
npm run build
|
|
42
35
|
```
|
|
43
36
|
|
|
44
|
-
|
|
37
|
+
The build output will be in the `dist/` directory, ready to be loaded into Metabase.
|
|
45
38
|
|
|
46
39
|
## Project Structure
|
|
47
40
|
|
|
48
41
|
```
|
|
49
42
|
src/
|
|
50
|
-
cli.ts
|
|
51
|
-
|
|
52
|
-
|
|
43
|
+
cli.ts # CLI entry point (commander)
|
|
44
|
+
index.ts # Library entry point (type exports)
|
|
45
|
+
templates.ts # Scaffolding templates
|
|
46
|
+
templates/ # Template files for `init` command
|
|
47
|
+
types/ # Custom visualization type definitions
|
|
48
|
+
dist/ # Build output
|
|
49
|
+
vite.config.ts # Vite build configuration
|
|
53
50
|
```
|
package/dist/cli.js
CHANGED
|
@@ -3,37 +3,37 @@ import { existsSync as e } from "node:fs";
|
|
|
3
3
|
import { mkdir as t, writeFile as n } from "node:fs/promises";
|
|
4
4
|
import { join as r } from "node:path";
|
|
5
5
|
import { Command as i } from "commander";
|
|
6
|
-
//#region
|
|
7
|
-
var a = "node_modules/\n.DS_Store\n# dist must be committed\n", o = "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", s = "{\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\": \"^0.0.1\",\n \"@types/react\": \"^19.2.14\",\n \"react\": \"^19.1.0\",\n \"typescript\": \"^5.9.3\",\n \"vite\": \"^8.0.0\"\n }\n}\n", c = "{\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", l = "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", u = "__CUSTOM_VIZ_NAME__";
|
|
8
|
-
function
|
|
9
|
-
return e.split(
|
|
6
|
+
//#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);
|
|
10
10
|
}
|
|
11
|
-
function
|
|
12
|
-
return
|
|
11
|
+
function m(e) {
|
|
12
|
+
return p(c, e).split(f).join(a);
|
|
13
13
|
}
|
|
14
|
-
function
|
|
15
|
-
return
|
|
14
|
+
function h() {
|
|
15
|
+
return u;
|
|
16
16
|
}
|
|
17
|
-
function
|
|
18
|
-
return
|
|
17
|
+
function g() {
|
|
18
|
+
return l;
|
|
19
19
|
}
|
|
20
|
-
function
|
|
21
|
-
return
|
|
20
|
+
function _(e) {
|
|
21
|
+
return p(s, e);
|
|
22
22
|
}
|
|
23
|
-
function
|
|
24
|
-
return
|
|
23
|
+
function v() {
|
|
24
|
+
return o;
|
|
25
25
|
}
|
|
26
26
|
//#endregion
|
|
27
27
|
//#region src/cli.ts
|
|
28
|
-
var
|
|
29
|
-
|
|
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
30
|
let a = i.trim().replace(/\s+/g, "-").toLowerCase();
|
|
31
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"),
|
|
33
|
-
n(r(a, "vite.config.ts"),
|
|
34
|
-
n(r(a, "tsconfig.json"),
|
|
35
|
-
n(r(a, "src", "index.tsx"),
|
|
36
|
-
n(r(a, ".gitignore"),
|
|
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
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
|
-
}),
|
|
38
|
+
}), y.parse();
|
|
39
39
|
//#endregion
|
package/dist/types/viz.d.ts
CHANGED
|
@@ -32,11 +32,11 @@ export type CustomVisualization<CustomVisualizationSettings> = {
|
|
|
32
32
|
/**
|
|
33
33
|
* Min size on dashboard grid.
|
|
34
34
|
*/
|
|
35
|
-
minSize
|
|
35
|
+
minSize?: VisualizationGridSize;
|
|
36
36
|
/**
|
|
37
37
|
* Default size on dashboard grid.
|
|
38
38
|
*/
|
|
39
|
-
defaultSize
|
|
39
|
+
defaultSize?: VisualizationGridSize;
|
|
40
40
|
/**
|
|
41
41
|
* Visualization settings definitions.
|
|
42
42
|
*/
|
|
@@ -57,10 +57,10 @@ export type CustomVisualization<CustomVisualizationSettings> = {
|
|
|
57
57
|
/**
|
|
58
58
|
* Component that renders the visualization.
|
|
59
59
|
*/
|
|
60
|
-
StaticVisualizationComponent
|
|
60
|
+
StaticVisualizationComponent?: ComponentType<CustomStaticVisualizationProps<CustomVisualizationSettings>>;
|
|
61
61
|
};
|
|
62
62
|
export type CustomVisualizationSettingsDefinitions<CustomVisualizationSettings, K extends keyof CustomVisualizationSettings = keyof CustomVisualizationSettings> = {
|
|
63
|
-
[Key in K]-?: VisualizationSettingDefinition<
|
|
63
|
+
[Key in K]-?: VisualizationSettingDefinition<CustomVisualizationSettings[Key], Record<string, unknown>, CustomVisualizationSettings>;
|
|
64
64
|
};
|
|
65
65
|
export type BaseWidgetProps<TValue, CustomVisualizationSettings> = {
|
|
66
66
|
id: string;
|
|
@@ -68,7 +68,7 @@ export type BaseWidgetProps<TValue, CustomVisualizationSettings> = {
|
|
|
68
68
|
onChange: (value?: TValue | null) => void;
|
|
69
69
|
onChangeSettings: (settings: Partial<CustomVisualizationSettings>) => void;
|
|
70
70
|
};
|
|
71
|
-
type VisualizationSettingDefinitionBase<
|
|
71
|
+
type VisualizationSettingDefinitionBase<TValue, CustomVisualizationSettings> = {
|
|
72
72
|
id: string;
|
|
73
73
|
section?: string;
|
|
74
74
|
title?: string;
|
|
@@ -82,25 +82,25 @@ type VisualizationSettingDefinitionBase<T, TValue, CustomVisualizationSettings>
|
|
|
82
82
|
readDependencies?: string[];
|
|
83
83
|
writeDependencies?: string[];
|
|
84
84
|
eraseDependencies?: string[];
|
|
85
|
-
isValid?: (
|
|
86
|
-
getDefault?: (
|
|
87
|
-
getValue?: (
|
|
85
|
+
isValid?: (series: Series, settings: CustomVisualizationSettings) => boolean;
|
|
86
|
+
getDefault?: (series: Series, settings: CustomVisualizationSettings) => TValue;
|
|
87
|
+
getValue?: (series: Series, settings: CustomVisualizationSettings) => TValue;
|
|
88
88
|
};
|
|
89
|
-
type VisualizationSettingDefinitionWithBuiltInWidget<
|
|
90
|
-
[Key in WidgetName]: VisualizationSettingDefinitionBase<
|
|
89
|
+
type VisualizationSettingDefinitionWithBuiltInWidget<TValue, CustomVisualizationSettings> = {
|
|
90
|
+
[Key in WidgetName]: VisualizationSettingDefinitionBase<TValue, CustomVisualizationSettings> & {
|
|
91
91
|
widget: Key;
|
|
92
|
-
getProps?: (object:
|
|
92
|
+
getProps?: (object: Series, vizSettings: CustomVisualizationSettings) => Widgets[Key];
|
|
93
93
|
};
|
|
94
94
|
}[WidgetName];
|
|
95
|
-
type VisualizationSettingDefinitionWithCustomWidget<
|
|
95
|
+
type VisualizationSettingDefinitionWithCustomWidget<TValue, TProps, CustomVisualizationSettings> = VisualizationSettingDefinitionBase<TValue, CustomVisualizationSettings> & {
|
|
96
96
|
widget: ComponentType<TProps & BaseWidgetProps<TValue, CustomVisualizationSettings>>;
|
|
97
|
-
getProps?: (object:
|
|
97
|
+
getProps?: (object: Series, vizSettings: CustomVisualizationSettings) => TProps;
|
|
98
98
|
};
|
|
99
|
-
type VisualizationSettingDefinitionWithoutWidget<
|
|
99
|
+
type VisualizationSettingDefinitionWithoutWidget<TValue, CustomVisualizationSettings> = VisualizationSettingDefinitionBase<TValue, CustomVisualizationSettings> & {
|
|
100
100
|
widget?: never;
|
|
101
101
|
getProps?: never;
|
|
102
102
|
};
|
|
103
|
-
export type VisualizationSettingDefinition<
|
|
103
|
+
export type VisualizationSettingDefinition<TValue, TProps, CustomVisualizationSettings> = VisualizationSettingDefinitionWithBuiltInWidget<TValue, CustomVisualizationSettings> | VisualizationSettingDefinitionWithCustomWidget<TValue, TProps, CustomVisualizationSettings> | VisualizationSettingDefinitionWithoutWidget<TValue, CustomVisualizationSettings>;
|
|
104
104
|
export type VisualizationGridSize = {
|
|
105
105
|
/**
|
|
106
106
|
* Number of grid columns in a Metabase dashboard.
|
package/dist/types/viz.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"viz.d.ts","sourceRoot":"","sources":["../../src/types/viz.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAE3C,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACpE,OAAO,KAAK,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAC5E,OAAO,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAE1D;;GAEG;AACH,MAAM,MAAM,yBAAyB,CAAC,2BAA2B,IAAI,CACnE,KAAK,EAAE,8BAA8B,KAClC,mBAAmB,CAAC,2BAA2B,CAAC,CAAC;AAEtD,MAAM,MAAM,8BAA8B,GAAG;IAC3C;;OAEG;IACH,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CAQrC,CAAC;AAEF,MAAM,MAAM,mBAAmB,CAAC,2BAA2B,IAAI;IAC7D;;OAEG;IACH,EAAE,EAAE,MAAM,CAAC;IAEX;;OAEG;IACH,OAAO,IAAI,MAAM,CAAC;IAElB;;OAEG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;OAEG;IACH,OAAO,EAAE,qBAAqB,CAAC;
|
|
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.
|
|
3
|
+
"version": "0.0.1-alpha.2",
|
|
4
4
|
"description": "Creating custom visualizations for Metabase",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"lint": "oxlint src/",
|
|
27
27
|
"format": "prettier --write .",
|
|
28
28
|
"format:check": "prettier --check .",
|
|
29
|
-
"release": "
|
|
29
|
+
"release": "bun run build && np"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"commander": "^13.1.0"
|
|
@@ -34,7 +34,6 @@
|
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@types/node": "^22.13.10",
|
|
36
36
|
"@types/react": "^19.2.14",
|
|
37
|
-
"lefthook": "^2.1.4",
|
|
38
37
|
"np": "^10.2.0",
|
|
39
38
|
"prettier": "^3.5.3",
|
|
40
39
|
"typescript": "^5.9.3",
|