@trackunit/react-widgets 2.13.3 → 2.13.4

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
@@ -6,6 +6,7 @@ var reactComponents = require('@trackunit/react-components');
6
6
  var irisAppRuntimeCore = require('@trackunit/iris-app-runtime-core');
7
7
  var cssClassVarianceUtilities = require('@trackunit/css-class-variance-utilities');
8
8
  var react = require('react');
9
+ var tailwindMerge = require('tailwind-merge');
9
10
 
10
11
  var defaultTranslations = {
11
12
  "widget.edit.cancel": "Cancel",
@@ -318,7 +319,7 @@ const cvaWidgetKPI = cssClassVarianceUtilities.cvaMerge(["w-full", "h-full", "fl
318
319
  variant: {
319
320
  small: ["px-3"],
320
321
  condensed: ["px-2", "py-0"],
321
- default: [""],
322
+ default: ["p-4"],
322
323
  },
323
324
  },
324
325
  defaultVariants: {
@@ -343,6 +344,28 @@ const cvaWidgetKPIvalueText = cssClassVarianceUtilities.cvaMerge([
343
344
  "font-medium",
344
345
  "!leading-none",
345
346
  ]);
347
+ const cvaWidgetKPIContentLayout = cssClassVarianceUtilities.cvaMerge(["flex", "flex-1"], {
348
+ variants: {
349
+ interactive: {
350
+ true: ["flex-row"],
351
+ false: ["flex-col"],
352
+ },
353
+ },
354
+ defaultVariants: {
355
+ interactive: false,
356
+ },
357
+ });
358
+ const cvaWidgetKPIBodyLayout = cssClassVarianceUtilities.cvaMerge(["flex", "flex-col", "flex-1"], {
359
+ variants: {
360
+ interactive: {
361
+ true: ["min-w-0"],
362
+ false: [],
363
+ },
364
+ },
365
+ defaultVariants: {
366
+ interactive: false,
367
+ },
368
+ });
346
369
 
347
370
  /**
348
371
  * The KPI Widget is a compact and flexible component designed to surface key metrics in a clear, impactful way. It provides at-a-glance insights through bold values, concise labels, and optional contextual elements such as trend indicators or time zones. Its goal is to drive user attention toward meaningful data that prompts action.
@@ -350,15 +373,24 @@ const cvaWidgetKPIvalueText = cssClassVarianceUtilities.cvaMerge([
350
373
  * @param {WidgetKPIProps} props - The props for the WidgetKPI component
351
374
  * @returns {ReactElement} WidgetKPI component
352
375
  */
353
- const WidgetKPI = ({ title, value, loading = false, unit, className, "data-testid": dataTestId, tooltipLabel, trends, iconName = undefined, iconColor = "info", notice, ref, ...rest }) => {
354
- return (jsxRuntime.jsx("div", { className: cvaWidgetKPI({ className }), "data-testid": dataTestId ?? undefined, ref: ref, ...rest, children: loading ? (jsxRuntime.jsx("div", { className: "flex flex-col gap-2 pt-6", "data-testid": dataTestId ? `${dataTestId}-loading` : "WidgetKPI-loading", children: trends ? (jsxRuntime.jsx(reactComponents.SkeletonLines, { lines: [
376
+ const WidgetKPI = ({ title, value, loading = false, unit, className, "data-testid": dataTestId, tooltipLabel, trends, iconName = undefined, iconColor = "info", notice, onClick, ref, ...rest }) => {
377
+ const baseClass = cvaWidgetKPI({ className });
378
+ const rootClass = onClick ? tailwindMerge.twMerge(baseClass, reactComponents.cvaInteractableItem({ cursor: "pointer" })) : baseClass;
379
+ const isInteractive = !!onClick;
380
+ const handleKeyDown = react.useCallback((event) => {
381
+ if (event.key === "Enter" || event.key === " ") {
382
+ event.preventDefault();
383
+ event.currentTarget.click();
384
+ }
385
+ }, []);
386
+ return (jsxRuntime.jsx("div", { className: rootClass, "data-testid": dataTestId ?? undefined, onClick: onClick, onKeyDown: isInteractive ? handleKeyDown : undefined, ref: ref, role: isInteractive ? "button" : undefined, tabIndex: isInteractive ? 0 : undefined, ...rest, children: loading ? (jsxRuntime.jsx("div", { className: "flex flex-col gap-2 pt-6", "data-testid": dataTestId ? `${dataTestId}-loading` : "WidgetKPI-loading", children: trends ? (jsxRuntime.jsx(reactComponents.SkeletonLines, { lines: [
355
387
  { textSize: "text-sm", width: 100 },
356
388
  { textSize: "text-lg", width: 40 },
357
389
  { textSize: "text-sm", width: 60 },
358
390
  ], variant: "custom" })) : (jsxRuntime.jsx(reactComponents.SkeletonLines, { lines: [
359
391
  { textSize: "text-sm", width: 100 },
360
392
  { textSize: "text-lg", width: 40 },
361
- ], variant: "custom" })) })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col justify-center gap-0.5", children: [jsxRuntime.jsx("div", { className: cvaWidgetKPIHeader(), children: jsxRuntime.jsxs("div", { className: "flex items-start gap-1", children: [iconName ? (jsxRuntime.jsx(reactComponents.Icon, { className: "flex-shrink-0 py-0.5", color: iconColor, name: iconName, size: "small" })) : null, jsxRuntime.jsx(reactComponents.Tooltip, { "data-testid": dataTestId ? `${dataTestId}-tooltip` : undefined, disabled: !tooltipLabel, label: tooltipLabel, placement: "bottom-start", children: jsxRuntime.jsx(reactComponents.Text, { className: cvaWidgetKPITitleText(), "data-testid": dataTestId ? `${dataTestId}-title` : undefined, children: title }) })] }) }), jsxRuntime.jsx("div", { className: "flex items-baseline justify-start", children: jsxRuntime.jsx(reactComponents.Text, { className: cvaWidgetKPIvalueText(), "data-testid": dataTestId ? `${dataTestId}-value` : undefined, type: "div", children: jsxRuntime.jsxs("div", { className: cvaWidgetKPIvalueText(), children: [value, " ", unit] }) }) })] }), jsxRuntime.jsx("div", { className: "flex min-h-[1lh] items-center gap-1 overflow-hidden", children: trends ? (jsxRuntime.jsx(reactComponents.TrendIndicators, { "data-testid": dataTestId ? `${dataTestId}-trend-indicators` : undefined, trends: trends })) : notice !== undefined ? (jsxRuntime.jsx(reactComponents.Notice, { "data-testid": dataTestId ? `${dataTestId}-notice` : undefined, size: "small", ...notice, className: "block", iconSize: "small" })) : null })] })) }));
393
+ ], variant: "custom" })) })) : (jsxRuntime.jsxs("div", { className: cvaWidgetKPIContentLayout({ interactive: isInteractive }), children: [jsxRuntime.jsxs("div", { className: cvaWidgetKPIBodyLayout({ interactive: isInteractive }), children: [jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col justify-center gap-0.5", children: [jsxRuntime.jsx("div", { className: cvaWidgetKPIHeader(), children: jsxRuntime.jsxs("div", { className: "flex min-w-0 items-start gap-1", children: [iconName ? (jsxRuntime.jsx(reactComponents.Icon, { className: "flex-shrink-0 py-0.5", color: iconColor, name: iconName, size: "small" })) : null, jsxRuntime.jsx(reactComponents.Tooltip, { "data-testid": dataTestId ? `${dataTestId}-tooltip` : undefined, disabled: !tooltipLabel, label: tooltipLabel, placement: "bottom-start", children: jsxRuntime.jsx(reactComponents.Text, { className: cvaWidgetKPITitleText(), "data-testid": dataTestId ? `${dataTestId}-title` : undefined, children: title }) })] }) }), jsxRuntime.jsx("div", { className: "flex items-baseline justify-start", children: jsxRuntime.jsx(reactComponents.Text, { className: cvaWidgetKPIvalueText(), "data-testid": dataTestId ? `${dataTestId}-value` : undefined, type: "div", children: jsxRuntime.jsxs("div", { className: cvaWidgetKPIvalueText(), children: [value, " ", unit] }) }) })] }), jsxRuntime.jsx("div", { className: "flex min-h-[1lh] items-center gap-1 overflow-hidden", children: trends ? (jsxRuntime.jsx(reactComponents.TrendIndicators, { "data-testid": dataTestId ? `${dataTestId}-trend-indicators` : undefined, trends: trends })) : notice !== undefined ? (jsxRuntime.jsx(reactComponents.Notice, { "data-testid": dataTestId ? `${dataTestId}-notice` : undefined, size: "small", ...notice, iconSize: "small" })) : null })] }), onClick ? (jsxRuntime.jsx(reactComponents.Icon, { ariaHidden: true, className: "flex-shrink-0 self-center", color: "neutral", "data-testid": dataTestId ? `${dataTestId}-chevron` : undefined, name: "ChevronRight", size: "small" })) : null] })) }));
362
394
  };
363
395
 
364
396
  /*
package/index.esm.js CHANGED
@@ -1,9 +1,10 @@
1
- import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
2
  import { registerTranslations, useNamespaceTranslation } from '@trackunit/i18n-library-translation';
3
- import { EmptyState, Card, CardHeader, CardBody, CardFooter, Button, SkeletonLines, Icon, Tooltip, Text, TrendIndicators, Notice } from '@trackunit/react-components';
3
+ import { EmptyState, Card, CardHeader, CardBody, CardFooter, Button, cvaInteractableItem, SkeletonLines, Icon, Tooltip, Text, TrendIndicators, Notice } from '@trackunit/react-components';
4
4
  import { WidgetConfigRuntime } from '@trackunit/iris-app-runtime-core';
5
5
  import { cvaMerge } from '@trackunit/css-class-variance-utilities';
6
- import { useState } from 'react';
6
+ import { useState, useCallback } from 'react';
7
+ import { twMerge } from 'tailwind-merge';
7
8
 
8
9
  var defaultTranslations = {
9
10
  "widget.edit.cancel": "Cancel",
@@ -316,7 +317,7 @@ const cvaWidgetKPI = cvaMerge(["w-full", "h-full", "flex", "flex-col"], {
316
317
  variant: {
317
318
  small: ["px-3"],
318
319
  condensed: ["px-2", "py-0"],
319
- default: [""],
320
+ default: ["p-4"],
320
321
  },
321
322
  },
322
323
  defaultVariants: {
@@ -341,6 +342,28 @@ const cvaWidgetKPIvalueText = cvaMerge([
341
342
  "font-medium",
342
343
  "!leading-none",
343
344
  ]);
345
+ const cvaWidgetKPIContentLayout = cvaMerge(["flex", "flex-1"], {
346
+ variants: {
347
+ interactive: {
348
+ true: ["flex-row"],
349
+ false: ["flex-col"],
350
+ },
351
+ },
352
+ defaultVariants: {
353
+ interactive: false,
354
+ },
355
+ });
356
+ const cvaWidgetKPIBodyLayout = cvaMerge(["flex", "flex-col", "flex-1"], {
357
+ variants: {
358
+ interactive: {
359
+ true: ["min-w-0"],
360
+ false: [],
361
+ },
362
+ },
363
+ defaultVariants: {
364
+ interactive: false,
365
+ },
366
+ });
344
367
 
345
368
  /**
346
369
  * The KPI Widget is a compact and flexible component designed to surface key metrics in a clear, impactful way. It provides at-a-glance insights through bold values, concise labels, and optional contextual elements such as trend indicators or time zones. Its goal is to drive user attention toward meaningful data that prompts action.
@@ -348,15 +371,24 @@ const cvaWidgetKPIvalueText = cvaMerge([
348
371
  * @param {WidgetKPIProps} props - The props for the WidgetKPI component
349
372
  * @returns {ReactElement} WidgetKPI component
350
373
  */
351
- const WidgetKPI = ({ title, value, loading = false, unit, className, "data-testid": dataTestId, tooltipLabel, trends, iconName = undefined, iconColor = "info", notice, ref, ...rest }) => {
352
- return (jsx("div", { className: cvaWidgetKPI({ className }), "data-testid": dataTestId ?? undefined, ref: ref, ...rest, children: loading ? (jsx("div", { className: "flex flex-col gap-2 pt-6", "data-testid": dataTestId ? `${dataTestId}-loading` : "WidgetKPI-loading", children: trends ? (jsx(SkeletonLines, { lines: [
374
+ const WidgetKPI = ({ title, value, loading = false, unit, className, "data-testid": dataTestId, tooltipLabel, trends, iconName = undefined, iconColor = "info", notice, onClick, ref, ...rest }) => {
375
+ const baseClass = cvaWidgetKPI({ className });
376
+ const rootClass = onClick ? twMerge(baseClass, cvaInteractableItem({ cursor: "pointer" })) : baseClass;
377
+ const isInteractive = !!onClick;
378
+ const handleKeyDown = useCallback((event) => {
379
+ if (event.key === "Enter" || event.key === " ") {
380
+ event.preventDefault();
381
+ event.currentTarget.click();
382
+ }
383
+ }, []);
384
+ return (jsx("div", { className: rootClass, "data-testid": dataTestId ?? undefined, onClick: onClick, onKeyDown: isInteractive ? handleKeyDown : undefined, ref: ref, role: isInteractive ? "button" : undefined, tabIndex: isInteractive ? 0 : undefined, ...rest, children: loading ? (jsx("div", { className: "flex flex-col gap-2 pt-6", "data-testid": dataTestId ? `${dataTestId}-loading` : "WidgetKPI-loading", children: trends ? (jsx(SkeletonLines, { lines: [
353
385
  { textSize: "text-sm", width: 100 },
354
386
  { textSize: "text-lg", width: 40 },
355
387
  { textSize: "text-sm", width: 60 },
356
388
  ], variant: "custom" })) : (jsx(SkeletonLines, { lines: [
357
389
  { textSize: "text-sm", width: 100 },
358
390
  { textSize: "text-lg", width: 40 },
359
- ], variant: "custom" })) })) : (jsxs(Fragment, { children: [jsxs("div", { className: "flex flex-1 flex-col justify-center gap-0.5", children: [jsx("div", { className: cvaWidgetKPIHeader(), children: jsxs("div", { className: "flex items-start gap-1", children: [iconName ? (jsx(Icon, { className: "flex-shrink-0 py-0.5", color: iconColor, name: iconName, size: "small" })) : null, jsx(Tooltip, { "data-testid": dataTestId ? `${dataTestId}-tooltip` : undefined, disabled: !tooltipLabel, label: tooltipLabel, placement: "bottom-start", children: jsx(Text, { className: cvaWidgetKPITitleText(), "data-testid": dataTestId ? `${dataTestId}-title` : undefined, children: title }) })] }) }), jsx("div", { className: "flex items-baseline justify-start", children: jsx(Text, { className: cvaWidgetKPIvalueText(), "data-testid": dataTestId ? `${dataTestId}-value` : undefined, type: "div", children: jsxs("div", { className: cvaWidgetKPIvalueText(), children: [value, " ", unit] }) }) })] }), jsx("div", { className: "flex min-h-[1lh] items-center gap-1 overflow-hidden", children: trends ? (jsx(TrendIndicators, { "data-testid": dataTestId ? `${dataTestId}-trend-indicators` : undefined, trends: trends })) : notice !== undefined ? (jsx(Notice, { "data-testid": dataTestId ? `${dataTestId}-notice` : undefined, size: "small", ...notice, className: "block", iconSize: "small" })) : null })] })) }));
391
+ ], variant: "custom" })) })) : (jsxs("div", { className: cvaWidgetKPIContentLayout({ interactive: isInteractive }), children: [jsxs("div", { className: cvaWidgetKPIBodyLayout({ interactive: isInteractive }), children: [jsxs("div", { className: "flex flex-1 flex-col justify-center gap-0.5", children: [jsx("div", { className: cvaWidgetKPIHeader(), children: jsxs("div", { className: "flex min-w-0 items-start gap-1", children: [iconName ? (jsx(Icon, { className: "flex-shrink-0 py-0.5", color: iconColor, name: iconName, size: "small" })) : null, jsx(Tooltip, { "data-testid": dataTestId ? `${dataTestId}-tooltip` : undefined, disabled: !tooltipLabel, label: tooltipLabel, placement: "bottom-start", children: jsx(Text, { className: cvaWidgetKPITitleText(), "data-testid": dataTestId ? `${dataTestId}-title` : undefined, children: title }) })] }) }), jsx("div", { className: "flex items-baseline justify-start", children: jsx(Text, { className: cvaWidgetKPIvalueText(), "data-testid": dataTestId ? `${dataTestId}-value` : undefined, type: "div", children: jsxs("div", { className: cvaWidgetKPIvalueText(), children: [value, " ", unit] }) }) })] }), jsx("div", { className: "flex min-h-[1lh] items-center gap-1 overflow-hidden", children: trends ? (jsx(TrendIndicators, { "data-testid": dataTestId ? `${dataTestId}-trend-indicators` : undefined, trends: trends })) : notice !== undefined ? (jsx(Notice, { "data-testid": dataTestId ? `${dataTestId}-notice` : undefined, size: "small", ...notice, iconSize: "small" })) : null })] }), onClick ? (jsx(Icon, { ariaHidden: true, className: "flex-shrink-0 self-center", color: "neutral", "data-testid": dataTestId ? `${dataTestId}-chevron` : undefined, name: "ChevronRight", size: "small" })) : null] })) }));
360
392
  };
361
393
 
362
394
  /*
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trackunit/react-widgets",
3
- "version": "2.13.3",
3
+ "version": "2.13.4",
4
4
  "repository": "https://github.com/Trackunit/manager",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "engines": {
@@ -12,7 +12,8 @@
12
12
  "@trackunit/css-class-variance-utilities": "1.11.77",
13
13
  "@trackunit/ui-design-tokens": "1.11.74",
14
14
  "@trackunit/ui-icons": "1.11.73",
15
- "@trackunit/i18n-library-translation": "1.15.3"
15
+ "@trackunit/i18n-library-translation": "1.15.3",
16
+ "tailwind-merge": "^2.0.0"
16
17
  },
17
18
  "peerDependencies": {
18
19
  "react": "^19.0.0"
@@ -1,7 +1,7 @@
1
1
  import { CommonProps, NoticeProps, Refable, TrendIndicatorProps } from "@trackunit/react-components";
2
2
  import { ActivityColors, CriticalityColors, IntentColors } from "@trackunit/ui-design-tokens";
3
3
  import { IconName } from "@trackunit/ui-icons";
4
- import { ReactElement } from "react";
4
+ import { type MouseEventHandler, type ReactElement } from "react";
5
5
  export interface WidgetKPIProps extends CommonProps, Refable<HTMLDivElement> {
6
6
  /**
7
7
  * The title of the WidgetKPI Card
@@ -41,6 +41,11 @@ export interface WidgetKPIProps extends CommonProps, Refable<HTMLDivElement> {
41
41
  * Text will be truncated to fit on a single line
42
42
  */
43
43
  notice?: Pick<NoticeProps, "label" | "iconName" | "iconColor">;
44
+ /**
45
+ * Click handler for the WidgetKPI Card.
46
+ * When provided, the KPI becomes interactive with a hover effect and a visible chevron.
47
+ */
48
+ onClick?: MouseEventHandler<HTMLDivElement>;
44
49
  }
45
50
  /**
46
51
  * The KPI Widget is a compact and flexible component designed to surface key metrics in a clear, impactful way. It provides at-a-glance insights through bold values, concise labels, and optional contextual elements such as trend indicators or time zones. Its goal is to drive user attention toward meaningful data that prompts action.
@@ -48,4 +53,4 @@ export interface WidgetKPIProps extends CommonProps, Refable<HTMLDivElement> {
48
53
  * @param {WidgetKPIProps} props - The props for the WidgetKPI component
49
54
  * @returns {ReactElement} WidgetKPI component
50
55
  */
51
- export declare const WidgetKPI: ({ title, value, loading, unit, className, "data-testid": dataTestId, tooltipLabel, trends, iconName, iconColor, notice, ref, ...rest }: WidgetKPIProps) => ReactElement;
56
+ export declare const WidgetKPI: ({ title, value, loading, unit, className, "data-testid": dataTestId, tooltipLabel, trends, iconName, iconColor, notice, onClick, ref, ...rest }: WidgetKPIProps) => ReactElement;
@@ -4,3 +4,9 @@ export declare const cvaWidgetKPI: (props?: ({
4
4
  export declare const cvaWidgetKPIHeader: (props?: import("class-variance-authority/dist/types").ClassProp | undefined) => string;
5
5
  export declare const cvaWidgetKPITitleText: (props?: import("class-variance-authority/dist/types").ClassProp | undefined) => string;
6
6
  export declare const cvaWidgetKPIvalueText: (props?: import("class-variance-authority/dist/types").ClassProp | undefined) => string;
7
+ export declare const cvaWidgetKPIContentLayout: (props?: ({
8
+ interactive?: boolean | null | undefined;
9
+ } & import("class-variance-authority/dist/types").ClassProp) | undefined) => string;
10
+ export declare const cvaWidgetKPIBodyLayout: (props?: ({
11
+ interactive?: boolean | null | undefined;
12
+ } & import("class-variance-authority/dist/types").ClassProp) | undefined) => string;