@trackunit/react-chart-components 2.6.38 → 2.6.40

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
@@ -249,7 +249,14 @@ const cvaLegendItem = cssClassVarianceUtilities.cvaMerge([
249
249
  },
250
250
  isClickable: {
251
251
  false: "cursor-default",
252
- true: "cursor-pointer",
252
+ true: [
253
+ "cursor-pointer",
254
+ "rounded-sm",
255
+ "focus-visible:outline-none",
256
+ "focus-visible:ring-2",
257
+ "focus-visible:ring-primary-500",
258
+ "focus-visible:ring-offset-2",
259
+ ],
253
260
  },
254
261
  },
255
262
  defaultVariants: {
@@ -258,27 +265,52 @@ const cvaLegendItem = cssClassVarianceUtilities.cvaMerge([
258
265
  isClickable: false,
259
266
  },
260
267
  });
261
- const cvaLegendItemIndicator = cssClassVarianceUtilities.cvaMerge(["w-3", "mr-1", "h-3", "rounded-[50%]", "flex-none"], {
262
- variants: {
263
- selected: {
264
- false: "",
265
- true: "",
266
- },
267
- },
268
- defaultVariants: {
269
- selected: false,
270
- },
271
- });
268
+ const cvaLegendItemIndicator = cssClassVarianceUtilities.cvaMerge(["w-2.5", "mr-1", "h-2.5", "rounded-[50%]", "flex-none"]);
269
+ const cvaLegendItemLineSwatch = cssClassVarianceUtilities.cvaMerge(["relative", "mr-1", "h-3", "w-5", "flex-none", "overflow-hidden"]);
270
+ const cvaLegendItemLineSvg = cssClassVarianceUtilities.cvaMerge(["h-full", "w-full"]);
272
271
 
272
+ const LINE_SWATCH_WIDTH = 20;
273
+ const LINE_SWATCH_HEIGHT = 12;
274
+ const LINE_SWATCH_MID_Y = 6;
275
+ const LINE_STROKE_WIDTH = 2;
276
+ const LINE_DASH_ARRAY = "3 2";
277
+ const EMPTY_CIRCLE_RADIUS = 3;
278
+ const LegendItemSwatch = ({ color, dataTestId, indicator, symbol }) => {
279
+ switch (indicator) {
280
+ case "fill":
281
+ return (jsxRuntime.jsx("div", { className: cvaLegendItemIndicator(), "data-testid": dataTestId ? `${dataTestId}-indicator` : null, style: { backgroundColor: color } }));
282
+ case "solid line":
283
+ case "dashed line":
284
+ return (jsxRuntime.jsx("div", { className: cvaLegendItemLineSwatch(), "data-testid": dataTestId ? `${dataTestId}-indicator` : null, children: jsxRuntime.jsxs("svg", { "aria-hidden": "true", className: cvaLegendItemLineSvg(), viewBox: `0 0 ${LINE_SWATCH_WIDTH} ${LINE_SWATCH_HEIGHT}`, children: [jsxRuntime.jsx("line", { "data-testid": dataTestId ? `${dataTestId}-indicator-line` : null, stroke: color, strokeDasharray: indicator === "dashed line" ? LINE_DASH_ARRAY : undefined, strokeWidth: LINE_STROKE_WIDTH, x1: 0, x2: LINE_SWATCH_WIDTH, y1: LINE_SWATCH_MID_Y, y2: LINE_SWATCH_MID_Y }), indicator === "solid line" && symbol === "emptyCircle" ? (jsxRuntime.jsx("circle", { cx: LINE_SWATCH_WIDTH / 2, cy: LINE_SWATCH_MID_Y, "data-testid": dataTestId ? `${dataTestId}-indicator-symbol` : null, fill: "white", r: EMPTY_CIRCLE_RADIUS, stroke: color, strokeWidth: LINE_STROKE_WIDTH })) : null] }) }));
285
+ default:
286
+ return sharedUtils.exhaustiveCheck(indicator);
287
+ }
288
+ };
273
289
  /**
274
- * The LegendItem component is used to show form legends.
290
+ * The LegendItem component is the standard chart legend row: a swatch, a label, and an optional count.
291
+ *
292
+ * ### When to use
293
+ * - Fill for field/sector bars and donut slices
294
+ * - Solid line with a hollow circle when individual data points are part of the reading
295
+ * - Dashed line with no symbol for averages, targets, and comparisons
275
296
  *
276
297
  * @param {LegendItem} props - The props for the LegendItem component
277
298
  * @returns {ReactElement} LegendItem component
278
299
  */
279
- const LegendItem = ({ className, style, count, label, disabled = false, selected = false, onClick, color, "data-testid": dataTestId, onMouseEnter, onMouseLeave, unit = undefined, hideValue = false, }) => {
300
+ const LegendItem = ({ className, style, count, label, disabled = false, selected = false, onClick, color, "data-testid": dataTestId, onMouseEnter, onMouseLeave, unit = undefined, hideValue = false, ...indicatorProps }) => {
280
301
  const handleOnClick = onClick && !disabled ? e => onClick(e) : undefined;
281
- return (jsxRuntime.jsxs("div", { className: cvaLegendItem({ disabled, selected, isClickable: !disabled && onClick !== undefined, className }), "data-testid": dataTestId, onClick: handleOnClick, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, style: style, 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 ?? ""] }))] }));
302
+ const handleOnKeyDown = handleOnClick
303
+ ? event => {
304
+ if (event.key === "Enter" || event.key === " ") {
305
+ event.preventDefault();
306
+ event.currentTarget.click();
307
+ }
308
+ }
309
+ : undefined;
310
+ const indicator = indicatorProps.indicator ?? "fill";
311
+ const symbol = indicatorProps.indicator === "solid line" ? (indicatorProps.symbol ?? "emptyCircle") : "none";
312
+ const isSwitch = handleOnClick !== undefined;
313
+ return (jsxRuntime.jsxs("div", { "aria-checked": isSwitch ? selected : undefined, className: cvaLegendItem({ disabled, selected, isClickable: isSwitch, className }), "data-testid": dataTestId, onClick: handleOnClick, onKeyDown: handleOnKeyDown, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, role: isSwitch ? "switch" : undefined, style: style, tabIndex: isSwitch ? 0 : undefined, children: [jsxRuntime.jsx(LegendItemSwatch, { color: color, dataTestId: dataTestId, indicator: indicator, symbol: symbol }), jsxRuntime.jsxs("p", { className: "truncate text-xs", children: [label, "\u00A0"] }), hideValue ? null : jsxRuntime.jsx("p", { className: "text-xs", children: `(${count ?? 0}${unit ?? ""})` })] }));
282
314
  };
283
315
 
284
316
  /**
package/index.esm.js CHANGED
@@ -5,7 +5,7 @@ import { useLocale, useDateAndTime } from '@trackunit/react-date-and-time-hooks'
5
5
  import { DEFAULT_CHART_COLORS, CHART_STATUS_COLORS, fontFamily, DEFAULT_CHART_OTHER } from '@trackunit/ui-design-tokens';
6
6
  import { useRef, useCallback, useEffect, useMemo, useState } from 'react';
7
7
  import { Spinner, Text, useMergeRefs } from '@trackunit/react-components';
8
- import { objectKeys } from '@trackunit/shared-utils';
8
+ import { objectKeys, exhaustiveCheck } from '@trackunit/shared-utils';
9
9
  import * as echarts from 'echarts';
10
10
 
11
11
  function isECElementEvent(value) {
@@ -228,7 +228,14 @@ const cvaLegendItem = cvaMerge([
228
228
  },
229
229
  isClickable: {
230
230
  false: "cursor-default",
231
- true: "cursor-pointer",
231
+ true: [
232
+ "cursor-pointer",
233
+ "rounded-sm",
234
+ "focus-visible:outline-none",
235
+ "focus-visible:ring-2",
236
+ "focus-visible:ring-primary-500",
237
+ "focus-visible:ring-offset-2",
238
+ ],
232
239
  },
233
240
  },
234
241
  defaultVariants: {
@@ -237,27 +244,52 @@ const cvaLegendItem = cvaMerge([
237
244
  isClickable: false,
238
245
  },
239
246
  });
240
- const cvaLegendItemIndicator = cvaMerge(["w-3", "mr-1", "h-3", "rounded-[50%]", "flex-none"], {
241
- variants: {
242
- selected: {
243
- false: "",
244
- true: "",
245
- },
246
- },
247
- defaultVariants: {
248
- selected: false,
249
- },
250
- });
247
+ const cvaLegendItemIndicator = cvaMerge(["w-2.5", "mr-1", "h-2.5", "rounded-[50%]", "flex-none"]);
248
+ const cvaLegendItemLineSwatch = cvaMerge(["relative", "mr-1", "h-3", "w-5", "flex-none", "overflow-hidden"]);
249
+ const cvaLegendItemLineSvg = cvaMerge(["h-full", "w-full"]);
251
250
 
251
+ const LINE_SWATCH_WIDTH = 20;
252
+ const LINE_SWATCH_HEIGHT = 12;
253
+ const LINE_SWATCH_MID_Y = 6;
254
+ const LINE_STROKE_WIDTH = 2;
255
+ const LINE_DASH_ARRAY = "3 2";
256
+ const EMPTY_CIRCLE_RADIUS = 3;
257
+ const LegendItemSwatch = ({ color, dataTestId, indicator, symbol }) => {
258
+ switch (indicator) {
259
+ case "fill":
260
+ return (jsx("div", { className: cvaLegendItemIndicator(), "data-testid": dataTestId ? `${dataTestId}-indicator` : null, style: { backgroundColor: color } }));
261
+ case "solid line":
262
+ case "dashed line":
263
+ return (jsx("div", { className: cvaLegendItemLineSwatch(), "data-testid": dataTestId ? `${dataTestId}-indicator` : null, children: jsxs("svg", { "aria-hidden": "true", className: cvaLegendItemLineSvg(), viewBox: `0 0 ${LINE_SWATCH_WIDTH} ${LINE_SWATCH_HEIGHT}`, children: [jsx("line", { "data-testid": dataTestId ? `${dataTestId}-indicator-line` : null, stroke: color, strokeDasharray: indicator === "dashed line" ? LINE_DASH_ARRAY : undefined, strokeWidth: LINE_STROKE_WIDTH, x1: 0, x2: LINE_SWATCH_WIDTH, y1: LINE_SWATCH_MID_Y, y2: LINE_SWATCH_MID_Y }), indicator === "solid line" && symbol === "emptyCircle" ? (jsx("circle", { cx: LINE_SWATCH_WIDTH / 2, cy: LINE_SWATCH_MID_Y, "data-testid": dataTestId ? `${dataTestId}-indicator-symbol` : null, fill: "white", r: EMPTY_CIRCLE_RADIUS, stroke: color, strokeWidth: LINE_STROKE_WIDTH })) : null] }) }));
264
+ default:
265
+ return exhaustiveCheck(indicator);
266
+ }
267
+ };
252
268
  /**
253
- * The LegendItem component is used to show form legends.
269
+ * The LegendItem component is the standard chart legend row: a swatch, a label, and an optional count.
270
+ *
271
+ * ### When to use
272
+ * - Fill for field/sector bars and donut slices
273
+ * - Solid line with a hollow circle when individual data points are part of the reading
274
+ * - Dashed line with no symbol for averages, targets, and comparisons
254
275
  *
255
276
  * @param {LegendItem} props - The props for the LegendItem component
256
277
  * @returns {ReactElement} LegendItem component
257
278
  */
258
- const LegendItem = ({ className, style, count, label, disabled = false, selected = false, onClick, color, "data-testid": dataTestId, onMouseEnter, onMouseLeave, unit = undefined, hideValue = false, }) => {
279
+ const LegendItem = ({ className, style, count, label, disabled = false, selected = false, onClick, color, "data-testid": dataTestId, onMouseEnter, onMouseLeave, unit = undefined, hideValue = false, ...indicatorProps }) => {
259
280
  const handleOnClick = onClick && !disabled ? e => onClick(e) : undefined;
260
- return (jsxs("div", { className: cvaLegendItem({ disabled, selected, isClickable: !disabled && onClick !== undefined, className }), "data-testid": dataTestId, onClick: handleOnClick, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, style: style, 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 ?? ""] }))] }));
281
+ const handleOnKeyDown = handleOnClick
282
+ ? event => {
283
+ if (event.key === "Enter" || event.key === " ") {
284
+ event.preventDefault();
285
+ event.currentTarget.click();
286
+ }
287
+ }
288
+ : undefined;
289
+ const indicator = indicatorProps.indicator ?? "fill";
290
+ const symbol = indicatorProps.indicator === "solid line" ? (indicatorProps.symbol ?? "emptyCircle") : "none";
291
+ const isSwitch = handleOnClick !== undefined;
292
+ return (jsxs("div", { "aria-checked": isSwitch ? selected : undefined, className: cvaLegendItem({ disabled, selected, isClickable: isSwitch, className }), "data-testid": dataTestId, onClick: handleOnClick, onKeyDown: handleOnKeyDown, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, role: isSwitch ? "switch" : undefined, style: style, tabIndex: isSwitch ? 0 : undefined, children: [jsx(LegendItemSwatch, { color: color, dataTestId: dataTestId, indicator: indicator, symbol: symbol }), jsxs("p", { className: "truncate text-xs", children: [label, "\u00A0"] }), hideValue ? null : jsx("p", { className: "text-xs", children: `(${count ?? 0}${unit ?? ""})` })] }));
261
293
  };
262
294
 
263
295
  /**
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entry.js","sourceRoot":"","sources":["../../../../../libs/react/chart-components/migrations/entry.ts"],"names":[],"mappings":"","sourcesContent":["// Migration entry point for @nx/js:tsc build target.\n// Migrations are registered in ../migrations.json and resolved by NX at runtime.\nexport {};\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trackunit/react-chart-components",
3
- "version": "2.6.38",
3
+ "version": "2.6.40",
4
4
  "repository": "https://github.com/Trackunit/manager",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "migrations": "./migrations.json",
@@ -10,11 +10,11 @@
10
10
  "dependencies": {
11
11
  "echarts": "5.6.0",
12
12
  "@trackunit/date-and-time-utils": "1.14.26",
13
- "@trackunit/react-date-and-time-hooks": "2.6.38",
13
+ "@trackunit/react-date-and-time-hooks": "2.6.39",
14
14
  "@trackunit/ui-design-tokens": "1.15.15",
15
15
  "@trackunit/shared-utils": "1.16.28",
16
16
  "@trackunit/css-class-variance-utilities": "1.14.25",
17
- "@trackunit/react-components": "2.11.2"
17
+ "@trackunit/react-components": "2.11.3"
18
18
  },
19
19
  "peerDependencies": {
20
20
  "@apollo/client": "^3.13.8",
@@ -1,6 +1,17 @@
1
1
  import { Refable, type Styleable } from "@trackunit/react-components";
2
- import { MouseEventHandler } from "react";
3
- export interface LegendItemProps extends Refable<HTMLDivElement>, Styleable {
2
+ import { MouseEventHandler, ReactElement } from "react";
3
+ /**
4
+ * Shape of the colour indicator. `fill` is a solid circle for a block of colour. `solid line` is a
5
+ * horizontal rule with an optional hollow-circle marker. `dashed line` is a reference series and
6
+ * never has a marker.
7
+ */
8
+ export type LegendItemIndicator = "fill" | "solid line" | "dashed line";
9
+ /**
10
+ * Marker on a solid-line indicator. `emptyCircle` is the default hollow circle. Pass `none` only when
11
+ * the reader should follow the trend rather than each sample. Dashed lines cannot take a symbol.
12
+ */
13
+ export type LegendItemSymbol = "none" | "emptyCircle";
14
+ interface LegendItemBaseProps extends Refable<HTMLDivElement>, Styleable {
4
15
  label: string;
5
16
  count?: number;
6
17
  selected?: boolean;
@@ -14,10 +25,51 @@ export interface LegendItemProps extends Refable<HTMLDivElement>, Styleable {
14
25
  unit?: "%";
15
26
  hideValue?: boolean;
16
27
  }
28
+ interface LegendItemFillProps extends LegendItemBaseProps {
29
+ /**
30
+ * Filled colour block for field/sector bars and donut slices. This is the default indicator.
31
+ *
32
+ * @category primary
33
+ */
34
+ indicator?: "fill";
35
+ }
36
+ interface LegendItemSolidLineProps extends LegendItemBaseProps {
37
+ /**
38
+ * Solid line for a series whose individual data points are part of the reading, such as a
39
+ * cumulative series. Defaults to a hollow circle marker.
40
+ *
41
+ * @category primary
42
+ */
43
+ indicator: "solid line";
44
+ /**
45
+ * Marker on a solid line. Defaults to a hollow circle. Pass `none` only when the reader should
46
+ * follow the trend rather than each sample. Do not use a solid line with `symbol="none"` for
47
+ * averages, targets, or comparisons — those use `indicator="dashed line"`.
48
+ *
49
+ * @category primary
50
+ */
51
+ symbol?: LegendItemSymbol;
52
+ }
53
+ interface LegendItemDashedLineProps extends LegendItemBaseProps {
54
+ /**
55
+ * Dashed line for a reference or secondary series (average, target, comparison). Dashed lines
56
+ * never have a symbol.
57
+ *
58
+ * @category primary
59
+ */
60
+ indicator: "dashed line";
61
+ }
62
+ export type LegendItemProps = LegendItemFillProps | LegendItemSolidLineProps | LegendItemDashedLineProps;
17
63
  /**
18
- * The LegendItem component is used to show form legends.
64
+ * The LegendItem component is the standard chart legend row: a swatch, a label, and an optional count.
65
+ *
66
+ * ### When to use
67
+ * - Fill for field/sector bars and donut slices
68
+ * - Solid line with a hollow circle when individual data points are part of the reading
69
+ * - Dashed line with no symbol for averages, targets, and comparisons
19
70
  *
20
71
  * @param {LegendItem} props - The props for the LegendItem component
21
72
  * @returns {ReactElement} LegendItem component
22
73
  */
23
- export declare const LegendItem: ({ className, style, count, label, disabled, selected, onClick, color, "data-testid": dataTestId, onMouseEnter, onMouseLeave, unit, hideValue, }: LegendItemProps) => import("react/jsx-runtime").JSX.Element;
74
+ export declare const LegendItem: ({ className, style, count, label, disabled, selected, onClick, color, "data-testid": dataTestId, onMouseEnter, onMouseLeave, unit, hideValue, ...indicatorProps }: LegendItemProps) => ReactElement;
75
+ export {};
@@ -3,6 +3,6 @@ export declare const cvaLegendItem: (props?: ({
3
3
  disabled?: boolean | null | undefined;
4
4
  isClickable?: boolean | null | undefined;
5
5
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
6
- export declare const cvaLegendItemIndicator: (props?: ({
7
- selected?: boolean | null | undefined;
8
- } & import("class-variance-authority/types").ClassProp) | undefined) => string;
6
+ export declare const cvaLegendItemIndicator: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
7
+ export declare const cvaLegendItemLineSwatch: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
8
+ export declare const cvaLegendItemLineSvg: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;