@trackunit/react-components 1.14.5 → 1.14.11
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 +293 -166
- package/index.esm.js +291 -167
- package/package.json +5 -5
- package/src/components/KPI/KPI.d.ts +1 -5
- package/src/components/KPI/KPISkeleton.d.ts +14 -0
- package/src/components/KPICard/KPICard.d.ts +1 -1
- package/src/components/KPICard/KPICard.variants.d.ts +2 -0
- package/src/components/KPICard/KPICardSkeleton.d.ts +30 -0
- package/src/components/PageHeader/components/PageHeaderKpiMetrics.d.ts +1 -1
- package/src/components/Skeleton/Skeleton.helpers.d.ts +28 -0
- package/src/components/Skeleton/SkeletonBlock/SkeletonBlock.d.ts +48 -0
- package/src/components/Skeleton/SkeletonBlock/index.d.ts +2 -0
- package/src/components/Skeleton/SkeletonLabel/SkeletonLabel.d.ts +51 -0
- package/src/components/Skeleton/SkeletonLabel/index.d.ts +2 -0
- package/src/components/Skeleton/index.d.ts +4 -2
- package/src/components/SkeletonLines/SkeletonLines.d.ts +138 -21
- package/src/components/SkeletonLines/SkeletonLines.presets.d.ts +5 -0
- package/src/index.d.ts +2 -0
- package/src/components/Skeleton/Skeleton.d.ts +0 -115
package/index.esm.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment as Fragment$1 } from 'react/jsx-runtime';
|
|
2
2
|
import { objectKeys, uuidv4, parseTailwindArbitraryValue, objectEntries, nonNullable, objectValues, filterByMultiple } from '@trackunit/shared-utils';
|
|
3
3
|
import { useRef, useMemo, useEffect, useState, useLayoutEffect, useCallback, createElement, forwardRef, Fragment, memo, useId, useReducer, Children, isValidElement, cloneElement, createContext, useContext } from 'react';
|
|
4
|
-
import { intentPalette, generalPalette, criticalityPalette, activityPalette, utilizationPalette, sitesPalette, rentalStatusPalette, themeScreenSizeAsNumber, themeContainerSize,
|
|
4
|
+
import { intentPalette, generalPalette, criticalityPalette, activityPalette, utilizationPalette, sitesPalette, rentalStatusPalette, themeScreenSizeAsNumber, themeContainerSize, color } from '@trackunit/ui-design-tokens';
|
|
5
5
|
import { iconNames } from '@trackunit/ui-icons';
|
|
6
6
|
import IconSpriteMicro from '@trackunit/ui-icons/icons-sprite-micro.svg';
|
|
7
7
|
import IconSpriteMini from '@trackunit/ui-icons/icons-sprite-mini.svg';
|
|
@@ -1832,21 +1832,74 @@ const DetailsList = ({ details, className, hasLink = false }) => {
|
|
|
1832
1832
|
return (jsx("div", { className: cvaDetailsList({ className, hasLink }), children: details.map((value, index, array) => (jsxs(Fragment, { children: [jsx("span", { className: cvaDetailsListItem({ className }), children: value }), index < array.length - 1 && (jsx("div", { className: "mx-0.5 flex items-center", children: jsx(Icon, { className: "w-4 text-neutral-300", color: "neutral", name: "Slash", size: "small" }) }))] }, index))) }));
|
|
1833
1833
|
};
|
|
1834
1834
|
|
|
1835
|
+
const VALID_SIZE_KEYS = [
|
|
1836
|
+
"xs",
|
|
1837
|
+
"sm",
|
|
1838
|
+
"base",
|
|
1839
|
+
"lg",
|
|
1840
|
+
"xl",
|
|
1841
|
+
"2xl",
|
|
1842
|
+
"3xl",
|
|
1843
|
+
"4xl",
|
|
1844
|
+
"5xl",
|
|
1845
|
+
"6xl",
|
|
1846
|
+
"7xl",
|
|
1847
|
+
"8xl",
|
|
1848
|
+
"9xl",
|
|
1849
|
+
];
|
|
1835
1850
|
/**
|
|
1836
|
-
*
|
|
1851
|
+
* Extract the size key from a text size string (e.g., "text-base" → "base").
|
|
1837
1852
|
*
|
|
1838
|
-
* @param
|
|
1839
|
-
* @
|
|
1840
|
-
* @param {number} params.max - Maximum percentage value (e.g., 80 for 80%)
|
|
1841
|
-
* @returns {string} A percentage string (e.g., "65%")
|
|
1853
|
+
* @param value - The text size string to parse
|
|
1854
|
+
* @returns {fontSizeKeys | null} The extracted size key or null if invalid
|
|
1842
1855
|
*/
|
|
1843
|
-
const
|
|
1844
|
-
|
|
1845
|
-
|
|
1856
|
+
const extractSizeKey = (value) => {
|
|
1857
|
+
if (!value.startsWith("text-")) {
|
|
1858
|
+
return null;
|
|
1859
|
+
}
|
|
1860
|
+
const sizeKey = value.replace("text-", "");
|
|
1861
|
+
return VALID_SIZE_KEYS.find(key => key === sizeKey) ?? null;
|
|
1862
|
+
};
|
|
1863
|
+
/**
|
|
1864
|
+
* Calculate the height value based on the height prop and variant.
|
|
1865
|
+
*
|
|
1866
|
+
* @param height - The height value (number, CSS length, or text size key)
|
|
1867
|
+
* @param variant - The skeleton variant ("text" or "block")
|
|
1868
|
+
* @returns {string} The calculated CSS height value
|
|
1869
|
+
*/
|
|
1870
|
+
const getHeightValue = (height, variant) => {
|
|
1871
|
+
if (typeof height === "number") {
|
|
1872
|
+
return `${height}px`;
|
|
1873
|
+
}
|
|
1874
|
+
const sizeKey = extractSizeKey(height);
|
|
1875
|
+
if (sizeKey) {
|
|
1876
|
+
// Text variant: use font-size × 0.7 to approximate cap-height
|
|
1877
|
+
// Block variant: use full line-height (for when text size keys are passed to block variant)
|
|
1878
|
+
return `calc(var(--font-size-${sizeKey}) * 0.7)` ;
|
|
1879
|
+
}
|
|
1880
|
+
return height;
|
|
1881
|
+
};
|
|
1882
|
+
/**
|
|
1883
|
+
* Calculate the vertical margin value for text variant to align with text baseline.
|
|
1884
|
+
* Formula: (line-height - cap-height) / 2, where cap-height = font-size × 0.7
|
|
1885
|
+
*
|
|
1886
|
+
* @param height - The height value (number, CSS length, or text size key)
|
|
1887
|
+
* @returns {string} The calculated CSS margin value
|
|
1888
|
+
*/
|
|
1889
|
+
const getMarginValue = (height) => {
|
|
1890
|
+
if (typeof height === "string") {
|
|
1891
|
+
const sizeKey = extractSizeKey(height);
|
|
1892
|
+
if (sizeKey) {
|
|
1893
|
+
// margin = (line-height - cap-height) / 2
|
|
1894
|
+
// cap-height = font-size × 0.7
|
|
1895
|
+
// For large text sizes, this may be negative, which matches how actual text extends beyond its line-height box
|
|
1896
|
+
return `calc((var(--line-height-${sizeKey}) - var(--font-size-${sizeKey}) * 0.7) / 2)`;
|
|
1897
|
+
}
|
|
1898
|
+
}
|
|
1899
|
+
return "0";
|
|
1846
1900
|
};
|
|
1847
1901
|
|
|
1848
|
-
const
|
|
1849
|
-
const cvaSkeletonLine = cvaMerge([
|
|
1902
|
+
const cvaSkeleton = cvaMerge([
|
|
1850
1903
|
"relative",
|
|
1851
1904
|
"overflow-hidden",
|
|
1852
1905
|
"rounded-lg",
|
|
@@ -1873,32 +1926,28 @@ const cvaSkeletonLine = cvaMerge([
|
|
|
1873
1926
|
]);
|
|
1874
1927
|
|
|
1875
1928
|
/**
|
|
1876
|
-
* Display placeholder
|
|
1877
|
-
*
|
|
1929
|
+
* Display a single placeholder line for text content before data gets loaded to reduce load-time frustration.
|
|
1930
|
+
*
|
|
1931
|
+
* Reduces height and adds vertical margins to match the visual space text occupies within its line-height.
|
|
1932
|
+
* Uses text-size keys (text-xs, text-sm, text-base, etc.) for height to match actual text elements.
|
|
1933
|
+
*
|
|
1934
|
+
* For multiple text lines, use SkeletonLines component instead.
|
|
1935
|
+
* For shape-based elements (images, badges, buttons), use SkeletonBlock component instead.
|
|
1878
1936
|
*/
|
|
1879
|
-
const
|
|
1880
|
-
const
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1937
|
+
const SkeletonLabel = memo((props) => {
|
|
1938
|
+
const { width = "100%", textSize = "text-base", flexibleWidth = true, className, "data-testid": dataTestId, children, } = props;
|
|
1939
|
+
const widthValue = typeof width === "number" ? `${width}px` : width;
|
|
1940
|
+
const heightValue = getHeightValue(textSize);
|
|
1941
|
+
const marginValue = getMarginValue(textSize);
|
|
1942
|
+
return (jsx("div", { "aria-label": "Loading", className: cvaSkeleton({ className }), "data-testid": dataTestId, role: "status", style: {
|
|
1943
|
+
width: flexibleWidth ? "100%" : widthValue,
|
|
1944
|
+
maxWidth: flexibleWidth ? widthValue : undefined,
|
|
1945
|
+
height: heightValue,
|
|
1946
|
+
marginTop: marginValue,
|
|
1947
|
+
marginBottom: marginValue,
|
|
1948
|
+
}, children: children }));
|
|
1885
1949
|
});
|
|
1886
|
-
|
|
1887
|
-
const { dimension, index } = params;
|
|
1888
|
-
let value;
|
|
1889
|
-
if (Array.isArray(dimension)) {
|
|
1890
|
-
const dimValue = dimension[index] ?? dimension[0] ?? "100%";
|
|
1891
|
-
value = typeof dimValue === "number" ? `${dimValue}px` : dimValue;
|
|
1892
|
-
}
|
|
1893
|
-
else {
|
|
1894
|
-
value = typeof dimension === "number" ? `${dimension}px` : dimension;
|
|
1895
|
-
}
|
|
1896
|
-
// For width values, wrap in min() to ensure max container width
|
|
1897
|
-
if (params.direction === "width") {
|
|
1898
|
-
return `min(${value}, ${params.maxWidth})`;
|
|
1899
|
-
}
|
|
1900
|
-
return value;
|
|
1901
|
-
};
|
|
1950
|
+
SkeletonLabel.displayName = "SkeletonLabel";
|
|
1902
1951
|
|
|
1903
1952
|
const cvaContainerStyles = cvaMerge([
|
|
1904
1953
|
"flex",
|
|
@@ -1991,7 +2040,7 @@ const EmptyState = ({ description, altText, image = "SEARCH_DOCUMENT", customIma
|
|
|
1991
2040
|
return SearchDocumentSVG;
|
|
1992
2041
|
}
|
|
1993
2042
|
}, [image]);
|
|
1994
|
-
return (jsx("div", { className: cvaContainerStyles({ className }), "data-testid": dataTestId ?? "empty-state", children: loading ? (jsxs(
|
|
2043
|
+
return (jsx("div", { className: cvaContainerStyles({ className }), "data-testid": dataTestId ?? "empty-state", children: loading ? (jsxs("div", { className: "flex w-full flex-col items-center gap-4", children: [jsx(Spinner, { centering: "centered", "data-testid": "spinner" }), jsx(SkeletonLabel, { textSize: "text-base", width: "clamp(20%, 200px, 80%)" })] })) : (jsxs(Fragment$1, { children: [customImageSrc !== null && customImageSrc !== undefined ? (typeof customImageSrc === "string" ? (jsx("img", { alt: altText, className: cvaImgStyles(), height: 200, src: customImageSrc, width: 200 })) : (customImageSrc)) : (typeof ImageSource !== "undefined" && (jsx(ImageSource, { "data-testid": "empty-state-image", height: 200, width: 200 }, image))), description !== undefined && description !== "" ? (jsx(Text, { align: "center", size: "large", children: description })) : null, jsxs("div", { className: "mt-4 grid gap-3", children: [jsxs("div", { className: "flex gap-3", children: [secondaryAction ? (jsx(Button, { "data-testid": "empty-state-secondary-button", disabled: secondaryAction.disabled, onClick: secondaryAction.onClick, variant: "secondary", children: secondaryAction.to ? (jsx(Link, { params: secondaryAction.to.parameters, to: secondaryAction.to.pathname, children: secondaryAction.title })) : (secondaryAction.title) })) : null, primaryAction ? (jsx(Button, { "data-testid": "empty-state-primary-button", disabled: primaryAction.disabled, onClick: primaryAction.onClick, children: primaryAction.to ? (jsx(Link, { params: primaryAction.to.parameters, to: primaryAction.to.pathname, children: primaryAction.title })) : (primaryAction.title) })) : null] }), additionalHelpAction?.to ? (jsx(Button, { asChild: true, "data-testid": "empty-state-additional-button", disabled: additionalHelpAction.disabled, onClick: additionalHelpAction.onClick, suffix: jsx(Icon, { name: "ArrowTopRightOnSquare", size: "small" }), variant: "ghost", children: jsx(Link, { params: additionalHelpAction.to.parameters, target: additionalHelpAction.to.target, to: additionalHelpAction.to.pathname, children: additionalHelpAction.title }) })) : null] })] })) }));
|
|
1995
2044
|
};
|
|
1996
2045
|
|
|
1997
2046
|
const cvaEmptyValue = cvaMerge(["text-neutral-400"]);
|
|
@@ -3930,9 +3979,189 @@ const cvaKPITrendPercentage = cvaMerge([""], {
|
|
|
3930
3979
|
* @param {KPIProps} props - The props for the KPI component
|
|
3931
3980
|
* @returns {ReactElement} KPI component
|
|
3932
3981
|
*/
|
|
3933
|
-
const KPI = ({ title, value,
|
|
3982
|
+
const KPI = ({ title, value, unit, className, "data-testid": dataTestId, tooltipLabel, variant = "default", style, ...rest }) => {
|
|
3934
3983
|
const isSmallVariant = variant === "small";
|
|
3935
|
-
return (jsx(Tooltip, { className: "min-w-8 shrink-0", "data-testid": dataTestId ? `${dataTestId}-tooltip` : undefined, disabled: tooltipLabel === undefined || tooltipLabel === "", label: tooltipLabel, placement: "bottom", children: jsxs("div", { className: cvaKPI({ variant, className }), "data-testid": dataTestId, style: style, ...rest, children: [
|
|
3984
|
+
return (jsx(Tooltip, { className: "min-w-8 shrink-0", "data-testid": dataTestId ? `${dataTestId}-tooltip` : undefined, disabled: tooltipLabel === undefined || tooltipLabel === "", label: tooltipLabel, placement: "bottom", children: jsxs("div", { className: cvaKPI({ variant, className }), "data-testid": dataTestId, style: style, ...rest, children: [jsx(Text, { className: twMerge("truncate", "whitespace-nowrap"), "data-testid": dataTestId ? `${dataTestId}-title` : undefined, size: isSmallVariant ? "small" : "medium", subtle: true, weight: isSmallVariant ? "normal" : "bold", children: title }), jsx("div", { className: twMerge("truncate", "whitespace-nowrap"), children: jsxs(Text, { className: "truncate whitespace-nowrap text-lg font-medium", "data-testid": dataTestId ? `${dataTestId}-value` : undefined, size: isSmallVariant ? "small" : "large", type: "div", weight: isSmallVariant ? "bold" : "thick", children: [value, " ", unit] }) })] }) }));
|
|
3985
|
+
};
|
|
3986
|
+
|
|
3987
|
+
/**
|
|
3988
|
+
* Display a single placeholder block for shape-based elements before data gets loaded to reduce load-time frustration.
|
|
3989
|
+
*
|
|
3990
|
+
* Fills the full height for images, badges, buttons, avatars, and other shape-based elements.
|
|
3991
|
+
* Uses numbers or CSS length values for height.
|
|
3992
|
+
*
|
|
3993
|
+
* For text content, use SkeletonLabel component instead.
|
|
3994
|
+
* For multiple text lines, use SkeletonLines component instead.
|
|
3995
|
+
*/
|
|
3996
|
+
const SkeletonBlock = memo((props) => {
|
|
3997
|
+
const { width = "100%", height = 16, flexibleWidth = false, className, "data-testid": dataTestId, children } = props;
|
|
3998
|
+
const widthValue = typeof width === "number" ? `${width}px` : width;
|
|
3999
|
+
const heightValue = typeof height === "number" ? `${height}px` : height;
|
|
4000
|
+
return (jsx("div", { "aria-label": "Loading", className: cvaSkeleton({ className }), "data-testid": dataTestId, role: "status", style: {
|
|
4001
|
+
width: flexibleWidth ? "100%" : widthValue,
|
|
4002
|
+
maxWidth: flexibleWidth ? widthValue : undefined,
|
|
4003
|
+
height: heightValue,
|
|
4004
|
+
}, children: children }));
|
|
4005
|
+
});
|
|
4006
|
+
SkeletonBlock.displayName = "SkeletonBlock";
|
|
4007
|
+
|
|
4008
|
+
/**
|
|
4009
|
+
* Generates a random width percentage string for skeleton loading components.
|
|
4010
|
+
*
|
|
4011
|
+
* @param {object} params - The parameter object
|
|
4012
|
+
* @param {number} params.min - Minimum percentage value (e.g., 30 for 30%)
|
|
4013
|
+
* @param {number} params.max - Maximum percentage value (e.g., 80 for 80%)
|
|
4014
|
+
* @returns {string} A percentage string (e.g., "65%")
|
|
4015
|
+
*/
|
|
4016
|
+
const getResponsiveRandomWidthPercentage = ({ min, max }) => {
|
|
4017
|
+
const randomWidth = Math.floor(Math.random() * (max - min + 1)) + min;
|
|
4018
|
+
return `${randomWidth}%`;
|
|
4019
|
+
};
|
|
4020
|
+
|
|
4021
|
+
/**
|
|
4022
|
+
* Preset configurations for common skeleton patterns.
|
|
4023
|
+
*/
|
|
4024
|
+
const PRESET_CONFIGURATIONS = {
|
|
4025
|
+
"short-paragraph": [
|
|
4026
|
+
{ textSize: "text-base", width: "100%" },
|
|
4027
|
+
{ textSize: "text-base", width: "100%" },
|
|
4028
|
+
{ textSize: "text-base", width: "60%" },
|
|
4029
|
+
],
|
|
4030
|
+
paragraph: [
|
|
4031
|
+
{ textSize: "text-base", width: "100%" },
|
|
4032
|
+
{ textSize: "text-base", width: "100%" },
|
|
4033
|
+
{ textSize: "text-base", width: "95%" },
|
|
4034
|
+
{ textSize: "text-base", width: "90%" },
|
|
4035
|
+
{ textSize: "text-base", width: "65%" },
|
|
4036
|
+
],
|
|
4037
|
+
"long-paragraph": [
|
|
4038
|
+
{ textSize: "text-base", width: "100%" },
|
|
4039
|
+
{ textSize: "text-base", width: "100%" },
|
|
4040
|
+
{ textSize: "text-base", width: "98%" },
|
|
4041
|
+
{ textSize: "text-base", width: "95%" },
|
|
4042
|
+
{ textSize: "text-base", width: "92%" },
|
|
4043
|
+
{ textSize: "text-base", width: "88%" },
|
|
4044
|
+
{ textSize: "text-base", width: "85%" },
|
|
4045
|
+
{ textSize: "text-base", width: "60%" },
|
|
4046
|
+
],
|
|
4047
|
+
"title-paragraph": [
|
|
4048
|
+
{ textSize: "text-2xl", width: "65%" },
|
|
4049
|
+
{ textSize: "text-base", width: "100%" },
|
|
4050
|
+
{ textSize: "text-base", width: "95%" },
|
|
4051
|
+
{ textSize: "text-base", width: "70%" },
|
|
4052
|
+
],
|
|
4053
|
+
"heading-text": [
|
|
4054
|
+
{ textSize: "text-xl", width: "60%" },
|
|
4055
|
+
{ textSize: "text-base", width: "100%" },
|
|
4056
|
+
{ textSize: "text-base", width: "100%" },
|
|
4057
|
+
{ textSize: "text-base", width: "90%" },
|
|
4058
|
+
{ textSize: "text-base", width: "65%" },
|
|
4059
|
+
],
|
|
4060
|
+
article: [
|
|
4061
|
+
{ textSize: "text-3xl", width: "70%" },
|
|
4062
|
+
{ textSize: "text-sm", width: "40%" },
|
|
4063
|
+
{ textSize: "text-base", width: "100%" },
|
|
4064
|
+
{ textSize: "text-base", width: "100%" },
|
|
4065
|
+
{ textSize: "text-base", width: "95%" },
|
|
4066
|
+
{ textSize: "text-base", width: "90%" },
|
|
4067
|
+
{ textSize: "text-base", width: "65%" },
|
|
4068
|
+
],
|
|
4069
|
+
};
|
|
4070
|
+
|
|
4071
|
+
const cvaSkeletonContainer = cvaMerge(["flex", "flex-col"]);
|
|
4072
|
+
cvaMerge([
|
|
4073
|
+
"relative",
|
|
4074
|
+
"overflow-hidden",
|
|
4075
|
+
"rounded-lg",
|
|
4076
|
+
// Gradient background
|
|
4077
|
+
"bg-gradient-to-r",
|
|
4078
|
+
"from-gray-200/80",
|
|
4079
|
+
"via-gray-300/60",
|
|
4080
|
+
"to-gray-200/80",
|
|
4081
|
+
// Pulse animation
|
|
4082
|
+
"animate-pulse",
|
|
4083
|
+
// Shimmer overlay
|
|
4084
|
+
"before:absolute",
|
|
4085
|
+
"before:inset-0",
|
|
4086
|
+
"before:bg-gradient-to-r",
|
|
4087
|
+
"before:from-transparent",
|
|
4088
|
+
"before:via-white/50",
|
|
4089
|
+
"before:to-transparent",
|
|
4090
|
+
"before:opacity-0",
|
|
4091
|
+
"before:animate-pulse",
|
|
4092
|
+
// Smooth transitions for accessibility
|
|
4093
|
+
"transition-all",
|
|
4094
|
+
"duration-300",
|
|
4095
|
+
"ease-in-out",
|
|
4096
|
+
]);
|
|
4097
|
+
|
|
4098
|
+
/**
|
|
4099
|
+
* Display multiple placeholder lines before data gets loaded to reduce load-time frustration.
|
|
4100
|
+
*
|
|
4101
|
+
* Supports three modes:
|
|
4102
|
+
* - **Uniform mode** (default): Display identical lines using `count` prop
|
|
4103
|
+
* - **Custom mode**: Display customized lines with per-line configuration using `variant="custom"` and `lines` prop
|
|
4104
|
+
* - **Preset mode**: Display common patterns using `variant="preset"` and `preset` name
|
|
4105
|
+
*
|
|
4106
|
+
* Built on top of the [SkeletonLabel](?path=/docs/components-loading-states-skeletonlabel--docs) component for text-specific margins and sizing.
|
|
4107
|
+
*
|
|
4108
|
+
* @example
|
|
4109
|
+
* // Uniform lines (simple mode)
|
|
4110
|
+
* <SkeletonLines count={3} textSize="text-base" variant="uniform" />
|
|
4111
|
+
* @example
|
|
4112
|
+
* // Custom lines (advanced mode)
|
|
4113
|
+
* <SkeletonLines
|
|
4114
|
+
* variant="custom"
|
|
4115
|
+
* lines={[
|
|
4116
|
+
* { textSize: "text-lg", width: "100%" },
|
|
4117
|
+
* { textSize: "text-base", width: "95%" },
|
|
4118
|
+
* { textSize: "text-base", width: "70%" }
|
|
4119
|
+
* ]}
|
|
4120
|
+
* />
|
|
4121
|
+
* @example
|
|
4122
|
+
* // Preset lines (quick patterns)
|
|
4123
|
+
* <SkeletonLines variant="preset" preset="title-paragraph" />
|
|
4124
|
+
*/
|
|
4125
|
+
const SkeletonLines = memo((props) => {
|
|
4126
|
+
const { className, "data-testid": dataTestId } = props;
|
|
4127
|
+
// Generate line configs based on variant
|
|
4128
|
+
let lineConfigs;
|
|
4129
|
+
if (props.variant === "preset") {
|
|
4130
|
+
// TypeScript now knows props is PresetSkeletonLinesProps
|
|
4131
|
+
lineConfigs = PRESET_CONFIGURATIONS[props.preset];
|
|
4132
|
+
}
|
|
4133
|
+
else if (props.variant === "custom") {
|
|
4134
|
+
// TypeScript now knows props is CustomSkeletonLinesProps
|
|
4135
|
+
lineConfigs = props.lines;
|
|
4136
|
+
}
|
|
4137
|
+
else {
|
|
4138
|
+
// TypeScript now knows props is UniformSkeletonLinesProps
|
|
4139
|
+
lineConfigs = Array.from({ length: props.count }, () => ({
|
|
4140
|
+
textSize: props.textSize,
|
|
4141
|
+
width: props.width,
|
|
4142
|
+
flexibleWidth: props.flexibleWidth,
|
|
4143
|
+
className: props.lineClassName,
|
|
4144
|
+
}));
|
|
4145
|
+
}
|
|
4146
|
+
const lineCount = lineConfigs.length;
|
|
4147
|
+
return (jsx("div", { "aria-label": `Loading ${lineCount} ${lineCount === 1 ? "item" : "items"}`, className: cvaSkeletonContainer({ className }), "data-testid": dataTestId, "data-variant": props.variant, role: "status", ...(props.variant === "preset" && { "data-preset": props.preset }), children: lineConfigs.map((config, index) => (jsx(SkeletonLabel, { className: config.className, "data-testid": dataTestId ? `${dataTestId}-${index}` : undefined, flexibleWidth: config.flexibleWidth ?? true, textSize: config.textSize ?? "text-base", width: config.width ?? "100%" }, index))) }));
|
|
4148
|
+
});
|
|
4149
|
+
SkeletonLines.displayName = "SkeletonLines";
|
|
4150
|
+
|
|
4151
|
+
/**
|
|
4152
|
+
* Skeleton loading indicator that mimics the KPI component structure.
|
|
4153
|
+
* Uses the same layout, spacing, and visual hierarchy as KPI.
|
|
4154
|
+
*/
|
|
4155
|
+
const KPISkeleton = ({ variant = "default", className, "data-testid": dataTestId, style, ...rest }) => {
|
|
4156
|
+
const isSmallVariant = variant === "small";
|
|
4157
|
+
// Generate stable random widths once and never change them
|
|
4158
|
+
const lineWidths = useMemo(() => {
|
|
4159
|
+
return {
|
|
4160
|
+
title: getResponsiveRandomWidthPercentage({ min: 60, max: 85 }),
|
|
4161
|
+
value: getResponsiveRandomWidthPercentage({ min: 70, max: 100 }),
|
|
4162
|
+
};
|
|
4163
|
+
}, []);
|
|
4164
|
+
return (jsxs("div", { className: cvaKPI({ variant, className }), "data-testid": dataTestId, style: style, ...rest, children: [jsx("div", { className: twMerge("flex", "items-center", "flex-row", isSmallVariant ? "h-4" : "h-5"), children: jsx(SkeletonLabel, { "data-testid": dataTestId ? `${dataTestId}-title-loading` : undefined, textSize: isSmallVariant ? "text-xs" : "text-sm", width: lineWidths.title }) }), jsx("div", { className: twMerge("truncate", "whitespace-nowrap", "flex h-7 flex-row items-center"), children: jsx(SkeletonLabel, { "data-testid": dataTestId ? `${dataTestId}-value-loading` : undefined, textSize: isSmallVariant ? "text-xs" : "text-lg", width: lineWidths.value }) })] }));
|
|
3936
4165
|
};
|
|
3937
4166
|
|
|
3938
4167
|
/**
|
|
@@ -4088,6 +4317,8 @@ const cvaKPICard = cvaMerge([
|
|
|
4088
4317
|
isActive: false,
|
|
4089
4318
|
},
|
|
4090
4319
|
});
|
|
4320
|
+
const cvaKPICardBody = cvaMerge(["grid", "gap-2", "px-3", "pb-2", "pt-3"]);
|
|
4321
|
+
const cvaKPICardHeader = cvaMerge(["grid", "grid-cols-[1fr_auto]", "justify-between", "gap-2"]);
|
|
4091
4322
|
const cvaKPIIconContainer = cvaMerge(["flex", "items-center", "justify-center", "w-7", "h-7", "rounded-md", "text-white"], {
|
|
4092
4323
|
variants: {
|
|
4093
4324
|
iconColor: {
|
|
@@ -4106,15 +4337,23 @@ const cvaKPIIconContainer = cvaMerge(["flex", "items-center", "justify-center",
|
|
|
4106
4337
|
* @param {KPICardProps} props - The props for the KPICard component
|
|
4107
4338
|
* @returns {ReactElement} KPICard component
|
|
4108
4339
|
*/
|
|
4109
|
-
const KPICard = ({ isActive = false, onClick, className, "data-testid": dataTestId, children, iconName = undefined, iconColor = "info",
|
|
4110
|
-
const isClickable = Boolean(onClick !== undefined
|
|
4340
|
+
const KPICard = ({ isActive = false, onClick, className, "data-testid": dataTestId, children, iconName = undefined, iconColor = "info", notice, valueBar, trends, unit, ...rest }) => {
|
|
4341
|
+
const isClickable = Boolean(onClick !== undefined);
|
|
4111
4342
|
return (jsx(Card, { className: cvaKPICard({
|
|
4112
4343
|
isClickable,
|
|
4113
4344
|
isActive,
|
|
4114
4345
|
className,
|
|
4115
|
-
}), "data-testid": dataTestId ? dataTestId : undefined, onClick: onClick, children: jsxs(CardBody, { className:
|
|
4346
|
+
}), "data-testid": dataTestId ? dataTestId : undefined, onClick: onClick, children: jsxs(CardBody, { className: cvaKPICardBody(), gap: "none", padding: "none", children: [jsxs("div", { className: cvaKPICardHeader(), children: [jsx(KPI, { ...rest, className: "p-0", "data-testid": dataTestId ? `${dataTestId}-kpi` : undefined, unit: unit }), iconName ? (jsx("div", { className: cvaKPIIconContainer({ iconColor }), children: jsx(Icon, { name: iconName, size: "small", type: "solid" }) })) : null] }), trends !== undefined && trends.length > 0 ? (jsx(TrendIndicators, { "data-testid": dataTestId ? `${dataTestId}-trend-indicators` : undefined, trends: trends })) : null, valueBar !== undefined ? (jsx(ValueBar, { className: "h-2", "data-testid": dataTestId ? `${dataTestId}-value-bar` : undefined, ...valueBar })) : null, notice !== undefined ? (
|
|
4116
4347
|
// NOTE: Can't use Notice component here due to the non-flexible text styling options
|
|
4117
|
-
jsxs("div", { className: "flex items-center gap-1 truncate", "data-testid": dataTestId ? `${dataTestId}-notice` : undefined, children: [notice.iconName ? (jsx(Icon, { color: notice.iconColor, "data-testid": dataTestId ? `${dataTestId}-notice-icon` : undefined, name: notice.iconName, size: "small" })) : null, jsx(Text, { className: "truncate text-neutral-900", "data-testid": dataTestId ? `${dataTestId}-notice-label` : undefined, size: "small", children: notice.label })] }))
|
|
4348
|
+
jsxs("div", { className: "flex items-center gap-1 truncate", "data-testid": dataTestId ? `${dataTestId}-notice` : undefined, children: [notice.iconName ? (jsx(Icon, { color: notice.iconColor, "data-testid": dataTestId ? `${dataTestId}-notice-icon` : undefined, name: notice.iconName, size: "small" })) : null, jsx(Text, { className: "truncate text-neutral-900", "data-testid": dataTestId ? `${dataTestId}-notice-label` : undefined, size: "small", children: notice.label })] })) : null, children] }) }));
|
|
4349
|
+
};
|
|
4350
|
+
|
|
4351
|
+
/**
|
|
4352
|
+
* Skeleton loading indicator that mimics the KPICard component structure.
|
|
4353
|
+
* Uses the same layout, spacing, and visual hierarchy as KPICard.
|
|
4354
|
+
*/
|
|
4355
|
+
const KPICardSkeleton = ({ hasIcon = false, hasTrends = false, hasValueBar = false, hasNotice = false, children, className, "data-testid": dataTestId, style, ...rest }) => {
|
|
4356
|
+
return (jsx(Card, { className: cvaKPICard({ className }), "data-testid": dataTestId, style: style, ...rest, children: jsxs(CardBody, { className: cvaKPICardBody(), gap: "none", padding: "none", children: [jsxs("div", { className: cvaKPICardHeader(), children: [jsx(KPISkeleton, { className: "p-0", "data-testid": dataTestId ? `${dataTestId}-kpi` : undefined }), hasIcon ? (jsx(SkeletonBlock, { "data-testid": dataTestId ? `${dataTestId}-icon-loading` : undefined, height: 28, width: 28 })) : null] }), hasTrends ? (jsx(SkeletonLabel, { "data-testid": dataTestId ? `${dataTestId}-trend-indicator-loading` : undefined, textSize: "text-xs", width: "100%" })) : null, hasValueBar ? (jsx(SkeletonLabel, { "data-testid": dataTestId ? `${dataTestId}-value-bar-loading` : undefined, textSize: "text-xs", width: "100%" })) : null, hasNotice ? (jsx(SkeletonLabel, { "data-testid": dataTestId ? `${dataTestId}-notice-loading` : undefined, textSize: "text-xs", width: "100%" })) : null, children] }) }));
|
|
4118
4357
|
};
|
|
4119
4358
|
|
|
4120
4359
|
const cvaListContainer = cvaMerge(["overflow-y-auto", "overflow-x-hidden", "h-full"], {
|
|
@@ -4157,124 +4396,6 @@ const cvaListItem$1 = cvaMerge(["absolute", "top-0", "left-0", "w-full"], {
|
|
|
4157
4396
|
},
|
|
4158
4397
|
});
|
|
4159
4398
|
|
|
4160
|
-
const cvaSkeleton = cvaMerge([
|
|
4161
|
-
"relative",
|
|
4162
|
-
"overflow-hidden",
|
|
4163
|
-
"rounded-lg",
|
|
4164
|
-
// Gradient background
|
|
4165
|
-
"bg-gradient-to-r",
|
|
4166
|
-
"from-gray-200/80",
|
|
4167
|
-
"via-gray-300/60",
|
|
4168
|
-
"to-gray-200/80",
|
|
4169
|
-
// Pulse animation
|
|
4170
|
-
"animate-pulse",
|
|
4171
|
-
// Shimmer overlay
|
|
4172
|
-
"before:absolute",
|
|
4173
|
-
"before:inset-0",
|
|
4174
|
-
"before:bg-gradient-to-r",
|
|
4175
|
-
"before:from-transparent",
|
|
4176
|
-
"before:via-white/50",
|
|
4177
|
-
"before:to-transparent",
|
|
4178
|
-
"before:opacity-0",
|
|
4179
|
-
"before:animate-pulse",
|
|
4180
|
-
// Smooth transitions for accessibility
|
|
4181
|
-
"transition-all",
|
|
4182
|
-
"duration-300",
|
|
4183
|
-
"ease-in-out",
|
|
4184
|
-
]);
|
|
4185
|
-
|
|
4186
|
-
const VALID_SIZE_KEYS = [
|
|
4187
|
-
"xs",
|
|
4188
|
-
"sm",
|
|
4189
|
-
"base",
|
|
4190
|
-
"lg",
|
|
4191
|
-
"xl",
|
|
4192
|
-
"2xl",
|
|
4193
|
-
"3xl",
|
|
4194
|
-
"4xl",
|
|
4195
|
-
"5xl",
|
|
4196
|
-
"6xl",
|
|
4197
|
-
"7xl",
|
|
4198
|
-
"8xl",
|
|
4199
|
-
"9xl",
|
|
4200
|
-
];
|
|
4201
|
-
/**
|
|
4202
|
-
* Extract the size key from a text size string (e.g., "text-base" → "base").
|
|
4203
|
-
*
|
|
4204
|
-
* @param value - The text size string to parse
|
|
4205
|
-
* @returns {fontSizeKeys | null} The extracted size key or null if invalid
|
|
4206
|
-
*/
|
|
4207
|
-
const extractSizeKey = (value) => {
|
|
4208
|
-
if (!value.startsWith("text-")) {
|
|
4209
|
-
return null;
|
|
4210
|
-
}
|
|
4211
|
-
const sizeKey = value.replace("text-", "");
|
|
4212
|
-
return VALID_SIZE_KEYS.find(key => key === sizeKey) ?? null;
|
|
4213
|
-
};
|
|
4214
|
-
/**
|
|
4215
|
-
* Calculate the height value based on the height prop and variant.
|
|
4216
|
-
*
|
|
4217
|
-
* @param height - The height value (number, CSS length, or text size key)
|
|
4218
|
-
* @param variant - The skeleton variant ("text" or "block")
|
|
4219
|
-
* @returns {string} The calculated CSS height value
|
|
4220
|
-
*/
|
|
4221
|
-
const getHeightValue = (height, variant) => {
|
|
4222
|
-
if (typeof height === "number") {
|
|
4223
|
-
return `${height}px`;
|
|
4224
|
-
}
|
|
4225
|
-
const sizeKey = extractSizeKey(height);
|
|
4226
|
-
if (sizeKey) {
|
|
4227
|
-
// Text variant: use font-size × 0.7 to approximate cap-height
|
|
4228
|
-
// Block variant: use full line-height (for when text size keys are passed to block variant)
|
|
4229
|
-
return variant === "text" ? `calc(var(--font-size-${sizeKey}) * 0.7)` : `var(--line-height-${sizeKey})`;
|
|
4230
|
-
}
|
|
4231
|
-
return height;
|
|
4232
|
-
};
|
|
4233
|
-
/**
|
|
4234
|
-
* Calculate the vertical margin value for text variant to align with text baseline.
|
|
4235
|
-
* Formula: (line-height - cap-height) / 2, where cap-height = font-size × 0.7
|
|
4236
|
-
*
|
|
4237
|
-
* @param height - The height value (number, CSS length, or text size key)
|
|
4238
|
-
* @returns {string} The calculated CSS margin value
|
|
4239
|
-
*/
|
|
4240
|
-
const getMarginValue = (height) => {
|
|
4241
|
-
if (typeof height === "string") {
|
|
4242
|
-
const sizeKey = extractSizeKey(height);
|
|
4243
|
-
if (sizeKey) {
|
|
4244
|
-
// margin = (line-height - cap-height) / 2
|
|
4245
|
-
// cap-height = font-size × 0.7
|
|
4246
|
-
// For large text sizes, this may be negative, which matches how actual text extends beyond its line-height box
|
|
4247
|
-
return `calc((var(--line-height-${sizeKey}) - var(--font-size-${sizeKey}) * 0.7) / 2)`;
|
|
4248
|
-
}
|
|
4249
|
-
}
|
|
4250
|
-
return "0";
|
|
4251
|
-
};
|
|
4252
|
-
/**
|
|
4253
|
-
* Display a single placeholder line before data gets loaded to reduce load-time frustration.
|
|
4254
|
-
*
|
|
4255
|
-
* Use `variant="text"` with text-size keys for text placeholders.
|
|
4256
|
-
* Use `variant="block"` with numbers/CSS for images, badges, buttons, and other shape-based elements.
|
|
4257
|
-
* Pass children to create custom skeleton layouts.
|
|
4258
|
-
*/
|
|
4259
|
-
const Skeleton = memo((props) => {
|
|
4260
|
-
const { width = "100%", className, "data-testid": dataTestId, children } = props;
|
|
4261
|
-
const variant = props.variant ?? "text";
|
|
4262
|
-
const height = props.height ?? (variant === "text" ? "text-base" : 16);
|
|
4263
|
-
const flexibleWidth = props.flexibleWidth ?? variant === "text";
|
|
4264
|
-
const widthValue = typeof width === "number" ? `${width}px` : width;
|
|
4265
|
-
const isTextVariant = variant === "text";
|
|
4266
|
-
const heightValue = getHeightValue(height, variant);
|
|
4267
|
-
const marginValue = isTextVariant ? getMarginValue(height) : undefined;
|
|
4268
|
-
return (jsx("div", { "aria-label": "Loading", className: cvaSkeleton({ className }), "data-testid": dataTestId, role: "status", style: {
|
|
4269
|
-
width: flexibleWidth ? "100%" : widthValue,
|
|
4270
|
-
maxWidth: flexibleWidth ? widthValue : undefined,
|
|
4271
|
-
height: heightValue,
|
|
4272
|
-
marginTop: marginValue,
|
|
4273
|
-
marginBottom: marginValue,
|
|
4274
|
-
}, children: children }));
|
|
4275
|
-
});
|
|
4276
|
-
Skeleton.displayName = "Skeleton";
|
|
4277
|
-
|
|
4278
4399
|
const cvaListItem = cvaMerge(["py-3", "px-4", "min-h-14", "w-full", "flex", "justify-between", "items-center"]);
|
|
4279
4400
|
const cvaMainInformationClass = cvaMerge(["grid", "items-center", "text-sm", "gap-2"], {
|
|
4280
4401
|
variants: {
|
|
@@ -4323,7 +4444,7 @@ const ListItemSkeleton = ({ hasThumbnail = DEFAULT_SKELETON_LIST_ITEM_PROPS.hasT
|
|
|
4323
4444
|
details: getResponsiveRandomWidthPercentage({ min: 25, max: 45 }),
|
|
4324
4445
|
};
|
|
4325
4446
|
}, []);
|
|
4326
|
-
return (jsxs("div", { className: cvaListItem({ className: "w-full" }), children: [jsxs("div", { className: cvaMainInformationClass({ hasThumbnail, className: "w-full" }), children: [hasThumbnail ? (jsx("div", { className: cvaThumbnailContainer({ className: "bg-gray-200" }), children: jsx("div", { className: twMerge("bg-gray-300", thumbnailShape === "circle" ? "rounded-full" : "rounded"), style: { width: 20, height: 20 } }) })) : null, jsxs("div", { className: cvaTextContainer(), children: [jsx("div", { className: cvaTitleRow(), children: jsx(
|
|
4447
|
+
return (jsxs("div", { className: cvaListItem({ className: "w-full" }), children: [jsxs("div", { className: cvaMainInformationClass({ hasThumbnail, className: "w-full" }), children: [hasThumbnail ? (jsx("div", { className: cvaThumbnailContainer({ className: "bg-gray-200" }), children: jsx("div", { className: twMerge("bg-gray-300", thumbnailShape === "circle" ? "rounded-full" : "rounded"), style: { width: 20, height: 20 } }) })) : null, jsxs("div", { className: cvaTextContainer(), children: [jsx("div", { className: cvaTitleRow(), children: jsx(SkeletonLabel, { textSize: "text-sm", width: lineWidths.title }) }), hasDescription ? (jsx("div", { className: cvaDescriptionRow(), children: jsx(SkeletonLabel, { textSize: "text-xs", width: lineWidths.description }) })) : null, hasMeta ? (jsx("div", { className: cvaMetaRow(), children: jsx(SkeletonLabel, { textSize: "text-xs", width: lineWidths.meta }) })) : null] })] }), hasDetails ? (jsx("div", { className: cvaDetailsContainer(), children: jsx(SkeletonLabel, { textSize: "text-sm", width: lineWidths.details }) })) : null] }));
|
|
4327
4448
|
};
|
|
4328
4449
|
|
|
4329
4450
|
/**
|
|
@@ -5230,9 +5351,12 @@ const PageContent = ({ className, children, "data-testid": dataTestId, layout, }
|
|
|
5230
5351
|
return (jsx("div", { className: cvaPageContent({ className, layout }), "data-testid": dataTestId ? dataTestId : "page-content", children: children }));
|
|
5231
5352
|
};
|
|
5232
5353
|
|
|
5233
|
-
const LoadingContent = () => (jsx("div", { className: "flex flex-row items-center
|
|
5354
|
+
const LoadingContent = () => (jsx("div", { className: "flex flex-row items-center", "data-testid": "kpi-card-loading-content", children: jsx("div", { className: "w-full", children: jsx(SkeletonLines, { lines: [
|
|
5355
|
+
{ textSize: "text-xs", width: 50 },
|
|
5356
|
+
{ textSize: "text-lg", width: 40 },
|
|
5357
|
+
], variant: "custom" }) }) }));
|
|
5234
5358
|
/**
|
|
5235
|
-
*
|
|
5359
|
+
* Renders KPI metrics in the PageHeader component.
|
|
5236
5360
|
*
|
|
5237
5361
|
* @param {object} props - The props for the PageHeaderKpiMetrics component
|
|
5238
5362
|
* @param {Array<PageHeaderKpiMetricsType>} props.kpiMetrics - The KPI metrics to render
|
|
@@ -5471,7 +5595,7 @@ const Pagination = ({ previousPage, nextPage, canPreviousPage = false, canNextPa
|
|
|
5471
5595
|
onPageChange?.({ from, to, description });
|
|
5472
5596
|
}, [page, onPageChange, previousPage, nextPage]);
|
|
5473
5597
|
if (loading) {
|
|
5474
|
-
return (jsx("div", { className: cvaPagination({ className }), children: jsx(
|
|
5598
|
+
return (jsx("div", { className: cvaPagination({ className }), children: jsx(SkeletonLabel, { textSize: "text-sm", width: 150 }) }));
|
|
5475
5599
|
}
|
|
5476
5600
|
return (jsxs("div", { className: cvaPagination({ className }), "data-testid": dataTestId, children: [jsx(IconButton, { "data-testid": "prev-page", disabled: cursorBase ? !canPreviousPage || false : page !== undefined && page <= 0, icon: jsx(Icon, { name: "ChevronLeft", size: "small" }), onClick: () => handlePageChange("prev"), size: "small", variant: "ghost-neutral" }), !cursorBase && (jsxs(Fragment$1, { children: [jsx("div", { className: cvaPaginationText(), "data-testid": "current-page", children: currentPage }), jsx("div", { className: cvaPaginationText(), "data-testid": "page-count", children: pageCount !== null && pageCount !== undefined && getTranslatedCount ? getTranslatedCount(pageCount) : null })] })), jsx(IconButton, { "data-testid": "next-page", disabled: cursorBase
|
|
5477
5601
|
? !canNextPage || false
|
|
@@ -5710,7 +5834,7 @@ const PreferenceCardSkeleton = ({ hasIcon = DEFAULT_SKELETON_PREFERENCE_CARD_PRO
|
|
|
5710
5834
|
description: getRandomWidth(160, 240),
|
|
5711
5835
|
};
|
|
5712
5836
|
}, []);
|
|
5713
|
-
return (jsxs("div", { className: cvaPreferenceCard(), children: [hasInput ? (jsx("div", { className: cvaInputContainer({ itemPlacement: "center" }), children: jsx(
|
|
5837
|
+
return (jsxs("div", { className: cvaPreferenceCard(), children: [hasInput ? (jsx("div", { className: cvaInputContainer({ itemPlacement: "center" }), children: jsx(SkeletonBlock, { height: 20, width: 20 }) })) : null, jsx("div", { className: cvaContentWrapper(), children: jsx(GridAreas, { ...gridAreas, className: cvaContentContainer({ itemPlacement: "center" }), children: slots => (jsxs(Fragment$1, { children: [hasIcon ? (jsx("div", { ...slots.icon, children: jsx(SkeletonBlock, { className: cvaIconBackground({ disabled: false }), height: 32, width: 32 }) })) : null, jsxs("div", { ...slots.information, className: "min-w-0 flex-1", children: [jsx("div", { className: "grid min-w-0 grid-cols-[1fr_auto] items-center gap-2", children: jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [jsx(SkeletonLabel, { textSize: "text-sm", width: lineWidths.title }), hasTitleTag ? jsx(TagSkeleton, { size: "small" }) : null] }) }), jsx(SkeletonLabel, { textSize: "text-xs", width: lineWidths.description })] }), hasCardTag ? (jsx("div", { ...slots.cardTag, className: "justify-self-end", children: jsx(TagSkeleton, { size: "medium" }) })) : null] })) }) })] }));
|
|
5714
5838
|
};
|
|
5715
5839
|
/**
|
|
5716
5840
|
* Simple tag skeleton for use within PreferenceCardSkeleton.
|
|
@@ -5719,7 +5843,7 @@ const PreferenceCardSkeleton = ({ hasIcon = DEFAULT_SKELETON_PREFERENCE_CARD_PRO
|
|
|
5719
5843
|
const TagSkeleton = ({ size }) => {
|
|
5720
5844
|
const width = useMemo(() => getRandomWidth(40, 64), []);
|
|
5721
5845
|
const height = size === "small" ? 18 : 24;
|
|
5722
|
-
return jsx(
|
|
5846
|
+
return jsx(SkeletonBlock, { className: "rounded-full", height: height, width: width });
|
|
5723
5847
|
};
|
|
5724
5848
|
|
|
5725
5849
|
function useConfirmExit(confirmExit, when = true) {
|
|
@@ -7165,4 +7289,4 @@ const useWindowActivity = ({ onFocus, onBlur, skip = false } = { onBlur: undefin
|
|
|
7165
7289
|
return useMemo(() => ({ focused }), [focused]);
|
|
7166
7290
|
};
|
|
7167
7291
|
|
|
7168
|
-
export { ActionRenderer, Alert, Badge, Breadcrumb, BreadcrumbContainer, Button, Card, CardBody, CardFooter, CardHeader, Collapse, CompletionStatusIndicator, CopyableText, DEFAULT_SKELETON_PREFERENCE_CARD_PROPS, DetailsList, EmptyState, EmptyValue, ExternalLink, GridAreas, Heading, Highlight, HorizontalOverflowScroller, Icon, IconButton, Indicator, KPI, KPICard, List, ListItem, MenuDivider, MenuItem, MenuList, MoreMenu, Notice, PackageNameStoryComponent, Page, PageContent, PageHeader, PageHeaderKpiMetrics, PageHeaderSecondaryActions, PageHeaderTitle, Pagination, Polygon, Popover, PopoverContent, PopoverTitle, PopoverTrigger, Portal, PreferenceCard, PreferenceCardSkeleton, Prompt, ROLE_CARD, SectionHeader, Sidebar,
|
|
7292
|
+
export { ActionRenderer, Alert, Badge, Breadcrumb, BreadcrumbContainer, Button, Card, CardBody, CardFooter, CardHeader, Collapse, CompletionStatusIndicator, CopyableText, DEFAULT_SKELETON_PREFERENCE_CARD_PROPS, DetailsList, EmptyState, EmptyValue, ExternalLink, GridAreas, Heading, Highlight, HorizontalOverflowScroller, Icon, IconButton, Indicator, KPI, KPICard, KPICardSkeleton, KPISkeleton, List, ListItem, MenuDivider, MenuItem, MenuList, MoreMenu, Notice, PackageNameStoryComponent, Page, PageContent, PageHeader, PageHeaderKpiMetrics, PageHeaderSecondaryActions, PageHeaderTitle, Pagination, Polygon, Popover, PopoverContent, PopoverTitle, PopoverTrigger, Portal, PreferenceCard, PreferenceCardSkeleton, Prompt, ROLE_CARD, SectionHeader, Sidebar, SkeletonBlock, SkeletonLabel, SkeletonLines, Spacer, Spinner, StarButton, Tab, TabContent, TabList, Tabs, Tag, Text, ToggleGroup, Tooltip, TrendIndicator, TrendIndicators, ValueBar, ZStack, createGrid, cvaButton, cvaButtonPrefixSuffix, cvaButtonSpinner, cvaButtonSpinnerContainer, cvaClickable, cvaContainerStyles, cvaContentContainer, cvaContentWrapper, cvaDescriptionCard, cvaIconBackground, cvaIconButton, cvaImgStyles, cvaIndicator, cvaIndicatorIcon, cvaIndicatorIconBackground, cvaIndicatorLabel, cvaIndicatorPing, cvaInputContainer, cvaInteractableItem, cvaList, cvaListContainer, cvaListItem$1 as cvaListItem, cvaMenu, cvaMenuItem, cvaMenuItemLabel, cvaMenuItemPrefix, cvaMenuItemStyle, cvaMenuItemSuffix, cvaMenuList, cvaMenuListDivider, cvaMenuListItem, cvaMenuListMultiSelect, cvaPageHeader, cvaPageHeaderContainer, cvaPageHeaderHeading, cvaPreferenceCard, cvaTitleCard, cvaToggleGroup, cvaToggleGroupWithSlidingBackground, cvaToggleItem, cvaToggleItemContent, cvaToggleItemText, cvaZStackContainer, cvaZStackItem, defaultPageSize, docs, getDevicePixelRatio, getResponsiveRandomWidthPercentage, getValueBarColorByValue, iconColorNames, iconPalette, noPagination, preferenceCardGrid, useClickOutside, useContainerBreakpoints, useContinuousTimeout, useCopyToClipboard, useCustomEncoding, useDebounce, useDevicePixelRatio, useElevatedReducer, useElevatedState, useGridAreas, useHover, useInfiniteScroll, useIsFirstRender, useIsFullscreen, useIsTextTruncated, useList, useListItemHeight, useLocalStorage, useLocalStorageReducer, useMeasure, useMergeRefs, useModifierKey, useOverflowItems, usePopoverContext, usePrevious, usePrompt, useRelayPagination, useResize, useScrollBlock, useScrollDetection, useSelfUpdatingRef, useTextSearch, useTimeout, useViewportBreakpoints, useWatch, useWindowActivity };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trackunit/react-components",
|
|
3
|
-
"version": "1.14.
|
|
3
|
+
"version": "1.14.11",
|
|
4
4
|
"repository": "https://github.com/Trackunit/manager",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
6
6
|
"engines": {
|
|
@@ -14,10 +14,10 @@
|
|
|
14
14
|
"@floating-ui/react": "^0.26.25",
|
|
15
15
|
"string-ts": "^2.0.0",
|
|
16
16
|
"tailwind-merge": "^2.0.0",
|
|
17
|
-
"@trackunit/ui-design-tokens": "1.10.
|
|
18
|
-
"@trackunit/css-class-variance-utilities": "1.10.
|
|
19
|
-
"@trackunit/shared-utils": "1.12.
|
|
20
|
-
"@trackunit/ui-icons": "1.10.
|
|
17
|
+
"@trackunit/ui-design-tokens": "1.10.9",
|
|
18
|
+
"@trackunit/css-class-variance-utilities": "1.10.9",
|
|
19
|
+
"@trackunit/shared-utils": "1.12.9",
|
|
20
|
+
"@trackunit/ui-icons": "1.10.9",
|
|
21
21
|
"@tanstack/react-router": "1.114.29",
|
|
22
22
|
"es-toolkit": "^1.39.10",
|
|
23
23
|
"@tanstack/react-virtual": "3.13.12",
|
|
@@ -14,10 +14,6 @@ export interface KPIProps extends CommonProps, Styleable {
|
|
|
14
14
|
* The unit of the KPI
|
|
15
15
|
*/
|
|
16
16
|
unit: string;
|
|
17
|
-
/**
|
|
18
|
-
* Whether or not to show the loading state
|
|
19
|
-
*/
|
|
20
|
-
loading?: boolean;
|
|
21
17
|
/**
|
|
22
18
|
* Label for the tooltip
|
|
23
19
|
*/
|
|
@@ -33,4 +29,4 @@ export interface KPIProps extends CommonProps, Styleable {
|
|
|
33
29
|
* @param {KPIProps} props - The props for the KPI component
|
|
34
30
|
* @returns {ReactElement} KPI component
|
|
35
31
|
*/
|
|
36
|
-
export declare const KPI: ({ title, value,
|
|
32
|
+
export declare const KPI: ({ title, value, unit, className, "data-testid": dataTestId, tooltipLabel, variant, style, ...rest }: KPIProps) => ReactElement;
|