diff --git a/api/package.json b/api/package.json index c2c3ce79d..c4740e833 100644 --- a/api/package.json +++ b/api/package.json @@ -1,6 +1,6 @@ { "name": "@devtable/api", - "version": "11.5.3", + "version": "11.6.1", "description": "", "main": "index.js", "scripts": { diff --git a/dashboard/package.json b/dashboard/package.json index 56845caf5..52b719c35 100644 --- a/dashboard/package.json +++ b/dashboard/package.json @@ -1,6 +1,6 @@ { "name": "@devtable/dashboard", - "version": "11.5.3", + "version": "11.6.1", "license": "Apache-2.0", "publishConfig": { "access": "public", @@ -42,7 +42,7 @@ "popmotion": "^11.0.3", "rc-select": "14.1.0", "rc-tree-select": "5.5.5", - "reactflow": "^11.5.3", + "reactflow": "^11.6.0", "uuid": "9.0.1" }, "devDependencies": { diff --git a/dashboard/src/components/plugins/viz-components/cartesian/editors/tooltip/index.tsx b/dashboard/src/components/plugins/viz-components/cartesian/editors/tooltip/index.tsx new file mode 100644 index 000000000..947db5971 --- /dev/null +++ b/dashboard/src/components/plugins/viz-components/cartesian/editors/tooltip/index.tsx @@ -0,0 +1,16 @@ +import { Stack } from '@mantine/core'; +import { Control, UseFormWatch } from 'react-hook-form'; +import { ICartesianChartConf } from '../../type'; +import { TooltipMetricsField } from './metrics'; + +interface ITooltipField { + control: Control; + watch: UseFormWatch; +} +export function TooltipField({ control, watch }: ITooltipField) { + return ( + + + + ); +} diff --git a/dashboard/src/components/plugins/viz-components/cartesian/editors/tooltip/metric.tsx b/dashboard/src/components/plugins/viz-components/cartesian/editors/tooltip/metric.tsx new file mode 100644 index 000000000..3853bd0b9 --- /dev/null +++ b/dashboard/src/components/plugins/viz-components/cartesian/editors/tooltip/metric.tsx @@ -0,0 +1,40 @@ +import { Button, Divider, Group, Stack, TextInput } from '@mantine/core'; +import { Control, Controller } from 'react-hook-form'; +import { Trash } from 'tabler-icons-react'; +import { DataFieldSelector } from '~/components/panel/settings/common/data-field-selector'; +import { ICartesianChartConf } from '../../type'; + +interface ITooltipMetricField { + control: Control; + index: number; + remove: (index: number) => void; +} + +export const TooltipMetricField = ({ control, index, remove }: ITooltipMetricField) => { + return ( + + + } + /> + } + /> + + + + + ); +}; diff --git a/dashboard/src/components/plugins/viz-components/cartesian/editors/tooltip/metrics.tsx b/dashboard/src/components/plugins/viz-components/cartesian/editors/tooltip/metrics.tsx new file mode 100644 index 000000000..4df9635bc --- /dev/null +++ b/dashboard/src/components/plugins/viz-components/cartesian/editors/tooltip/metrics.tsx @@ -0,0 +1,82 @@ +import { Group, Tabs, Text } from '@mantine/core'; +import { useEffect, useState } from 'react'; +import { Control, UseFormWatch, useFieldArray } from 'react-hook-form'; +import { InfoCircle, Plus } from 'tabler-icons-react'; +import { ICartesianChartConf } from '../../type'; +import { TooltipMetricField } from './metric'; + +interface ITooltipMetricsField { + control: Control; + watch: UseFormWatch; +} + +export const TooltipMetricsField = ({ control, watch }: ITooltipMetricsField) => { + const { fields, append, remove } = useFieldArray({ + control, + name: 'tooltip.metrics', + }); + + const watchFieldArray = watch('tooltip.metrics'); + const controlledFields = fields.map((field, index) => { + return { + ...field, + ...watchFieldArray[index], + }; + }); + + const addMetric = () => + append({ + id: Date.now().toString(), + data_key: '', + name: '', + }); + + const firstID = watch('tooltip.metrics.0.id'); + const [tab, setTab] = useState(() => firstID ?? null); + useEffect(() => { + if (firstID) { + setTab((t) => (t !== null ? t : firstID)); + } + }, [firstID]); + + return ( + <> + + + + Configure additional metrics to show in tooltip + + + setTab(t)} + styles={{ + tab: { + paddingTop: '0px', + paddingBottom: '0px', + }, + panel: { + padding: '0px', + paddingTop: '6px', + }, + }} + > + + {controlledFields.map((m, i) => ( + + {m.name ? m.name : i} + + ))} + + + + + {controlledFields.map((m, index) => ( + + + + ))} + + + ); +}; diff --git a/dashboard/src/components/plugins/viz-components/cartesian/index.tsx b/dashboard/src/components/plugins/viz-components/cartesian/index.tsx index dbf04b1fe..51a3c76ca 100644 --- a/dashboard/src/components/plugins/viz-components/cartesian/index.tsx +++ b/dashboard/src/components/plugins/viz-components/cartesian/index.tsx @@ -144,8 +144,15 @@ export class VizCartesianMigrator extends VersionBasedMigrator { config: Migrators.v18(data.config, env), }; }); + this.version(19, (data) => { + return { + ...data, + version: 19, + config: Migrators.v19(data.config), + }; + }); } - readonly VERSION = 18; + readonly VERSION = 19; } export const CartesianVizComponent: VizComponent = { @@ -157,7 +164,7 @@ export const CartesianVizComponent: VizComponent = { configRender: VizCartesianEditor, createConfig() { return { - version: 18, + version: 19, config: cloneDeep(DEFAULT_CONFIG) as ICartesianChartConf, }; }, diff --git a/dashboard/src/components/plugins/viz-components/cartesian/migrators/index.ts b/dashboard/src/components/plugins/viz-components/cartesian/migrators/index.ts index f2ab91568..c469be347 100644 --- a/dashboard/src/components/plugins/viz-components/cartesian/migrators/index.ts +++ b/dashboard/src/components/plugins/viz-components/cartesian/migrators/index.ts @@ -285,3 +285,10 @@ export function v18(legacyConf: any, { panelModel }: IMigrationEnv): ICartesianC throw error; } } +export function v19(legacyConf: any): ICartesianChartConf { + const { tooltip = { metrics: [] }, ...rest } = legacyConf; + return { + ...legacyConf, + tooltip, + }; +} diff --git a/dashboard/src/components/plugins/viz-components/cartesian/option/index.ts b/dashboard/src/components/plugins/viz-components/cartesian/option/index.ts index 096621114..3d22f67cf 100644 --- a/dashboard/src/components/plugins/viz-components/cartesian/option/index.ts +++ b/dashboard/src/components/plugins/viz-components/cartesian/option/index.ts @@ -47,7 +47,7 @@ export function getOption(conf: ICartesianChartConf, data: TPanelData, variables xAxis: getXAxes(conf, xAxisData), yAxis: getYAxes(conf, labelFormatters, series), series: [...series, ...regressionSeries], - tooltip: getTooltip(conf, series, labelFormatters), + tooltip: getTooltip(conf, data, series, labelFormatters), grid: getGrid(conf), legend: getLegend(series), dataZoom: getEchartsDataZoomOption(conf.dataZoom), diff --git a/dashboard/src/components/plugins/viz-components/cartesian/option/tooltip.ts b/dashboard/src/components/plugins/viz-components/cartesian/option/tooltip.ts index d51e8757f..17f24d962 100644 --- a/dashboard/src/components/plugins/viz-components/cartesian/option/tooltip.ts +++ b/dashboard/src/components/plugins/viz-components/cartesian/option/tooltip.ts @@ -5,6 +5,8 @@ import { getEchartsXAxisLabel } from '../editors/x-axis/x-axis-label-formatter/g import { ICartesianChartConf } from '../type'; import { IEchartsSeriesItem } from './utils/types'; import { defaultEchartsOptions } from '~/styles/default-echarts-options'; +import { extractData, formatNumber, readColumnIgnoringQuery } from '~/utils'; +import _ from 'lodash'; function getXAxisLabel(params: AnyObject[], conf: ICartesianChartConf) { const basis = params.find((p) => p.axisDim === 'x' && p.axisId === 'main-x-axis'); @@ -15,8 +17,18 @@ function getXAxisLabel(params: AnyObject[], conf: ICartesianChartConf) { return getEchartsXAxisLabel(conf.x_axis.axisLabel.formatter)(axisValue, axisIndex); } +const formatAdditionalMetric = (v: number) => { + return formatNumber(v, { + output: 'number', + trimMantissa: true, + mantissa: 2, + absolute: false, + }); +}; + export function getTooltip( conf: ICartesianChartConf, + data: TPanelData, series: IEchartsSeriesItem[], labelFormatters: Record string>, ) { @@ -51,6 +63,23 @@ export function getTooltip( `; }); + const additionalMetrics = conf.tooltip.metrics.map((m) => { + const metricData = extractData(data, m.data_key); + const metricValues = _.uniq( + arr.map(({ dataIndex }) => { + return metricData[dataIndex]; + }), + ); + return ` + + ${m.name} + ${metricValues.map((v) => { + return `${formatAdditionalMetric(v)}`; + })} + `; + }); + lines.push(...additionalMetrics); + const xAxisLabelStyle = getLabelOverflowStyleInTooltip(conf.x_axis.axisLabel.overflow.in_tooltip); const xAxisLabel = getXAxisLabel(arr, conf); return ` diff --git a/dashboard/src/components/plugins/viz-components/cartesian/type.ts b/dashboard/src/components/plugins/viz-components/cartesian/type.ts index 7e415e7f6..d0b313492 100644 --- a/dashboard/src/components/plugins/viz-components/cartesian/type.ts +++ b/dashboard/src/components/plugins/viz-components/cartesian/type.ts @@ -7,6 +7,7 @@ import { AggregationType, defaultNumberFormat, TNumberFormat } from '~/utils'; import { DEFAULT_DATA_ZOOM_CONFIG, TEchartsDataZoomConfig } from './editors/echarts-zooming-field/types'; import { TScatterSize } from './editors/scatter-size-select/types'; import { DEFAULT_X_AXIS_LABEL_FORMATTER, IXAxisLabelFormatter } from './editors/x-axis/x-axis-label-formatter/types'; +import { IEchartsTooltipMetric } from '../../common-echarts-fields/tooltip-metric'; export interface ICartesianChartSeriesItem { type: 'line' | 'bar' | 'scatter'; @@ -97,6 +98,9 @@ export interface ICartesianChartConf { bottom: string; }; }; + tooltip: { + metrics: IEchartsTooltipMetric[]; + }; reference_lines: ICartesianReferenceLine[]; reference_areas: ICartesianReferenceArea[]; dataZoom: TEchartsDataZoomConfig; @@ -114,6 +118,7 @@ export const DEFAULT_CONFIG: ICartesianChartConf = { overflow: getDefaultAxisLabelOverflow(), }, }, + tooltip: { metrics: [] }, x_axis_data_key: '', x_axis_name: '', y_axes: [ diff --git a/dashboard/src/components/plugins/viz-components/cartesian/viz-cartesian-editor.tsx b/dashboard/src/components/plugins/viz-components/cartesian/viz-cartesian-editor.tsx index 7f7f61bca..839fc5275 100644 --- a/dashboard/src/components/plugins/viz-components/cartesian/viz-cartesian-editor.tsx +++ b/dashboard/src/components/plugins/viz-components/cartesian/viz-cartesian-editor.tsx @@ -15,6 +15,7 @@ import { StatsField } from './editors/stats'; import { XAxisField } from './editors/x-axis'; import { YAxesField } from './editors/y-axes'; import { DEFAULT_CONFIG, ICartesianChartConf } from './type'; +import { TooltipField } from './editors/tooltip'; export function VizCartesianEditor({ context }: VizConfigProps) { const { value: confValue, set: setConf } = useStorageData(context.instanceData, 'config'); @@ -60,6 +61,7 @@ export function VizCartesianEditor({ context }: VizConfigProps) { Y Axes Series Regression Lines + Tooltip Stats Reference Lines Reference Areas @@ -82,6 +84,10 @@ export function VizCartesianEditor({ context }: VizConfigProps) { + + + + diff --git a/dashboard/src/components/plugins/viz-components/scatter-chart/viz-scatter-chart-editor.tsx b/dashboard/src/components/plugins/viz-components/scatter-chart/viz-scatter-chart-editor.tsx index 4ac6a0af6..6248d6ca9 100644 --- a/dashboard/src/components/plugins/viz-components/scatter-chart/viz-scatter-chart-editor.tsx +++ b/dashboard/src/components/plugins/viz-components/scatter-chart/viz-scatter-chart-editor.tsx @@ -8,7 +8,6 @@ import { useStorageData } from '~/components/plugins/hooks'; import { VizConfigProps } from '~/types/plugin'; import { EchartsZoomingField } from '../cartesian/editors/echarts-zooming-field'; import { ReferenceAreasField } from './editors/reference-areas'; -// import { ReferenceAreasField } from './panel/reference-areas'; import { ReferenceLinesField } from './editors/reference-lines'; import { ScatterField } from './editors/scatter'; import { StatsField } from './editors/stats'; diff --git a/package.json b/package.json index c3ab1d03b..8c124cc83 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@devtable/root", - "version": "11.5.3", + "version": "11.6.1", "private": true, "workspaces": [ "api", diff --git a/settings-form/package.json b/settings-form/package.json index 7d943179b..84530f9ee 100644 --- a/settings-form/package.json +++ b/settings-form/package.json @@ -1,6 +1,6 @@ { "name": "@devtable/settings-form", - "version": "11.5.3", + "version": "11.6.1", "license": "Apache-2.0", "publishConfig": { "access": "public", diff --git a/website/package.json b/website/package.json index fcd7be88a..b451414b2 100644 --- a/website/package.json +++ b/website/package.json @@ -2,7 +2,7 @@ "name": "@devtable/website", "private": true, "license": "Apache-2.0", - "version": "11.5.3", + "version": "11.6.1", "scripts": { "dev": "vite", "preview": "vite preview" diff --git a/yarn.lock b/yarn.lock index 454f041d5..64999ff13 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1699,7 +1699,7 @@ __metadata: rc-tree-select: 5.5.5 react-grid-layout: ^1.3.4 react-hook-form: ^7.31.2 - reactflow: ^11.5.3 + reactflow: ^11.6.0 rollup-plugin-visualizer: 5.9.0 tabler-icons-react: ^1.48.0 typescript: ^4.6.3 @@ -3883,37 +3883,37 @@ __metadata: languageName: node linkType: hard -"@reactflow/background@npm:11.2.4": - version: 11.2.4 - resolution: "@reactflow/background@npm:11.2.4" +"@reactflow/background@npm:11.3.6": + version: 11.3.6 + resolution: "@reactflow/background@npm:11.3.6" dependencies: - "@reactflow/core": 11.7.4 + "@reactflow/core": 11.10.1 classcat: ^5.0.3 - zustand: ^4.3.1 + zustand: ^4.4.1 peerDependencies: react: ">=17" react-dom: ">=17" - checksum: 17ca98de506254701876e58e5c7baf49b2d9ccd71fc3d2349a3fed06a2402bc111f48374c06b5a81f08c8059a0caacce71a5553a9fd6232cbdd4c98ebec57e9d + checksum: c5dafd7d10540a13ea4b4309d400254d97b16be0c43f36ae099de8557b2ac8104d456bcb963e83763970474f8661fadee8f81a56d92c47116c823717bb039089 languageName: node linkType: hard -"@reactflow/controls@npm:11.1.15": - version: 11.1.15 - resolution: "@reactflow/controls@npm:11.1.15" +"@reactflow/controls@npm:11.2.6": + version: 11.2.6 + resolution: "@reactflow/controls@npm:11.2.6" dependencies: - "@reactflow/core": 11.7.4 + "@reactflow/core": 11.10.1 classcat: ^5.0.3 - zustand: ^4.3.1 + zustand: ^4.4.1 peerDependencies: react: ">=17" react-dom: ">=17" - checksum: 7568c69bfa32d674645877c3d0b5c9d7ae3c1a08640e6bb7310868f3001f624f2c535335aa16270b9ff31abe64b7d5960b930cba2c6539fa815bba07accd7403 + checksum: b0e1766f44a4592dd6b743b4ff86376360faa4cc14c9b0e874d027f8b3f6321cc0ff080d54b807eee0af8e85edb8c405a86fd6f6178d1b71264c6b453644e655 languageName: node linkType: hard -"@reactflow/core@npm:11.7.4, @reactflow/core@npm:^11.6.0": - version: 11.7.4 - resolution: "@reactflow/core@npm:11.7.4" +"@reactflow/core@npm:11.10.1": + version: 11.10.1 + resolution: "@reactflow/core@npm:11.10.1" dependencies: "@types/d3": ^7.4.0 "@types/d3-drag": ^3.0.1 @@ -3923,59 +3923,59 @@ __metadata: d3-drag: ^3.0.0 d3-selection: ^3.0.0 d3-zoom: ^3.0.0 - zustand: ^4.3.1 + zustand: ^4.4.1 peerDependencies: react: ">=17" react-dom: ">=17" - checksum: 8e0bef41d6cd348dd98454d3a0d50495bd7e7a89c99757abccd68a80c916a109900265b9c20fc2bc1e94e6eca235a56d0b15c214fe8106133253d1db457c69b9 + checksum: 5ba2087474320ffff1257a5dcaf9b613e0e06a3ca5ffed582d55dd71af8a9acf6782de9258ea8f6b410e3a1c0627cb9e52808ef7497a81bff891ca7f14ccfb81 languageName: node linkType: hard -"@reactflow/minimap@npm:11.5.4": - version: 11.5.4 - resolution: "@reactflow/minimap@npm:11.5.4" +"@reactflow/minimap@npm:11.7.6": + version: 11.7.6 + resolution: "@reactflow/minimap@npm:11.7.6" dependencies: - "@reactflow/core": 11.7.4 + "@reactflow/core": 11.10.1 "@types/d3-selection": ^3.0.3 "@types/d3-zoom": ^3.0.1 classcat: ^5.0.3 d3-selection: ^3.0.0 d3-zoom: ^3.0.0 - zustand: ^4.3.1 + zustand: ^4.4.1 peerDependencies: react: ">=17" react-dom: ">=17" - checksum: 01373f6345f0864c2758057f3fd3328da154ef22b164c2b4888aeaeb28e5a5a1a84cdd80e47e067c0b2da920fc5ef885fcb6baab6f3d0ebccb4fac5310dcd094 + checksum: 9b622dbfc92be50d0c4ce1deb0670f9c41b32198b31ff2ac6948b58e36db2034d656f55f460966206c68fc558fc46603bf26ea066beeb691c466f55d4612dabc languageName: node linkType: hard -"@reactflow/node-resizer@npm:2.1.1": - version: 2.1.1 - resolution: "@reactflow/node-resizer@npm:2.1.1" +"@reactflow/node-resizer@npm:2.2.6": + version: 2.2.6 + resolution: "@reactflow/node-resizer@npm:2.2.6" dependencies: - "@reactflow/core": ^11.6.0 + "@reactflow/core": 11.10.1 classcat: ^5.0.4 d3-drag: ^3.0.0 d3-selection: ^3.0.0 - zustand: ^4.3.1 + zustand: ^4.4.1 peerDependencies: react: ">=17" react-dom: ">=17" - checksum: f820d3e738eb7eca35eed93b157914ba9e177dfc594a503f3ca29a67dc848918e2962552b7a8a14ed92a2aed3900e3bb463a79773d387cd7d03241dbf4d7c666 + checksum: cac448bf8e376c886991be8a920e59cb7471fe1898220ffad09aff3a81011f76d1b0f335c204027f924ad19b63599ff3a392cab60e67ac145b585664c4461400 languageName: node linkType: hard -"@reactflow/node-toolbar@npm:1.2.3": - version: 1.2.3 - resolution: "@reactflow/node-toolbar@npm:1.2.3" +"@reactflow/node-toolbar@npm:1.3.6": + version: 1.3.6 + resolution: "@reactflow/node-toolbar@npm:1.3.6" dependencies: - "@reactflow/core": 11.7.4 + "@reactflow/core": 11.10.1 classcat: ^5.0.3 - zustand: ^4.3.1 + zustand: ^4.4.1 peerDependencies: react: ">=17" react-dom: ">=17" - checksum: 09d81021909563a83c5ff82a578c2bb820eaaf7f0593b8bbfff5decd48ce43fb4470a4c7939a3cf75a235d21625af886c8707a34184a1f4d6576af348088db47 + checksum: 08d8e05f4befe5d69dd8c30d50d0b60ff0d1b3fcfd227a291d595dad1fb584e08579e0a3f6710436c550bda84fa07d2027da12059aa63fbd494b832ea176c833 languageName: node linkType: hard @@ -16841,20 +16841,20 @@ __metadata: languageName: node linkType: hard -"reactflow@npm:^11.5.3": - version: 11.7.4 - resolution: "reactflow@npm:11.7.4" +"reactflow@npm:^11.6.0": + version: 11.10.1 + resolution: "reactflow@npm:11.10.1" dependencies: - "@reactflow/background": 11.2.4 - "@reactflow/controls": 11.1.15 - "@reactflow/core": 11.7.4 - "@reactflow/minimap": 11.5.4 - "@reactflow/node-resizer": 2.1.1 - "@reactflow/node-toolbar": 1.2.3 + "@reactflow/background": 11.3.6 + "@reactflow/controls": 11.2.6 + "@reactflow/core": 11.10.1 + "@reactflow/minimap": 11.7.6 + "@reactflow/node-resizer": 2.2.6 + "@reactflow/node-toolbar": 1.3.6 peerDependencies: react: ">=17" react-dom: ">=17" - checksum: c5178deb5db1764b7e819e2ba8fa6b410e7ff3ee217c3d6c95ece80fcf103f8b9304084a96dda518f097bc0259e53b72199fdbd3291b3fdd1412117fc0a8e8a3 + checksum: 58a8c75266fd6c385cf13c4d87ce877632eecf2ce0c1f8c106a1e9874d3a252367bc6525fa0fb2acb369d2f00585db4df67dcdd048043752a42812419e836713 languageName: node linkType: hard @@ -20465,19 +20465,22 @@ __metadata: languageName: node linkType: hard -"zustand@npm:^4.3.1": - version: 4.3.9 - resolution: "zustand@npm:4.3.9" +"zustand@npm:^4.4.1": + version: 4.4.7 + resolution: "zustand@npm:4.4.7" dependencies: use-sync-external-store: 1.2.0 peerDependencies: + "@types/react": ">=16.8" immer: ">=9.0" react: ">=16.8" peerDependenciesMeta: + "@types/react": + optional: true immer: optional: true react: optional: true - checksum: fc83d653913fa537c354ba8b95d3a4fdebe62d2ebd3d9f5aeff2edf062811c0f5af48e02ab4da32b666752fd4f3e78c2b44624e445254f48503595435d4a7d70 + checksum: 9aeb6cc86162296c1dac504b8906ff952252c129fb3f05cc8926a7f5c9d7fbe098571d5161b3efe3347c0ee1d80197f787b768c7522721864f7323c28766717e languageName: node linkType: hard