From 6a96e32544012f319a672a7c5880282e40d4f8c1 Mon Sep 17 00:00:00 2001 From: Leto Date: Tue, 25 Jun 2024 18:59:12 +0800 Subject: [PATCH 1/5] feat(dashboard): apply visualmap to merico-heatmap --- .../visual-map/visual-map-editor/index.tsx | 1 - .../editors/heat_block/index.tsx | 12 ------ .../merico-heatmap/editors/index.tsx | 22 +++++++--- .../viz-components/merico-heatmap/index.tsx | 12 +++++- .../merico-heatmap/migrators/index.ts | 19 +++++++++ .../merico-heatmap/render/option/index.ts | 40 ++++++++++++++++--- .../merico-heatmap/render/option/series.ts | 13 +----- .../render/option/visual-map.ts | 16 -------- .../merico-heatmap/render/option/x-axis.ts | 14 ++----- .../merico-heatmap/render/option/y-axis.ts | 14 ++----- .../viz-components/merico-heatmap/type.ts | 14 ++----- 11 files changed, 93 insertions(+), 84 deletions(-) create mode 100644 dashboard/src/components/plugins/viz-components/merico-heatmap/migrators/index.ts delete mode 100644 dashboard/src/components/plugins/viz-components/merico-heatmap/render/option/visual-map.ts diff --git a/dashboard/src/components/plugins/common-echarts-fields/visual-map/visual-map-editor/index.tsx b/dashboard/src/components/plugins/common-echarts-fields/visual-map/visual-map-editor/index.tsx index ce4af2193..0879a30cf 100644 --- a/dashboard/src/components/plugins/common-echarts-fields/visual-map/visual-map-editor/index.tsx +++ b/dashboard/src/components/plugins/common-echarts-fields/visual-map/visual-map-editor/index.tsx @@ -20,7 +20,6 @@ export const VisualMapEditor = ({ form }: Props) => { const { t, i18n } = useTranslation(); const control = form.control; const visualMap = form.watch('visualMap'); - const { type, orient } = visualMap; const visualMapTypeOptions = useMemo(() => { return [ diff --git a/dashboard/src/components/plugins/viz-components/merico-heatmap/editors/heat_block/index.tsx b/dashboard/src/components/plugins/viz-components/merico-heatmap/editors/heat_block/index.tsx index d1f1b2eab..e9a53c6c7 100644 --- a/dashboard/src/components/plugins/viz-components/merico-heatmap/editors/heat_block/index.tsx +++ b/dashboard/src/components/plugins/viz-components/merico-heatmap/editors/heat_block/index.tsx @@ -31,18 +31,6 @@ export function HeatBlockField({ control, watch }: IHeatBlockField) { render={({ field }) => } /> - - } - /> - } - /> - (context.instanceData, 'config'); - const { variables } = context; - const conf: TMericoHeatmapConf = useMemo(() => defaultsDeep({}, confValue, DEFAULT_CONFIG), [confValue]); + const conf: TMericoHeatmapConf = useMemo(() => { + if (!confValue) { + return DEFAULT_CONFIG; + } + return defaults({}, confValue); + }, [confValue]); const defaultValues: TMericoHeatmapConf = useMemo(() => { return _.cloneDeep(conf); }, [conf]); @@ -30,7 +35,8 @@ export function EditMericoHeatmap({ context }: VizConfigProps) { } }, [conf, defaultValues]); - const { control, handleSubmit, watch, getValues, reset } = useForm({ defaultValues }); + const form = useForm({ defaultValues }); + const { control, handleSubmit, watch, getValues, reset } = form; useEffect(() => { reset(defaultValues); }, [defaultValues]); @@ -66,6 +72,7 @@ export function EditMericoHeatmap({ context }: VizConfigProps) { {t('chart.x_axis.label')} {t('chart.y_axis.label')} {t('chart.heatmap.heatblock.label')} + {t('chart.visual_map.label')} {t('chart.tooltip.label')} @@ -81,6 +88,11 @@ export function EditMericoHeatmap({ context }: VizConfigProps) { + + {/* @ts-expect-error Types of property 'watch' are incompatible. */} + + + diff --git a/dashboard/src/components/plugins/viz-components/merico-heatmap/index.tsx b/dashboard/src/components/plugins/viz-components/merico-heatmap/index.tsx index 1e8e419e2..16485a6c0 100644 --- a/dashboard/src/components/plugins/viz-components/merico-heatmap/index.tsx +++ b/dashboard/src/components/plugins/viz-components/merico-heatmap/index.tsx @@ -5,9 +5,10 @@ import { RenderMericoHeatmap } from './render'; import { ClickHeatBlock } from './triggers'; import { DEFAULT_CONFIG, TMericoHeatmapConf } from './type'; import { translation } from './translation'; +import * as Migrators from './migrators'; class MericoHeatmapMigrator extends VersionBasedMigrator { - readonly VERSION = 1; + readonly VERSION = 2; configVersions(): void { this.version(1, (data: any) => { @@ -16,6 +17,13 @@ class MericoHeatmapMigrator extends VersionBasedMigrator { config: data, }; }); + this.version(2, (data) => { + return { + ...data, + version: 2, + config: Migrators.v2(data.config), + }; + }); } } @@ -30,7 +38,7 @@ export const MericoHeatmapVizComponent: VizComponent = { version: number; config: TMericoHeatmapConf; } => ({ - version: 1, + version: 2, config: DEFAULT_CONFIG, }), triggers: [ClickHeatBlock], diff --git a/dashboard/src/components/plugins/viz-components/merico-heatmap/migrators/index.ts b/dashboard/src/components/plugins/viz-components/merico-heatmap/migrators/index.ts new file mode 100644 index 000000000..2f6974e24 --- /dev/null +++ b/dashboard/src/components/plugins/viz-components/merico-heatmap/migrators/index.ts @@ -0,0 +1,19 @@ +import { getDefaultVisualMap } from '~/components/plugins/common-echarts-fields/visual-map'; +import { TMericoHeatmapConf } from '../type'; + +export function v2(legacyConf: any): TMericoHeatmapConf { + const { + visualMap = getDefaultVisualMap(), + heat_block: { min, max, ...restHeatBlock }, + ...rest + } = legacyConf; + return { + ...rest, + heat_block: restHeatBlock, + visualMap: { + ...getDefaultVisualMap(), + min, + max, + }, + }; +} diff --git a/dashboard/src/components/plugins/viz-components/merico-heatmap/render/option/index.ts b/dashboard/src/components/plugins/viz-components/merico-heatmap/render/option/index.ts index 56ae2e9af..9a95bd76b 100644 --- a/dashboard/src/components/plugins/viz-components/merico-heatmap/render/option/index.ts +++ b/dashboard/src/components/plugins/viz-components/merico-heatmap/render/option/index.ts @@ -1,12 +1,14 @@ -import { ITemplateVariable, formatAggregatedValue, getAggregatedValue } from '~/utils'; +import { ITemplateVariable, formatAggregatedValue, getAggregatedValue, parseDataKey } from '~/utils'; import { TMericoHeatmapConf } from '../../type'; import { getLabelFormatters, getValueFormatters } from './formatters'; import { getGrid } from './grid'; import { getSeries } from './series'; import { getTooltip } from './tooltip'; -import { getVisualMap } from './visual-map'; + import { getXAxis } from './x-axis'; import { getYAxis } from './y-axis'; +import { getSkipRangeColor, getVisualMap } from '~/components/plugins/common-echarts-fields/visual-map'; +import _ from 'lodash'; export function getOption(conf: TMericoHeatmapConf, data: TPanelData, variables: ITemplateVariable[]) { if (!conf.x_axis.data_key || !conf.y_axis.data_key || !conf.heat_block.data_key) { @@ -21,13 +23,39 @@ export function getOption(conf: TMericoHeatmapConf, data: TPanelData, variables: const labelFormatters = getLabelFormatters(conf); const valueFormatters = getValueFormatters(conf); + const visualMap = getVisualMap(conf.visualMap, variableValueMap); + const { min, max } = visualMap; + + const x = parseDataKey(conf.x_axis.data_key); + const y = parseDataKey(conf.y_axis.data_key); + const h = parseDataKey(conf.heat_block.data_key); + const xData = _.uniq(data[x.queryID].map((d) => d[x.columnKey])); + const yData = _.uniq(data[x.queryID].map((d) => d[y.columnKey])); + const seriesData = data[x.queryID].map((d) => { + const vx = _.get(d, x.columnKey); + const vy = _.get(d, y.columnKey); + const vh = _.get(d, h.columnKey); + const ret: any = { + value: [vx, vy, vh], + }; + + const { followVisualMap, color } = getSkipRangeColor(vh, min, max, conf.visualMap); + if (!followVisualMap) { + ret.visualMap = false; + ret.itemStyle = { + color, + }; + } + return ret; + }); + const options = { - xAxis: getXAxis(conf, data, labelFormatters.x_axis), - yAxis: getYAxis(conf, data, labelFormatters.y_axis), - series: getSeries(conf, data), + xAxis: getXAxis(conf, xData, labelFormatters.x_axis), + yAxis: getYAxis(conf, yData, labelFormatters.y_axis), + series: getSeries(conf, seriesData), tooltip: getTooltip(conf, data, labelFormatters, valueFormatters), grid: getGrid(conf), - visualMap: getVisualMap(conf, variableValueMap), + visualMap, }; return options; } diff --git a/dashboard/src/components/plugins/viz-components/merico-heatmap/render/option/series.ts b/dashboard/src/components/plugins/viz-components/merico-heatmap/render/option/series.ts index 0d7d63566..2bedf3790 100644 --- a/dashboard/src/components/plugins/viz-components/merico-heatmap/render/option/series.ts +++ b/dashboard/src/components/plugins/viz-components/merico-heatmap/render/option/series.ts @@ -1,16 +1,7 @@ -import _ from 'lodash'; -import { parseDataKey } from '~/utils'; import { TMericoHeatmapConf } from '../../type'; -export function getSeries(conf: TMericoHeatmapConf, data: TPanelData) { - const { x_axis, y_axis, heat_block } = conf; - const x = parseDataKey(x_axis.data_key); - const y = parseDataKey(y_axis.data_key); - const h = parseDataKey(heat_block.data_key); - - const seriesData = data[x.queryID].map((d) => { - return [_.get(d, x.columnKey), _.get(d, y.columnKey), _.get(d, h.columnKey)]; - }); +export function getSeries(conf: TMericoHeatmapConf, seriesData: any[]) { + const { heat_block } = conf; return { type: 'heatmap', diff --git a/dashboard/src/components/plugins/viz-components/merico-heatmap/render/option/visual-map.ts b/dashboard/src/components/plugins/viz-components/merico-heatmap/render/option/visual-map.ts deleted file mode 100644 index aafabac4b..000000000 --- a/dashboard/src/components/plugins/viz-components/merico-heatmap/render/option/visual-map.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { getNumberOrDynamicValue } from '~/components/plugins/common-echarts-fields/number-or-dynamic-value'; -import { TMericoHeatmapConf } from '../../type'; - -export function getVisualMap(conf: TMericoHeatmapConf, variableValueMap: Record) { - const min = getNumberOrDynamicValue(conf.heat_block.min, variableValueMap); - const max = getNumberOrDynamicValue(conf.heat_block.max, variableValueMap); - return { - min, - max, - calculable: true, - orient: 'horizontal', - left: 'center', - top: 0, - itemWidth: 15, - }; -} diff --git a/dashboard/src/components/plugins/viz-components/merico-heatmap/render/option/x-axis.ts b/dashboard/src/components/plugins/viz-components/merico-heatmap/render/option/x-axis.ts index 96a63a4ba..3840cb987 100644 --- a/dashboard/src/components/plugins/viz-components/merico-heatmap/render/option/x-axis.ts +++ b/dashboard/src/components/plugins/viz-components/merico-heatmap/render/option/x-axis.ts @@ -1,15 +1,9 @@ -import _ from 'lodash'; - -import { TMericoHeatmapConf } from '../../type'; -import { getLabelOverflowOptionOnAxis } from '../../../../common-echarts-fields/axis-label-overflow'; -import { parseDataKey } from '~/utils'; -import { defaultEchartsOptions } from '~/styles/default-echarts-options'; import { FormatterFuncType } from '~/components/plugins/common-echarts-fields/x-axis-label-formatter'; +import { defaultEchartsOptions } from '~/styles/default-echarts-options'; +import { getLabelOverflowOptionOnAxis } from '../../../../common-echarts-fields/axis-label-overflow'; +import { TMericoHeatmapConf } from '../../type'; -export function getXAxis(conf: TMericoHeatmapConf, data: TPanelData, formatterFunc: FormatterFuncType) { - const x = parseDataKey(conf.x_axis.data_key); - const xData = _.uniq(data[x.queryID].map((d) => d[x.columnKey])); - +export function getXAxis(conf: TMericoHeatmapConf, xData: any[], formatterFunc: FormatterFuncType) { const { overflow, rotate } = conf.x_axis.axisLabel; const overflowOption = getLabelOverflowOptionOnAxis(overflow.on_axis); return defaultEchartsOptions.getXAxis({ diff --git a/dashboard/src/components/plugins/viz-components/merico-heatmap/render/option/y-axis.ts b/dashboard/src/components/plugins/viz-components/merico-heatmap/render/option/y-axis.ts index 09ae8e970..a38ce5b48 100644 --- a/dashboard/src/components/plugins/viz-components/merico-heatmap/render/option/y-axis.ts +++ b/dashboard/src/components/plugins/viz-components/merico-heatmap/render/option/y-axis.ts @@ -1,16 +1,10 @@ -import _ from 'lodash'; -import { TMericoHeatmapConf } from '../../type'; -import { getLabelOverflowOptionOnAxis } from '../../../../common-echarts-fields/axis-label-overflow'; -import { parseDataKey } from '~/utils'; -import { defaultEchartsOptions } from '~/styles/default-echarts-options'; import { FormatterFuncType } from '~/components/plugins/common-echarts-fields/x-axis-label-formatter'; +import { defaultEchartsOptions } from '~/styles/default-echarts-options'; +import { getLabelOverflowOptionOnAxis } from '../../../../common-echarts-fields/axis-label-overflow'; +import { TMericoHeatmapConf } from '../../type'; -export function getYAxis(conf: TMericoHeatmapConf, data: TPanelData, formatterFunc: FormatterFuncType) { - const x = parseDataKey(conf.x_axis.data_key); - const y = parseDataKey(conf.y_axis.data_key); - +export function getYAxis(conf: TMericoHeatmapConf, yData: any[], formatterFunc: FormatterFuncType) { const { nameAlignment, data_key, ...rest } = conf.y_axis; - const yData = _.uniq(data[x.queryID].map((d) => d[y.columnKey])); const { overflow, rotate } = conf.y_axis.axisLabel; const overflowOption = getLabelOverflowOptionOnAxis(overflow.on_axis); diff --git a/dashboard/src/components/plugins/viz-components/merico-heatmap/type.ts b/dashboard/src/components/plugins/viz-components/merico-heatmap/type.ts index 2910333b8..dfc45d278 100644 --- a/dashboard/src/components/plugins/viz-components/merico-heatmap/type.ts +++ b/dashboard/src/components/plugins/viz-components/merico-heatmap/type.ts @@ -5,8 +5,8 @@ import { import { IEchartsTooltipMetric } from '~/components/plugins/common-echarts-fields/tooltip-metric'; import { defaultNumberFormat, TNumberFormat } from '~/utils'; -import { TNumberOrDynamic } from '~/components/plugins/common-echarts-fields/number-or-dynamic-value/types'; import { EChartsNameTextAlign } from '../../common-echarts-fields/name-text-align'; +import { getDefaultVisualMap, VisualMap } from '../../common-echarts-fields/visual-map'; import { getDefaultXAxisLabelFormatter, IXAxisLabelFormatter, @@ -33,8 +33,6 @@ export type TMericoHeatmapConf = { }; }; heat_block: { - min: TNumberOrDynamic; - max: TNumberOrDynamic; name: string; data_key: TDataKey; value_formatter: TNumberFormat; @@ -46,6 +44,7 @@ export type TMericoHeatmapConf = { tooltip: { metrics: IEchartsTooltipMetric[]; }; + visualMap: VisualMap; }; export const DEFAULT_CONFIG: TMericoHeatmapConf = { @@ -69,14 +68,6 @@ export const DEFAULT_CONFIG: TMericoHeatmapConf = { }, }, heat_block: { - min: { - type: 'static', - value: 0, - }, - max: { - type: 'static', - value: 100, - }, name: 'Value', data_key: '', value_formatter: defaultNumberFormat, @@ -88,4 +79,5 @@ export const DEFAULT_CONFIG: TMericoHeatmapConf = { tooltip: { metrics: [], }, + visualMap: getDefaultVisualMap(), }; From 9f0e93ad5129d0d519d574aa57849831364718b8 Mon Sep 17 00:00:00 2001 From: Leto Date: Tue, 25 Jun 2024 19:45:40 +0800 Subject: [PATCH 2/5] feat(dashboard): apply visualmap to calendar heatmap --- .../editors/heat_block/index.tsx | 16 +--- .../viz-components/calendar-heatmap/index.tsx | 83 ++++--------------- .../calendar-heatmap/migrators/index.ts | 78 +++++++++++++++++ .../calendar-heatmap/option/index.ts | 63 +++++++++----- .../calendar-heatmap/option/series.ts | 20 +---- .../calendar-heatmap/option/visual-map.ts | 20 ----- .../viz-components/calendar-heatmap/type.ts | 16 +--- .../viz-calendar-heatmap-editor.tsx | 31 +++++-- 8 files changed, 167 insertions(+), 160 deletions(-) create mode 100644 dashboard/src/components/plugins/viz-components/calendar-heatmap/migrators/index.ts delete mode 100644 dashboard/src/components/plugins/viz-components/calendar-heatmap/option/visual-map.ts diff --git a/dashboard/src/components/plugins/viz-components/calendar-heatmap/editors/heat_block/index.tsx b/dashboard/src/components/plugins/viz-components/calendar-heatmap/editors/heat_block/index.tsx index e9a743454..017680f92 100644 --- a/dashboard/src/components/plugins/viz-components/calendar-heatmap/editors/heat_block/index.tsx +++ b/dashboard/src/components/plugins/viz-components/calendar-heatmap/editors/heat_block/index.tsx @@ -1,10 +1,9 @@ import { Divider, Group, Stack, TextInput } from '@mantine/core'; import { Control, Controller, UseFormWatch } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; import { DataFieldSelector } from '~/components/panel/settings/common/data-field-selector'; import { NumbroFormatSelector } from '~/components/panel/settings/common/numbro-format-selector'; -import { NumberOrDynamicValue } from '~/components/plugins/common-echarts-fields/number-or-dynamic-value'; import { ICalendarHeatmapConf } from '../../type'; -import { useTranslation } from 'react-i18next'; interface IHeatBlockField { control: Control; @@ -27,18 +26,7 @@ export function HeatBlockField({ control, watch }: IHeatBlockField) { render={({ field }) => } /> - - } - /> - } - /> - + (key ? `${queryID}.${key}` : key); - const { calendar, heat_block, tooltip, ...rest } = legacyConf; - return { - ...rest, - calendar: { - ...calendar, - data_key: changeKey(calendar.data_key), - }, - heat_block: { - ...heat_block, - data_key: changeKey(heat_block.data_key), - }, - tooltip: { - ...tooltip, - metrics: tooltip.metrics.map((m: any) => ({ - ...m, - data_key: changeKey(m.data_key), - })), - }, - }; - } catch (error) { - console.error('[Migration failed]', error); - throw error; - } -} - -function v3(legacyConf: any): ICalendarHeatmapConf { - const { heat_block } = legacyConf; - let { min, max } = heat_block; - if (typeof min !== 'number') { - min = 0; - } - if (typeof max !== 'number') { - max = 100; - } - - return { - ...legacyConf, - heat_block: { - ...heat_block, - min: { - type: 'static', - value: min, - }, - max: { - type: 'static', - value: max, - }, - }, - }; -} class VizCalendarHeatmapMigrator extends VersionBasedMigrator { - readonly VERSION = 3; + readonly VERSION = 4; configVersions(): void { this.version(1, (data: any) => { @@ -79,14 +21,21 @@ class VizCalendarHeatmapMigrator extends VersionBasedMigrator { return { ...data, version: 2, - config: v2(data.config, env), + config: Migrators.v2(data.config, env), }; }); this.version(3, (data) => { return { ...data, version: 3, - config: v3(data.config), + config: Migrators.v3(data.config), + }; + }); + this.version(4, (data) => { + return { + ...data, + version: 4, + config: Migrators.v4(data.config), }; }); } @@ -104,7 +53,7 @@ export const CalendarHeatmapVizComponent: VizComponent = { name: 'calendarHeatmap', viewRender: VizCalendarHeatmap, configRender: VizCalendarHeatmapEditor, - createConfig: (): ConfigType => ({ version: 3, config: DEFAULT_CONFIG }), + createConfig: (): ConfigType => ({ version: 4, config: DEFAULT_CONFIG }), triggers: [ClickCalendarDate], translation, }; diff --git a/dashboard/src/components/plugins/viz-components/calendar-heatmap/migrators/index.ts b/dashboard/src/components/plugins/viz-components/calendar-heatmap/migrators/index.ts new file mode 100644 index 000000000..4f1b198da --- /dev/null +++ b/dashboard/src/components/plugins/viz-components/calendar-heatmap/migrators/index.ts @@ -0,0 +1,78 @@ +import { getDefaultVisualMap } from '~/components/plugins/common-echarts-fields/visual-map'; +import { ICalendarHeatmapConf } from '../type'; +import { IMigrationEnv } from '~/components/plugins/plugin-data-migrator'; + +export function v2(legacyConf: any, { panelModel }: IMigrationEnv): ICalendarHeatmapConf { + try { + const queryID = panelModel.queryIDs[0]; + if (!queryID) { + throw new Error('cannot migrate when queryID is empty'); + } + const changeKey = (key: string) => (key ? `${queryID}.${key}` : key); + const { calendar, heat_block, tooltip, ...rest } = legacyConf; + return { + ...rest, + calendar: { + ...calendar, + data_key: changeKey(calendar.data_key), + }, + heat_block: { + ...heat_block, + data_key: changeKey(heat_block.data_key), + }, + tooltip: { + ...tooltip, + metrics: tooltip.metrics.map((m: any) => ({ + ...m, + data_key: changeKey(m.data_key), + })), + }, + }; + } catch (error) { + console.error('[Migration failed]', error); + throw error; + } +} + +export function v3(legacyConf: any): ICalendarHeatmapConf { + const { heat_block } = legacyConf; + let { min, max } = heat_block; + if (typeof min !== 'number') { + min = 0; + } + if (typeof max !== 'number') { + max = 100; + } + + return { + ...legacyConf, + heat_block: { + ...heat_block, + min: { + type: 'static', + value: min, + }, + max: { + type: 'static', + value: max, + }, + }, + }; +} + +export function v4(legacyConf: any): ICalendarHeatmapConf { + const { + visualMap = getDefaultVisualMap(), + heat_block: { min, max, ...restHeatBlock }, + ...rest + } = legacyConf; + return { + ...rest, + heat_block: restHeatBlock, + visualMap: { + ...getDefaultVisualMap(), + min, + max, + }, + }; +} diff --git a/dashboard/src/components/plugins/viz-components/calendar-heatmap/option/index.ts b/dashboard/src/components/plugins/viz-components/calendar-heatmap/option/index.ts index 5cd0ccdf3..14a5de3ab 100644 --- a/dashboard/src/components/plugins/viz-components/calendar-heatmap/option/index.ts +++ b/dashboard/src/components/plugins/viz-components/calendar-heatmap/option/index.ts @@ -1,31 +1,20 @@ import dayjs from 'dayjs'; import _ from 'lodash'; -import { defaultsDeep } from 'lodash'; -import { AnyObject } from '~/types'; -import { ITemplateVariable, formatAggregatedValue, getAggregatedValue } from '~/utils'; +import { getSkipRangeColor, getVisualMap } from '~/components/plugins/common-echarts-fields/visual-map'; +import { ITemplateVariable, formatAggregatedValue, getAggregatedValue, parseDataKey } from '~/utils'; import { ICalendarHeatmapConf } from '../type'; import { getCalendar } from './calendar'; import { getValueFormatters } from './formatters'; import { getLegend } from './legend'; import { getSeries } from './series'; import { getTooltip } from './tooltip'; -import { getVisualMap } from './visual-map'; -import { extractData, parseDataKey } from '~/utils'; - -const defaultOption = { - grid: { - containLabel: true, - }, -}; const getYear = (d: string | number) => dayjs(d).get('year'); -function getDateStuff(conf: ICalendarHeatmapConf, data: TPanelData) { - const k = parseDataKey(conf.calendar.data_key); - const queryData = data[k.queryID]; - const dataByYear = _.groupBy(queryData, (d) => getYear(d[k.columnKey])); +function getDateStuff(plotData: any[]) { + const dataByYear = _.groupBy(plotData, (d) => d.value[2]); const years = Object.keys(dataByYear); - const dates = queryData.map((d) => dayjs(d[k.columnKey]).valueOf()); + const dates = plotData.map((d) => dayjs(d.value[0]).valueOf()); const minDate = _.min(dates); const maxDate = _.max(dates); return { @@ -43,16 +32,48 @@ export function getOption(conf: ICalendarHeatmapConf, data: TPanelData, variable prev[variable.name] = formatAggregatedValue(variable, value); return prev; }, {} as Record); + const visualMap = getVisualMap(conf.visualMap, variableValueMap); + const { min, max } = visualMap; const valueFormatters = getValueFormatters(conf); - const { dateSpan, minDate, dataByYear, years } = getDateStuff(conf, data); + const c = parseDataKey(conf.calendar.data_key); + const h = parseDataKey(conf.heat_block.data_key); + const plotData = data[c.queryID].map((d) => { + const vc = _.get(d, c.columnKey); + const vh = _.get(d, h.columnKey); + const year = getYear(d[c.columnKey]); + const ret: any = { + value: [vc, vh, year], + }; + + const { followVisualMap, color } = getSkipRangeColor(vh, min, max, conf.visualMap); + if (!followVisualMap) { + ret.visualMap = false; + ret.itemStyle = { + color, + }; + } + return ret; + }); + + const { dateSpan, minDate, dataByYear, years } = getDateStuff(plotData); const oneYearMode = dateSpan <= 366; - const customOptions = { + + const options = { calendar: getCalendar(oneYearMode, minDate, years), - series: getSeries(conf, oneYearMode, dataByYear, data), + series: getSeries(oneYearMode, dataByYear, plotData), tooltip: getTooltip(conf, data, valueFormatters), - visualMap: getVisualMap(conf, oneYearMode, variableValueMap), + visualMap: { + ...visualMap, + orient: 'horizontal', + left: oneYearMode ? 'center' : 5, + dimension: 1, + }, legend: getLegend(oneYearMode, years), + grid: { + containLabel: true, + }, }; - return defaultsDeep({}, customOptions, defaultOption); + + return options; } diff --git a/dashboard/src/components/plugins/viz-components/calendar-heatmap/option/series.ts b/dashboard/src/components/plugins/viz-components/calendar-heatmap/option/series.ts index aadb2ceac..cddb84e7b 100644 --- a/dashboard/src/components/plugins/viz-components/calendar-heatmap/option/series.ts +++ b/dashboard/src/components/plugins/viz-components/calendar-heatmap/option/series.ts @@ -1,26 +1,14 @@ -import _ from 'lodash'; import { AnyObject } from '~/types'; import { ICalendarHeatmapConf } from '../type'; -import { parseDataKey } from '~/utils'; -export function getSeries( - conf: ICalendarHeatmapConf, - oneYearMode: boolean, - dataByYear: Record, - data: TPanelData, -) { - const { calendar, heat_block } = conf; - const c = parseDataKey(calendar.data_key); - const h = parseDataKey(heat_block.data_key); +export function getSeries(oneYearMode: boolean, dataByYear: Record, plotData: AnyObject[]) { if (oneYearMode) { return { type: 'heatmap', name: 'heatmap', coordinateSystem: 'calendar', calendarIndex: 0, - data: data[c.queryID].map((d) => { - return [_.get(d, c.columnKey), _.get(d, h.columnKey)]; - }), + data: plotData, }; } @@ -29,8 +17,6 @@ export function getSeries( name: year, coordinateSystem: 'calendar', calendarIndex: 0, - data: yearData.map((d) => { - return [_.get(d, c.columnKey), _.get(d, h.columnKey)]; - }), + data: yearData, })); } diff --git a/dashboard/src/components/plugins/viz-components/calendar-heatmap/option/visual-map.ts b/dashboard/src/components/plugins/viz-components/calendar-heatmap/option/visual-map.ts deleted file mode 100644 index 2d67d9b91..000000000 --- a/dashboard/src/components/plugins/viz-components/calendar-heatmap/option/visual-map.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { getNumberOrDynamicValue } from '~/components/plugins/common-echarts-fields/number-or-dynamic-value'; -import { ICalendarHeatmapConf } from '../type'; - -export function getVisualMap( - conf: ICalendarHeatmapConf, - oneYearMode: boolean, - variableValueMap: Record, -) { - const min = getNumberOrDynamicValue(conf.heat_block.min, variableValueMap); - const max = getNumberOrDynamicValue(conf.heat_block.max, variableValueMap); - return { - min, - max, - calculable: true, - orient: 'horizontal', - left: oneYearMode ? 'center' : 5, - top: 0, - itemWidth: 15, - }; -} diff --git a/dashboard/src/components/plugins/viz-components/calendar-heatmap/type.ts b/dashboard/src/components/plugins/viz-components/calendar-heatmap/type.ts index b2da32946..72f8625ff 100644 --- a/dashboard/src/components/plugins/viz-components/calendar-heatmap/type.ts +++ b/dashboard/src/components/plugins/viz-components/calendar-heatmap/type.ts @@ -1,6 +1,6 @@ -import { defaultNumberFormat, TNumberFormat } from '~/utils'; -import { TNumberOrDynamic } from '~/components/plugins/common-echarts-fields/number-or-dynamic-value'; import { IEchartsTooltipMetric } from '~/components/plugins/common-echarts-fields/tooltip-metric'; +import { defaultNumberFormat, TNumberFormat } from '~/utils'; +import { getDefaultVisualMap, VisualMap } from '../../common-echarts-fields/visual-map'; export interface ICalendarHeatmapConf { calendar: { @@ -8,8 +8,6 @@ export interface ICalendarHeatmapConf { locale: 'ZH' | 'EN'; }; heat_block: { - min: TNumberOrDynamic; - max: TNumberOrDynamic; name: string; data_key: TDataKey; value_formatter: TNumberFormat; @@ -17,6 +15,7 @@ export interface ICalendarHeatmapConf { tooltip: { metrics: IEchartsTooltipMetric[]; }; + visualMap: VisualMap; } export const DEFAULT_CONFIG: ICalendarHeatmapConf = { @@ -25,14 +24,6 @@ export const DEFAULT_CONFIG: ICalendarHeatmapConf = { locale: 'EN', }, heat_block: { - min: { - type: 'static', - value: 0, - }, - max: { - type: 'static', - value: 100, - }, name: 'Value', data_key: '', value_formatter: defaultNumberFormat, @@ -40,4 +31,5 @@ export const DEFAULT_CONFIG: ICalendarHeatmapConf = { tooltip: { metrics: [], }, + visualMap: getDefaultVisualMap(), }; diff --git a/dashboard/src/components/plugins/viz-components/calendar-heatmap/viz-calendar-heatmap-editor.tsx b/dashboard/src/components/plugins/viz-components/calendar-heatmap/viz-calendar-heatmap-editor.tsx index 04e821b1e..7516c859b 100644 --- a/dashboard/src/components/plugins/viz-components/calendar-heatmap/viz-calendar-heatmap-editor.tsx +++ b/dashboard/src/components/plugins/viz-components/calendar-heatmap/viz-calendar-heatmap-editor.tsx @@ -1,23 +1,29 @@ -import { ActionIcon, Group, Stack, Tabs, Text } from '@mantine/core'; -import _, { defaultsDeep, isEqual } from 'lodash'; +import { Stack, Tabs } from '@mantine/core'; +import _, { defaults, isEqual } from 'lodash'; import { useEffect, useMemo } from 'react'; import { useForm } from 'react-hook-form'; -import { DeviceFloppy } from 'tabler-icons-react'; +import { useTranslation } from 'react-i18next'; import { useStorageData } from '~/components/plugins/hooks'; import { VizConfigProps } from '~/types/plugin'; +import { VisualMapEditor } from '../../common-echarts-fields/visual-map'; +import { VizConfigBanner } from '../../editor-components'; +import { CalendarField } from './editors/calendar'; import { HeatBlockField } from './editors/heat_block'; import { TooltipField } from './editors/tooltip'; -import { CalendarField } from './editors/calendar'; import { DEFAULT_CONFIG, ICalendarHeatmapConf } from './type'; -import { useTranslation } from 'react-i18next'; -import { VizConfigBanner } from '../../editor-components'; export function VizCalendarHeatmapEditor({ context }: VizConfigProps) { const { t } = useTranslation(); const { value: confValue, set: setConf } = useStorageData(context.instanceData, 'config'); - const { variables } = context; - const conf: ICalendarHeatmapConf = useMemo(() => defaultsDeep({}, confValue, DEFAULT_CONFIG), [confValue]); + + const conf: ICalendarHeatmapConf = useMemo(() => { + if (!confValue) { + return DEFAULT_CONFIG; + } + return defaults({}, confValue); + }, [confValue]); + const defaultValues: ICalendarHeatmapConf = useMemo(() => { return _.cloneDeep(conf); }, [conf]); @@ -30,7 +36,8 @@ export function VizCalendarHeatmapEditor({ context }: VizConfigProps) { } }, [conf, defaultValues]); - const { control, handleSubmit, watch, getValues, reset } = useForm({ defaultValues }); + const form = useForm({ defaultValues }); + const { control, handleSubmit, watch, getValues, reset } = form; useEffect(() => { reset(defaultValues); }, [defaultValues]); @@ -61,6 +68,7 @@ export function VizCalendarHeatmapEditor({ context }: VizConfigProps) { {t('viz.calendar_heatmap.calendar.label')} {t('chart.heatmap.heatblock.label')} + {t('chart.visual_map.label')} {t('chart.tooltip.label')} @@ -72,6 +80,11 @@ export function VizCalendarHeatmapEditor({ context }: VizConfigProps) { + + {/* @ts-expect-error Types of property 'watch' are incompatible. */} + + + From 5606968de7db7180fb250b1ff63c43595aedacc7 Mon Sep 17 00:00:00 2001 From: Leto Date: Tue, 25 Jun 2024 20:14:47 +0800 Subject: [PATCH 3/5] feat(dashboard): make sure getNumberOrDynamicValue returns number --- .../number-or-dynamic-value/get-number-or-dynamic-value.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dashboard/src/components/plugins/common-echarts-fields/number-or-dynamic-value/get-number-or-dynamic-value.ts b/dashboard/src/components/plugins/common-echarts-fields/number-or-dynamic-value/get-number-or-dynamic-value.ts index 48af90390..cc1309b05 100644 --- a/dashboard/src/components/plugins/common-echarts-fields/number-or-dynamic-value/get-number-or-dynamic-value.ts +++ b/dashboard/src/components/plugins/common-echarts-fields/number-or-dynamic-value/get-number-or-dynamic-value.ts @@ -13,12 +13,13 @@ export function getNumberOrDynamicValue( if (conf.type === 'static') { const { value } = conf as TNumberOrDynamic_Static; - return value; + return Number(value); } const { value } = conf as TNumberOrDynamic_Dynamic; try { - return new Function(`return ${value}`)()({ variables: variableValueMap }, { lodash, interpolate }); + const ret = new Function(`return ${value}`)()({ variables: variableValueMap }, { lodash, interpolate }); + return Number(ret); } catch (error) { // @ts-expect-error Object is of type 'unknown'. console.error(`[getNumberOrDynamicValue] failed parsing custom function, error: ${error.message}`); From 8a3a89b506d94c6c0767da5fecb9eac18d664167 Mon Sep 17 00:00:00 2001 From: Leto Date: Tue, 25 Jun 2024 20:15:32 +0800 Subject: [PATCH 4/5] feat(dashboard): use min&max on piecewise visualmap --- .../common-echarts-fields/visual-map/utils.ts | 14 +++++++--- .../piecewise/interval-editor.tsx | 26 +++++++++++-------- .../piecewise/piece-editor.tsx | 2 +- .../piecewise/pieces-editor.tsx | 6 ++--- dashboard/src/i18n/en.ts | 2 ++ dashboard/src/i18n/zh.ts | 2 ++ 6 files changed, 33 insertions(+), 19 deletions(-) diff --git a/dashboard/src/components/plugins/common-echarts-fields/visual-map/utils.ts b/dashboard/src/components/plugins/common-echarts-fields/visual-map/utils.ts index 04f8cfa62..206737977 100644 --- a/dashboard/src/components/plugins/common-echarts-fields/visual-map/utils.ts +++ b/dashboard/src/components/plugins/common-echarts-fields/visual-map/utils.ts @@ -93,9 +93,10 @@ export function getVisualMap(visualMap: VisualMap, variableValueMap: Record { const item: AnyObject = {}; @@ -106,11 +107,16 @@ export function getVisualMap(visualMap: VisualMap, variableValueMap: Record { - if (v === '' || Number.isNaN(Number(v))) { - return sign + '∞'; +const toIntervalValue = (v: string, extremum: '') => { + if (v === '') { + return extremum; } - return sign + v; + if (Number.isNaN(Number(v))) { + return 'INVALID VALUE'; + } + return v; }; type Props = { @@ -32,15 +36,17 @@ type Props = { index: number; }; export const IntervalEditor = ({ form, index }: Props) => { + const { t } = useTranslation(); + const { control, watch } = form; const piece = watch(`visualMap.pieces.${index}`); const { lower, upper } = piece; const intervalPreview = [ symbolToBracket[lower.symbol], - toIntervalValue(lower.value, '-'), + toIntervalValue(lower.value, t('chart.visual_map.min_value')), ',', - toIntervalValue(upper.value, ''), + toIntervalValue(upper.value, t('chart.visual_map.max_value')), symbolToBracket[upper.symbol], ].join(''); @@ -77,11 +83,10 @@ export const IntervalEditor = ({ form, index }: Props) => { render={({ field }) => ( field.onChange(e.currentTarget.value)} error={field.value !== '' && Number.isNaN(Number(field.value))} - styles={{ input: { '&::placeholder': { fontSize: '16px' } } }} /> )} /> @@ -93,7 +98,7 @@ export const IntervalEditor = ({ form, index }: Props) => { )} /> - value + {t('common.value').toLowerCase()} { field.onChange(e.currentTarget.value)} error={field.value !== '' && Number.isNaN(Number(field.value))} - styles={{ input: { '&::placeholder': { fontSize: '16px' } } }} /> )} /> diff --git a/dashboard/src/components/plugins/common-echarts-fields/visual-map/visual-map-editor/piecewise/piece-editor.tsx b/dashboard/src/components/plugins/common-echarts-fields/visual-map/visual-map-editor/piecewise/piece-editor.tsx index 0d8cc1aa5..e4770b43a 100644 --- a/dashboard/src/components/plugins/common-echarts-fields/visual-map/visual-map-editor/piecewise/piece-editor.tsx +++ b/dashboard/src/components/plugins/common-echarts-fields/visual-map/visual-map-editor/piecewise/piece-editor.tsx @@ -14,7 +14,7 @@ export const PieceEditor = ({ form, index, remove }: Props) => { const { t } = useTranslation(); return ( - {index.toString()} + {(index + 1).toString()} diff --git a/dashboard/src/components/plugins/common-echarts-fields/visual-map/visual-map-editor/piecewise/pieces-editor.tsx b/dashboard/src/components/plugins/common-echarts-fields/visual-map/visual-map-editor/piecewise/pieces-editor.tsx index ea777fd4d..179941199 100644 --- a/dashboard/src/components/plugins/common-echarts-fields/visual-map/visual-map-editor/piecewise/pieces-editor.tsx +++ b/dashboard/src/components/plugins/common-echarts-fields/visual-map/visual-map-editor/piecewise/pieces-editor.tsx @@ -34,9 +34,9 @@ export const PiecesEditor = ({ form }: Props) => { - Interval - Label - Color + {t('chart.visual_map.piecewise.interval')} + {t('chart.visual_map.piecewise.piece_label')} + {t('chart.color.label')} diff --git a/dashboard/src/i18n/en.ts b/dashboard/src/i18n/en.ts index 3a1838458..5de4b702c 100644 --- a/dashboard/src/i18n/en.ts +++ b/dashboard/src/i18n/en.ts @@ -609,6 +609,8 @@ export const en = { categories: 'Categories', }, add_a_piece: 'Add a piece', + interval: 'Interval', + piece_label: 'Label', }, bar_width: 'Bar Width', bar_height: 'Bar Height', diff --git a/dashboard/src/i18n/zh.ts b/dashboard/src/i18n/zh.ts index b0a919d80..cce095263 100644 --- a/dashboard/src/i18n/zh.ts +++ b/dashboard/src/i18n/zh.ts @@ -609,6 +609,8 @@ export const zh = { categories: '离散数据', }, add_a_piece: '加一个分段', + interval: '值区间', + piece_label: '文案', }, bar_width: '长条的宽度', bar_height: '长条的高度', From 89de81a94217082c8d2a1c610dc691dcc80ae952 Mon Sep 17 00:00:00 2001 From: Leto Date: Tue, 25 Jun 2024 20:16:18 +0800 Subject: [PATCH 5/5] chore: publish v13.19.0 --- api/package.json | 2 +- dashboard/package.json | 2 +- package.json | 2 +- settings-form/package.json | 2 +- website/package.json | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/api/package.json b/api/package.json index 832744f69..45b06b30a 100644 --- a/api/package.json +++ b/api/package.json @@ -1,6 +1,6 @@ { "name": "@devtable/api", - "version": "13.18.0", + "version": "13.19.0", "description": "", "main": "index.js", "scripts": { diff --git a/dashboard/package.json b/dashboard/package.json index 10de110c1..8d43a2835 100644 --- a/dashboard/package.json +++ b/dashboard/package.json @@ -1,6 +1,6 @@ { "name": "@devtable/dashboard", - "version": "13.18.0", + "version": "13.19.0", "license": "Apache-2.0", "publishConfig": { "access": "public", diff --git a/package.json b/package.json index 3f233fa1f..9bac79811 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@devtable/root", - "version": "13.18.0", + "version": "13.19.0", "private": true, "workspaces": [ "api", diff --git a/settings-form/package.json b/settings-form/package.json index 761af8f61..40b9c340e 100644 --- a/settings-form/package.json +++ b/settings-form/package.json @@ -1,6 +1,6 @@ { "name": "@devtable/settings-form", - "version": "13.18.0", + "version": "13.19.0", "license": "Apache-2.0", "publishConfig": { "access": "public", diff --git a/website/package.json b/website/package.json index cc5c8dc0b..236e2ea6a 100644 --- a/website/package.json +++ b/website/package.json @@ -2,7 +2,7 @@ "name": "@devtable/website", "private": true, "license": "Apache-2.0", - "version": "13.18.0", + "version": "13.19.0", "scripts": { "dev": "vite", "preview": "vite preview"