@perses-dev/pie-chart-plugin 0.13.0 → 0.13.1

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/PieChartOptionsEditorSettings.tsx"],"sourcesContent":["// Copyright The Perses Authors\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport merge from 'lodash/merge';\nimport {\n CalculationSelector,\n CalculationSelectorProps,\n LegendOptionsEditor,\n LegendOptionsEditorProps,\n} from '@perses-dev/plugin-system';\nimport { produce } from 'immer';\nimport {\n FormatControls,\n FormatControlsProps,\n OptionsColorPicker,\n OptionsEditorGroup,\n OptionsEditorGrid,\n OptionsEditorColumn,\n SortSelector,\n SortSelectorProps,\n ModeSelector,\n ModeSelectorProps,\n ModeOption,\n SortOption,\n OptionsEditorControl,\n useChartsTheme,\n} from '@perses-dev/components';\nimport { CalculationType, isPercentUnit, FormatOptions } from '@perses-dev/core';\nimport {\n Button,\n FormControl,\n InputLabel,\n MenuItem,\n Select,\n Stack,\n Switch,\n SwitchProps,\n Typography,\n} from '@mui/material';\nimport { ReactElement, useMemo } from 'react';\nimport { PieChartOptions, PieChartOptionsEditorProps, DEFAULT_FORMAT } from './pie-chart-model';\n\nexport function PieChartOptionsEditorSettings(props: PieChartOptionsEditorProps): ReactElement {\n const { onChange, value } = props;\n\n const handleCalculationChange: CalculationSelectorProps['onChange'] = (newCalculation: CalculationType) => {\n onChange(\n produce(value, (draft: PieChartOptions) => {\n draft.calculation = newCalculation;\n })\n );\n };\n\n const handleLegendChange: LegendOptionsEditorProps['onChange'] = (newLegend) => {\n onChange(\n produce(value, (draft: PieChartOptions) => {\n draft.legend = newLegend;\n })\n );\n };\n\n const handleUnitChange: FormatControlsProps['onChange'] = (newFormat: FormatOptions) => {\n onChange(\n produce(value, (draft: PieChartOptions) => {\n draft.format = newFormat;\n })\n );\n };\n\n const handleSortChange: SortSelectorProps['onChange'] = (newSort: SortOption) => {\n onChange(\n produce(value, (draft: PieChartOptions) => {\n draft.sort = newSort;\n })\n );\n };\n\n const handleModeChange: ModeSelectorProps['onChange'] = (newMode: ModeOption) => {\n onChange(\n produce(value, (draft: PieChartOptions) => {\n draft.mode = newMode;\n })\n );\n };\n\n const handleShowLabelsChange: SwitchProps['onChange'] = (_: unknown, checked: boolean) => {\n onChange(\n produce(value, (draft: PieChartOptions) => {\n draft.showLabels = checked;\n })\n );\n };\n\n const chartsTheme = useChartsTheme();\n const themePalette = chartsTheme.echartsTheme.color;\n\n const colorPalette: string[] | undefined = useMemo(() => {\n return value.colorPalette || undefined;\n }, [value.colorPalette]);\n\n const handleColorChange = (color?: string[]) => {\n onChange(\n produce(value, (draft: PieChartOptions) => {\n if (Array.isArray(color)) {\n draft.colorPalette = color;\n } else if (typeof color === 'string') {\n draft.colorPalette = [color];\n } else {\n draft.colorPalette = undefined;\n }\n })\n );\n };\n\n // ensures decimalPlaces defaults to correct value\n const format = merge({}, DEFAULT_FORMAT, value.format);\n\n type ColorScheme = 'default' | 'theme' | 'gradient';\n\n const colorScheme: ColorScheme = useMemo(() => {\n return Array.isArray(colorPalette) ? (colorPalette.length === 1 ? 'gradient' : 'theme') : 'default';\n }, [colorPalette]);\n\n const handleColorSchemeChange = (scheme: ColorScheme) => {\n if (scheme === 'theme') {\n handleColorChange(themePalette as string[]);\n } else if (scheme === 'default') {\n handleColorChange();\n } else {\n // gradient: keep existing single color if present (user-chosen via OptionsColorPicker)\n if (Array.isArray(colorPalette) && colorPalette.length === 1) {\n return;\n }\n // initialize with a sensible default so the color picker shows a color\n handleColorChange(['#ff0000']);\n }\n };\n\n const colorHelpText = useMemo(() => {\n if (colorPalette === undefined) {\n return 'Colors will be automatically assigned using metrics name hash.';\n }\n if (Array.isArray(colorPalette) && colorPalette.length > 1) {\n return 'Colors will be automatically assigned using the current theme color palette.';\n }\n if (Array.isArray(colorPalette) && colorPalette.length === 1) {\n return 'All series will use a gradient based on the selected color.';\n }\n return undefined;\n }, [colorPalette]);\n\n return (\n <OptionsEditorGrid>\n <OptionsEditorColumn>\n <LegendOptionsEditor calculation=\"comparison\" value={value.legend} onChange={handleLegendChange} />\n <OptionsEditorGroup title=\"Misc\">\n <OptionsEditorControl\n label=\"Show Labels\"\n control={<Switch checked={Boolean(value.showLabels)} onChange={handleShowLabelsChange} />}\n />\n <FormatControls value={format} onChange={handleUnitChange} disabled={value.mode === 'percentage'} />\n <CalculationSelector value={value.calculation} onChange={handleCalculationChange} />\n <SortSelector value={value.sort} onChange={handleSortChange} />\n <ModeSelector value={value.mode} onChange={handleModeChange} disablePercentageMode={isPercentUnit(format)} />\n </OptionsEditorGroup>\n </OptionsEditorColumn>\n <OptionsEditorColumn>\n <OptionsEditorGroup title=\"Colors\">\n <Stack spacing={2}>\n <Stack direction=\"row\" spacing={2} alignItems=\"center\">\n <FormControl size=\"small\" sx={{ minWidth: 150 }}>\n <InputLabel>Color Scheme</InputLabel>\n <Select\n value={colorScheme}\n label=\"Color Scheme\"\n onChange={(e) => handleColorSchemeChange(e.target.value as ColorScheme)}\n >\n <MenuItem value=\"default\">Default</MenuItem>\n <MenuItem value=\"theme\">Theme</MenuItem>\n <MenuItem value=\"gradient\">Gradient</MenuItem>\n </Select>\n </FormControl>\n {Array.isArray(colorPalette) && colorPalette.length === 1 && (\n <OptionsColorPicker\n label=\"Color\"\n color={colorPalette?.[0] ?? (themePalette as string[])[0] ?? '#ff0000'}\n onColorChange={(c: string) => handleColorChange([c])}\n />\n )}\n </Stack>\n {colorHelpText && (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {colorHelpText}\n </Typography>\n )}\n </Stack>\n </OptionsEditorGroup>\n <OptionsEditorGroup title=\"Reset Settings\">\n <Button\n variant=\"outlined\"\n color=\"secondary\"\n onClick={() => {\n onChange(\n produce(value, (draft: PieChartOptions) => {\n // reset button removes all optional panel options\n draft.legend = undefined;\n draft.colorPalette = undefined;\n })\n );\n }}\n >\n Reset To Defaults\n </Button>\n </OptionsEditorGroup>\n </OptionsEditorColumn>\n </OptionsEditorGrid>\n );\n}\n"],"names":["merge","CalculationSelector","LegendOptionsEditor","produce","FormatControls","OptionsColorPicker","OptionsEditorGroup","OptionsEditorGrid","OptionsEditorColumn","SortSelector","ModeSelector","OptionsEditorControl","useChartsTheme","isPercentUnit","Button","FormControl","InputLabel","MenuItem","Select","Stack","Switch","Typography","useMemo","DEFAULT_FORMAT","PieChartOptionsEditorSettings","props","onChange","value","handleCalculationChange","newCalculation","draft","calculation","handleLegendChange","newLegend","legend","handleUnitChange","newFormat","format","handleSortChange","newSort","sort","handleModeChange","newMode","mode","handleShowLabelsChange","_","checked","showLabels","chartsTheme","themePalette","echartsTheme","color","colorPalette","undefined","handleColorChange","Array","isArray","colorScheme","length","handleColorSchemeChange","scheme","colorHelpText","title","label","control","Boolean","disabled","disablePercentageMode","spacing","direction","alignItems","size","sx","minWidth","e","target","onColorChange","c","variant","onClick"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/PieChartOptionsEditorSettings.tsx"],"sourcesContent":["// Copyright The Perses Authors\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport merge from 'lodash/merge';\nimport {\n CalculationSelector,\n CalculationSelectorProps,\n LegendOptionsEditor,\n LegendOptionsEditorProps,\n} from '@perses-dev/plugin-system';\nimport { produce } from 'immer';\nimport {\n FormatControls,\n FormatControlsProps,\n OptionsColorPicker,\n OptionsEditorGroup,\n OptionsEditorGrid,\n OptionsEditorColumn,\n SortSelector,\n SortSelectorProps,\n ModeSelector,\n ModeSelectorProps,\n ModeOption,\n SortOption,\n OptionsEditorControl,\n useChartsTheme,\n} from '@perses-dev/components';\nimport { CalculationType, isPercentUnit, FormatOptions } from '@perses-dev/core';\nimport {\n Button,\n FormControl,\n InputLabel,\n MenuItem,\n Select,\n Stack,\n Switch,\n SwitchProps,\n Typography,\n} from '@mui/material';\nimport { ReactElement, useMemo } from 'react';\nimport { PieChartOptions, PieChartOptionsEditorProps, DEFAULT_FORMAT } from './pie-chart-model';\n\nexport function PieChartOptionsEditorSettings(props: PieChartOptionsEditorProps): ReactElement {\n const { onChange, value } = props;\n\n const handleCalculationChange: CalculationSelectorProps['onChange'] = (newCalculation: CalculationType) => {\n onChange(\n produce(value, (draft: PieChartOptions) => {\n draft.calculation = newCalculation;\n })\n );\n };\n\n const handleLegendChange: LegendOptionsEditorProps['onChange'] = (newLegend) => {\n onChange(\n produce(value, (draft: PieChartOptions) => {\n draft.legend = newLegend;\n })\n );\n };\n\n const handleUnitChange: FormatControlsProps['onChange'] = (newFormat: FormatOptions) => {\n onChange(\n produce(value, (draft: PieChartOptions) => {\n draft.format = newFormat;\n })\n );\n };\n\n const handleSortChange: SortSelectorProps['onChange'] = (newSort: SortOption) => {\n onChange(\n produce(value, (draft: PieChartOptions) => {\n draft.sort = newSort;\n })\n );\n };\n\n const handleModeChange: ModeSelectorProps['onChange'] = (newMode: ModeOption) => {\n onChange(\n produce(value, (draft: PieChartOptions) => {\n draft.mode = newMode;\n })\n );\n };\n\n const handleShowLabelsChange: SwitchProps['onChange'] = (_: unknown, checked: boolean) => {\n onChange(\n produce(value, (draft: PieChartOptions) => {\n draft.showLabels = checked;\n })\n );\n };\n\n const chartsTheme = useChartsTheme();\n const themePalette = chartsTheme.echartsTheme.color;\n\n const colorPalette: string[] | undefined = useMemo(() => {\n return value.colorPalette || undefined;\n }, [value.colorPalette]);\n\n const handleColorChange = (color?: string[]): void => {\n onChange(\n produce(value, (draft: PieChartOptions) => {\n if (Array.isArray(color)) {\n draft.colorPalette = color;\n } else if (typeof color === 'string') {\n draft.colorPalette = [color];\n } else {\n draft.colorPalette = undefined;\n }\n })\n );\n };\n\n // ensures decimalPlaces defaults to correct value\n const format = merge({}, DEFAULT_FORMAT, value.format);\n\n type ColorScheme = 'default' | 'theme' | 'gradient';\n\n const colorScheme: ColorScheme = useMemo(() => {\n return Array.isArray(colorPalette) ? (colorPalette.length === 1 ? 'gradient' : 'theme') : 'default';\n }, [colorPalette]);\n\n const handleColorSchemeChange = (scheme: ColorScheme): void => {\n if (scheme === 'theme') {\n handleColorChange(themePalette as string[]);\n } else if (scheme === 'default') {\n handleColorChange();\n } else {\n // gradient: keep existing single color if present (user-chosen via OptionsColorPicker)\n if (Array.isArray(colorPalette) && colorPalette.length === 1) {\n return;\n }\n // initialize with a sensible default so the color picker shows a color\n handleColorChange(['#ff0000']);\n }\n };\n\n const colorHelpText = useMemo(() => {\n if (colorPalette === undefined) {\n return 'Colors will be automatically assigned using metrics name hash.';\n }\n if (Array.isArray(colorPalette) && colorPalette.length > 1) {\n return 'Colors will be automatically assigned using the current theme color palette.';\n }\n if (Array.isArray(colorPalette) && colorPalette.length === 1) {\n return 'All series will use a gradient based on the selected color.';\n }\n return undefined;\n }, [colorPalette]);\n\n return (\n <OptionsEditorGrid>\n <OptionsEditorColumn>\n <LegendOptionsEditor calculation=\"comparison\" value={value.legend} onChange={handleLegendChange} />\n <OptionsEditorGroup title=\"Misc\">\n <OptionsEditorControl\n label=\"Show Labels\"\n control={<Switch checked={Boolean(value.showLabels)} onChange={handleShowLabelsChange} />}\n />\n <FormatControls value={format} onChange={handleUnitChange} disabled={value.mode === 'percentage'} />\n <CalculationSelector value={value.calculation} onChange={handleCalculationChange} />\n <SortSelector value={value.sort} onChange={handleSortChange} />\n <ModeSelector value={value.mode} onChange={handleModeChange} disablePercentageMode={isPercentUnit(format)} />\n </OptionsEditorGroup>\n </OptionsEditorColumn>\n <OptionsEditorColumn>\n <OptionsEditorGroup title=\"Colors\">\n <Stack spacing={2}>\n <Stack direction=\"row\" spacing={2} alignItems=\"center\">\n <FormControl size=\"small\" sx={{ minWidth: 150 }}>\n <InputLabel>Color Scheme</InputLabel>\n <Select\n value={colorScheme}\n label=\"Color Scheme\"\n onChange={(e) => handleColorSchemeChange(e.target.value as ColorScheme)}\n >\n <MenuItem value=\"default\">Default</MenuItem>\n <MenuItem value=\"theme\">Theme</MenuItem>\n <MenuItem value=\"gradient\">Gradient</MenuItem>\n </Select>\n </FormControl>\n {Array.isArray(colorPalette) && colorPalette.length === 1 && (\n <OptionsColorPicker\n label=\"Color\"\n color={colorPalette?.[0] ?? (themePalette as string[])[0] ?? '#ff0000'}\n onColorChange={(c: string) => handleColorChange([c])}\n />\n )}\n </Stack>\n {colorHelpText && (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {colorHelpText}\n </Typography>\n )}\n </Stack>\n </OptionsEditorGroup>\n <OptionsEditorGroup title=\"Reset Settings\">\n <Button\n variant=\"outlined\"\n color=\"secondary\"\n onClick={() => {\n onChange(\n produce(value, (draft: PieChartOptions) => {\n // reset button removes all optional panel options\n draft.legend = undefined;\n draft.colorPalette = undefined;\n })\n );\n }}\n >\n Reset To Defaults\n </Button>\n </OptionsEditorGroup>\n </OptionsEditorColumn>\n </OptionsEditorGrid>\n );\n}\n"],"names":["merge","CalculationSelector","LegendOptionsEditor","produce","FormatControls","OptionsColorPicker","OptionsEditorGroup","OptionsEditorGrid","OptionsEditorColumn","SortSelector","ModeSelector","OptionsEditorControl","useChartsTheme","isPercentUnit","Button","FormControl","InputLabel","MenuItem","Select","Stack","Switch","Typography","useMemo","DEFAULT_FORMAT","PieChartOptionsEditorSettings","props","onChange","value","handleCalculationChange","newCalculation","draft","calculation","handleLegendChange","newLegend","legend","handleUnitChange","newFormat","format","handleSortChange","newSort","sort","handleModeChange","newMode","mode","handleShowLabelsChange","_","checked","showLabels","chartsTheme","themePalette","echartsTheme","color","colorPalette","undefined","handleColorChange","Array","isArray","colorScheme","length","handleColorSchemeChange","scheme","colorHelpText","title","label","control","Boolean","disabled","disablePercentageMode","spacing","direction","alignItems","size","sx","minWidth","e","target","onColorChange","c","variant","onClick"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"PieChartPanel.d.ts","sourceRoot":"","sources":["../../src/PieChartPanel.tsx"],"names":[],"mappings":"AAwBA,OAAO,EAAoD,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACpG,OAAO,EAAuC,UAAU,EAAsB,MAAM,2BAA2B,CAAC;AAEhH,OAAO,EAAE,YAAY,EAA6B,MAAM,OAAO,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAKpD,MAAM,MAAM,kBAAkB,GAAG,UAAU,CAAC,eAAe,EAAE,cAAc,CAAC,CAAC;AAE7E,wBAAgB,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,YAAY,GAAG,IAAI,CA2K5E"}
1
+ {"version":3,"file":"PieChartPanel.d.ts","sourceRoot":"","sources":["../../src/PieChartPanel.tsx"],"names":[],"mappings":"AAsBA,OAAO,EAAoD,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACpG,OAAO,EAAE,UAAU,EAAsB,MAAM,2BAA2B,CAAC;AAE3E,OAAO,EAAE,YAAY,EAA6B,MAAM,OAAO,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAKpD,MAAM,MAAM,kBAAkB,GAAG,UAAU,CAAC,eAAe,EAAE,cAAc,CAAC,CAAC;AAE7E,wBAAgB,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,YAAY,GAAG,IAAI,CA4H5E"}
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- //Copyright 2024 The Perses Authors
2
+ // Copyright The Perses Authors
3
3
  // Licensed under the Apache License, Version 2.0 (the "License");
4
4
  // you may not use this file except in compliance with the License.
5
5
  // You may obtain a copy of the License at
@@ -14,14 +14,14 @@ import { jsx as _jsx } from "react/jsx-runtime";
14
14
  import { Box } from '@mui/material';
15
15
  import { ContentWithLegend, useChartsTheme, useId } from '@perses-dev/components';
16
16
  import { CalculationsMap, DEFAULT_LEGEND } from '@perses-dev/core';
17
- import { comparisonLegends, validateLegendSpec } from '@perses-dev/plugin-system';
17
+ import { validateLegendSpec } from '@perses-dev/plugin-system';
18
18
  import merge from 'lodash/merge';
19
19
  import { useMemo, useRef, useState } from 'react';
20
- import { calculatePercentages, sortSeriesData } from './utils';
20
+ import { PieChartListLegendMapper, PieChartTableLegendMapper, sortSeriesData } from './utils';
21
21
  import { getSeriesColor } from './colors';
22
22
  import { PieChartBase } from './PieChartBase';
23
23
  export function PieChartPanel(props) {
24
- const { spec: { calculation, sort, mode, legend: pieChartLegend, colorPalette: colorPalette }, contentDimensions, queryResults } = props;
24
+ const { spec: { calculation, sort, mode, format: formatOptions, legend: pieChartLegend, colorPalette: colorPalette }, contentDimensions, queryResults } = props;
25
25
  const chartsTheme = useChartsTheme();
26
26
  const chartId = useId('time-series-panel');
27
27
  const seriesNames = queryResults.flatMap((result)=>result?.data.series?.map((series)=>series.name) || []);
@@ -32,11 +32,9 @@ export function PieChartPanel(props) {
32
32
  colorPalette,
33
33
  seriesNames
34
34
  ]);
35
- const { pieChartData, legendItems, legendColumns } = useMemo(()=>{
35
+ const pieChartData = useMemo(()=>{
36
36
  const calculate = CalculationsMap[calculation];
37
37
  const pieChartData = [];
38
- const legendItems = [];
39
- const legendColumns = [];
40
38
  queryResults.forEach((result, queryIndex)=>{
41
39
  const series = result?.data.series ?? [];
42
40
  series.forEach((seriesData, seriesIndex)=>{
@@ -51,74 +49,30 @@ export function PieChartPanel(props) {
51
49
  }
52
50
  };
53
51
  pieChartData.push(seriesItem);
54
- legendItems.push({
55
- id: seriesId,
56
- label: seriesData.formattedName ?? '',
57
- color: seriesColor,
58
- data: {}
59
- });
60
52
  });
61
53
  });
62
- const sortedPieChartData = sortSeriesData(pieChartData, sort);
63
- // Reorder legend items to reflect the current sorting order of series
64
- const valueById = new Map(sortedPieChartData.map((pd)=>[
65
- pd.id ?? pd.name,
66
- pd.value ?? 0
67
- ]));
68
- legendItems.sort((a, b)=>{
69
- const av = valueById.get(a.id) ?? 0;
70
- const bv = valueById.get(b.id) ?? 0;
71
- return sort === 'asc' ? av - bv : bv - av;
72
- });
73
- if (pieChartLegend?.values?.length && pieChartLegend?.mode === 'table') {
74
- const { values } = pieChartLegend;
75
- [
76
- ...values
77
- ].sort().forEach((v)=>{
78
- /* First, create a column for the current legend value */ legendColumns.push({
79
- accessorKey: `data.${v}`,
80
- header: comparisonLegends[v]?.label || v,
81
- headerDescription: comparisonLegends[v]?.description,
82
- width: 90,
83
- align: 'right',
84
- cellDescription: true,
85
- enableSorting: true
86
- });
87
- /* Then, settle the legend items related to this legend value */ switch(v){
88
- case 'abs':
89
- legendItems.forEach((li)=>{
90
- const { value: itemAbsoluteValue } = pieChartData.find((pd)=>li.id === pd.id) || {};
91
- if (typeof itemAbsoluteValue === 'number' && li.data) {
92
- li.data['abs'] = itemAbsoluteValue;
93
- }
94
- });
95
- break;
96
- case 'relative':
97
- legendItems.forEach((li)=>{
98
- const { value: itemPercentageValue } = calculatePercentages(sortedPieChartData).find((ppd)=>li.id === ppd.id) || {};
99
- if (typeof itemPercentageValue === 'number' && li.data) {
100
- li.data['relative'] = `${itemPercentageValue.toFixed(2)}%`;
101
- }
102
- });
103
- break;
104
- default:
105
- break;
106
- }
107
- });
108
- }
54
+ return sortSeriesData(pieChartData, sort);
55
+ }, [
56
+ calculation,
57
+ chartId,
58
+ colorList,
59
+ queryResults,
60
+ sort
61
+ ]);
62
+ const { legendItems, legendColumns } = useMemo(()=>{
63
+ const pieChartLegendMapper = pieChartLegend?.mode === 'table' ? new PieChartTableLegendMapper() : new PieChartListLegendMapper();
64
+ const values = pieChartLegend?.values;
65
+ const legendItems = pieChartLegendMapper.mapToLegendItems(pieChartData, values);
66
+ const legendColumns = pieChartLegendMapper.mapToLegendColumns(values, formatOptions);
109
67
  return {
110
- pieChartData: mode === 'percentage' ? calculatePercentages(sortedPieChartData) : sortedPieChartData,
111
68
  legendItems,
112
69
  legendColumns
113
70
  };
114
71
  }, [
115
- calculation,
116
- sort,
117
- mode,
118
- queryResults,
119
- colorList,
120
- chartId,
121
- pieChartLegend
72
+ formatOptions,
73
+ pieChartData,
74
+ pieChartLegend?.mode,
75
+ pieChartLegend?.values
122
76
  ]);
123
77
  const contentPadding = chartsTheme.container.padding.default;
124
78
  const adjustedContentDimensions = contentDimensions ? {
@@ -176,6 +130,8 @@ export function PieChartPanel(props) {
176
130
  data: pieChartData,
177
131
  width: width,
178
132
  height: height,
133
+ mode: mode,
134
+ formatOptions: formatOptions,
179
135
  showLabels: Boolean(props.spec.showLabels)
180
136
  })
181
137
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/PieChartPanel.tsx"],"sourcesContent":["//Copyright 2024 The Perses Authors\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport { Box } from '@mui/material';\nimport {\n ChartInstance,\n ContentWithLegend,\n LegendItem,\n LegendProps,\n SelectedLegendItemState,\n TableColumnConfig,\n useChartsTheme,\n useId,\n} from '@perses-dev/components';\nimport { CalculationType, CalculationsMap, DEFAULT_LEGEND, TimeSeriesData } from '@perses-dev/core';\nimport { comparisonLegends, ComparisonValues, PanelProps, validateLegendSpec } from '@perses-dev/plugin-system';\nimport merge from 'lodash/merge';\nimport { ReactElement, useMemo, useRef, useState } from 'react';\nimport { PieChartOptions } from './pie-chart-model';\nimport { calculatePercentages, sortSeriesData } from './utils';\nimport { getSeriesColor } from './colors';\nimport { PieChartBase, PieChartData } from './PieChartBase';\n\nexport type PieChartPanelProps = PanelProps<PieChartOptions, TimeSeriesData>;\n\nexport function PieChartPanel(props: PieChartPanelProps): ReactElement | null {\n const {\n spec: { calculation, sort, mode, legend: pieChartLegend, colorPalette: colorPalette },\n contentDimensions,\n queryResults,\n } = props;\n const chartsTheme = useChartsTheme();\n const chartId = useId('time-series-panel');\n const seriesNames = queryResults.flatMap((result) => result?.data.series?.map((series) => series.name) || []);\n\n // Memoize the color list so it only regenerates when color/palette/series count changes\n const colorList = useMemo(() => {\n return getSeriesColor(seriesNames, colorPalette);\n }, [colorPalette, seriesNames]);\n\n const { pieChartData, legendItems, legendColumns } = useMemo(() => {\n const calculate = CalculationsMap[calculation as CalculationType];\n const pieChartData: PieChartData[] = [];\n const legendItems: LegendItem[] = [];\n const legendColumns: Array<TableColumnConfig<LegendItem>> = [];\n\n queryResults.forEach((result, queryIndex) => {\n const series = result?.data.series ?? [];\n\n series.forEach((seriesData, seriesIndex) => {\n const seriesId = `${chartId}${seriesData.name}${seriesIndex}${queryIndex}`;\n const seriesColor = colorList[queryIndex * series.length + seriesIndex] ?? '#ff0000';\n\n const seriesItem = {\n id: seriesId,\n value: calculate(seriesData.values) ?? null,\n name: seriesData.formattedName ?? '',\n itemStyle: {\n color: seriesColor,\n },\n };\n\n pieChartData.push(seriesItem);\n legendItems.push({\n id: seriesId,\n label: seriesData.formattedName ?? '',\n color: seriesColor,\n data: {},\n });\n });\n });\n\n const sortedPieChartData = sortSeriesData(pieChartData, sort);\n\n // Reorder legend items to reflect the current sorting order of series\n const valueById = new Map(sortedPieChartData.map((pd) => [pd.id ?? pd.name, pd.value ?? 0]));\n legendItems.sort((a, b) => {\n const av = valueById.get(a.id) ?? 0;\n const bv = valueById.get(b.id) ?? 0;\n return sort === 'asc' ? av - bv : bv - av;\n });\n\n if (pieChartLegend?.values?.length && pieChartLegend?.mode === 'table') {\n const { values } = pieChartLegend;\n [...values].sort().forEach((v) => {\n /* First, create a column for the current legend value */\n legendColumns.push({\n accessorKey: `data.${v}`,\n header: comparisonLegends[v as ComparisonValues]?.label || v,\n headerDescription: comparisonLegends[v as ComparisonValues]?.description,\n width: 90,\n align: 'right',\n cellDescription: true,\n enableSorting: true,\n });\n /* Then, settle the legend items related to this legend value */\n switch (v) {\n case 'abs':\n legendItems.forEach((li) => {\n const { value: itemAbsoluteValue } = pieChartData.find((pd) => li.id === pd.id) || {};\n if (typeof itemAbsoluteValue === 'number' && li.data) {\n li.data['abs'] = itemAbsoluteValue;\n }\n });\n break;\n case 'relative':\n legendItems.forEach((li) => {\n const { value: itemPercentageValue } =\n calculatePercentages(sortedPieChartData).find((ppd) => li.id === ppd.id) || {};\n if (typeof itemPercentageValue === 'number' && li.data) {\n li.data['relative'] = `${itemPercentageValue.toFixed(2)}%`;\n }\n });\n break;\n default:\n break;\n }\n });\n }\n\n return {\n pieChartData: mode === 'percentage' ? calculatePercentages(sortedPieChartData) : sortedPieChartData,\n legendItems,\n legendColumns,\n };\n }, [calculation, sort, mode, queryResults, colorList, chartId, pieChartLegend]);\n\n const contentPadding = chartsTheme.container.padding.default;\n const adjustedContentDimensions: typeof contentDimensions = contentDimensions\n ? {\n width: contentDimensions.width - contentPadding * 2,\n height: contentDimensions.height - contentPadding * 2,\n }\n : undefined;\n\n const legend = useMemo(() => {\n return props.spec.legend && validateLegendSpec(props.spec.legend)\n ? merge({}, DEFAULT_LEGEND, props.spec.legend)\n : undefined;\n }, [props.spec.legend]);\n\n const [selectedLegendItems, setSelectedLegendItems] = useState<SelectedLegendItemState>('ALL');\n\n const [legendSorting, setLegendSorting] = useState<NonNullable<LegendProps['tableProps']>['sorting']>();\n\n const chartRef = useRef<ChartInstance>(null);\n\n // ensures there are fallbacks for unset properties since most\n // users should not need to customize visual display\n\n if (!contentDimensions) return null;\n\n return (\n <Box sx={{ padding: `${contentPadding}px` }}>\n <ContentWithLegend\n width={adjustedContentDimensions?.width ?? 400}\n height={adjustedContentDimensions?.height ?? 1000}\n // Making this small enough that the medium size doesn't get\n // responsive-handling-ed away when in the panel options editor.\n minChildrenHeight={50}\n legendSize={legend?.size}\n legendProps={\n legend && {\n options: legend,\n data: legendItems,\n selectedItems: selectedLegendItems,\n onSelectedItemsChange: setSelectedLegendItems,\n tableProps: {\n columns: legendColumns,\n sorting: legendSorting,\n onSortingChange: setLegendSorting,\n },\n onItemMouseOver: (e, { id }): void => {\n chartRef.current?.highlightSeries({ name: id });\n },\n onItemMouseOut: (): void => {\n chartRef.current?.clearHighlightedSeries();\n },\n }\n }\n >\n {({ height, width }) => {\n return (\n <Box style={{ height, width }}>\n <PieChartBase\n data={pieChartData}\n width={width}\n height={height}\n showLabels={Boolean(props.spec.showLabels)}\n />\n </Box>\n );\n }}\n </ContentWithLegend>\n </Box>\n );\n}\n"],"names":["Box","ContentWithLegend","useChartsTheme","useId","CalculationsMap","DEFAULT_LEGEND","comparisonLegends","validateLegendSpec","merge","useMemo","useRef","useState","calculatePercentages","sortSeriesData","getSeriesColor","PieChartBase","PieChartPanel","props","spec","calculation","sort","mode","legend","pieChartLegend","colorPalette","contentDimensions","queryResults","chartsTheme","chartId","seriesNames","flatMap","result","data","series","map","name","colorList","pieChartData","legendItems","legendColumns","calculate","forEach","queryIndex","seriesData","seriesIndex","seriesId","seriesColor","length","seriesItem","id","value","values","formattedName","itemStyle","color","push","label","sortedPieChartData","valueById","Map","pd","a","b","av","get","bv","v","accessorKey","header","headerDescription","description","width","align","cellDescription","enableSorting","li","itemAbsoluteValue","find","itemPercentageValue","ppd","toFixed","contentPadding","container","padding","default","adjustedContentDimensions","height","undefined","selectedLegendItems","setSelectedLegendItems","legendSorting","setLegendSorting","chartRef","sx","minChildrenHeight","legendSize","size","legendProps","options","selectedItems","onSelectedItemsChange","tableProps","columns","sorting","onSortingChange","onItemMouseOver","e","current","highlightSeries","onItemMouseOut","clearHighlightedSeries","style","showLabels","Boolean"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/PieChartPanel.tsx"],"sourcesContent":["// Copyright The Perses Authors\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport { Box } from '@mui/material';\nimport {\n ChartInstance,\n ContentWithLegend,\n LegendProps,\n SelectedLegendItemState,\n useChartsTheme,\n useId,\n} from '@perses-dev/components';\nimport { CalculationsMap, CalculationType, DEFAULT_LEGEND, TimeSeriesData } from '@perses-dev/core';\nimport { PanelProps, validateLegendSpec } from '@perses-dev/plugin-system';\nimport merge from 'lodash/merge';\nimport { ReactElement, useMemo, useRef, useState } from 'react';\nimport { PieChartOptions } from './pie-chart-model';\nimport { PieChartLegendMapper, PieChartListLegendMapper, PieChartTableLegendMapper, sortSeriesData } from './utils';\nimport { getSeriesColor } from './colors';\nimport { PieChartBase, PieChartData } from './PieChartBase';\n\nexport type PieChartPanelProps = PanelProps<PieChartOptions, TimeSeriesData>;\n\nexport function PieChartPanel(props: PieChartPanelProps): ReactElement | null {\n const {\n spec: { calculation, sort, mode, format: formatOptions, legend: pieChartLegend, colorPalette: colorPalette },\n contentDimensions,\n queryResults,\n } = props;\n const chartsTheme = useChartsTheme();\n const chartId = useId('time-series-panel');\n const seriesNames = queryResults.flatMap((result) => result?.data.series?.map((series) => series.name) || []);\n\n // Memoize the color list so it only regenerates when color/palette/series count changes\n const colorList = useMemo(() => {\n return getSeriesColor(seriesNames, colorPalette);\n }, [colorPalette, seriesNames]);\n\n const pieChartData = useMemo((): Array<Required<PieChartData>> => {\n const calculate = CalculationsMap[calculation as CalculationType];\n const pieChartData: Array<Required<PieChartData>> = [];\n queryResults.forEach((result, queryIndex) => {\n const series = result?.data.series ?? [];\n\n series.forEach((seriesData, seriesIndex) => {\n const seriesId = `${chartId}${seriesData.name}${seriesIndex}${queryIndex}`;\n const seriesColor = colorList[queryIndex * series.length + seriesIndex] ?? '#ff0000';\n\n const seriesItem = {\n id: seriesId,\n value: calculate(seriesData.values) ?? null,\n name: seriesData.formattedName ?? '',\n itemStyle: {\n color: seriesColor,\n },\n };\n\n pieChartData.push(seriesItem);\n });\n });\n\n return sortSeriesData(pieChartData, sort);\n }, [calculation, chartId, colorList, queryResults, sort]);\n\n const { legendItems, legendColumns } = useMemo(() => {\n const pieChartLegendMapper: PieChartLegendMapper =\n pieChartLegend?.mode === 'table' ? new PieChartTableLegendMapper() : new PieChartListLegendMapper();\n const values = pieChartLegend?.values;\n const legendItems = pieChartLegendMapper.mapToLegendItems(pieChartData, values);\n const legendColumns = pieChartLegendMapper.mapToLegendColumns(values, formatOptions);\n return {\n legendItems,\n legendColumns,\n };\n }, [formatOptions, pieChartData, pieChartLegend?.mode, pieChartLegend?.values]);\n\n const contentPadding = chartsTheme.container.padding.default;\n const adjustedContentDimensions: typeof contentDimensions = contentDimensions\n ? {\n width: contentDimensions.width - contentPadding * 2,\n height: contentDimensions.height - contentPadding * 2,\n }\n : undefined;\n\n const legend = useMemo(() => {\n return props.spec.legend && validateLegendSpec(props.spec.legend)\n ? merge({}, DEFAULT_LEGEND, props.spec.legend)\n : undefined;\n }, [props.spec.legend]);\n\n const [selectedLegendItems, setSelectedLegendItems] = useState<SelectedLegendItemState>('ALL');\n\n const [legendSorting, setLegendSorting] = useState<NonNullable<LegendProps['tableProps']>['sorting']>();\n\n const chartRef = useRef<ChartInstance>(null);\n\n // ensures there are fallbacks for unset properties since most\n // users should not need to customize visual display\n\n if (!contentDimensions) return null;\n\n return (\n <Box sx={{ padding: `${contentPadding}px` }}>\n <ContentWithLegend\n width={adjustedContentDimensions?.width ?? 400}\n height={adjustedContentDimensions?.height ?? 1000}\n // Making this small enough that the medium size doesn't get\n // responsive-handling-ed away when in the panel options editor.\n minChildrenHeight={50}\n legendSize={legend?.size}\n legendProps={\n legend && {\n options: legend,\n data: legendItems,\n selectedItems: selectedLegendItems,\n onSelectedItemsChange: setSelectedLegendItems,\n tableProps: {\n columns: legendColumns,\n sorting: legendSorting,\n onSortingChange: setLegendSorting,\n },\n onItemMouseOver: (e, { id }): void => {\n chartRef.current?.highlightSeries({ name: id });\n },\n onItemMouseOut: (): void => {\n chartRef.current?.clearHighlightedSeries();\n },\n }\n }\n >\n {({ height, width }) => {\n return (\n <Box style={{ height, width }}>\n <PieChartBase\n data={pieChartData}\n width={width}\n height={height}\n mode={mode}\n formatOptions={formatOptions}\n showLabels={Boolean(props.spec.showLabels)}\n />\n </Box>\n );\n }}\n </ContentWithLegend>\n </Box>\n );\n}\n"],"names":["Box","ContentWithLegend","useChartsTheme","useId","CalculationsMap","DEFAULT_LEGEND","validateLegendSpec","merge","useMemo","useRef","useState","PieChartListLegendMapper","PieChartTableLegendMapper","sortSeriesData","getSeriesColor","PieChartBase","PieChartPanel","props","spec","calculation","sort","mode","format","formatOptions","legend","pieChartLegend","colorPalette","contentDimensions","queryResults","chartsTheme","chartId","seriesNames","flatMap","result","data","series","map","name","colorList","pieChartData","calculate","forEach","queryIndex","seriesData","seriesIndex","seriesId","seriesColor","length","seriesItem","id","value","values","formattedName","itemStyle","color","push","legendItems","legendColumns","pieChartLegendMapper","mapToLegendItems","mapToLegendColumns","contentPadding","container","padding","default","adjustedContentDimensions","width","height","undefined","selectedLegendItems","setSelectedLegendItems","legendSorting","setLegendSorting","chartRef","sx","minChildrenHeight","legendSize","size","legendProps","options","selectedItems","onSelectedItemsChange","tableProps","columns","sorting","onSortingChange","onItemMouseOver","e","current","highlightSeries","onItemMouseOut","clearHighlightedSeries","style","showLabels","Boolean"],"mappings":"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"}
@@ -27,6 +27,7 @@ const _components = require("echarts/components");
27
27
  const _renderers = require("echarts/renderers");
28
28
  const _material = require("@mui/material");
29
29
  const _components1 = require("@perses-dev/components");
30
+ const _utils = require("./utils");
30
31
  (0, _core.use)([
31
32
  _charts.PieChart,
32
33
  _components.GridComponent,
@@ -37,13 +38,13 @@ const _components1 = require("@perses-dev/components");
37
38
  _renderers.CanvasRenderer
38
39
  ]);
39
40
  function PieChartBase(props) {
40
- const { width, height, data, showLabels } = props;
41
+ const { width, height, data, mode, formatOptions, showLabels } = props;
41
42
  const chartsTheme = (0, _components1.useChartsTheme)();
42
43
  const muiTheme = (0, _material.useTheme)();
43
44
  const option = {
44
45
  tooltip: {
45
46
  trigger: 'item',
46
- formatter: '{b}: {c} ({d}%)',
47
+ formatter: (0, _utils.getTooltipFormatter)(formatOptions),
47
48
  appendTo: document.body,
48
49
  confine: false
49
50
  },
@@ -55,7 +56,7 @@ function PieChartBase(props) {
55
56
  show: Boolean(showLabels),
56
57
  position: 'inner',
57
58
  fontSize: 14,
58
- formatter: '{b}\n{c}',
59
+ formatter: (0, _utils.getLabelFormatter)(mode, formatOptions),
59
60
  overflow: 'truncate',
60
61
  fontWeight: 'bold'
61
62
  },
@@ -1,4 +1,4 @@
1
- //Copyright 2024 The Perses Authors
1
+ // Copyright The Perses Authors
2
2
  // Licensed under the Apache License, Version 2.0 (the "License");
3
3
  // you may not use this file except in compliance with the License.
4
4
  // You may obtain a copy of the License at
@@ -36,7 +36,7 @@ function _interop_require_default(obj) {
36
36
  };
37
37
  }
38
38
  function PieChartPanel(props) {
39
- const { spec: { calculation, sort, mode, legend: pieChartLegend, colorPalette: colorPalette }, contentDimensions, queryResults } = props;
39
+ const { spec: { calculation, sort, mode, format: formatOptions, legend: pieChartLegend, colorPalette: colorPalette }, contentDimensions, queryResults } = props;
40
40
  const chartsTheme = (0, _components.useChartsTheme)();
41
41
  const chartId = (0, _components.useId)('time-series-panel');
42
42
  const seriesNames = queryResults.flatMap((result)=>result?.data.series?.map((series)=>series.name) || []);
@@ -47,11 +47,9 @@ function PieChartPanel(props) {
47
47
  colorPalette,
48
48
  seriesNames
49
49
  ]);
50
- const { pieChartData, legendItems, legendColumns } = (0, _react.useMemo)(()=>{
50
+ const pieChartData = (0, _react.useMemo)(()=>{
51
51
  const calculate = _core.CalculationsMap[calculation];
52
52
  const pieChartData = [];
53
- const legendItems = [];
54
- const legendColumns = [];
55
53
  queryResults.forEach((result, queryIndex)=>{
56
54
  const series = result?.data.series ?? [];
57
55
  series.forEach((seriesData, seriesIndex)=>{
@@ -66,74 +64,30 @@ function PieChartPanel(props) {
66
64
  }
67
65
  };
68
66
  pieChartData.push(seriesItem);
69
- legendItems.push({
70
- id: seriesId,
71
- label: seriesData.formattedName ?? '',
72
- color: seriesColor,
73
- data: {}
74
- });
75
67
  });
76
68
  });
77
- const sortedPieChartData = (0, _utils.sortSeriesData)(pieChartData, sort);
78
- // Reorder legend items to reflect the current sorting order of series
79
- const valueById = new Map(sortedPieChartData.map((pd)=>[
80
- pd.id ?? pd.name,
81
- pd.value ?? 0
82
- ]));
83
- legendItems.sort((a, b)=>{
84
- const av = valueById.get(a.id) ?? 0;
85
- const bv = valueById.get(b.id) ?? 0;
86
- return sort === 'asc' ? av - bv : bv - av;
87
- });
88
- if (pieChartLegend?.values?.length && pieChartLegend?.mode === 'table') {
89
- const { values } = pieChartLegend;
90
- [
91
- ...values
92
- ].sort().forEach((v)=>{
93
- /* First, create a column for the current legend value */ legendColumns.push({
94
- accessorKey: `data.${v}`,
95
- header: _pluginsystem.comparisonLegends[v]?.label || v,
96
- headerDescription: _pluginsystem.comparisonLegends[v]?.description,
97
- width: 90,
98
- align: 'right',
99
- cellDescription: true,
100
- enableSorting: true
101
- });
102
- /* Then, settle the legend items related to this legend value */ switch(v){
103
- case 'abs':
104
- legendItems.forEach((li)=>{
105
- const { value: itemAbsoluteValue } = pieChartData.find((pd)=>li.id === pd.id) || {};
106
- if (typeof itemAbsoluteValue === 'number' && li.data) {
107
- li.data['abs'] = itemAbsoluteValue;
108
- }
109
- });
110
- break;
111
- case 'relative':
112
- legendItems.forEach((li)=>{
113
- const { value: itemPercentageValue } = (0, _utils.calculatePercentages)(sortedPieChartData).find((ppd)=>li.id === ppd.id) || {};
114
- if (typeof itemPercentageValue === 'number' && li.data) {
115
- li.data['relative'] = `${itemPercentageValue.toFixed(2)}%`;
116
- }
117
- });
118
- break;
119
- default:
120
- break;
121
- }
122
- });
123
- }
69
+ return (0, _utils.sortSeriesData)(pieChartData, sort);
70
+ }, [
71
+ calculation,
72
+ chartId,
73
+ colorList,
74
+ queryResults,
75
+ sort
76
+ ]);
77
+ const { legendItems, legendColumns } = (0, _react.useMemo)(()=>{
78
+ const pieChartLegendMapper = pieChartLegend?.mode === 'table' ? new _utils.PieChartTableLegendMapper() : new _utils.PieChartListLegendMapper();
79
+ const values = pieChartLegend?.values;
80
+ const legendItems = pieChartLegendMapper.mapToLegendItems(pieChartData, values);
81
+ const legendColumns = pieChartLegendMapper.mapToLegendColumns(values, formatOptions);
124
82
  return {
125
- pieChartData: mode === 'percentage' ? (0, _utils.calculatePercentages)(sortedPieChartData) : sortedPieChartData,
126
83
  legendItems,
127
84
  legendColumns
128
85
  };
129
86
  }, [
130
- calculation,
131
- sort,
132
- mode,
133
- queryResults,
134
- colorList,
135
- chartId,
136
- pieChartLegend
87
+ formatOptions,
88
+ pieChartData,
89
+ pieChartLegend?.mode,
90
+ pieChartLegend?.values
137
91
  ]);
138
92
  const contentPadding = chartsTheme.container.padding.default;
139
93
  const adjustedContentDimensions = contentDimensions ? {
@@ -191,6 +145,8 @@ function PieChartPanel(props) {
191
145
  data: pieChartData,
192
146
  width: width,
193
147
  height: height,
148
+ mode: mode,
149
+ formatOptions: formatOptions,
194
150
  showLabels: Boolean(props.spec.showLabels)
195
151
  })
196
152
  });
package/lib/cjs/utils.js CHANGED
@@ -21,24 +21,26 @@ function _export(target, all) {
21
21
  });
22
22
  }
23
23
  _export(exports, {
24
- get calculatePercentages () {
25
- return calculatePercentages;
24
+ get PieChartListLegendMapper () {
25
+ return PieChartListLegendMapper;
26
+ },
27
+ get PieChartTableLegendMapper () {
28
+ return PieChartTableLegendMapper;
29
+ },
30
+ get getLabelFormatter () {
31
+ return getLabelFormatter;
32
+ },
33
+ get getTooltipFormatter () {
34
+ return getTooltipFormatter;
26
35
  },
27
36
  get sortSeriesData () {
28
37
  return sortSeriesData;
29
38
  }
30
39
  });
40
+ const _core = require("@perses-dev/core");
41
+ const _echarts = require("echarts");
42
+ const _pluginsystem = require("@perses-dev/plugin-system");
31
43
  const _piechartmodel = require("./pie-chart-model");
32
- function calculatePercentages(data) {
33
- const sum = data.reduce((accumulator, { value })=>accumulator + (value ?? 0), 0);
34
- return data.map((seriesData)=>{
35
- const percentage = (seriesData.value ?? 0) / sum * 100;
36
- return {
37
- ...seriesData,
38
- value: Number(percentage.toFixed(2))
39
- };
40
- });
41
- }
42
44
  function sortSeriesData(data, sortOrder = _piechartmodel.DEFAULT_SORT) {
43
45
  return data.sort((a, b)=>{
44
46
  // Handle null values - push them to the end regardless of sort order
@@ -50,3 +52,112 @@ function sortSeriesData(data, sortOrder = _piechartmodel.DEFAULT_SORT) {
50
52
  return sortOrder === 'asc' ? diff : -diff;
51
53
  });
52
54
  }
55
+ const getTooltipFormatter = (formatOptions)=>{
56
+ const relativeFormatOptions = {
57
+ unit: 'percent',
58
+ decimalPlaces: formatOptions?.decimalPlaces
59
+ };
60
+ return ({ name, value, percent })=>{
61
+ if (typeof value === 'number') {
62
+ return `${_echarts.format.encodeHTML(name)}: ${(0, _core.formatValue)(value, formatOptions)} (${(0, _core.formatValue)(percent, relativeFormatOptions)})`;
63
+ }
64
+ return `${_echarts.format.encodeHTML(name)}: ${_echarts.format.encodeHTML(value.toString())}`;
65
+ };
66
+ };
67
+ const getLabelFormatter = (mode, formatOptions)=>{
68
+ if (mode === 'percentage') {
69
+ return percentageLabelFormatter(formatOptions);
70
+ }
71
+ return labelFormatter(formatOptions);
72
+ };
73
+ const labelFormatter = (formatOptions)=>{
74
+ return ({ name, value })=>{
75
+ if (typeof value === 'number') {
76
+ return `${name}:\n${(0, _core.formatValue)(value, formatOptions)}`;
77
+ }
78
+ return `${name}:\n${_echarts.format.encodeHTML(value.toString())}`;
79
+ };
80
+ };
81
+ const percentageLabelFormatter = (formatOptions)=>{
82
+ const relativeFormatOptions = {
83
+ unit: 'percent',
84
+ decimalPlaces: formatOptions?.decimalPlaces
85
+ };
86
+ return ({ name, percent })=>{
87
+ return `${name}:\n${(0, _core.formatValue)(percent, relativeFormatOptions)}`;
88
+ };
89
+ };
90
+ class PieChartListLegendMapper {
91
+ mapToLegendItems(pieChartData) {
92
+ return pieChartData.map(({ id, name, itemStyle })=>({
93
+ id: id,
94
+ label: name,
95
+ color: itemStyle.color,
96
+ data: {}
97
+ }));
98
+ }
99
+ mapToLegendColumns() {
100
+ return [];
101
+ }
102
+ }
103
+ class PieChartTableLegendMapper {
104
+ mapToLegendItems(pieChartData, selectedValues) {
105
+ const relativePieChartData = calculatePercentages(pieChartData);
106
+ const absoluteValueSelected = selectedValues?.includes('abs');
107
+ const relativeValueSelected = selectedValues?.includes('relative');
108
+ return pieChartData.map(({ id, name, itemStyle, value })=>{
109
+ const data = {};
110
+ if (absoluteValueSelected && typeof value === 'number') {
111
+ data['abs'] = value;
112
+ }
113
+ if (relativeValueSelected) {
114
+ const itemPercentageValue = relativePieChartData.find((rpd)=>rpd.id === id)?.value;
115
+ if (typeof itemPercentageValue === 'number') {
116
+ data['relative'] = itemPercentageValue;
117
+ }
118
+ }
119
+ return {
120
+ id: id,
121
+ label: name,
122
+ color: itemStyle.color,
123
+ data: data
124
+ };
125
+ });
126
+ }
127
+ mapToLegendColumns(selectedValues, formatOptions) {
128
+ const relativeFormatOptions = {
129
+ unit: 'percent',
130
+ decimalPlaces: formatOptions?.decimalPlaces
131
+ };
132
+ return selectedValues?.toSorted().map((v)=>{
133
+ const comparisonValue = isComparisonValue(v);
134
+ return {
135
+ accessorKey: `data.${v}`,
136
+ header: comparisonValue ? _pluginsystem.comparisonLegends[v].label : v,
137
+ headerDescription: comparisonValue ? _pluginsystem.comparisonLegends[v].description : undefined,
138
+ width: 90,
139
+ align: 'right',
140
+ cellDescription: true,
141
+ enableSorting: true,
142
+ cell: ({ getValue })=>{
143
+ const cellValue = getValue();
144
+ return typeof cellValue === 'number' && formatOptions ? (0, _core.formatValue)(cellValue, v === 'relative' ? relativeFormatOptions : formatOptions) : cellValue;
145
+ }
146
+ };
147
+ }) ?? [];
148
+ }
149
+ }
150
+ function calculatePercentages(data) {
151
+ const sum = data.reduce((accumulator, { value })=>accumulator + (value ?? 0), 0);
152
+ return data.map((seriesData)=>{
153
+ const percentage = (seriesData.value ?? 0) / sum * 100;
154
+ return {
155
+ ...seriesData,
156
+ value: Number(percentage.toFixed(4))
157
+ };
158
+ });
159
+ }
160
+ const comparisonValuesArray = Object.keys(_pluginsystem.comparisonLegends);
161
+ function isComparisonValue(value) {
162
+ return typeof value === 'string' && comparisonValuesArray.includes(value);
163
+ }
package/lib/utils.d.ts CHANGED
@@ -1,9 +1,26 @@
1
- import { SortOption } from '@perses-dev/components';
1
+ import { LegendItem, ModeOption, SortOption, TableColumnConfig } from '@perses-dev/components';
2
+ import { FormatOptions } from '@perses-dev/core';
3
+ import { ComparisonValues, LegendValue } from '@perses-dev/plugin-system';
2
4
  import { PieChartData } from './PieChartBase';
3
- export declare function calculatePercentages(data: PieChartData[]): Array<{
4
- id?: string;
5
+ export declare function sortSeriesData<T extends PieChartData>(data: T[], sortOrder?: SortOption): T[];
6
+ type formatterProps = {
5
7
  name: string;
6
- value: number;
7
- }>;
8
- export declare function sortSeriesData(data: PieChartData[], sortOrder?: SortOption): PieChartData[];
8
+ value: number | unknown[] | object;
9
+ percent: number;
10
+ };
11
+ export declare const getTooltipFormatter: (formatOptions?: FormatOptions) => ((props: formatterProps) => string);
12
+ export declare const getLabelFormatter: (mode?: ModeOption, formatOptions?: FormatOptions) => ((props: formatterProps) => string);
13
+ export interface PieChartLegendMapper {
14
+ mapToLegendItems: (pieChartData: Array<Required<PieChartData>>, selectedValues?: Array<LegendValue | ComparisonValues>) => LegendItem[];
15
+ mapToLegendColumns: (selectedValues?: Array<LegendValue | ComparisonValues>, formatOptions?: FormatOptions) => Array<TableColumnConfig<LegendItem>>;
16
+ }
17
+ export declare class PieChartListLegendMapper implements PieChartLegendMapper {
18
+ mapToLegendItems(pieChartData: Array<Required<PieChartData>>): LegendItem[];
19
+ mapToLegendColumns(): Array<TableColumnConfig<LegendItem>>;
20
+ }
21
+ export declare class PieChartTableLegendMapper implements PieChartLegendMapper {
22
+ mapToLegendItems(pieChartData: Array<Required<PieChartData>>, selectedValues?: Array<LegendValue | ComparisonValues>): LegendItem[];
23
+ mapToLegendColumns(selectedValues?: Array<LegendValue | ComparisonValues>, formatOptions?: FormatOptions): Array<TableColumnConfig<LegendItem>>;
24
+ }
25
+ export {};
9
26
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEpD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,YAAY,EAAE,GAAG,KAAK,CAAC;IAAE,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAS9G;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,YAAY,EAAE,EAAE,SAAS,GAAE,UAAyB,GAAG,YAAY,EAAE,CAWzG"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC/F,OAAO,EAAE,aAAa,EAAe,MAAM,kBAAkB,CAAC;AAE9D,OAAO,EAAqB,gBAAgB,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAC7F,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAG9C,wBAAgB,cAAc,CAAC,CAAC,SAAS,YAAY,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,SAAS,GAAE,UAAyB,GAAG,CAAC,EAAE,CAW3G;AAED,KAAK,cAAc,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,OAAO,EAAE,GAAG,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAC5F,eAAO,MAAM,mBAAmB,GAAI,gBAAgB,aAAa,KAAG,CAAC,CAAC,KAAK,EAAE,cAAc,KAAK,MAAM,CAQrG,CAAC;AACF,eAAO,MAAM,iBAAiB,GAC5B,OAAO,UAAU,EACjB,gBAAgB,aAAa,KAC5B,CAAC,CAAC,KAAK,EAAE,cAAc,KAAK,MAAM,CAKpC,CAAC;AAkBF,MAAM,WAAW,oBAAoB;IACnC,gBAAgB,EAAE,CAChB,YAAY,EAAE,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,EAC3C,cAAc,CAAC,EAAE,KAAK,CAAC,WAAW,GAAG,gBAAgB,CAAC,KACnD,UAAU,EAAE,CAAC;IAClB,kBAAkB,EAAE,CAClB,cAAc,CAAC,EAAE,KAAK,CAAC,WAAW,GAAG,gBAAgB,CAAC,EACtD,aAAa,CAAC,EAAE,aAAa,KAC1B,KAAK,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC,CAAC;CAC3C;AAED,qBAAa,wBAAyB,YAAW,oBAAoB;IACnE,gBAAgB,CAAC,YAAY,EAAE,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,GAAG,UAAU,EAAE;IAG3E,kBAAkB,IAAI,KAAK,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC;CAG3D;AAED,qBAAa,yBAA0B,YAAW,oBAAoB;IACpE,gBAAgB,CACd,YAAY,EAAE,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,EAC3C,cAAc,CAAC,EAAE,KAAK,CAAC,WAAW,GAAG,gBAAgB,CAAC,GACrD,UAAU,EAAE;IAwBf,kBAAkB,CAChB,cAAc,CAAC,EAAE,KAAK,CAAC,WAAW,GAAG,gBAAgB,CAAC,EACtD,aAAa,CAAC,EAAE,aAAa,GAC5B,KAAK,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC;CAuBxC"}