@metabase/custom-viz 0.0.1-alpha.3 → 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 +79 -33
- package/dist/column-types.d.ts.map +1 -1
- package/dist/format.d.ts.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -4
- package/dist/lib/defineSetting.d.ts +22 -0
- package/dist/lib/defineSetting.d.ts.map +1 -0
- package/dist/lib/index.d.ts +2 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/measure-text.d.ts +5 -0
- package/dist/measure-text.d.ts.map +1 -0
- package/dist/templates/icon.svg +2 -4
- package/dist/templates/package.json +1 -1
- package/dist/templates/vite.config.ts +74 -35
- package/dist/types/viz.d.ts +31 -53
- package/dist/types/viz.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2,53 +2,99 @@
|
|
|
2
2
|
import { existsSync as e, readFileSync as t } from "node:fs";
|
|
3
3
|
import { mkdir as n, writeFile as r } from "node:fs/promises";
|
|
4
4
|
import { dirname as i, join as a } from "node:path";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { createInterface as o } from "node:readline";
|
|
6
|
+
import { Command as s } from "commander";
|
|
7
|
+
import { fileURLToPath as c } from "node:url";
|
|
7
8
|
//#region package.json
|
|
8
|
-
var c = "0.0.1-alpha.3", l = "node_modules/\n.DS_Store\n# dist must be committed\n", u = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" ry=\"2\"/>\n <line x1=\"3\" y1=\"9\" x2=\"21\" y2=\"9\"/>\n <line x1=\"9\" y1=\"21\" x2=\"9\" y2=\"9\"/>\n</svg>\n", d = "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", f = "{\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", p = "{\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", m = "{\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", h = "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 const VIRTUAL_CUSTOM_VIZ = \"\\0virtual:@metabase/custom-viz\";\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 if (source === \"@metabase/custom-viz\") {\n return VIRTUAL_CUSTOM_VIZ;\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 if (id === VIRTUAL_CUSTOM_VIZ) {\n return [\n \"const ct = window.__METABASE_VIZ_API__.columnTypes;\",\n \"export const { isDate, isNumeric, isInteger, isBoolean, isString, isStringLike, isSummable, isNumericBaseType, isDateWithoutTime, isNumber, isFloat, isTime, isFK, isPK, isEntityName, isTitle, isProduct, isSource, isAddress, isScore, isQuantity, isCategory, isAny, isState, isCountry, isCoordinate, isLatitude, isLongitude, isCurrency, isPercentage, isID, isURL, isEmail, isAvatarURL, isImageURL, hasLatitudeAndLongitudeColumns } = ct;\",\n \"export const formatValue = window.__METABASE_VIZ_API__.formatValue;\",\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 // 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(`[metabase-notify] 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 `[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 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 preview: {\n port: 5174,\n host: true,\n cors: true,\n },\n});\n", g = "__CUSTOM_VIZ_NAME__", _ = "__CUSTOM_VIZ_VERSION__";
|
|
9
|
-
function
|
|
10
|
-
return e.split(
|
|
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);
|
|
11
12
|
}
|
|
12
|
-
function
|
|
13
|
-
return
|
|
14
|
-
}
|
|
15
|
-
function b() {
|
|
16
|
-
return h;
|
|
13
|
+
function b(e) {
|
|
14
|
+
return y(m, e).split(v).join(l);
|
|
17
15
|
}
|
|
18
16
|
function x() {
|
|
19
|
-
return
|
|
17
|
+
return g;
|
|
20
18
|
}
|
|
21
|
-
function S(
|
|
22
|
-
return
|
|
19
|
+
function S() {
|
|
20
|
+
return h;
|
|
23
21
|
}
|
|
24
22
|
function C(e) {
|
|
25
|
-
return
|
|
23
|
+
return y(f, e);
|
|
26
24
|
}
|
|
27
|
-
function w() {
|
|
28
|
-
return
|
|
25
|
+
function w(e) {
|
|
26
|
+
return y(p, e);
|
|
29
27
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
return t(a(T, e));
|
|
28
|
+
function T() {
|
|
29
|
+
return d;
|
|
33
30
|
}
|
|
34
|
-
|
|
35
|
-
|
|
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";
|
|
36
47
|
}
|
|
37
48
|
//#endregion
|
|
38
49
|
//#region src/cli.ts
|
|
39
|
-
var
|
|
40
|
-
|
|
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) => {
|
|
41
52
|
let i = t.trim().replace(/\s+/g, "-").toLowerCase();
|
|
42
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([
|
|
43
|
-
r(a(i, "package.json"),
|
|
44
|
-
r(a(i, "vite.config.ts"),
|
|
45
|
-
r(a(i, "tsconfig.json"),
|
|
46
|
-
r(a(i, "src", "index.tsx"),
|
|
47
|
-
r(a(i, "metabase-plugin.json"),
|
|
48
|
-
r(a(i, "public", "assets", "icon.svg"),
|
|
49
|
-
r(a(i, "public", "assets", "thumbs-up.png"),
|
|
50
|
-
r(a(i, "public", "assets", "thumbs-down.png"),
|
|
51
|
-
r(a(i, ".gitignore"),
|
|
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())
|
|
52
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");
|
|
53
|
-
}),
|
|
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();
|
|
54
100
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"column-types.d.ts","sourceRoot":"","sources":["../src/column-types.ts"],"names":[],"mappings":"AAAA
|
|
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"}
|
package/dist/format.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAAA
|
|
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
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,UAAU,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
|
|
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
|
-
],
|
|
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
|
-
],
|
|
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 {
|
|
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 @@
|
|
|
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"}
|
package/dist/templates/icon.svg
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
<svg
|
|
2
|
-
<
|
|
3
|
-
<line x1="3" y1="9" x2="21" y2="9"/>
|
|
4
|
-
<line x1="9" y1="21" x2="9" y2="9"/>
|
|
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"/>
|
|
5
3
|
</svg>
|
|
@@ -13,8 +13,6 @@ import { defineConfig } from "vite";
|
|
|
13
13
|
function metabaseVizExternals() {
|
|
14
14
|
const VIRTUAL_REACT = "\0virtual:react";
|
|
15
15
|
const VIRTUAL_JSX_RUNTIME = "\0virtual:react/jsx-runtime";
|
|
16
|
-
const VIRTUAL_CUSTOM_VIZ = "\0virtual:@metabase/custom-viz";
|
|
17
|
-
|
|
18
16
|
return {
|
|
19
17
|
name: "metabase-viz-externals",
|
|
20
18
|
enforce: "pre" as const,
|
|
@@ -26,9 +24,6 @@ function metabaseVizExternals() {
|
|
|
26
24
|
if (source === "react/jsx-runtime") {
|
|
27
25
|
return VIRTUAL_JSX_RUNTIME;
|
|
28
26
|
}
|
|
29
|
-
if (source === "@metabase/custom-viz") {
|
|
30
|
-
return VIRTUAL_CUSTOM_VIZ;
|
|
31
|
-
}
|
|
32
27
|
return null;
|
|
33
28
|
},
|
|
34
29
|
|
|
@@ -46,49 +41,98 @@ function metabaseVizExternals() {
|
|
|
46
41
|
"export const { jsx, jsxs, Fragment } = jsxRuntime;",
|
|
47
42
|
].join("\n");
|
|
48
43
|
}
|
|
49
|
-
if (id === VIRTUAL_CUSTOM_VIZ) {
|
|
50
|
-
return [
|
|
51
|
-
"const ct = window.__METABASE_VIZ_API__.columnTypes;",
|
|
52
|
-
"export const { isDate, isNumeric, isInteger, isBoolean, isString, isStringLike, isSummable, isNumericBaseType, isDateWithoutTime, isNumber, isFloat, isTime, isFK, isPK, isEntityName, isTitle, isProduct, isSource, isAddress, isScore, isQuantity, isCategory, isAny, isState, isCountry, isCoordinate, isLatitude, isLongitude, isCurrency, isPercentage, isID, isURL, isEmail, isAvatarURL, isImageURL, hasLatitudeAndLongitudeColumns } = ct;",
|
|
53
|
-
"export const formatValue = window.__METABASE_VIZ_API__.formatValue;",
|
|
54
|
-
].join("\n");
|
|
55
|
-
}
|
|
56
44
|
return null;
|
|
57
45
|
},
|
|
58
46
|
};
|
|
59
47
|
}
|
|
60
48
|
|
|
61
|
-
const
|
|
49
|
+
const DEV_PORT = 5174;
|
|
62
50
|
|
|
63
51
|
/**
|
|
64
|
-
* Vite plugin that starts a
|
|
65
|
-
*
|
|
66
|
-
* Metabase's frontend connects to
|
|
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.
|
|
67
55
|
*/
|
|
68
|
-
function
|
|
56
|
+
function metabaseDevServer() {
|
|
69
57
|
const clients = new Set<import("http").ServerResponse>();
|
|
70
58
|
let server: ReturnType<typeof createServer> | null = null;
|
|
71
59
|
|
|
72
60
|
return {
|
|
73
|
-
name: "metabase-
|
|
61
|
+
name: "metabase-dev-server",
|
|
74
62
|
|
|
75
63
|
buildStart() {
|
|
76
64
|
if (server) {
|
|
77
65
|
return;
|
|
78
66
|
}
|
|
67
|
+
|
|
68
|
+
const distDir = resolve(__dirname, "dist");
|
|
69
|
+
|
|
79
70
|
server = createServer((req, res) => {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
+
});
|
|
85
130
|
});
|
|
86
|
-
clients.add(res);
|
|
87
|
-
req.on("close", () => clients.delete(res));
|
|
88
131
|
});
|
|
89
|
-
|
|
132
|
+
|
|
133
|
+
server.listen(DEV_PORT, () => {
|
|
90
134
|
console.log(
|
|
91
|
-
`[
|
|
135
|
+
`[custom-viz] Dev server listening on http://localhost:${DEV_PORT}`,
|
|
92
136
|
);
|
|
93
137
|
});
|
|
94
138
|
|
|
@@ -102,7 +146,7 @@ function metabaseNotifyReload() {
|
|
|
102
146
|
for (const client of clients) {
|
|
103
147
|
client.write("data: reload\n\n");
|
|
104
148
|
}
|
|
105
|
-
console.log(`[
|
|
149
|
+
console.log(`[custom-viz] Asset changed: ${filename}, notified ${clients.size} client(s)`);
|
|
106
150
|
});
|
|
107
151
|
},
|
|
108
152
|
|
|
@@ -111,7 +155,7 @@ function metabaseNotifyReload() {
|
|
|
111
155
|
client.write("data: reload\n\n");
|
|
112
156
|
}
|
|
113
157
|
console.log(
|
|
114
|
-
`[
|
|
158
|
+
`[custom-viz] Build complete, notified ${clients.size} client(s)`,
|
|
115
159
|
);
|
|
116
160
|
},
|
|
117
161
|
};
|
|
@@ -120,7 +164,7 @@ function metabaseNotifyReload() {
|
|
|
120
164
|
const isWatch = process.argv.includes("--watch");
|
|
121
165
|
|
|
122
166
|
export default defineConfig({
|
|
123
|
-
plugins: [metabaseVizExternals(), ...(isWatch ? [
|
|
167
|
+
plugins: [metabaseVizExternals(), ...(isWatch ? [metabaseDevServer()] : [])],
|
|
124
168
|
publicDir: "public",
|
|
125
169
|
build: {
|
|
126
170
|
outDir: "dist",
|
|
@@ -131,9 +175,4 @@ export default defineConfig({
|
|
|
131
175
|
name: "__customVizPlugin__",
|
|
132
176
|
},
|
|
133
177
|
},
|
|
134
|
-
preview: {
|
|
135
|
-
port: 5174,
|
|
136
|
-
host: true,
|
|
137
|
-
cors: true,
|
|
138
|
-
},
|
|
139
178
|
});
|
package/dist/types/viz.d.ts
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { ComponentType } from 'react';
|
|
2
2
|
import { Column, DatasetData, RowValue, Series } from './data';
|
|
3
|
-
import { TextHeightMeasurer,
|
|
4
|
-
import { WidgetName, Widgets } from './viz-settings';
|
|
3
|
+
import { TextHeightMeasurer, TextWidthMeasurer } from './measure-text';
|
|
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
|
*/
|
|
@@ -17,18 +20,10 @@ export type CreateCustomVisualizationProps = {
|
|
|
17
20
|
* @example getAssetUrl("icon.svg")
|
|
18
21
|
*/
|
|
19
22
|
getAssetUrl: (assetPath: string) => string;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Measures text width for given text and font style.
|
|
26
|
-
*/
|
|
27
|
-
measureTextWidth: TextWidthMeasurer;
|
|
28
|
-
/**
|
|
29
|
-
* Measures text height for given text and font style.
|
|
30
|
-
*/
|
|
31
|
-
measureTextHeight: TextHeightMeasurer;
|
|
23
|
+
};
|
|
24
|
+
declare const SettingDefinitionSymbol: unique symbol;
|
|
25
|
+
export type CustomVisualizationSettingDefinition<_CustomVisualizationSettings> = {
|
|
26
|
+
readonly [SettingDefinitionSymbol]: never;
|
|
32
27
|
};
|
|
33
28
|
export type CustomVisualization<CustomVisualizationSettings> = {
|
|
34
29
|
/**
|
|
@@ -58,7 +53,7 @@ export type CustomVisualization<CustomVisualizationSettings> = {
|
|
|
58
53
|
/**
|
|
59
54
|
* Visualization settings definitions.
|
|
60
55
|
*/
|
|
61
|
-
settings?:
|
|
56
|
+
settings?: Record<keyof CustomVisualizationSettings, CustomVisualizationSettingDefinition<CustomVisualizationSettings>>;
|
|
62
57
|
/**
|
|
63
58
|
* This function should return true if the data shape makes sense for this visualization.
|
|
64
59
|
* TODO: should it get series: Series instead?
|
|
@@ -77,48 +72,12 @@ export type CustomVisualization<CustomVisualizationSettings> = {
|
|
|
77
72
|
*/
|
|
78
73
|
StaticVisualizationComponent?: ComponentType<CustomStaticVisualizationProps<CustomVisualizationSettings>>;
|
|
79
74
|
};
|
|
80
|
-
export type CustomVisualizationSettingsDefinitions<CustomVisualizationSettings, K extends keyof CustomVisualizationSettings = keyof CustomVisualizationSettings> = {
|
|
81
|
-
[Key in K]-?: VisualizationSettingDefinition<CustomVisualizationSettings[Key], Record<string, unknown>, CustomVisualizationSettings>;
|
|
82
|
-
};
|
|
83
75
|
export type BaseWidgetProps<TValue, CustomVisualizationSettings> = {
|
|
84
76
|
id: string;
|
|
85
77
|
value: TValue | undefined;
|
|
86
78
|
onChange: (value?: TValue | null) => void;
|
|
87
79
|
onChangeSettings: (settings: Partial<CustomVisualizationSettings>) => void;
|
|
88
80
|
};
|
|
89
|
-
type VisualizationSettingDefinitionBase<TValue, CustomVisualizationSettings> = {
|
|
90
|
-
id: string;
|
|
91
|
-
section?: string;
|
|
92
|
-
title?: string;
|
|
93
|
-
group?: string;
|
|
94
|
-
index?: number;
|
|
95
|
-
inline?: boolean;
|
|
96
|
-
default?: TValue;
|
|
97
|
-
persistDefault?: boolean;
|
|
98
|
-
set?: boolean;
|
|
99
|
-
value?: TValue;
|
|
100
|
-
readDependencies?: string[];
|
|
101
|
-
writeDependencies?: string[];
|
|
102
|
-
eraseDependencies?: string[];
|
|
103
|
-
isValid?: (series: Series, settings: CustomVisualizationSettings) => boolean;
|
|
104
|
-
getDefault?: (series: Series, settings: CustomVisualizationSettings) => TValue;
|
|
105
|
-
getValue?: (series: Series, settings: CustomVisualizationSettings) => TValue;
|
|
106
|
-
};
|
|
107
|
-
type VisualizationSettingDefinitionWithBuiltInWidget<TValue, CustomVisualizationSettings> = {
|
|
108
|
-
[Key in WidgetName]: VisualizationSettingDefinitionBase<TValue, CustomVisualizationSettings> & {
|
|
109
|
-
widget: Key;
|
|
110
|
-
getProps?: (object: Series, vizSettings: CustomVisualizationSettings) => Widgets[Key];
|
|
111
|
-
};
|
|
112
|
-
}[WidgetName];
|
|
113
|
-
type VisualizationSettingDefinitionWithCustomWidget<TValue, TProps, CustomVisualizationSettings> = VisualizationSettingDefinitionBase<TValue, CustomVisualizationSettings> & {
|
|
114
|
-
widget: ComponentType<TProps & BaseWidgetProps<TValue, CustomVisualizationSettings>>;
|
|
115
|
-
getProps?: (object: Series, vizSettings: CustomVisualizationSettings) => TProps;
|
|
116
|
-
};
|
|
117
|
-
type VisualizationSettingDefinitionWithoutWidget<TValue, CustomVisualizationSettings> = VisualizationSettingDefinitionBase<TValue, CustomVisualizationSettings> & {
|
|
118
|
-
widget?: never;
|
|
119
|
-
getProps?: never;
|
|
120
|
-
};
|
|
121
|
-
export type VisualizationSettingDefinition<TValue, TProps, CustomVisualizationSettings> = VisualizationSettingDefinitionWithBuiltInWidget<TValue, CustomVisualizationSettings> | VisualizationSettingDefinitionWithCustomWidget<TValue, TProps, CustomVisualizationSettings> | VisualizationSettingDefinitionWithoutWidget<TValue, CustomVisualizationSettings>;
|
|
122
81
|
export type VisualizationGridSize = {
|
|
123
82
|
/**
|
|
124
83
|
* Number of grid columns in a Metabase dashboard.
|
|
@@ -134,7 +93,8 @@ export type CustomVisualizationProps<CustomVisualizationSettings> = {
|
|
|
134
93
|
height: number;
|
|
135
94
|
series: Series;
|
|
136
95
|
settings: CustomVisualizationSettings;
|
|
137
|
-
|
|
96
|
+
onVisualizationClick: (clickObject: ClickObject<CustomVisualizationSettings> | null) => void;
|
|
97
|
+
onHoverChange: (hoverObject?: HoveredObject | null) => void;
|
|
138
98
|
};
|
|
139
99
|
export type ColorGetter = (colorName: string) => string;
|
|
140
100
|
export interface RenderingContext {
|
|
@@ -150,7 +110,6 @@ export type CustomStaticVisualizationProps<CustomVisualizationSettings> = {
|
|
|
150
110
|
settings: CustomVisualizationSettings;
|
|
151
111
|
hasDevWatermark?: boolean;
|
|
152
112
|
};
|
|
153
|
-
export type CustomVisualizationSettingsProps = {};
|
|
154
113
|
export type ClickObject<CustomVisualizationSettings> = {
|
|
155
114
|
value?: RowValue;
|
|
156
115
|
column?: Column;
|
|
@@ -167,5 +126,24 @@ export interface ClickObjectDimension {
|
|
|
167
126
|
value: RowValue;
|
|
168
127
|
column: Column;
|
|
169
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
|
+
};
|
|
170
148
|
export {};
|
|
171
149
|
//# sourceMappingURL=viz.d.ts.map
|
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,
|
|
1
|
+
{"version":3,"file":"viz.d.ts","sourceRoot":"","sources":["../../src/types/viz.ts"],"names":[],"mappings":"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"}
|