@trackunit/react-components 1.14.6 → 1.14.12
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.cjs.js
CHANGED
|
@@ -1834,21 +1834,74 @@ const DetailsList = ({ details, className, hasLink = false }) => {
|
|
|
1834
1834
|
return (jsxRuntime.jsx("div", { className: cvaDetailsList({ className, hasLink }), children: details.map((value, index, array) => (jsxRuntime.jsxs(react.Fragment, { children: [jsxRuntime.jsx("span", { className: cvaDetailsListItem({ className }), children: value }), index < array.length - 1 && (jsxRuntime.jsx("div", { className: "mx-0.5 flex items-center", children: jsxRuntime.jsx(Icon, { className: "w-4 text-neutral-300", color: "neutral", name: "Slash", size: "small" }) }))] }, index))) }));
|
|
1835
1835
|
};
|
|
1836
1836
|
|
|
1837
|
+
const VALID_SIZE_KEYS = [
|
|
1838
|
+
"xs",
|
|
1839
|
+
"sm",
|
|
1840
|
+
"base",
|
|
1841
|
+
"lg",
|
|
1842
|
+
"xl",
|
|
1843
|
+
"2xl",
|
|
1844
|
+
"3xl",
|
|
1845
|
+
"4xl",
|
|
1846
|
+
"5xl",
|
|
1847
|
+
"6xl",
|
|
1848
|
+
"7xl",
|
|
1849
|
+
"8xl",
|
|
1850
|
+
"9xl",
|
|
1851
|
+
];
|
|
1837
1852
|
/**
|
|
1838
|
-
*
|
|
1853
|
+
* Extract the size key from a text size string (e.g., "text-base" → "base").
|
|
1839
1854
|
*
|
|
1840
|
-
* @param
|
|
1841
|
-
* @
|
|
1842
|
-
* @param {number} params.max - Maximum percentage value (e.g., 80 for 80%)
|
|
1843
|
-
* @returns {string} A percentage string (e.g., "65%")
|
|
1855
|
+
* @param value - The text size string to parse
|
|
1856
|
+
* @returns {fontSizeKeys | null} The extracted size key or null if invalid
|
|
1844
1857
|
*/
|
|
1845
|
-
const
|
|
1846
|
-
|
|
1847
|
-
|
|
1858
|
+
const extractSizeKey = (value) => {
|
|
1859
|
+
if (!value.startsWith("text-")) {
|
|
1860
|
+
return null;
|
|
1861
|
+
}
|
|
1862
|
+
const sizeKey = value.replace("text-", "");
|
|
1863
|
+
return VALID_SIZE_KEYS.find(key => key === sizeKey) ?? null;
|
|
1864
|
+
};
|
|
1865
|
+
/**
|
|
1866
|
+
* Calculate the height value based on the height prop and variant.
|
|
1867
|
+
*
|
|
1868
|
+
* @param height - The height value (number, CSS length, or text size key)
|
|
1869
|
+
* @param variant - The skeleton variant ("text" or "block")
|
|
1870
|
+
* @returns {string} The calculated CSS height value
|
|
1871
|
+
*/
|
|
1872
|
+
const getHeightValue = (height, variant) => {
|
|
1873
|
+
if (typeof height === "number") {
|
|
1874
|
+
return `${height}px`;
|
|
1875
|
+
}
|
|
1876
|
+
const sizeKey = extractSizeKey(height);
|
|
1877
|
+
if (sizeKey) {
|
|
1878
|
+
// Text variant: use font-size × 0.7 to approximate cap-height
|
|
1879
|
+
// Block variant: use full line-height (for when text size keys are passed to block variant)
|
|
1880
|
+
return `calc(var(--font-size-${sizeKey}) * 0.7)` ;
|
|
1881
|
+
}
|
|
1882
|
+
return height;
|
|
1883
|
+
};
|
|
1884
|
+
/**
|
|
1885
|
+
* Calculate the vertical margin value for text variant to align with text baseline.
|
|
1886
|
+
* Formula: (line-height - cap-height) / 2, where cap-height = font-size × 0.7
|
|
1887
|
+
*
|
|
1888
|
+
* @param height - The height value (number, CSS length, or text size key)
|
|
1889
|
+
* @returns {string} The calculated CSS margin value
|
|
1890
|
+
*/
|
|
1891
|
+
const getMarginValue = (height) => {
|
|
1892
|
+
if (typeof height === "string") {
|
|
1893
|
+
const sizeKey = extractSizeKey(height);
|
|
1894
|
+
if (sizeKey) {
|
|
1895
|
+
// margin = (line-height - cap-height) / 2
|
|
1896
|
+
// cap-height = font-size × 0.7
|
|
1897
|
+
// For large text sizes, this may be negative, which matches how actual text extends beyond its line-height box
|
|
1898
|
+
return `calc((var(--line-height-${sizeKey}) - var(--font-size-${sizeKey}) * 0.7) / 2)`;
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1901
|
+
return "0";
|
|
1848
1902
|
};
|
|
1849
1903
|
|
|
1850
|
-
const
|
|
1851
|
-
const cvaSkeletonLine = cssClassVarianceUtilities.cvaMerge([
|
|
1904
|
+
const cvaSkeleton = cssClassVarianceUtilities.cvaMerge([
|
|
1852
1905
|
"relative",
|
|
1853
1906
|
"overflow-hidden",
|
|
1854
1907
|
"rounded-lg",
|
|
@@ -1875,32 +1928,28 @@ const cvaSkeletonLine = cssClassVarianceUtilities.cvaMerge([
|
|
|
1875
1928
|
]);
|
|
1876
1929
|
|
|
1877
1930
|
/**
|
|
1878
|
-
* Display placeholder
|
|
1879
|
-
*
|
|
1931
|
+
* Display a single placeholder line for text content before data gets loaded to reduce load-time frustration.
|
|
1932
|
+
*
|
|
1933
|
+
* Reduces height and adds vertical margins to match the visual space text occupies within its line-height.
|
|
1934
|
+
* Uses text-size keys (text-xs, text-sm, text-base, etc.) for height to match actual text elements.
|
|
1935
|
+
*
|
|
1936
|
+
* For multiple text lines, use SkeletonLines component instead.
|
|
1937
|
+
* For shape-based elements (images, badges, buttons), use SkeletonBlock component instead.
|
|
1880
1938
|
*/
|
|
1881
|
-
const
|
|
1882
|
-
const
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1939
|
+
const SkeletonLabel = react.memo((props) => {
|
|
1940
|
+
const { width = "100%", textSize = "text-base", flexibleWidth = true, className, "data-testid": dataTestId, children, } = props;
|
|
1941
|
+
const widthValue = typeof width === "number" ? `${width}px` : width;
|
|
1942
|
+
const heightValue = getHeightValue(textSize);
|
|
1943
|
+
const marginValue = getMarginValue(textSize);
|
|
1944
|
+
return (jsxRuntime.jsx("div", { "aria-label": "Loading", className: cvaSkeleton({ className }), "data-testid": dataTestId, role: "status", style: {
|
|
1945
|
+
width: flexibleWidth ? "100%" : widthValue,
|
|
1946
|
+
maxWidth: flexibleWidth ? widthValue : undefined,
|
|
1947
|
+
height: heightValue,
|
|
1948
|
+
marginTop: marginValue,
|
|
1949
|
+
marginBottom: marginValue,
|
|
1950
|
+
}, children: children }));
|
|
1887
1951
|
});
|
|
1888
|
-
|
|
1889
|
-
const { dimension, index } = params;
|
|
1890
|
-
let value;
|
|
1891
|
-
if (Array.isArray(dimension)) {
|
|
1892
|
-
const dimValue = dimension[index] ?? dimension[0] ?? "100%";
|
|
1893
|
-
value = typeof dimValue === "number" ? `${dimValue}px` : dimValue;
|
|
1894
|
-
}
|
|
1895
|
-
else {
|
|
1896
|
-
value = typeof dimension === "number" ? `${dimension}px` : dimension;
|
|
1897
|
-
}
|
|
1898
|
-
// For width values, wrap in min() to ensure max container width
|
|
1899
|
-
if (params.direction === "width") {
|
|
1900
|
-
return `min(${value}, ${params.maxWidth})`;
|
|
1901
|
-
}
|
|
1902
|
-
return value;
|
|
1903
|
-
};
|
|
1952
|
+
SkeletonLabel.displayName = "SkeletonLabel";
|
|
1904
1953
|
|
|
1905
1954
|
const cvaContainerStyles = cssClassVarianceUtilities.cvaMerge([
|
|
1906
1955
|
"flex",
|
|
@@ -1993,7 +2042,7 @@ const EmptyState = ({ description, altText, image = "SEARCH_DOCUMENT", customIma
|
|
|
1993
2042
|
return SearchDocumentSVG;
|
|
1994
2043
|
}
|
|
1995
2044
|
}, [image]);
|
|
1996
|
-
return (jsxRuntime.jsx("div", { className: cvaContainerStyles({ className }), "data-testid": dataTestId ?? "empty-state", children: loading ? (jsxRuntime.jsxs(
|
|
2045
|
+
return (jsxRuntime.jsx("div", { className: cvaContainerStyles({ className }), "data-testid": dataTestId ?? "empty-state", children: loading ? (jsxRuntime.jsxs("div", { className: "flex w-full flex-col items-center gap-4", children: [jsxRuntime.jsx(Spinner, { centering: "centered", "data-testid": "spinner" }), jsxRuntime.jsx(SkeletonLabel, { textSize: "text-base", width: "clamp(20%, 200px, 80%)" })] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [customImageSrc !== null && customImageSrc !== undefined ? (typeof customImageSrc === "string" ? (jsxRuntime.jsx("img", { alt: altText, className: cvaImgStyles(), height: 200, src: customImageSrc, width: 200 })) : (customImageSrc)) : (typeof ImageSource !== "undefined" && (jsxRuntime.jsx(ImageSource, { "data-testid": "empty-state-image", height: 200, width: 200 }, image))), description !== undefined && description !== "" ? (jsxRuntime.jsx(Text, { align: "center", size: "large", children: description })) : null, jsxRuntime.jsxs("div", { className: "mt-4 grid gap-3", children: [jsxRuntime.jsxs("div", { className: "flex gap-3", children: [secondaryAction ? (jsxRuntime.jsx(Button, { "data-testid": "empty-state-secondary-button", disabled: secondaryAction.disabled, onClick: secondaryAction.onClick, variant: "secondary", children: secondaryAction.to ? (jsxRuntime.jsx(reactRouter.Link, { params: secondaryAction.to.parameters, to: secondaryAction.to.pathname, children: secondaryAction.title })) : (secondaryAction.title) })) : null, primaryAction ? (jsxRuntime.jsx(Button, { "data-testid": "empty-state-primary-button", disabled: primaryAction.disabled, onClick: primaryAction.onClick, children: primaryAction.to ? (jsxRuntime.jsx(reactRouter.Link, { params: primaryAction.to.parameters, to: primaryAction.to.pathname, children: primaryAction.title })) : (primaryAction.title) })) : null] }), additionalHelpAction?.to ? (jsxRuntime.jsx(Button, { asChild: true, "data-testid": "empty-state-additional-button", disabled: additionalHelpAction.disabled, onClick: additionalHelpAction.onClick, suffix: jsxRuntime.jsx(Icon, { name: "ArrowTopRightOnSquare", size: "small" }), variant: "ghost", children: jsxRuntime.jsx(reactRouter.Link, { params: additionalHelpAction.to.parameters, target: additionalHelpAction.to.target, to: additionalHelpAction.to.pathname, children: additionalHelpAction.title }) })) : null] })] })) }));
|
|
1997
2046
|
};
|
|
1998
2047
|
|
|
1999
2048
|
const cvaEmptyValue = cssClassVarianceUtilities.cvaMerge(["text-neutral-400"]);
|
|
@@ -3932,9 +3981,189 @@ const cvaKPITrendPercentage = cssClassVarianceUtilities.cvaMerge([""], {
|
|
|
3932
3981
|
* @param {KPIProps} props - The props for the KPI component
|
|
3933
3982
|
* @returns {ReactElement} KPI component
|
|
3934
3983
|
*/
|
|
3935
|
-
const KPI = ({ title, value,
|
|
3984
|
+
const KPI = ({ title, value, unit, className, "data-testid": dataTestId, tooltipLabel, variant = "default", style, ...rest }) => {
|
|
3936
3985
|
const isSmallVariant = variant === "small";
|
|
3937
|
-
return (jsxRuntime.jsx(Tooltip, { className: "min-w-8 shrink-0", "data-testid": dataTestId ? `${dataTestId}-tooltip` : undefined, disabled: tooltipLabel === undefined || tooltipLabel === "", label: tooltipLabel, placement: "bottom", children: jsxRuntime.jsxs("div", { className: cvaKPI({ variant, className }), "data-testid": dataTestId, style: style, ...rest, children: [
|
|
3986
|
+
return (jsxRuntime.jsx(Tooltip, { className: "min-w-8 shrink-0", "data-testid": dataTestId ? `${dataTestId}-tooltip` : undefined, disabled: tooltipLabel === undefined || tooltipLabel === "", label: tooltipLabel, placement: "bottom", children: jsxRuntime.jsxs("div", { className: cvaKPI({ variant, className }), "data-testid": dataTestId, style: style, ...rest, children: [jsxRuntime.jsx(Text, { className: tailwindMerge.twMerge("truncate", "whitespace-nowrap"), "data-testid": dataTestId ? `${dataTestId}-title` : undefined, size: isSmallVariant ? "small" : "medium", subtle: true, weight: isSmallVariant ? "normal" : "bold", children: title }), jsxRuntime.jsx("div", { className: tailwindMerge.twMerge("truncate", "whitespace-nowrap"), children: jsxRuntime.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] }) })] }) }));
|
|
3987
|
+
};
|
|
3988
|
+
|
|
3989
|
+
/**
|
|
3990
|
+
* Display a single placeholder block for shape-based elements before data gets loaded to reduce load-time frustration.
|
|
3991
|
+
*
|
|
3992
|
+
* Fills the full height for images, badges, buttons, avatars, and other shape-based elements.
|
|
3993
|
+
* Uses numbers or CSS length values for height.
|
|
3994
|
+
*
|
|
3995
|
+
* For text content, use SkeletonLabel component instead.
|
|
3996
|
+
* For multiple text lines, use SkeletonLines component instead.
|
|
3997
|
+
*/
|
|
3998
|
+
const SkeletonBlock = react.memo((props) => {
|
|
3999
|
+
const { width = "100%", height = 16, flexibleWidth = false, className, "data-testid": dataTestId, children } = props;
|
|
4000
|
+
const widthValue = typeof width === "number" ? `${width}px` : width;
|
|
4001
|
+
const heightValue = typeof height === "number" ? `${height}px` : height;
|
|
4002
|
+
return (jsxRuntime.jsx("div", { "aria-label": "Loading", className: cvaSkeleton({ className }), "data-testid": dataTestId, role: "status", style: {
|
|
4003
|
+
width: flexibleWidth ? "100%" : widthValue,
|
|
4004
|
+
maxWidth: flexibleWidth ? widthValue : undefined,
|
|
4005
|
+
height: heightValue,
|
|
4006
|
+
}, children: children }));
|
|
4007
|
+
});
|
|
4008
|
+
SkeletonBlock.displayName = "SkeletonBlock";
|
|
4009
|
+
|
|
4010
|
+
/**
|
|
4011
|
+
* Generates a random width percentage string for skeleton loading components.
|
|
4012
|
+
*
|
|
4013
|
+
* @param {object} params - The parameter object
|
|
4014
|
+
* @param {number} params.min - Minimum percentage value (e.g., 30 for 30%)
|
|
4015
|
+
* @param {number} params.max - Maximum percentage value (e.g., 80 for 80%)
|
|
4016
|
+
* @returns {string} A percentage string (e.g., "65%")
|
|
4017
|
+
*/
|
|
4018
|
+
const getResponsiveRandomWidthPercentage = ({ min, max }) => {
|
|
4019
|
+
const randomWidth = Math.floor(Math.random() * (max - min + 1)) + min;
|
|
4020
|
+
return `${randomWidth}%`;
|
|
4021
|
+
};
|
|
4022
|
+
|
|
4023
|
+
/**
|
|
4024
|
+
* Preset configurations for common skeleton patterns.
|
|
4025
|
+
*/
|
|
4026
|
+
const PRESET_CONFIGURATIONS = {
|
|
4027
|
+
"short-paragraph": [
|
|
4028
|
+
{ textSize: "text-base", width: "100%" },
|
|
4029
|
+
{ textSize: "text-base", width: "100%" },
|
|
4030
|
+
{ textSize: "text-base", width: "60%" },
|
|
4031
|
+
],
|
|
4032
|
+
paragraph: [
|
|
4033
|
+
{ textSize: "text-base", width: "100%" },
|
|
4034
|
+
{ textSize: "text-base", width: "100%" },
|
|
4035
|
+
{ textSize: "text-base", width: "95%" },
|
|
4036
|
+
{ textSize: "text-base", width: "90%" },
|
|
4037
|
+
{ textSize: "text-base", width: "65%" },
|
|
4038
|
+
],
|
|
4039
|
+
"long-paragraph": [
|
|
4040
|
+
{ textSize: "text-base", width: "100%" },
|
|
4041
|
+
{ textSize: "text-base", width: "100%" },
|
|
4042
|
+
{ textSize: "text-base", width: "98%" },
|
|
4043
|
+
{ textSize: "text-base", width: "95%" },
|
|
4044
|
+
{ textSize: "text-base", width: "92%" },
|
|
4045
|
+
{ textSize: "text-base", width: "88%" },
|
|
4046
|
+
{ textSize: "text-base", width: "85%" },
|
|
4047
|
+
{ textSize: "text-base", width: "60%" },
|
|
4048
|
+
],
|
|
4049
|
+
"title-paragraph": [
|
|
4050
|
+
{ textSize: "text-2xl", width: "65%" },
|
|
4051
|
+
{ textSize: "text-base", width: "100%" },
|
|
4052
|
+
{ textSize: "text-base", width: "95%" },
|
|
4053
|
+
{ textSize: "text-base", width: "70%" },
|
|
4054
|
+
],
|
|
4055
|
+
"heading-text": [
|
|
4056
|
+
{ textSize: "text-xl", width: "60%" },
|
|
4057
|
+
{ textSize: "text-base", width: "100%" },
|
|
4058
|
+
{ textSize: "text-base", width: "100%" },
|
|
4059
|
+
{ textSize: "text-base", width: "90%" },
|
|
4060
|
+
{ textSize: "text-base", width: "65%" },
|
|
4061
|
+
],
|
|
4062
|
+
article: [
|
|
4063
|
+
{ textSize: "text-3xl", width: "70%" },
|
|
4064
|
+
{ textSize: "text-sm", width: "40%" },
|
|
4065
|
+
{ textSize: "text-base", width: "100%" },
|
|
4066
|
+
{ textSize: "text-base", width: "100%" },
|
|
4067
|
+
{ textSize: "text-base", width: "95%" },
|
|
4068
|
+
{ textSize: "text-base", width: "90%" },
|
|
4069
|
+
{ textSize: "text-base", width: "65%" },
|
|
4070
|
+
],
|
|
4071
|
+
};
|
|
4072
|
+
|
|
4073
|
+
const cvaSkeletonContainer = cssClassVarianceUtilities.cvaMerge(["flex", "flex-col"]);
|
|
4074
|
+
cssClassVarianceUtilities.cvaMerge([
|
|
4075
|
+
"relative",
|
|
4076
|
+
"overflow-hidden",
|
|
4077
|
+
"rounded-lg",
|
|
4078
|
+
// Gradient background
|
|
4079
|
+
"bg-gradient-to-r",
|
|
4080
|
+
"from-gray-200/80",
|
|
4081
|
+
"via-gray-300/60",
|
|
4082
|
+
"to-gray-200/80",
|
|
4083
|
+
// Pulse animation
|
|
4084
|
+
"animate-pulse",
|
|
4085
|
+
// Shimmer overlay
|
|
4086
|
+
"before:absolute",
|
|
4087
|
+
"before:inset-0",
|
|
4088
|
+
"before:bg-gradient-to-r",
|
|
4089
|
+
"before:from-transparent",
|
|
4090
|
+
"before:via-white/50",
|
|
4091
|
+
"before:to-transparent",
|
|
4092
|
+
"before:opacity-0",
|
|
4093
|
+
"before:animate-pulse",
|
|
4094
|
+
// Smooth transitions for accessibility
|
|
4095
|
+
"transition-all",
|
|
4096
|
+
"duration-300",
|
|
4097
|
+
"ease-in-out",
|
|
4098
|
+
]);
|
|
4099
|
+
|
|
4100
|
+
/**
|
|
4101
|
+
* Display multiple placeholder lines before data gets loaded to reduce load-time frustration.
|
|
4102
|
+
*
|
|
4103
|
+
* Supports three modes:
|
|
4104
|
+
* - **Uniform mode** (default): Display identical lines using `count` prop
|
|
4105
|
+
* - **Custom mode**: Display customized lines with per-line configuration using `variant="custom"` and `lines` prop
|
|
4106
|
+
* - **Preset mode**: Display common patterns using `variant="preset"` and `preset` name
|
|
4107
|
+
*
|
|
4108
|
+
* Built on top of the [SkeletonLabel](?path=/docs/components-loading-states-skeletonlabel--docs) component for text-specific margins and sizing.
|
|
4109
|
+
*
|
|
4110
|
+
* @example
|
|
4111
|
+
* // Uniform lines (simple mode)
|
|
4112
|
+
* <SkeletonLines count={3} textSize="text-base" variant="uniform" />
|
|
4113
|
+
* @example
|
|
4114
|
+
* // Custom lines (advanced mode)
|
|
4115
|
+
* <SkeletonLines
|
|
4116
|
+
* variant="custom"
|
|
4117
|
+
* lines={[
|
|
4118
|
+
* { textSize: "text-lg", width: "100%" },
|
|
4119
|
+
* { textSize: "text-base", width: "95%" },
|
|
4120
|
+
* { textSize: "text-base", width: "70%" }
|
|
4121
|
+
* ]}
|
|
4122
|
+
* />
|
|
4123
|
+
* @example
|
|
4124
|
+
* // Preset lines (quick patterns)
|
|
4125
|
+
* <SkeletonLines variant="preset" preset="title-paragraph" />
|
|
4126
|
+
*/
|
|
4127
|
+
const SkeletonLines = react.memo((props) => {
|
|
4128
|
+
const { className, "data-testid": dataTestId } = props;
|
|
4129
|
+
// Generate line configs based on variant
|
|
4130
|
+
let lineConfigs;
|
|
4131
|
+
if (props.variant === "preset") {
|
|
4132
|
+
// TypeScript now knows props is PresetSkeletonLinesProps
|
|
4133
|
+
lineConfigs = PRESET_CONFIGURATIONS[props.preset];
|
|
4134
|
+
}
|
|
4135
|
+
else if (props.variant === "custom") {
|
|
4136
|
+
// TypeScript now knows props is CustomSkeletonLinesProps
|
|
4137
|
+
lineConfigs = props.lines;
|
|
4138
|
+
}
|
|
4139
|
+
else {
|
|
4140
|
+
// TypeScript now knows props is UniformSkeletonLinesProps
|
|
4141
|
+
lineConfigs = Array.from({ length: props.count }, () => ({
|
|
4142
|
+
textSize: props.textSize,
|
|
4143
|
+
width: props.width,
|
|
4144
|
+
flexibleWidth: props.flexibleWidth,
|
|
4145
|
+
className: props.lineClassName,
|
|
4146
|
+
}));
|
|
4147
|
+
}
|
|
4148
|
+
const lineCount = lineConfigs.length;
|
|
4149
|
+
return (jsxRuntime.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) => (jsxRuntime.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))) }));
|
|
4150
|
+
});
|
|
4151
|
+
SkeletonLines.displayName = "SkeletonLines";
|
|
4152
|
+
|
|
4153
|
+
/**
|
|
4154
|
+
* Skeleton loading indicator that mimics the KPI component structure.
|
|
4155
|
+
* Uses the same layout, spacing, and visual hierarchy as KPI.
|
|
4156
|
+
*/
|
|
4157
|
+
const KPISkeleton = ({ variant = "default", className, "data-testid": dataTestId, style, ...rest }) => {
|
|
4158
|
+
const isSmallVariant = variant === "small";
|
|
4159
|
+
// Generate stable random widths once and never change them
|
|
4160
|
+
const lineWidths = react.useMemo(() => {
|
|
4161
|
+
return {
|
|
4162
|
+
title: getResponsiveRandomWidthPercentage({ min: 60, max: 85 }),
|
|
4163
|
+
value: getResponsiveRandomWidthPercentage({ min: 70, max: 100 }),
|
|
4164
|
+
};
|
|
4165
|
+
}, []);
|
|
4166
|
+
return (jsxRuntime.jsxs("div", { className: cvaKPI({ variant, className }), "data-testid": dataTestId, style: style, ...rest, children: [jsxRuntime.jsx("div", { className: tailwindMerge.twMerge("flex", "items-center", "flex-row", isSmallVariant ? "h-4" : "h-5"), children: jsxRuntime.jsx(SkeletonLabel, { "data-testid": dataTestId ? `${dataTestId}-title-loading` : undefined, textSize: isSmallVariant ? "text-xs" : "text-sm", width: lineWidths.title }) }), jsxRuntime.jsx("div", { className: tailwindMerge.twMerge("truncate", "whitespace-nowrap", "flex h-7 flex-row items-center"), children: jsxRuntime.jsx(SkeletonLabel, { "data-testid": dataTestId ? `${dataTestId}-value-loading` : undefined, textSize: isSmallVariant ? "text-xs" : "text-lg", width: lineWidths.value }) })] }));
|
|
3938
4167
|
};
|
|
3939
4168
|
|
|
3940
4169
|
/**
|
|
@@ -4090,6 +4319,8 @@ const cvaKPICard = cssClassVarianceUtilities.cvaMerge([
|
|
|
4090
4319
|
isActive: false,
|
|
4091
4320
|
},
|
|
4092
4321
|
});
|
|
4322
|
+
const cvaKPICardBody = cssClassVarianceUtilities.cvaMerge(["grid", "gap-2", "px-3", "pb-2", "pt-3"]);
|
|
4323
|
+
const cvaKPICardHeader = cssClassVarianceUtilities.cvaMerge(["grid", "grid-cols-[1fr_auto]", "justify-between", "gap-2"]);
|
|
4093
4324
|
const cvaKPIIconContainer = cssClassVarianceUtilities.cvaMerge(["flex", "items-center", "justify-center", "w-7", "h-7", "rounded-md", "text-white"], {
|
|
4094
4325
|
variants: {
|
|
4095
4326
|
iconColor: {
|
|
@@ -4108,15 +4339,23 @@ const cvaKPIIconContainer = cssClassVarianceUtilities.cvaMerge(["flex", "items-c
|
|
|
4108
4339
|
* @param {KPICardProps} props - The props for the KPICard component
|
|
4109
4340
|
* @returns {ReactElement} KPICard component
|
|
4110
4341
|
*/
|
|
4111
|
-
const KPICard = ({ isActive = false, onClick, className, "data-testid": dataTestId, children, iconName = undefined, iconColor = "info",
|
|
4112
|
-
const isClickable = Boolean(onClick !== undefined
|
|
4342
|
+
const KPICard = ({ isActive = false, onClick, className, "data-testid": dataTestId, children, iconName = undefined, iconColor = "info", notice, valueBar, trends, unit, ...rest }) => {
|
|
4343
|
+
const isClickable = Boolean(onClick !== undefined);
|
|
4113
4344
|
return (jsxRuntime.jsx(Card, { className: cvaKPICard({
|
|
4114
4345
|
isClickable,
|
|
4115
4346
|
isActive,
|
|
4116
4347
|
className,
|
|
4117
|
-
}), "data-testid": dataTestId ? dataTestId : undefined, onClick: onClick, children: jsxRuntime.jsxs(CardBody, { className:
|
|
4348
|
+
}), "data-testid": dataTestId ? dataTestId : undefined, onClick: onClick, children: jsxRuntime.jsxs(CardBody, { className: cvaKPICardBody(), gap: "none", padding: "none", children: [jsxRuntime.jsxs("div", { className: cvaKPICardHeader(), children: [jsxRuntime.jsx(KPI, { ...rest, className: "p-0", "data-testid": dataTestId ? `${dataTestId}-kpi` : undefined, unit: unit }), iconName ? (jsxRuntime.jsx("div", { className: cvaKPIIconContainer({ iconColor }), children: jsxRuntime.jsx(Icon, { name: iconName, size: "small", type: "solid" }) })) : null] }), trends !== undefined && trends.length > 0 ? (jsxRuntime.jsx(TrendIndicators, { "data-testid": dataTestId ? `${dataTestId}-trend-indicators` : undefined, trends: trends })) : null, valueBar !== undefined ? (jsxRuntime.jsx(ValueBar, { className: "h-2", "data-testid": dataTestId ? `${dataTestId}-value-bar` : undefined, ...valueBar })) : null, notice !== undefined ? (
|
|
4118
4349
|
// NOTE: Can't use Notice component here due to the non-flexible text styling options
|
|
4119
|
-
jsxRuntime.jsxs("div", { className: "flex items-center gap-1 truncate", "data-testid": dataTestId ? `${dataTestId}-notice` : undefined, children: [notice.iconName ? (jsxRuntime.jsx(Icon, { color: notice.iconColor, "data-testid": dataTestId ? `${dataTestId}-notice-icon` : undefined, name: notice.iconName, size: "small" })) : null, jsxRuntime.jsx(Text, { className: "truncate text-neutral-900", "data-testid": dataTestId ? `${dataTestId}-notice-label` : undefined, size: "small", children: notice.label })] }))
|
|
4350
|
+
jsxRuntime.jsxs("div", { className: "flex items-center gap-1 truncate", "data-testid": dataTestId ? `${dataTestId}-notice` : undefined, children: [notice.iconName ? (jsxRuntime.jsx(Icon, { color: notice.iconColor, "data-testid": dataTestId ? `${dataTestId}-notice-icon` : undefined, name: notice.iconName, size: "small" })) : null, jsxRuntime.jsx(Text, { className: "truncate text-neutral-900", "data-testid": dataTestId ? `${dataTestId}-notice-label` : undefined, size: "small", children: notice.label })] })) : null, children] }) }));
|
|
4351
|
+
};
|
|
4352
|
+
|
|
4353
|
+
/**
|
|
4354
|
+
* Skeleton loading indicator that mimics the KPICard component structure.
|
|
4355
|
+
* Uses the same layout, spacing, and visual hierarchy as KPICard.
|
|
4356
|
+
*/
|
|
4357
|
+
const KPICardSkeleton = ({ hasIcon = false, hasTrends = false, hasValueBar = false, hasNotice = false, children, className, "data-testid": dataTestId, style, ...rest }) => {
|
|
4358
|
+
return (jsxRuntime.jsx(Card, { className: cvaKPICard({ className }), "data-testid": dataTestId, style: style, ...rest, children: jsxRuntime.jsxs(CardBody, { className: cvaKPICardBody(), gap: "none", padding: "none", children: [jsxRuntime.jsxs("div", { className: cvaKPICardHeader(), children: [jsxRuntime.jsx(KPISkeleton, { className: "p-0", "data-testid": dataTestId ? `${dataTestId}-kpi` : undefined }), hasIcon ? (jsxRuntime.jsx(SkeletonBlock, { "data-testid": dataTestId ? `${dataTestId}-icon-loading` : undefined, height: 28, width: 28 })) : null] }), hasTrends ? (jsxRuntime.jsx(SkeletonLabel, { "data-testid": dataTestId ? `${dataTestId}-trend-indicator-loading` : undefined, textSize: "text-xs", width: "100%" })) : null, hasValueBar ? (jsxRuntime.jsx(SkeletonLabel, { "data-testid": dataTestId ? `${dataTestId}-value-bar-loading` : undefined, textSize: "text-xs", width: "100%" })) : null, hasNotice ? (jsxRuntime.jsx(SkeletonLabel, { "data-testid": dataTestId ? `${dataTestId}-notice-loading` : undefined, textSize: "text-xs", width: "100%" })) : null, children] }) }));
|
|
4120
4359
|
};
|
|
4121
4360
|
|
|
4122
4361
|
const cvaListContainer = cssClassVarianceUtilities.cvaMerge(["overflow-y-auto", "overflow-x-hidden", "h-full"], {
|
|
@@ -4159,124 +4398,6 @@ const cvaListItem$1 = cssClassVarianceUtilities.cvaMerge(["absolute", "top-0", "
|
|
|
4159
4398
|
},
|
|
4160
4399
|
});
|
|
4161
4400
|
|
|
4162
|
-
const cvaSkeleton = cssClassVarianceUtilities.cvaMerge([
|
|
4163
|
-
"relative",
|
|
4164
|
-
"overflow-hidden",
|
|
4165
|
-
"rounded-lg",
|
|
4166
|
-
// Gradient background
|
|
4167
|
-
"bg-gradient-to-r",
|
|
4168
|
-
"from-gray-200/80",
|
|
4169
|
-
"via-gray-300/60",
|
|
4170
|
-
"to-gray-200/80",
|
|
4171
|
-
// Pulse animation
|
|
4172
|
-
"animate-pulse",
|
|
4173
|
-
// Shimmer overlay
|
|
4174
|
-
"before:absolute",
|
|
4175
|
-
"before:inset-0",
|
|
4176
|
-
"before:bg-gradient-to-r",
|
|
4177
|
-
"before:from-transparent",
|
|
4178
|
-
"before:via-white/50",
|
|
4179
|
-
"before:to-transparent",
|
|
4180
|
-
"before:opacity-0",
|
|
4181
|
-
"before:animate-pulse",
|
|
4182
|
-
// Smooth transitions for accessibility
|
|
4183
|
-
"transition-all",
|
|
4184
|
-
"duration-300",
|
|
4185
|
-
"ease-in-out",
|
|
4186
|
-
]);
|
|
4187
|
-
|
|
4188
|
-
const VALID_SIZE_KEYS = [
|
|
4189
|
-
"xs",
|
|
4190
|
-
"sm",
|
|
4191
|
-
"base",
|
|
4192
|
-
"lg",
|
|
4193
|
-
"xl",
|
|
4194
|
-
"2xl",
|
|
4195
|
-
"3xl",
|
|
4196
|
-
"4xl",
|
|
4197
|
-
"5xl",
|
|
4198
|
-
"6xl",
|
|
4199
|
-
"7xl",
|
|
4200
|
-
"8xl",
|
|
4201
|
-
"9xl",
|
|
4202
|
-
];
|
|
4203
|
-
/**
|
|
4204
|
-
* Extract the size key from a text size string (e.g., "text-base" → "base").
|
|
4205
|
-
*
|
|
4206
|
-
* @param value - The text size string to parse
|
|
4207
|
-
* @returns {fontSizeKeys | null} The extracted size key or null if invalid
|
|
4208
|
-
*/
|
|
4209
|
-
const extractSizeKey = (value) => {
|
|
4210
|
-
if (!value.startsWith("text-")) {
|
|
4211
|
-
return null;
|
|
4212
|
-
}
|
|
4213
|
-
const sizeKey = value.replace("text-", "");
|
|
4214
|
-
return VALID_SIZE_KEYS.find(key => key === sizeKey) ?? null;
|
|
4215
|
-
};
|
|
4216
|
-
/**
|
|
4217
|
-
* Calculate the height value based on the height prop and variant.
|
|
4218
|
-
*
|
|
4219
|
-
* @param height - The height value (number, CSS length, or text size key)
|
|
4220
|
-
* @param variant - The skeleton variant ("text" or "block")
|
|
4221
|
-
* @returns {string} The calculated CSS height value
|
|
4222
|
-
*/
|
|
4223
|
-
const getHeightValue = (height, variant) => {
|
|
4224
|
-
if (typeof height === "number") {
|
|
4225
|
-
return `${height}px`;
|
|
4226
|
-
}
|
|
4227
|
-
const sizeKey = extractSizeKey(height);
|
|
4228
|
-
if (sizeKey) {
|
|
4229
|
-
// Text variant: use font-size × 0.7 to approximate cap-height
|
|
4230
|
-
// Block variant: use full line-height (for when text size keys are passed to block variant)
|
|
4231
|
-
return variant === "text" ? `calc(var(--font-size-${sizeKey}) * 0.7)` : `var(--line-height-${sizeKey})`;
|
|
4232
|
-
}
|
|
4233
|
-
return height;
|
|
4234
|
-
};
|
|
4235
|
-
/**
|
|
4236
|
-
* Calculate the vertical margin value for text variant to align with text baseline.
|
|
4237
|
-
* Formula: (line-height - cap-height) / 2, where cap-height = font-size × 0.7
|
|
4238
|
-
*
|
|
4239
|
-
* @param height - The height value (number, CSS length, or text size key)
|
|
4240
|
-
* @returns {string} The calculated CSS margin value
|
|
4241
|
-
*/
|
|
4242
|
-
const getMarginValue = (height) => {
|
|
4243
|
-
if (typeof height === "string") {
|
|
4244
|
-
const sizeKey = extractSizeKey(height);
|
|
4245
|
-
if (sizeKey) {
|
|
4246
|
-
// margin = (line-height - cap-height) / 2
|
|
4247
|
-
// cap-height = font-size × 0.7
|
|
4248
|
-
// For large text sizes, this may be negative, which matches how actual text extends beyond its line-height box
|
|
4249
|
-
return `calc((var(--line-height-${sizeKey}) - var(--font-size-${sizeKey}) * 0.7) / 2)`;
|
|
4250
|
-
}
|
|
4251
|
-
}
|
|
4252
|
-
return "0";
|
|
4253
|
-
};
|
|
4254
|
-
/**
|
|
4255
|
-
* Display a single placeholder line before data gets loaded to reduce load-time frustration.
|
|
4256
|
-
*
|
|
4257
|
-
* Use `variant="text"` with text-size keys for text placeholders.
|
|
4258
|
-
* Use `variant="block"` with numbers/CSS for images, badges, buttons, and other shape-based elements.
|
|
4259
|
-
* Pass children to create custom skeleton layouts.
|
|
4260
|
-
*/
|
|
4261
|
-
const Skeleton = react.memo((props) => {
|
|
4262
|
-
const { width = "100%", className, "data-testid": dataTestId, children } = props;
|
|
4263
|
-
const variant = props.variant ?? "text";
|
|
4264
|
-
const height = props.height ?? (variant === "text" ? "text-base" : 16);
|
|
4265
|
-
const flexibleWidth = props.flexibleWidth ?? variant === "text";
|
|
4266
|
-
const widthValue = typeof width === "number" ? `${width}px` : width;
|
|
4267
|
-
const isTextVariant = variant === "text";
|
|
4268
|
-
const heightValue = getHeightValue(height, variant);
|
|
4269
|
-
const marginValue = isTextVariant ? getMarginValue(height) : undefined;
|
|
4270
|
-
return (jsxRuntime.jsx("div", { "aria-label": "Loading", className: cvaSkeleton({ className }), "data-testid": dataTestId, role: "status", style: {
|
|
4271
|
-
width: flexibleWidth ? "100%" : widthValue,
|
|
4272
|
-
maxWidth: flexibleWidth ? widthValue : undefined,
|
|
4273
|
-
height: heightValue,
|
|
4274
|
-
marginTop: marginValue,
|
|
4275
|
-
marginBottom: marginValue,
|
|
4276
|
-
}, children: children }));
|
|
4277
|
-
});
|
|
4278
|
-
Skeleton.displayName = "Skeleton";
|
|
4279
|
-
|
|
4280
4401
|
const cvaListItem = cssClassVarianceUtilities.cvaMerge(["py-3", "px-4", "min-h-14", "w-full", "flex", "justify-between", "items-center"]);
|
|
4281
4402
|
const cvaMainInformationClass = cssClassVarianceUtilities.cvaMerge(["grid", "items-center", "text-sm", "gap-2"], {
|
|
4282
4403
|
variants: {
|
|
@@ -4325,7 +4446,7 @@ const ListItemSkeleton = ({ hasThumbnail = DEFAULT_SKELETON_LIST_ITEM_PROPS.hasT
|
|
|
4325
4446
|
details: getResponsiveRandomWidthPercentage({ min: 25, max: 45 }),
|
|
4326
4447
|
};
|
|
4327
4448
|
}, []);
|
|
4328
|
-
return (jsxRuntime.jsxs("div", { className: cvaListItem({ className: "w-full" }), children: [jsxRuntime.jsxs("div", { className: cvaMainInformationClass({ hasThumbnail, className: "w-full" }), children: [hasThumbnail ? (jsxRuntime.jsx("div", { className: cvaThumbnailContainer({ className: "bg-gray-200" }), children: jsxRuntime.jsx("div", { className: tailwindMerge.twMerge("bg-gray-300", thumbnailShape === "circle" ? "rounded-full" : "rounded"), style: { width: 20, height: 20 } }) })) : null, jsxRuntime.jsxs("div", { className: cvaTextContainer(), children: [jsxRuntime.jsx("div", { className: cvaTitleRow(), children: jsxRuntime.jsx(
|
|
4449
|
+
return (jsxRuntime.jsxs("div", { className: cvaListItem({ className: "w-full" }), children: [jsxRuntime.jsxs("div", { className: cvaMainInformationClass({ hasThumbnail, className: "w-full" }), children: [hasThumbnail ? (jsxRuntime.jsx("div", { className: cvaThumbnailContainer({ className: "bg-gray-200" }), children: jsxRuntime.jsx("div", { className: tailwindMerge.twMerge("bg-gray-300", thumbnailShape === "circle" ? "rounded-full" : "rounded"), style: { width: 20, height: 20 } }) })) : null, jsxRuntime.jsxs("div", { className: cvaTextContainer(), children: [jsxRuntime.jsx("div", { className: cvaTitleRow(), children: jsxRuntime.jsx(SkeletonLabel, { textSize: "text-sm", width: lineWidths.title }) }), hasDescription ? (jsxRuntime.jsx("div", { className: cvaDescriptionRow(), children: jsxRuntime.jsx(SkeletonLabel, { textSize: "text-xs", width: lineWidths.description }) })) : null, hasMeta ? (jsxRuntime.jsx("div", { className: cvaMetaRow(), children: jsxRuntime.jsx(SkeletonLabel, { textSize: "text-xs", width: lineWidths.meta }) })) : null] })] }), hasDetails ? (jsxRuntime.jsx("div", { className: cvaDetailsContainer(), children: jsxRuntime.jsx(SkeletonLabel, { textSize: "text-sm", width: lineWidths.details }) })) : null] }));
|
|
4329
4450
|
};
|
|
4330
4451
|
|
|
4331
4452
|
/**
|
|
@@ -5232,9 +5353,12 @@ const PageContent = ({ className, children, "data-testid": dataTestId, layout, }
|
|
|
5232
5353
|
return (jsxRuntime.jsx("div", { className: cvaPageContent({ className, layout }), "data-testid": dataTestId ? dataTestId : "page-content", children: children }));
|
|
5233
5354
|
};
|
|
5234
5355
|
|
|
5235
|
-
const LoadingContent = () => (jsxRuntime.jsx("div", { className: "flex flex-row items-center
|
|
5356
|
+
const LoadingContent = () => (jsxRuntime.jsx("div", { className: "flex flex-row items-center", "data-testid": "kpi-card-loading-content", children: jsxRuntime.jsx("div", { className: "w-full", children: jsxRuntime.jsx(SkeletonLines, { lines: [
|
|
5357
|
+
{ textSize: "text-xs", width: 50 },
|
|
5358
|
+
{ textSize: "text-lg", width: 40 },
|
|
5359
|
+
], variant: "custom" }) }) }));
|
|
5236
5360
|
/**
|
|
5237
|
-
*
|
|
5361
|
+
* Renders KPI metrics in the PageHeader component.
|
|
5238
5362
|
*
|
|
5239
5363
|
* @param {object} props - The props for the PageHeaderKpiMetrics component
|
|
5240
5364
|
* @param {Array<PageHeaderKpiMetricsType>} props.kpiMetrics - The KPI metrics to render
|
|
@@ -5473,7 +5597,7 @@ const Pagination = ({ previousPage, nextPage, canPreviousPage = false, canNextPa
|
|
|
5473
5597
|
onPageChange?.({ from, to, description });
|
|
5474
5598
|
}, [page, onPageChange, previousPage, nextPage]);
|
|
5475
5599
|
if (loading) {
|
|
5476
|
-
return (jsxRuntime.jsx("div", { className: cvaPagination({ className }), children: jsxRuntime.jsx(
|
|
5600
|
+
return (jsxRuntime.jsx("div", { className: cvaPagination({ className }), children: jsxRuntime.jsx(SkeletonLabel, { textSize: "text-sm", width: 150 }) }));
|
|
5477
5601
|
}
|
|
5478
5602
|
return (jsxRuntime.jsxs("div", { className: cvaPagination({ className }), "data-testid": dataTestId, children: [jsxRuntime.jsx(IconButton, { "data-testid": "prev-page", disabled: cursorBase ? !canPreviousPage || false : page !== undefined && page <= 0, icon: jsxRuntime.jsx(Icon, { name: "ChevronLeft", size: "small" }), onClick: () => handlePageChange("prev"), size: "small", variant: "ghost-neutral" }), !cursorBase && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { className: cvaPaginationText(), "data-testid": "current-page", children: currentPage }), jsxRuntime.jsx("div", { className: cvaPaginationText(), "data-testid": "page-count", children: pageCount !== null && pageCount !== undefined && getTranslatedCount ? getTranslatedCount(pageCount) : null })] })), jsxRuntime.jsx(IconButton, { "data-testid": "next-page", disabled: cursorBase
|
|
5479
5603
|
? !canNextPage || false
|
|
@@ -5712,7 +5836,7 @@ const PreferenceCardSkeleton = ({ hasIcon = DEFAULT_SKELETON_PREFERENCE_CARD_PRO
|
|
|
5712
5836
|
description: getRandomWidth(160, 240),
|
|
5713
5837
|
};
|
|
5714
5838
|
}, []);
|
|
5715
|
-
return (jsxRuntime.jsxs("div", { className: cvaPreferenceCard(), children: [hasInput ? (jsxRuntime.jsx("div", { className: cvaInputContainer({ itemPlacement: "center" }), children: jsxRuntime.jsx(
|
|
5839
|
+
return (jsxRuntime.jsxs("div", { className: cvaPreferenceCard(), children: [hasInput ? (jsxRuntime.jsx("div", { className: cvaInputContainer({ itemPlacement: "center" }), children: jsxRuntime.jsx(SkeletonBlock, { height: 20, width: 20 }) })) : null, jsxRuntime.jsx("div", { className: cvaContentWrapper(), children: jsxRuntime.jsx(GridAreas, { ...gridAreas, className: cvaContentContainer({ itemPlacement: "center" }), children: slots => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [hasIcon ? (jsxRuntime.jsx("div", { ...slots.icon, children: jsxRuntime.jsx(SkeletonBlock, { className: cvaIconBackground({ disabled: false }), height: 32, width: 32 }) })) : null, jsxRuntime.jsxs("div", { ...slots.information, className: "min-w-0 flex-1", children: [jsxRuntime.jsx("div", { className: "grid min-w-0 grid-cols-[1fr_auto] items-center gap-2", children: jsxRuntime.jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [jsxRuntime.jsx(SkeletonLabel, { textSize: "text-sm", width: lineWidths.title }), hasTitleTag ? jsxRuntime.jsx(TagSkeleton, { size: "small" }) : null] }) }), jsxRuntime.jsx(SkeletonLabel, { textSize: "text-xs", width: lineWidths.description })] }), hasCardTag ? (jsxRuntime.jsx("div", { ...slots.cardTag, className: "justify-self-end", children: jsxRuntime.jsx(TagSkeleton, { size: "medium" }) })) : null] })) }) })] }));
|
|
5716
5840
|
};
|
|
5717
5841
|
/**
|
|
5718
5842
|
* Simple tag skeleton for use within PreferenceCardSkeleton.
|
|
@@ -5721,7 +5845,7 @@ const PreferenceCardSkeleton = ({ hasIcon = DEFAULT_SKELETON_PREFERENCE_CARD_PRO
|
|
|
5721
5845
|
const TagSkeleton = ({ size }) => {
|
|
5722
5846
|
const width = react.useMemo(() => getRandomWidth(40, 64), []);
|
|
5723
5847
|
const height = size === "small" ? 18 : 24;
|
|
5724
|
-
return jsxRuntime.jsx(
|
|
5848
|
+
return jsxRuntime.jsx(SkeletonBlock, { className: "rounded-full", height: height, width: width });
|
|
5725
5849
|
};
|
|
5726
5850
|
|
|
5727
5851
|
function useConfirmExit(confirmExit, when = true) {
|
|
@@ -7194,6 +7318,8 @@ exports.IconButton = IconButton;
|
|
|
7194
7318
|
exports.Indicator = Indicator;
|
|
7195
7319
|
exports.KPI = KPI;
|
|
7196
7320
|
exports.KPICard = KPICard;
|
|
7321
|
+
exports.KPICardSkeleton = KPICardSkeleton;
|
|
7322
|
+
exports.KPISkeleton = KPISkeleton;
|
|
7197
7323
|
exports.List = List;
|
|
7198
7324
|
exports.ListItem = ListItem;
|
|
7199
7325
|
exports.MenuDivider = MenuDivider;
|
|
@@ -7221,7 +7347,8 @@ exports.Prompt = Prompt;
|
|
|
7221
7347
|
exports.ROLE_CARD = ROLE_CARD;
|
|
7222
7348
|
exports.SectionHeader = SectionHeader;
|
|
7223
7349
|
exports.Sidebar = Sidebar;
|
|
7224
|
-
exports.
|
|
7350
|
+
exports.SkeletonBlock = SkeletonBlock;
|
|
7351
|
+
exports.SkeletonLabel = SkeletonLabel;
|
|
7225
7352
|
exports.SkeletonLines = SkeletonLines;
|
|
7226
7353
|
exports.Spacer = Spacer;
|
|
7227
7354
|
exports.Spinner = Spinner;
|