@trackunit/react-chart-components 1.4.49 → 1.4.52

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.cjs.js CHANGED
@@ -221,9 +221,9 @@ const cvaLegendItemIndicator = cssClassVarianceUtilities.cvaMerge(["w-3", "mr-1"
221
221
  * @param {LegendItem} props - The props for the LegendItem component
222
222
  * @returns {ReactElement} LegendItem component
223
223
  */
224
- const LegendItem = ({ className, count, label, disabled, selected, onClick, color, dataTestId, onMouseEnter, onMouseLeave, }) => {
224
+ const LegendItem = ({ className, count, label, disabled, selected, onClick, color, dataTestId, onMouseEnter, onMouseLeave, unit, hideValue = false, }) => {
225
225
  const handleOnClick = onClick && !disabled ? () => onClick() : undefined;
226
- return (jsxRuntime.jsxs("div", { className: cvaLegendItem({ disabled, selected, isClickable: !disabled && Boolean(onClick), className }), "data-testid": dataTestId, onClick: handleOnClick, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, children: [jsxRuntime.jsx("div", { className: cvaLegendItemIndicator({ selected }), "data-testid": dataTestId ? `${dataTestId}-indicator` : null, style: { backgroundColor: color } }), jsxRuntime.jsxs("p", { className: "truncate text-xs", children: [label, "\u00A0"] }), jsxRuntime.jsxs("p", { className: "text-xs", children: ["(", count || 0, ")"] })] }));
226
+ return (jsxRuntime.jsxs("div", { className: cvaLegendItem({ disabled, selected, isClickable: !disabled && Boolean(onClick), className }), "data-testid": dataTestId, onClick: handleOnClick, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, children: [jsxRuntime.jsx("div", { className: cvaLegendItemIndicator({ selected }), "data-testid": dataTestId ? `${dataTestId}-indicator` : null, style: { backgroundColor: color } }), jsxRuntime.jsxs("p", { className: "truncate text-xs", children: [label, "\u00A0"] }), hideValue ? null : (jsxRuntime.jsxs("p", { className: "text-xs", children: ["(", count || 0, ")", unit || ""] }))] }));
227
227
  };
228
228
 
229
229
  /**
@@ -254,11 +254,12 @@ const useChartColor = () => {
254
254
  *
255
255
  * @param data - The data set to limit.
256
256
  * @param limit - The limit to apply to the data set.
257
+ * @param autoSortData - If true, the data will be sorted by value in descending order.
257
258
  * @returns {object} The limited data set with and without the "others" category.
258
259
  */
259
- const useLimitDataSet = (data, limit) => {
260
+ const useLimitDataSet = (data, limit, autoSortData = true) => {
260
261
  const limitedData = react.useMemo(() => {
261
- const sortedData = data.sort((a, b) => (b.value ?? 0) - (a.value ?? 0));
262
+ const sortedData = autoSortData ? data.toSorted((a, b) => (b.value ?? 0) - (a.value ?? 0)) : data;
262
263
  if (sortedData.length > limit) {
263
264
  const result = sortedData.slice(0, limit);
264
265
  result.push({
@@ -272,7 +273,7 @@ const useLimitDataSet = (data, limit) => {
272
273
  return result;
273
274
  }
274
275
  return sortedData;
275
- }, [data, limit]);
276
+ }, [data, limit, autoSortData]);
276
277
  const limitedDataWithoutOthers = react.useMemo(() => limitedData.filter(item => !item.original?.defaultOther), [limitedData]);
277
278
  return { limitedData, limitedDataWithoutOthers };
278
279
  };
@@ -283,13 +284,13 @@ const useLimitDataSet = (data, limit) => {
283
284
  * @param {DonutChartProps} props - The props for the Chart component
284
285
  * @returns {ReactElement} Chart component
285
286
  */
286
- const DonutChart = ({ data, size = "full", loading, onClick, className, dataTestId, legendPosition = "Right", maxDataPoints = 6, showOthers = true, }) => {
287
+ const DonutChart = ({ data, size = "full", loading, onClick, className, dataTestId, legendPosition = "Right", maxDataPoints = 6, showOthers = true, unit, overrideTotal, autoSortData = true, hideLegendValues = false, }) => {
287
288
  const containerRef = react.useRef(null);
288
289
  const chartRef = react.useRef(null);
289
- const totalValue = react.useMemo(() => data?.map(item => item.value ?? 0).reduce((a, b) => a + b, 0) ?? 0, [data]);
290
+ const totalValue = react.useMemo(() => overrideTotal ?? data?.map(item => item.value ?? 0).reduce((a, b) => a + b, 0) ?? 0, [data, overrideTotal]);
290
291
  const [hoveringItem, setHoveringItem] = react.useState(null);
291
292
  const currentValue = react.useMemo(() => hoveringItem?.value ?? totalValue, [hoveringItem, totalValue]);
292
- const { limitedData, limitedDataWithoutOthers } = useLimitDataSet(data ?? [], maxDataPoints);
293
+ const { limitedData, limitedDataWithoutOthers } = useLimitDataSet(data ?? [], maxDataPoints, autoSortData);
293
294
  const { chartColor } = useChartColor();
294
295
  const handleChartReady = react.useCallback((chart) => {
295
296
  chartRef.current = chart;
@@ -375,12 +376,12 @@ const DonutChart = ({ data, size = "full", loading, onClick, className, dataTest
375
376
  position: "center",
376
377
  fontSize: size === "full" ? 18 : 12,
377
378
  fontWeight: "bold",
378
- formatter: currentValue.toString(),
379
+ formatter: unit ? currentValue.toString() + unit : currentValue.toString(),
379
380
  },
380
381
  emphasis: {
381
382
  label: {
382
383
  show: true,
383
- formatter: "{c}",
384
+ formatter: unit ? "{c}" + unit : "{c}",
384
385
  },
385
386
  },
386
387
  labelLine: {
@@ -408,8 +409,8 @@ const DonutChart = ({ data, size = "full", loading, onClick, className, dataTest
408
409
  },
409
410
  ],
410
411
  };
411
- }, [size, currentValue, showOthers, limitedData, limitedDataWithoutOthers, chartColor, hoveringItem?.id]);
412
- return (jsxRuntime.jsxs("div", { className: cvaChartRoot$1({ className, legendPosition }), "data-testid": dataTestId, ref: containerRef, children: [jsxRuntime.jsx("div", { className: cvaChartContainer({ legendPosition }), children: jsxRuntime.jsx(Chart, { className: cvaChart({ legendPosition, size }), dataTestId: "pie-chart", onChartReady: handleChartReady, onClick: handleChartClick, onEvents: handleChartEvents, options: chartOptions, showLoading: loading, style: { width: "100%", height: "100%" } }) }), size === "full" && (jsxRuntime.jsx("div", { className: cvaLegend({ legendPosition }), "data-testid": "legend", children: limitedData.map((item, index) => (jsxRuntime.jsx(LegendItem, { className: "p-1.5 py-0.5", color: item.color ?? chartColor(index), count: item.value, dataTestId: `legend-${item.id}`, disabled: (item.value ?? 0) === 0, label: item.name, onClick: onClick ? () => onClick(item) : undefined, onMouseEnter: () => handleLegendMouseEnter(item), onMouseLeave: handleLegendMouseLeave, selected: item.selected }, item.id))) }))] }));
412
+ }, [size, currentValue, showOthers, limitedData, limitedDataWithoutOthers, chartColor, hoveringItem?.id, unit]);
413
+ return (jsxRuntime.jsxs("div", { className: cvaChartRoot$1({ className, legendPosition }), "data-testid": dataTestId, ref: containerRef, children: [jsxRuntime.jsx("div", { className: cvaChartContainer({ legendPosition }), children: jsxRuntime.jsx(Chart, { className: cvaChart({ legendPosition, size }), dataTestId: "pie-chart", onChartReady: handleChartReady, onClick: handleChartClick, onEvents: handleChartEvents, options: chartOptions, showLoading: loading, style: { width: "100%", height: "100%" } }) }), size === "full" && (jsxRuntime.jsx("div", { className: cvaLegend({ legendPosition }), "data-testid": "legend", children: limitedData.map((item, index) => (jsxRuntime.jsx(LegendItem, { className: "p-1.5 py-0.5", color: item.color ?? chartColor(index), count: item.value, dataTestId: `legend-${item.id}`, disabled: (item.value ?? 0) === 0, hideValue: hideLegendValues, label: item.name, onClick: onClick ? () => onClick(item) : undefined, onMouseEnter: () => handleLegendMouseEnter(item), onMouseLeave: handleLegendMouseLeave, selected: item.selected, unit: unit }, item.id))) }))] }));
413
414
  };
414
415
  const cvaChartRoot$1 = cssClassVarianceUtilities.cvaMerge(["flex", "w-full", "h-full", "gap-4"], {
415
416
  variants: {
package/index.esm.js CHANGED
@@ -200,9 +200,9 @@ const cvaLegendItemIndicator = cvaMerge(["w-3", "mr-1", "h-3", "rounded-[50%]",
200
200
  * @param {LegendItem} props - The props for the LegendItem component
201
201
  * @returns {ReactElement} LegendItem component
202
202
  */
203
- const LegendItem = ({ className, count, label, disabled, selected, onClick, color, dataTestId, onMouseEnter, onMouseLeave, }) => {
203
+ const LegendItem = ({ className, count, label, disabled, selected, onClick, color, dataTestId, onMouseEnter, onMouseLeave, unit, hideValue = false, }) => {
204
204
  const handleOnClick = onClick && !disabled ? () => onClick() : undefined;
205
- return (jsxs("div", { className: cvaLegendItem({ disabled, selected, isClickable: !disabled && Boolean(onClick), className }), "data-testid": dataTestId, onClick: handleOnClick, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, children: [jsx("div", { className: cvaLegendItemIndicator({ selected }), "data-testid": dataTestId ? `${dataTestId}-indicator` : null, style: { backgroundColor: color } }), jsxs("p", { className: "truncate text-xs", children: [label, "\u00A0"] }), jsxs("p", { className: "text-xs", children: ["(", count || 0, ")"] })] }));
205
+ return (jsxs("div", { className: cvaLegendItem({ disabled, selected, isClickable: !disabled && Boolean(onClick), className }), "data-testid": dataTestId, onClick: handleOnClick, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, children: [jsx("div", { className: cvaLegendItemIndicator({ selected }), "data-testid": dataTestId ? `${dataTestId}-indicator` : null, style: { backgroundColor: color } }), jsxs("p", { className: "truncate text-xs", children: [label, "\u00A0"] }), hideValue ? null : (jsxs("p", { className: "text-xs", children: ["(", count || 0, ")", unit || ""] }))] }));
206
206
  };
207
207
 
208
208
  /**
@@ -233,11 +233,12 @@ const useChartColor = () => {
233
233
  *
234
234
  * @param data - The data set to limit.
235
235
  * @param limit - The limit to apply to the data set.
236
+ * @param autoSortData - If true, the data will be sorted by value in descending order.
236
237
  * @returns {object} The limited data set with and without the "others" category.
237
238
  */
238
- const useLimitDataSet = (data, limit) => {
239
+ const useLimitDataSet = (data, limit, autoSortData = true) => {
239
240
  const limitedData = useMemo(() => {
240
- const sortedData = data.sort((a, b) => (b.value ?? 0) - (a.value ?? 0));
241
+ const sortedData = autoSortData ? data.toSorted((a, b) => (b.value ?? 0) - (a.value ?? 0)) : data;
241
242
  if (sortedData.length > limit) {
242
243
  const result = sortedData.slice(0, limit);
243
244
  result.push({
@@ -251,7 +252,7 @@ const useLimitDataSet = (data, limit) => {
251
252
  return result;
252
253
  }
253
254
  return sortedData;
254
- }, [data, limit]);
255
+ }, [data, limit, autoSortData]);
255
256
  const limitedDataWithoutOthers = useMemo(() => limitedData.filter(item => !item.original?.defaultOther), [limitedData]);
256
257
  return { limitedData, limitedDataWithoutOthers };
257
258
  };
@@ -262,13 +263,13 @@ const useLimitDataSet = (data, limit) => {
262
263
  * @param {DonutChartProps} props - The props for the Chart component
263
264
  * @returns {ReactElement} Chart component
264
265
  */
265
- const DonutChart = ({ data, size = "full", loading, onClick, className, dataTestId, legendPosition = "Right", maxDataPoints = 6, showOthers = true, }) => {
266
+ const DonutChart = ({ data, size = "full", loading, onClick, className, dataTestId, legendPosition = "Right", maxDataPoints = 6, showOthers = true, unit, overrideTotal, autoSortData = true, hideLegendValues = false, }) => {
266
267
  const containerRef = useRef(null);
267
268
  const chartRef = useRef(null);
268
- const totalValue = useMemo(() => data?.map(item => item.value ?? 0).reduce((a, b) => a + b, 0) ?? 0, [data]);
269
+ const totalValue = useMemo(() => overrideTotal ?? data?.map(item => item.value ?? 0).reduce((a, b) => a + b, 0) ?? 0, [data, overrideTotal]);
269
270
  const [hoveringItem, setHoveringItem] = useState(null);
270
271
  const currentValue = useMemo(() => hoveringItem?.value ?? totalValue, [hoveringItem, totalValue]);
271
- const { limitedData, limitedDataWithoutOthers } = useLimitDataSet(data ?? [], maxDataPoints);
272
+ const { limitedData, limitedDataWithoutOthers } = useLimitDataSet(data ?? [], maxDataPoints, autoSortData);
272
273
  const { chartColor } = useChartColor();
273
274
  const handleChartReady = useCallback((chart) => {
274
275
  chartRef.current = chart;
@@ -354,12 +355,12 @@ const DonutChart = ({ data, size = "full", loading, onClick, className, dataTest
354
355
  position: "center",
355
356
  fontSize: size === "full" ? 18 : 12,
356
357
  fontWeight: "bold",
357
- formatter: currentValue.toString(),
358
+ formatter: unit ? currentValue.toString() + unit : currentValue.toString(),
358
359
  },
359
360
  emphasis: {
360
361
  label: {
361
362
  show: true,
362
- formatter: "{c}",
363
+ formatter: unit ? "{c}" + unit : "{c}",
363
364
  },
364
365
  },
365
366
  labelLine: {
@@ -387,8 +388,8 @@ const DonutChart = ({ data, size = "full", loading, onClick, className, dataTest
387
388
  },
388
389
  ],
389
390
  };
390
- }, [size, currentValue, showOthers, limitedData, limitedDataWithoutOthers, chartColor, hoveringItem?.id]);
391
- return (jsxs("div", { className: cvaChartRoot$1({ className, legendPosition }), "data-testid": dataTestId, ref: containerRef, children: [jsx("div", { className: cvaChartContainer({ legendPosition }), children: jsx(Chart, { className: cvaChart({ legendPosition, size }), dataTestId: "pie-chart", onChartReady: handleChartReady, onClick: handleChartClick, onEvents: handleChartEvents, options: chartOptions, showLoading: loading, style: { width: "100%", height: "100%" } }) }), size === "full" && (jsx("div", { className: cvaLegend({ legendPosition }), "data-testid": "legend", children: limitedData.map((item, index) => (jsx(LegendItem, { className: "p-1.5 py-0.5", color: item.color ?? chartColor(index), count: item.value, dataTestId: `legend-${item.id}`, disabled: (item.value ?? 0) === 0, label: item.name, onClick: onClick ? () => onClick(item) : undefined, onMouseEnter: () => handleLegendMouseEnter(item), onMouseLeave: handleLegendMouseLeave, selected: item.selected }, item.id))) }))] }));
391
+ }, [size, currentValue, showOthers, limitedData, limitedDataWithoutOthers, chartColor, hoveringItem?.id, unit]);
392
+ return (jsxs("div", { className: cvaChartRoot$1({ className, legendPosition }), "data-testid": dataTestId, ref: containerRef, children: [jsx("div", { className: cvaChartContainer({ legendPosition }), children: jsx(Chart, { className: cvaChart({ legendPosition, size }), dataTestId: "pie-chart", onChartReady: handleChartReady, onClick: handleChartClick, onEvents: handleChartEvents, options: chartOptions, showLoading: loading, style: { width: "100%", height: "100%" } }) }), size === "full" && (jsx("div", { className: cvaLegend({ legendPosition }), "data-testid": "legend", children: limitedData.map((item, index) => (jsx(LegendItem, { className: "p-1.5 py-0.5", color: item.color ?? chartColor(index), count: item.value, dataTestId: `legend-${item.id}`, disabled: (item.value ?? 0) === 0, hideValue: hideLegendValues, label: item.name, onClick: onClick ? () => onClick(item) : undefined, onMouseEnter: () => handleLegendMouseEnter(item), onMouseLeave: handleLegendMouseLeave, selected: item.selected, unit: unit }, item.id))) }))] }));
392
393
  };
393
394
  const cvaChartRoot$1 = cvaMerge(["flex", "w-full", "h-full", "gap-4"], {
394
395
  variants: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trackunit/react-chart-components",
3
- "version": "1.4.49",
3
+ "version": "1.4.52",
4
4
  "repository": "https://github.com/Trackunit/manager",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "engines": {
@@ -9,13 +9,13 @@
9
9
  "dependencies": {
10
10
  "echarts": "5.6.0",
11
11
  "react": "19.0.0",
12
- "@trackunit/date-and-time-utils": "1.4.28",
13
- "@trackunit/react-date-and-time-hooks": "1.4.36",
14
- "@trackunit/ui-design-tokens": "1.4.30",
15
- "@trackunit/shared-utils": "1.6.28",
16
- "@trackunit/css-class-variance-utilities": "1.4.28",
17
- "@trackunit/react-components": "1.5.45",
18
- "@trackunit/react-test-setup": "1.1.28"
12
+ "@trackunit/date-and-time-utils": "1.4.31",
13
+ "@trackunit/react-date-and-time-hooks": "1.4.39",
14
+ "@trackunit/ui-design-tokens": "1.4.33",
15
+ "@trackunit/shared-utils": "1.6.31",
16
+ "@trackunit/css-class-variance-utilities": "1.4.31",
17
+ "@trackunit/react-components": "1.5.48",
18
+ "@trackunit/react-test-setup": "1.1.31"
19
19
  },
20
20
  "module": "./index.esm.js",
21
21
  "main": "./index.cjs.js",
@@ -62,6 +62,31 @@ export interface DonutChartProps<TProps extends object> extends CommonProps {
62
62
  * @default true
63
63
  */
64
64
  showOthers?: boolean;
65
+ /**
66
+ * The unit of the chart.
67
+ *
68
+ */
69
+ unit?: "%";
70
+ /**
71
+ * The value to use as the total value of the chart.
72
+ * Example of use case:
73
+ * - If the data is a progress percentage, the total should display the value of the progress instead of 100%.
74
+ *
75
+ * If not provided, the total value will be calculated from the data.
76
+ */
77
+ overrideTotal?: number;
78
+ /**
79
+ * If true, the data will be sorted by value in descending order.
80
+ *
81
+ * @default true
82
+ */
83
+ autoSortData?: boolean;
84
+ /**
85
+ * If true, the value will not be shown in the legend.
86
+ *
87
+ * @default false
88
+ */
89
+ hideLegendValues?: boolean;
65
90
  }
66
91
  /**
67
92
  * Create a DonutChart with legends based on our current Chart component
@@ -69,5 +94,5 @@ export interface DonutChartProps<TProps extends object> extends CommonProps {
69
94
  * @param {DonutChartProps} props - The props for the Chart component
70
95
  * @returns {ReactElement} Chart component
71
96
  */
72
- export declare const DonutChart: <TProps extends object>({ data, size, loading, onClick, className, dataTestId, legendPosition, maxDataPoints, showOthers, }: DonutChartProps<TProps>) => ReactElement;
97
+ export declare const DonutChart: <TProps extends object>({ data, size, loading, onClick, className, dataTestId, legendPosition, maxDataPoints, showOthers, unit, overrideTotal, autoSortData, hideLegendValues, }: DonutChartProps<TProps>) => ReactElement;
73
98
  export {};
@@ -9,6 +9,8 @@ export interface LegendItemProps {
9
9
  color: string;
10
10
  className?: string;
11
11
  dataTestId?: string;
12
+ unit?: "%";
13
+ hideValue?: boolean;
12
14
  }
13
15
  /**
14
16
  * The LegendItem component is used to show form legends.
@@ -16,4 +18,4 @@ export interface LegendItemProps {
16
18
  * @param {LegendItem} props - The props for the LegendItem component
17
19
  * @returns {ReactElement} LegendItem component
18
20
  */
19
- export declare const LegendItem: ({ className, count, label, disabled, selected, onClick, color, dataTestId, onMouseEnter, onMouseLeave, }: LegendItemProps) => import("react/jsx-runtime").JSX.Element;
21
+ export declare const LegendItem: ({ className, count, label, disabled, selected, onClick, color, dataTestId, onMouseEnter, onMouseLeave, unit, hideValue, }: LegendItemProps) => import("react/jsx-runtime").JSX.Element;
@@ -11,7 +11,8 @@ type LimitDataSetReturn<TProps extends object> = {
11
11
  *
12
12
  * @param data - The data set to limit.
13
13
  * @param limit - The limit to apply to the data set.
14
+ * @param autoSortData - If true, the data will be sorted by value in descending order.
14
15
  * @returns {object} The limited data set with and without the "others" category.
15
16
  */
16
- export declare const useLimitDataSet: <TProps extends object>(data: Array<ChartData<TProps>>, limit: number) => LimitDataSetReturn<TProps>;
17
+ export declare const useLimitDataSet: <TProps extends object>(data: Array<ChartData<TProps>>, limit: number, autoSortData?: boolean) => LimitDataSetReturn<TProps>;
17
18
  export {};