@trackunit/react-components 1.9.7 → 1.9.9
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 +438 -165
- package/index.esm.js +443 -173
- package/package.json +7 -7
- package/src/common/Styleable.d.ts +3 -0
- package/src/common/index.d.ts +1 -0
- package/src/components/HorizontalOverflowScroller/HorizontalOverflowScroller.d.ts +21 -0
- package/src/components/HorizontalOverflowScroller/HorizontalOverflowScroller.variants.d.ts +2 -0
- package/src/components/HorizontalOverflowScroller/OverflowIndicator.d.ts +22 -0
- package/src/components/HorizontalOverflowScroller/OverflowIndicator.variants.d.ts +8 -0
- package/src/components/KPI/KPI.d.ts +3 -3
- package/src/components/List/List.d.ts +109 -19
- package/src/components/List/List.variants.d.ts +2 -2
- package/src/components/List/ListLoadingIndicator.d.ts +71 -0
- package/src/components/ListItem/ListItem.d.ts +11 -3
- package/src/components/ListItem/ListItemSkeleton.d.ts +25 -0
- package/src/components/SkeletonLines/SkeletonLines.d.ts +9 -8
- package/src/components/SkeletonLines/SkeletonLines.variants.d.ts +1 -0
- package/src/components/SkeletonLines/index.d.ts +1 -0
- package/src/components/SkeletonLines/skeleton-utils.d.ts +12 -0
- package/src/components/Tooltip/Tooltip.d.ts +3 -3
- package/src/components/index.d.ts +1 -0
- package/src/hooks/index.d.ts +1 -0
- package/src/hooks/useGeometry.d.ts +5 -1
- package/src/hooks/useResize.d.ts +6 -1
- package/src/hooks/useScrollDetection.d.ts +19 -11
- package/src/hooks/useStable.d.ts +22 -0
package/index.esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
|
-
import { useRef, useMemo, useEffect, useState, useCallback, createElement, useReducer, forwardRef, Fragment
|
|
1
|
+
import { jsx, jsxs, Fragment as Fragment$1 } from 'react/jsx-runtime';
|
|
2
|
+
import { useRef, useMemo, useEffect, useState, useCallback, createElement, useReducer, forwardRef, Fragment, memo, Children, isValidElement, cloneElement, createContext, useContext } from 'react';
|
|
3
3
|
import { objectKeys, uuidv4, objectEntries, objectValues, nonNullable } from '@trackunit/shared-utils';
|
|
4
4
|
import { intentPalette, generalPalette, criticalityPalette, activityPalette, utilizationPalette, sitesPalette, rentalStatusPalette, themeScreenSizeAsNumber, color } from '@trackunit/ui-design-tokens';
|
|
5
5
|
import { iconNames } from '@trackunit/ui-icons';
|
|
@@ -15,7 +15,7 @@ import { Link, useBlocker } from '@tanstack/react-router';
|
|
|
15
15
|
import { useFloating, autoUpdate, offset, flip, shift, size, useClick, useDismiss, useHover as useHover$1, useRole, useInteractions, FloatingPortal, useMergeRefs, FloatingFocusManager, arrow, useTransitionStatus, FloatingArrow } from '@floating-ui/react';
|
|
16
16
|
import { omit } from 'es-toolkit';
|
|
17
17
|
import { twMerge } from 'tailwind-merge';
|
|
18
|
-
import {
|
|
18
|
+
import { useInfiniteScroll, noPagination } from '@trackunit/react-table-pagination';
|
|
19
19
|
import { HelmetProvider, Helmet } from 'react-helmet-async';
|
|
20
20
|
import { Trigger, Content, List as List$1, Root } from '@radix-ui/react-tabs';
|
|
21
21
|
|
|
@@ -1311,7 +1311,7 @@ const useElevatedState = (initialState, customState) => {
|
|
|
1311
1311
|
* Custom hook to get the geometry of an element.
|
|
1312
1312
|
* Size and position of the element relative to the viewport.
|
|
1313
1313
|
*/
|
|
1314
|
-
const useGeometry = (ref) => {
|
|
1314
|
+
const useGeometry = (ref, { skip = false } = {}) => {
|
|
1315
1315
|
const [geometry, setGeometry] = useState({
|
|
1316
1316
|
width: 0,
|
|
1317
1317
|
height: 0,
|
|
@@ -1324,7 +1324,7 @@ const useGeometry = (ref) => {
|
|
|
1324
1324
|
});
|
|
1325
1325
|
const resizeObserver = useRef(null);
|
|
1326
1326
|
useEffect(() => {
|
|
1327
|
-
if (!ref.current) {
|
|
1327
|
+
if (skip || !ref.current) {
|
|
1328
1328
|
return;
|
|
1329
1329
|
}
|
|
1330
1330
|
const observe = () => {
|
|
@@ -1355,7 +1355,7 @@ const useGeometry = (ref) => {
|
|
|
1355
1355
|
resizeObserver.current.disconnect();
|
|
1356
1356
|
}
|
|
1357
1357
|
};
|
|
1358
|
-
}, [ref]);
|
|
1358
|
+
}, [ref, skip]);
|
|
1359
1359
|
return geometry;
|
|
1360
1360
|
};
|
|
1361
1361
|
|
|
@@ -1484,17 +1484,21 @@ const useModifierKey = ({ exclude = [] } = {}) => {
|
|
|
1484
1484
|
/**
|
|
1485
1485
|
* Custom hook to handle window resize events and provide the current window size.
|
|
1486
1486
|
*
|
|
1487
|
+
* @param {UseResizeOptions} options - Options for the hook.
|
|
1487
1488
|
* @returns {WindowSize} An object containing the current window height and width.
|
|
1488
1489
|
*/
|
|
1489
|
-
const useResize = () => {
|
|
1490
|
+
const useResize = ({ skip = false } = {}) => {
|
|
1490
1491
|
const [size, setSize] = useState(getWindowSize());
|
|
1491
1492
|
useEffect(() => {
|
|
1493
|
+
if (skip) {
|
|
1494
|
+
return;
|
|
1495
|
+
}
|
|
1492
1496
|
const handleResize = () => {
|
|
1493
1497
|
setSize(getWindowSize());
|
|
1494
1498
|
};
|
|
1495
1499
|
window.addEventListener("resize", handleResize, false);
|
|
1496
1500
|
return () => window.removeEventListener("resize", handleResize);
|
|
1497
|
-
}, [setSize]);
|
|
1501
|
+
}, [setSize, skip]);
|
|
1498
1502
|
return size;
|
|
1499
1503
|
};
|
|
1500
1504
|
/**
|
|
@@ -1519,36 +1523,73 @@ const getWindowSize = () => {
|
|
|
1519
1523
|
|
|
1520
1524
|
const SCROLL_DEBOUNCE_TIME = 50;
|
|
1521
1525
|
/**
|
|
1522
|
-
* Hook for
|
|
1526
|
+
* Hook for detecting scroll values in horizontal or vertical direction.
|
|
1523
1527
|
*
|
|
1524
1528
|
* @param {useRef} elementRef - Ref hook holding the element that needs to be observed during scrolling
|
|
1525
|
-
* @
|
|
1529
|
+
* @param {object} options - Options object containing direction and onScrollStateChange callback
|
|
1530
|
+
* @returns {object} An object containing if the element is scrollable, is at the beginning, is at the end, and its current scroll position.
|
|
1526
1531
|
*/
|
|
1527
|
-
const useScrollDetection = (elementRef) => {
|
|
1532
|
+
const useScrollDetection = (elementRef, options) => {
|
|
1533
|
+
const { direction = "vertical", onScrollStateChange } = options ?? {};
|
|
1528
1534
|
const [isScrollable, setIsScrollable] = useState(false);
|
|
1529
|
-
const [
|
|
1530
|
-
const [
|
|
1531
|
-
const [scrollPosition, setScrollPosition] = useState({
|
|
1535
|
+
const [isAtBeginning, setIsAtBeginning] = useState(true);
|
|
1536
|
+
const [isAtEnd, setIsAtEnd] = useState(false);
|
|
1537
|
+
const [scrollPosition, setScrollPosition] = useState({ start: 0, end: 0 });
|
|
1532
1538
|
const observerRef = useRef(null);
|
|
1539
|
+
const isFirstRender = useIsFirstRender();
|
|
1533
1540
|
const checkScrollable = useCallback(() => {
|
|
1534
1541
|
const element = elementRef?.current;
|
|
1535
1542
|
if (!element) {
|
|
1536
1543
|
return;
|
|
1537
1544
|
}
|
|
1538
|
-
const hasOverflow =
|
|
1539
|
-
|
|
1540
|
-
|
|
1545
|
+
const hasOverflow = direction === "horizontal"
|
|
1546
|
+
? element.scrollWidth > element.clientWidth
|
|
1547
|
+
: element.scrollHeight > element.clientHeight;
|
|
1548
|
+
setIsScrollable(prev => {
|
|
1549
|
+
if (prev !== hasOverflow) {
|
|
1550
|
+
// State will be updated, so we'll notify in the next effect
|
|
1551
|
+
return hasOverflow;
|
|
1552
|
+
}
|
|
1553
|
+
return prev;
|
|
1554
|
+
});
|
|
1555
|
+
}, [elementRef, direction]);
|
|
1541
1556
|
const checkScrollPosition = useCallback(() => {
|
|
1542
1557
|
const element = elementRef?.current;
|
|
1543
1558
|
if (!element) {
|
|
1544
1559
|
return;
|
|
1545
1560
|
}
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1561
|
+
if (direction === "horizontal") {
|
|
1562
|
+
const { scrollLeft, scrollWidth, clientWidth } = element;
|
|
1563
|
+
const newIsAtBeginning = scrollLeft === 0;
|
|
1564
|
+
const newIsAtEnd = Math.abs(scrollWidth - scrollLeft - clientWidth) <= 1;
|
|
1565
|
+
const newScrollPosition = { start: scrollLeft, end: clientWidth - scrollLeft };
|
|
1566
|
+
setIsAtBeginning(newIsAtBeginning);
|
|
1567
|
+
setIsAtEnd(newIsAtEnd);
|
|
1568
|
+
setScrollPosition(newScrollPosition);
|
|
1569
|
+
}
|
|
1570
|
+
else {
|
|
1571
|
+
const { scrollTop, scrollHeight, clientHeight } = element;
|
|
1572
|
+
const newIsAtBeginning = scrollTop === 0;
|
|
1573
|
+
const newIsAtEnd = Math.abs(scrollHeight - scrollTop - clientHeight) <= 1;
|
|
1574
|
+
const newScrollPosition = { start: scrollTop, end: clientHeight - scrollTop };
|
|
1575
|
+
setIsAtBeginning(newIsAtBeginning);
|
|
1576
|
+
setIsAtEnd(newIsAtEnd);
|
|
1577
|
+
setScrollPosition(newScrollPosition);
|
|
1578
|
+
}
|
|
1579
|
+
}, [elementRef, direction]);
|
|
1551
1580
|
const debouncedCheckScrollPosition = useDebounceCallback(checkScrollPosition, SCROLL_DEBOUNCE_TIME);
|
|
1581
|
+
// Notify about state changes whenever any state value changes
|
|
1582
|
+
useEffect(() => {
|
|
1583
|
+
if (isFirstRender) {
|
|
1584
|
+
return;
|
|
1585
|
+
}
|
|
1586
|
+
onScrollStateChange?.({
|
|
1587
|
+
isScrollable,
|
|
1588
|
+
isAtBeginning,
|
|
1589
|
+
isAtEnd,
|
|
1590
|
+
scrollPosition,
|
|
1591
|
+
});
|
|
1592
|
+
}, [isScrollable, isAtBeginning, isAtEnd, scrollPosition, onScrollStateChange, isFirstRender]);
|
|
1552
1593
|
useEffect(() => {
|
|
1553
1594
|
const element = elementRef?.current;
|
|
1554
1595
|
if (!element) {
|
|
@@ -1570,7 +1611,7 @@ const useScrollDetection = (elementRef) => {
|
|
|
1570
1611
|
element.removeEventListener("scroll", debouncedCheckScrollPosition);
|
|
1571
1612
|
};
|
|
1572
1613
|
}, [elementRef, checkScrollable, checkScrollPosition, debouncedCheckScrollPosition]);
|
|
1573
|
-
return { isScrollable,
|
|
1614
|
+
return useMemo(() => ({ isScrollable, isAtBeginning, isAtEnd, scrollPosition }), [isScrollable, isAtBeginning, isAtEnd, scrollPosition]);
|
|
1574
1615
|
};
|
|
1575
1616
|
|
|
1576
1617
|
/**
|
|
@@ -1586,6 +1627,36 @@ const useSelfUpdatingRef = (initialState) => {
|
|
|
1586
1627
|
return stateRef;
|
|
1587
1628
|
};
|
|
1588
1629
|
|
|
1630
|
+
/**
|
|
1631
|
+
* The useStable hook ensures that a value is computed only once and remains stable across renders.
|
|
1632
|
+
* This is useful for expensive computations or when you need a value to never change after initial creation.
|
|
1633
|
+
* Perfect for generating stable random values, creating stable object references, or any one-time computation.
|
|
1634
|
+
*
|
|
1635
|
+
* @example
|
|
1636
|
+
* // Stable random ID that never changes
|
|
1637
|
+
* const stableId = useStable(() => Math.random().toString(36));
|
|
1638
|
+
* @example
|
|
1639
|
+
* // Stable configuration object
|
|
1640
|
+
* const config = useStable(() => ({
|
|
1641
|
+
* apiKey: generateApiKey(),
|
|
1642
|
+
* timestamp: Date.now()
|
|
1643
|
+
* }));
|
|
1644
|
+
* @example
|
|
1645
|
+
* // Stable random widths for skeleton loading
|
|
1646
|
+
* const lineWidths = useStable(() => ({
|
|
1647
|
+
* title: Math.floor(Math.random() * 100) + 120,
|
|
1648
|
+
* description: Math.floor(Math.random() * 80) + 80
|
|
1649
|
+
* }));
|
|
1650
|
+
*/
|
|
1651
|
+
const useStable = (factory) => {
|
|
1652
|
+
const ref = useRef(null);
|
|
1653
|
+
// Compute value only once and store it
|
|
1654
|
+
if (ref.current === null) {
|
|
1655
|
+
ref.current = { value: factory() };
|
|
1656
|
+
}
|
|
1657
|
+
return ref.current.value;
|
|
1658
|
+
};
|
|
1659
|
+
|
|
1589
1660
|
/**
|
|
1590
1661
|
* A custom React hook that provides real-time information about the current viewport size.
|
|
1591
1662
|
* ! Consider using `useContainerBreakpoints` instead, and only use this when you need to actually react to the viewport size, not the container size.
|
|
@@ -1755,11 +1826,9 @@ const useBreadcrumbItemsToRender = (breadcrumbItems) => {
|
|
|
1755
1826
|
const breadCrumbItemsToJSX = breadcrumbItems.map((item, index, array) => {
|
|
1756
1827
|
const isLast = index === array.length - 1;
|
|
1757
1828
|
if (!isLast) {
|
|
1758
|
-
return (jsxs(
|
|
1759
|
-
}
|
|
1760
|
-
else {
|
|
1761
|
-
return (jsx(Text, { className: "text-nowrap", size: "small", children: item.label }));
|
|
1829
|
+
return (jsxs("div", { children: [jsx(Button, { asChild: true, size: "small", variant: "ghost-neutral", children: jsx(Link, { to: item.to, children: item.label }) }), jsx(Icon, { className: "text-secondary-300", name: "Slash", size: "small" })] }, index));
|
|
1762
1830
|
}
|
|
1831
|
+
return (jsx(Text, { className: "text-nowrap", size: "small", children: item.label }, index));
|
|
1763
1832
|
});
|
|
1764
1833
|
return breadCrumbItemsToJSX;
|
|
1765
1834
|
};
|
|
@@ -2180,45 +2249,66 @@ const cvaDetailsListItem = cvaMerge(["last:truncate"]);
|
|
|
2180
2249
|
* @returns {ReactElement} The details list element.
|
|
2181
2250
|
*/
|
|
2182
2251
|
const DetailsList = ({ details, className, density = "default" }) => {
|
|
2183
|
-
return (jsx("div", { className: cvaDetailsList({ className, density }), children: details.map((value, index, array) => (jsxs(Fragment
|
|
2252
|
+
return (jsx("div", { className: cvaDetailsList({ className, density }), 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))) }));
|
|
2184
2253
|
};
|
|
2185
2254
|
|
|
2255
|
+
/**
|
|
2256
|
+
* Generates a random width percentage string for skeleton loading components.
|
|
2257
|
+
*
|
|
2258
|
+
* @param {object} params - The parameter object
|
|
2259
|
+
* @param {number} params.min - Minimum percentage value (e.g., 30 for 30%)
|
|
2260
|
+
* @param {number} params.max - Maximum percentage value (e.g., 80 for 80%)
|
|
2261
|
+
* @returns {string} A percentage string (e.g., "65%")
|
|
2262
|
+
*/
|
|
2263
|
+
const getResponsiveRandomWidthPercentage = ({ min, max }) => {
|
|
2264
|
+
const randomWidth = Math.floor(Math.random() * (max - min + 1)) + min;
|
|
2265
|
+
return `${randomWidth}%`;
|
|
2266
|
+
};
|
|
2267
|
+
|
|
2268
|
+
const cvaSkeletonContainer = cvaMerge(["flex", "flex-col"]);
|
|
2186
2269
|
const cvaSkeletonLine = cvaMerge([
|
|
2187
|
-
"
|
|
2188
|
-
"
|
|
2189
|
-
"
|
|
2190
|
-
|
|
2191
|
-
"bg-black/50",
|
|
2192
|
-
"bg-auto",
|
|
2270
|
+
"relative",
|
|
2271
|
+
"overflow-hidden",
|
|
2272
|
+
"rounded-lg",
|
|
2273
|
+
// Gradient background
|
|
2193
2274
|
"bg-gradient-to-r",
|
|
2194
|
-
"from-
|
|
2195
|
-
"via-
|
|
2196
|
-
"to-
|
|
2275
|
+
"from-gray-200/80",
|
|
2276
|
+
"via-gray-300/60",
|
|
2277
|
+
"to-gray-200/80",
|
|
2278
|
+
// Pulse animation
|
|
2279
|
+
"animate-pulse",
|
|
2280
|
+
// Shimmer overlay
|
|
2281
|
+
"before:absolute",
|
|
2282
|
+
"before:inset-0",
|
|
2283
|
+
"before:bg-gradient-to-r",
|
|
2284
|
+
"before:from-transparent",
|
|
2285
|
+
"before:via-white/50",
|
|
2286
|
+
"before:to-transparent",
|
|
2287
|
+
"before:opacity-0",
|
|
2288
|
+
"before:animate-pulse",
|
|
2289
|
+
// Smooth transitions for accessibility
|
|
2290
|
+
"transition-all",
|
|
2291
|
+
"duration-300",
|
|
2292
|
+
"ease-in-out",
|
|
2197
2293
|
]);
|
|
2198
2294
|
|
|
2199
2295
|
/**
|
|
2200
2296
|
* Display placeholder lines before the data gets loaded to reduce load-time frustration.
|
|
2201
2297
|
*/
|
|
2202
|
-
const SkeletonLines = memo(({
|
|
2203
|
-
const
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
return width;
|
|
2209
|
-
};
|
|
2210
|
-
const getHeight = (index) => {
|
|
2211
|
-
if (Array.isArray(height)) {
|
|
2212
|
-
return height[index] ?? "auto";
|
|
2213
|
-
}
|
|
2214
|
-
return height;
|
|
2215
|
-
};
|
|
2216
|
-
for (let i = 0; i < lines; i++) {
|
|
2217
|
-
skeletonLines.push(jsx("div", { className: cvaSkeletonLine({ className }), "data-testid": dataTestId ? `${dataTestId}-${i}` : `skeleton-lines-${i}`, "data-type": "loading-skeleton-line", style: { height: getHeight(i), width: getWidth(i), margin: lines > 1 && i >= 1 ? margin : "" }, children: lines > 1 && jsx(Fragment, { children: "\u00A0" }) }, i));
|
|
2218
|
-
}
|
|
2219
|
-
return jsx(Fragment, { children: skeletonLines });
|
|
2298
|
+
const SkeletonLines = memo(({ lines = 1, height = "0.75rem", width = "100%", margin = 10, className, dataTestId }) => {
|
|
2299
|
+
const gapStyle = typeof margin === "number" ? `${margin}px` : margin;
|
|
2300
|
+
return (jsx("div", { "aria-label": `Loading ${lines} ${lines === 1 ? "item" : "items"}`, className: cvaSkeletonContainer({ className }), "data-testid": dataTestId, role: "status", style: { gap: gapStyle }, children: Array.from({ length: lines }, (_, index) => (jsx("div", { className: cvaSkeletonLine(), "data-testid": dataTestId ? `${dataTestId}-${index}` : `skeleton-lines-${index}`, "data-type": "loading-skeleton-line", style: {
|
|
2301
|
+
width: getDimension(width, index),
|
|
2302
|
+
height: getDimension(height, index),
|
|
2303
|
+
} }, index))) }));
|
|
2220
2304
|
});
|
|
2221
|
-
|
|
2305
|
+
const getDimension = (dimension, index) => {
|
|
2306
|
+
if (Array.isArray(dimension)) {
|
|
2307
|
+
const value = dimension[index] ?? dimension[0] ?? "100%";
|
|
2308
|
+
return typeof value === "number" ? `${value}px` : value;
|
|
2309
|
+
}
|
|
2310
|
+
return typeof dimension === "number" ? `${dimension}px` : dimension;
|
|
2311
|
+
};
|
|
2222
2312
|
|
|
2223
2313
|
const cvaContainerStyles = cvaMerge([
|
|
2224
2314
|
"flex",
|
|
@@ -2311,7 +2401,7 @@ const EmptyState = ({ description, altText, image = "SEARCH_DOCUMENT", customIma
|
|
|
2311
2401
|
return SearchDocumentSVG;
|
|
2312
2402
|
}
|
|
2313
2403
|
}, [image]);
|
|
2314
|
-
return (jsx("div", { className: cvaContainerStyles({ className }), "data-testid": dataTestId ?? "empty-state", children: loading ? (jsxs(Fragment, { children: [jsx(Spinner, { centering: "centered", dataTestId: "spinner" }), jsx(SkeletonLines, { dataTestId: "skeleton-lines", width: 50 })] })) : (jsxs(Fragment, { 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, { dataTestId: "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, { dataTestId: "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, dataTestId: "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] })] })) }));
|
|
2404
|
+
return (jsx("div", { className: cvaContainerStyles({ className }), "data-testid": dataTestId ?? "empty-state", children: loading ? (jsxs(Fragment$1, { children: [jsx(Spinner, { centering: "centered", dataTestId: "spinner" }), jsx(SkeletonLines, { dataTestId: "skeleton-lines", width: 50 })] })) : (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, { dataTestId: "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, { dataTestId: "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, dataTestId: "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] })] })) }));
|
|
2315
2405
|
};
|
|
2316
2406
|
|
|
2317
2407
|
const cvaEmptyValue = cvaMerge(["text-neutral-400"]);
|
|
@@ -2390,6 +2480,119 @@ const Highlight = ({ className, dataTestId, children, size = "small", color = "w
|
|
|
2390
2480
|
};
|
|
2391
2481
|
Highlight.displayName = "Highlight";
|
|
2392
2482
|
|
|
2483
|
+
const cvaZStackContainer = cvaMerge(["grid", "grid-cols-1", "grid-rows-1"]);
|
|
2484
|
+
const cvaZStackItem = cvaMerge(["col-start-1", "col-end-1", "row-start-1", "row-end-2"]);
|
|
2485
|
+
|
|
2486
|
+
/**
|
|
2487
|
+
* ZStack is a component that stacks its children on the z-axis.
|
|
2488
|
+
* Is a good alternative to "position: absolute" that avoids some of the unfortunate side effects of absolute positioning.
|
|
2489
|
+
*
|
|
2490
|
+
* @param { ZStackProps} props - The props for the ZStack component
|
|
2491
|
+
* @returns {Element} ZStack component
|
|
2492
|
+
*/
|
|
2493
|
+
const ZStack = ({ children, className, dataTestId }) => {
|
|
2494
|
+
return (jsx("div", { className: cvaZStackContainer({ className }), "data-testid": dataTestId, children: Children.map(children, (child, index) => {
|
|
2495
|
+
if (!isValidElement(child)) {
|
|
2496
|
+
return child;
|
|
2497
|
+
}
|
|
2498
|
+
return cloneElement(child, {
|
|
2499
|
+
className: cvaZStackItem({ className: child.props.className }),
|
|
2500
|
+
key: index,
|
|
2501
|
+
});
|
|
2502
|
+
}) }));
|
|
2503
|
+
};
|
|
2504
|
+
|
|
2505
|
+
const cvaHorizontalOverflowScroller = cvaMerge([
|
|
2506
|
+
"flex",
|
|
2507
|
+
"flex-nowrap",
|
|
2508
|
+
"gap-1",
|
|
2509
|
+
"overflow-y-hidden",
|
|
2510
|
+
"overflow-x-scroll",
|
|
2511
|
+
"w-full",
|
|
2512
|
+
"no-scrollbar",
|
|
2513
|
+
]);
|
|
2514
|
+
const cvaHorizontalOverflowScrollerAndIndicatorsContainer = cvaMerge(["group", "w-full", "overflow-clip"]);
|
|
2515
|
+
|
|
2516
|
+
const cvaOverflowIndicatorContainer = cvaMerge(["pointer-events-none", "h-full", "w-full", "isolate"]);
|
|
2517
|
+
const cvaOverflowIndicatorGradient = cvaMerge(["pointer-events-none", "h-full", "w-8"], {
|
|
2518
|
+
variants: {
|
|
2519
|
+
direction: {
|
|
2520
|
+
left: ["bg-gradient-to-r", "from-white", "to-transparent"],
|
|
2521
|
+
right: ["bg-gradient-to-l", "from-white", "to-transparent"],
|
|
2522
|
+
},
|
|
2523
|
+
},
|
|
2524
|
+
});
|
|
2525
|
+
const cvaJustificationContainer = cvaMerge(["flex", "w-full", "items-center", "pointer-events-none"], {
|
|
2526
|
+
variants: {
|
|
2527
|
+
direction: {
|
|
2528
|
+
left: ["justify-start"],
|
|
2529
|
+
right: ["justify-end"],
|
|
2530
|
+
},
|
|
2531
|
+
},
|
|
2532
|
+
});
|
|
2533
|
+
const cvaOverflowIndicatorButton = cvaMerge([
|
|
2534
|
+
"shadow-md",
|
|
2535
|
+
"opacity-0",
|
|
2536
|
+
"transition-opacity",
|
|
2537
|
+
"duration-200",
|
|
2538
|
+
"pointer-events-auto",
|
|
2539
|
+
"starting:opacity-0",
|
|
2540
|
+
"group-hover:opacity-100",
|
|
2541
|
+
]);
|
|
2542
|
+
|
|
2543
|
+
/**
|
|
2544
|
+
* Overflow indicator component that shows visual cues when content extends beyond visible area
|
|
2545
|
+
* Shows a scroll button on hover to navigate in the specified direction
|
|
2546
|
+
*
|
|
2547
|
+
* @param {OverflowIndicatorProps} props - The props for the component
|
|
2548
|
+
* @returns {ReactElement} OverflowIndicator component
|
|
2549
|
+
*/
|
|
2550
|
+
const OverflowIndicator = ({ className, dataTestId, direction, onClickScroll, }) => {
|
|
2551
|
+
const iconName = direction === "left" ? "ChevronLeft" : "ChevronRight";
|
|
2552
|
+
return (jsxs(ZStack, { className: cvaOverflowIndicatorContainer({ className }), dataTestId: dataTestId, children: [jsx("div", { className: cvaJustificationContainer({ direction }), children: jsx("div", { className: cvaOverflowIndicatorGradient({ direction }), "data-testid": dataTestId ? `${dataTestId}-gradient` : undefined }) }), jsx("div", { className: cvaJustificationContainer({ direction }), children: jsx(IconButton, { circular: true, className: cvaOverflowIndicatorButton(), "data-testid": dataTestId ? `${dataTestId}-button` : undefined, icon: jsx(Icon, { name: iconName, size: "small" }), onClick: onClickScroll, size: "small", variant: "secondary" }) })] }));
|
|
2553
|
+
};
|
|
2554
|
+
|
|
2555
|
+
/**
|
|
2556
|
+
* Container for displaying components in a horizontal layout with overflow detection
|
|
2557
|
+
*
|
|
2558
|
+
* @param {HorizontalOverflowScrollerProps} props - The props for the component
|
|
2559
|
+
* @returns {Element} HorizontalOverflowScroller component
|
|
2560
|
+
*/
|
|
2561
|
+
const HorizontalOverflowScroller = ({ className, dataTestId, children, onScrollStateChange, }) => {
|
|
2562
|
+
const containerRef = useRef(null);
|
|
2563
|
+
const childrenArray = Children.toArray(children);
|
|
2564
|
+
const { width: containerWidth } = useGeometry(containerRef);
|
|
2565
|
+
const { isScrollable, isAtBeginning, isAtEnd } = useScrollDetection(containerRef, {
|
|
2566
|
+
direction: "horizontal",
|
|
2567
|
+
onScrollStateChange: onScrollStateChange
|
|
2568
|
+
? (state) => onScrollStateChange({
|
|
2569
|
+
isScrollable: state.isScrollable,
|
|
2570
|
+
isAtBeginning: state.isAtBeginning,
|
|
2571
|
+
isAtEnd: state.isAtEnd,
|
|
2572
|
+
})
|
|
2573
|
+
: undefined,
|
|
2574
|
+
});
|
|
2575
|
+
const handleScrollLeft = () => {
|
|
2576
|
+
const element = containerRef.current;
|
|
2577
|
+
if (!element || !containerWidth)
|
|
2578
|
+
return;
|
|
2579
|
+
element.scrollBy({
|
|
2580
|
+
left: -containerWidth,
|
|
2581
|
+
behavior: "smooth",
|
|
2582
|
+
});
|
|
2583
|
+
};
|
|
2584
|
+
const handleScrollRight = () => {
|
|
2585
|
+
const element = containerRef.current;
|
|
2586
|
+
if (!element || !containerWidth)
|
|
2587
|
+
return;
|
|
2588
|
+
element.scrollBy({
|
|
2589
|
+
left: containerWidth,
|
|
2590
|
+
behavior: "smooth",
|
|
2591
|
+
});
|
|
2592
|
+
};
|
|
2593
|
+
return (jsxs(ZStack, { className: cvaHorizontalOverflowScrollerAndIndicatorsContainer({ className }), children: [jsx("div", { className: cvaHorizontalOverflowScroller({ className }), "data-testid": dataTestId, ref: containerRef, children: childrenArray.map((child, index) => (jsx(Fragment, { children: child }, index))) }), isScrollable && !isAtBeginning ? (jsx(OverflowIndicator, { dataTestId: `${dataTestId}-left-indicator`, direction: "left", onClickScroll: handleScrollLeft })) : null, isScrollable && !isAtEnd ? (jsx(OverflowIndicator, { dataTestId: `${dataTestId}-right-indicator`, direction: "right", onClickScroll: handleScrollRight })) : null] }));
|
|
2594
|
+
};
|
|
2595
|
+
|
|
2393
2596
|
const PADDING = 12;
|
|
2394
2597
|
/**
|
|
2395
2598
|
* Converts a width size value into a CSS dimension value for max constraints
|
|
@@ -2774,7 +2977,7 @@ const FloatingArrowContainer = ({ arrowRef, mode = "dark", }) => {
|
|
|
2774
2977
|
* @param {TooltipProps} props - The props for the Tooltip component
|
|
2775
2978
|
* @returns {ReactElement} Tooltip component
|
|
2776
2979
|
*/
|
|
2777
|
-
const Tooltip = ({ children, dataTestId, disabled = false, className, label, placement = "auto", mode = "dark", iconProps, id, }) => {
|
|
2980
|
+
const Tooltip = ({ children, dataTestId, disabled = false, className, label, placement = "auto", mode = "dark", iconProps, id, style, }) => {
|
|
2778
2981
|
const [isOpen, setIsOpen] = useState(false);
|
|
2779
2982
|
const arrowRef = useRef(null);
|
|
2780
2983
|
const { refs, floatingStyles, context } = useFloating({
|
|
@@ -2807,7 +3010,7 @@ const Tooltip = ({ children, dataTestId, disabled = false, className, label, pla
|
|
|
2807
3010
|
}
|
|
2808
3011
|
setIsOpen(false);
|
|
2809
3012
|
}, [disabled]);
|
|
2810
|
-
return (jsxs(Popover, { activation: { hover: true }, className: cvaTooltipPopover(), dataTestId: dataTestId, id: id, placement: placement === "auto" ? "bottom" : placement, children: [jsx(PopoverTrigger, { className: cvaTooltipIcon({ color: mode, className }), "data-testid": dataTestId ? `${dataTestId}-trigger` : null, onMouseDown: closeTooltip, onMouseEnter: openTooltip, onMouseLeave: closeTooltip, ref: refs.setReference, children: children === undefined ? (jsx("div", { children: jsx(Icon, { dataTestId: dataTestId ? `${dataTestId}-icon` : undefined, name: "QuestionMarkCircle", size: "small", ...iconProps }) })) : (wrappedChildren) }), isMounted ? (jsx("div", { ref: refs.setFloating, style: floatingStyles, children: jsx(PopoverContent, { children: jsxs("div", { "aria-label": typeof label === "string" ? label : undefined, className: cvaTooltipPopoverContent({ color: mode }), "data-testid": `${dataTestId}-content`, children: [jsx(Text, { dataTestId: `${dataTestId}-text`, inverted: mode === "dark", size: "small", type: typeof label === "string" ? "p" : "span", children: label }), placement !== "auto" && jsx(FloatingArrowContainer, { arrowRef: arrowRef, mode: mode })] }) }) })) : null] }));
|
|
3013
|
+
return (jsxs(Popover, { activation: { hover: true }, className: cvaTooltipPopover(), dataTestId: dataTestId, id: id, placement: placement === "auto" ? "bottom" : placement, children: [jsx(PopoverTrigger, { className: cvaTooltipIcon({ color: mode, className }), "data-testid": dataTestId ? `${dataTestId}-trigger` : null, onMouseDown: closeTooltip, onMouseEnter: openTooltip, onMouseLeave: closeTooltip, ref: refs.setReference, style: style, children: children === undefined ? (jsx("div", { children: jsx(Icon, { dataTestId: dataTestId ? `${dataTestId}-icon` : undefined, name: "QuestionMarkCircle", size: "small", ...iconProps }) })) : (wrappedChildren) }), isMounted ? (jsx("div", { ref: refs.setFloating, style: floatingStyles, children: jsx(PopoverContent, { children: jsxs("div", { "aria-label": typeof label === "string" ? label : undefined, className: cvaTooltipPopoverContent({ color: mode }), "data-testid": `${dataTestId}-content`, children: [jsx(Text, { dataTestId: `${dataTestId}-text`, inverted: mode === "dark", size: "small", type: typeof label === "string" ? "p" : "span", children: label }), placement !== "auto" && jsx(FloatingArrowContainer, { arrowRef: arrowRef, mode: mode })] }) }) })) : null] }));
|
|
2811
3014
|
};
|
|
2812
3015
|
|
|
2813
3016
|
const cvaIndicator = cvaMerge(["flex", "items-center"]);
|
|
@@ -2944,12 +3147,12 @@ const Indicator = ({ dataTestId, icon, label, color = "unknown", withBackground
|
|
|
2944
3147
|
return (jsx(Tooltip, { className: className, disabled: withLabel, label: label, placement: "bottom", children: jsxs("div", { "aria-label": label, className: cvaIndicator(), "data-testid": dataTestId, ...rest, children: [jsxs("div", { className: cvaIndicatorIconBackground({ color, background: withBackground ? "visible" : "hidden" }), "data-testid": dataTestId ? `${dataTestId}-background` : "indicator-background", children: [ping ? (jsx("div", { className: cvaIndicatorPing({ color }), "data-testid": dataTestId ? `${dataTestId}-ping` : "indicator-ping" })) : null, icon] }), label && withLabel ? (jsx("div", { className: cvaIndicatorLabel({ size, weight, background: withBackground ? "visible" : "hidden" }), "data-testid": dataTestId ? `${dataTestId}-label` : undefined, children: label })) : null] }) }));
|
|
2945
3148
|
};
|
|
2946
3149
|
|
|
2947
|
-
const cvaKPI = cvaMerge(["w-full", "
|
|
3150
|
+
const cvaKPI = cvaMerge(["w-full", "flex", "flex-col"], {
|
|
2948
3151
|
variants: {
|
|
2949
3152
|
variant: {
|
|
2950
|
-
small: ["px-3"],
|
|
3153
|
+
small: ["px-3", "py-2"],
|
|
2951
3154
|
condensed: ["px-2", "py-0"],
|
|
2952
|
-
default: [""],
|
|
3155
|
+
default: ["px-4", "py-2"],
|
|
2953
3156
|
},
|
|
2954
3157
|
},
|
|
2955
3158
|
defaultVariants: {
|
|
@@ -3001,9 +3204,9 @@ const LoadingContent$1 = () => (jsx("div", { className: "flex h-11 flex-row item
|
|
|
3001
3204
|
* @param {KPIProps} props - The props for the KPI component
|
|
3002
3205
|
* @returns {ReactElement} KPI component
|
|
3003
3206
|
*/
|
|
3004
|
-
const KPI = ({ title, value, loading = false, unit, className, dataTestId, tooltipLabel, variant = "default", trend, ...rest }) => {
|
|
3207
|
+
const KPI = ({ title, value, loading = false, unit, className, dataTestId, tooltipLabel, variant = "default", trend, style, ...rest }) => {
|
|
3005
3208
|
const isSmallVariant = variant === "small";
|
|
3006
|
-
return (jsx(Tooltip, { dataTestId: dataTestId ? `${dataTestId}-tooltip` : undefined, disabled: tooltipLabel === undefined || tooltipLabel === "", label: tooltipLabel, placement: "bottom", children: jsx("div", { className: cvaKPI({ variant, className }), "data-testid": dataTestId ? `${dataTestId}` : undefined, ...rest, children: loading ? (jsx(LoadingContent$1, {})) : (jsxs(Fragment, { children: [jsx("div", { className: cvaKPIHeader(), children: jsx(Text, { className: cvaKPITitleText(), dataTestId: dataTestId ? `${dataTestId}-title` : undefined, size: isSmallVariant ? "small" : "medium", subtle: true, weight: isSmallVariant ? "normal" : "bold", children: title }) }), jsx(Text, { className: cvaKPIvalueText({ variant }), dataTestId: dataTestId ? `${dataTestId}-value` : undefined, size: isSmallVariant ? "small" : "large", type: "div", weight: isSmallVariant ? "bold" : "thick", children: jsxs("div", { className: cvaKPIValueContainer({
|
|
3209
|
+
return (jsx(Tooltip, { dataTestId: dataTestId ? `${dataTestId}-tooltip` : undefined, disabled: tooltipLabel === undefined || tooltipLabel === "", label: tooltipLabel, placement: "bottom", style: style, children: jsx("div", { className: cvaKPI({ variant, className }), "data-testid": dataTestId ? `${dataTestId}` : undefined, ...rest, children: loading ? (jsx(LoadingContent$1, {})) : (jsxs(Fragment$1, { children: [jsx("div", { className: cvaKPIHeader(), children: jsx(Text, { className: cvaKPITitleText(), dataTestId: dataTestId ? `${dataTestId}-title` : undefined, size: isSmallVariant ? "small" : "medium", subtle: true, weight: isSmallVariant ? "normal" : "bold", children: title }) }), jsx(Text, { className: cvaKPIvalueText({ variant }), dataTestId: dataTestId ? `${dataTestId}-value` : undefined, size: isSmallVariant ? "small" : "large", type: "div", weight: isSmallVariant ? "bold" : "thick", children: jsxs("div", { className: cvaKPIValueContainer({
|
|
3007
3210
|
isDefaultAndHasTrendValue: Boolean(trend !== undefined && trend.value !== undefined && !isSmallVariant),
|
|
3008
3211
|
className,
|
|
3009
3212
|
}), children: [jsxs("span", { className: cvaKPIvalueText({ variant }), children: [value, " ", unit] }), jsx(TrendIndicator, { isSmallVariant: isSmallVariant, trend: trend, unit: unit })] }) })] })) }) }));
|
|
@@ -3075,25 +3278,66 @@ const cvaCardBodyDensityContainer = cvaMerge(["grid", "grid-cols-[1fr_auto]"], {
|
|
|
3075
3278
|
},
|
|
3076
3279
|
});
|
|
3077
3280
|
|
|
3078
|
-
const
|
|
3281
|
+
const cvaListItem$1 = cvaMerge(["py-3", "px-4", "min-h-14", "w-full", "flex", "justify-between", "items-center"]);
|
|
3282
|
+
const cvaMainInformationClass = cvaMerge(["grid", "items-center", "text-sm", "gap-2"], {
|
|
3283
|
+
variants: {
|
|
3284
|
+
hasThumbnail: {
|
|
3285
|
+
true: "grid-cols-min-fr",
|
|
3286
|
+
false: "grid-cols-1",
|
|
3287
|
+
},
|
|
3288
|
+
},
|
|
3289
|
+
});
|
|
3290
|
+
const cvaThumbnailContainer = cvaMerge([
|
|
3291
|
+
"flex",
|
|
3292
|
+
"h-8",
|
|
3293
|
+
"w-8",
|
|
3294
|
+
"items-center",
|
|
3295
|
+
"justify-center",
|
|
3296
|
+
"overflow-hidden",
|
|
3297
|
+
"rounded-md",
|
|
3298
|
+
]);
|
|
3299
|
+
|
|
3300
|
+
const DEFAULT_SKELETON_LIST_ITEM_PROPS = {
|
|
3301
|
+
hasThumbnail: true,
|
|
3302
|
+
thumbnailShape: "circle",
|
|
3303
|
+
hasDescription: true,
|
|
3304
|
+
hasMeta: false,
|
|
3305
|
+
hasDetails: false,
|
|
3306
|
+
};
|
|
3307
|
+
/**
|
|
3308
|
+
* Skeleton loading indicator that mimics the ListItem component structure.
|
|
3309
|
+
* Uses the same layout, spacing, and visual hierarchy as ListItem.
|
|
3310
|
+
*/
|
|
3311
|
+
const ListItemSkeleton = ({ hasThumbnail = DEFAULT_SKELETON_LIST_ITEM_PROPS.hasThumbnail, thumbnailShape = "circle", hasDescription = DEFAULT_SKELETON_LIST_ITEM_PROPS.hasDescription, hasMeta = DEFAULT_SKELETON_LIST_ITEM_PROPS.hasMeta, hasDetails = DEFAULT_SKELETON_LIST_ITEM_PROPS.hasDetails, }) => {
|
|
3312
|
+
// Generate stable random widths once and never change them
|
|
3313
|
+
const lineWidths = useStable(() => {
|
|
3314
|
+
return {
|
|
3315
|
+
title: getResponsiveRandomWidthPercentage({ min: 60, max: 85 }),
|
|
3316
|
+
description: getResponsiveRandomWidthPercentage({ min: 45, max: 70 }),
|
|
3317
|
+
meta: getResponsiveRandomWidthPercentage({ min: 30, max: 55 }),
|
|
3318
|
+
details: getResponsiveRandomWidthPercentage({ min: 25, max: 45 }),
|
|
3319
|
+
};
|
|
3320
|
+
});
|
|
3321
|
+
return (jsxs("div", { className: cvaListItem$1({ 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: "grid-rows-min-fr grid w-full items-center gap-1 text-sm", children: [jsx(SkeletonLines, { height: "0.875em", lines: 1, width: lineWidths.title }), hasDescription ? jsx(SkeletonLines, { height: "0.75em", lines: 1, width: lineWidths.description }) : null, hasMeta ? jsx(SkeletonLines, { height: "0.75em", lines: 1, width: lineWidths.meta }) : null] })] }), hasDetails ? (jsx("div", { className: "pl-2 text-sm", children: jsx(SkeletonLines, { height: "0.875em", lines: 1, width: lineWidths.details }) })) : null] }));
|
|
3322
|
+
};
|
|
3323
|
+
|
|
3324
|
+
const cvaListContainer = cvaMerge(["overflow-auto", "h-full"], {
|
|
3079
3325
|
variants: {
|
|
3080
|
-
|
|
3081
|
-
true: [""],
|
|
3082
|
-
false: ["
|
|
3326
|
+
withTopSeparator: {
|
|
3327
|
+
true: ["border-t", "border-neutral-200", "transition-colors duration-200 ease-in"],
|
|
3328
|
+
false: ["border-t", "border-transparent", "transition-colors duration-200 ease-in"],
|
|
3083
3329
|
},
|
|
3084
3330
|
},
|
|
3085
3331
|
defaultVariants: {
|
|
3086
|
-
|
|
3332
|
+
withTopSeparator: false,
|
|
3087
3333
|
},
|
|
3088
3334
|
});
|
|
3089
3335
|
const cvaList = cvaMerge(["relative"]);
|
|
3090
|
-
const cvaListItem
|
|
3336
|
+
const cvaListItem = cvaMerge(["absolute", "top-0", "left-0", "w-full"], {
|
|
3091
3337
|
variants: {
|
|
3092
3338
|
separator: {
|
|
3093
|
-
|
|
3094
|
-
line: ["[&:not(:last-child)]:border-b", "border-gray-200"],
|
|
3339
|
+
line: ["[&:not(:last-child)]:border-b", "border-neutral-200"],
|
|
3095
3340
|
none: "",
|
|
3096
|
-
space: "[&:not(:last-child)]:pb-0.5",
|
|
3097
3341
|
},
|
|
3098
3342
|
},
|
|
3099
3343
|
defaultVariants: {
|
|
@@ -3102,62 +3346,129 @@ const cvaListItem$1 = cvaMerge(["absolute", "top-0", "left-0", "w-full"], {
|
|
|
3102
3346
|
});
|
|
3103
3347
|
|
|
3104
3348
|
/**
|
|
3105
|
-
* Render a performant virtualized list of items. Optionally with infinite scrolling.
|
|
3106
3349
|
*
|
|
3107
|
-
* @property {number} count - The total number of items in the list.
|
|
3108
|
-
* @property {number} [rowHeight="40"] - The estimated height of each row in the list.
|
|
3109
|
-
* @property {RelayPagination | undefined} pagination - Pagination configuration for the list.
|
|
3110
|
-
* @property {separator} [separator="line"] - The separator style between items in the list.
|
|
3111
|
-
* @property {(index: number) =>ReactElement} children - A function that takes an index and returns the JSX element to be rendered at said index.
|
|
3112
|
-
* @property {loadingIndicator} [loadingIndicator="spinner"] - The type of loading indicator in the list.
|
|
3113
|
-
* @property {skeletonLinesHeight} [skeletonLinesHeight="2rem"] - The height of the skeleton lines.
|
|
3114
3350
|
*/
|
|
3115
|
-
const
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3351
|
+
const ListLoadingIndicator = ({ type, hasThumbnail, thumbnailShape, hasDescription, component, hasMeta, hasDetails, }) => {
|
|
3352
|
+
switch (type) {
|
|
3353
|
+
case "none":
|
|
3354
|
+
return null;
|
|
3355
|
+
case "spinner":
|
|
3356
|
+
return jsx(Spinner, { centering: "horizontally", containerClassName: "p-4" });
|
|
3357
|
+
case "custom":
|
|
3358
|
+
return component;
|
|
3359
|
+
case "skeleton":
|
|
3360
|
+
return (jsx(ListItemSkeleton, { hasDescription: hasDescription, hasDetails: hasDetails, hasMeta: hasMeta, hasThumbnail: hasThumbnail, thumbnailShape: thumbnailShape }));
|
|
3361
|
+
default: {
|
|
3362
|
+
throw new Error(`${type} is not known`);
|
|
3124
3363
|
}
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3364
|
+
}
|
|
3365
|
+
};
|
|
3366
|
+
|
|
3367
|
+
const DEFAULT_ROW_HEIGHT = 61; // 61px is the height of the ListItem component as of 2025-09-26
|
|
3368
|
+
/**
|
|
3369
|
+
* A performant virtualized list component with infinite scrolling support.
|
|
3370
|
+
*
|
|
3371
|
+
* ⚠️ **Important**: Requires a container with defined height to work properly.
|
|
3372
|
+
*
|
|
3373
|
+
* Features:
|
|
3374
|
+
* - Virtualized rendering using TanStack Virtual for performance with large datasets
|
|
3375
|
+
* - Automatic infinite scroll loading when approaching the end of the list
|
|
3376
|
+
* - Optional header support (automatically managed, scrolls with content)
|
|
3377
|
+
* - Built-in pagination with relay-style cursor support
|
|
3378
|
+
* - Configurable loading indicators (skeleton, spinner, or custom)
|
|
3379
|
+
* - Scroll state detection and callbacks
|
|
3380
|
+
* - Variable-height item support via `estimateItemSize`
|
|
3381
|
+
*
|
|
3382
|
+
* The component automatically loads more data when:
|
|
3383
|
+
* - User scrolls to the last visible item
|
|
3384
|
+
* - Content height is insufficient to fill the container
|
|
3385
|
+
*/
|
|
3386
|
+
const List = ({ count, pagination, children, className, dataTestId, separator = "line", loadingIndicator = { type: "skeleton", ...DEFAULT_SKELETON_LIST_ITEM_PROPS }, onRowClick, onScrollStateChange, topSeparatorOnScroll = false, estimateItemSize, header, getItem, }) => {
|
|
3387
|
+
const parentRef = useRef(null);
|
|
3388
|
+
// Calculate the actual count including header
|
|
3389
|
+
const actualCount = count + (header ? 1 : 0);
|
|
3390
|
+
// Helper function to get item for a given data index
|
|
3391
|
+
const getItemAtIndex = useCallback((dataIndex) => {
|
|
3392
|
+
return getItem(dataIndex);
|
|
3393
|
+
}, [getItem]);
|
|
3394
|
+
// Calculate how many loading rows we need
|
|
3395
|
+
const getLoadingRowsCount = useCallback(() => {
|
|
3396
|
+
const { type: loadingIndicatorType } = loadingIndicator;
|
|
3397
|
+
if (pagination?.isLoading === false)
|
|
3398
|
+
return 0;
|
|
3399
|
+
switch (loadingIndicatorType) {
|
|
3400
|
+
case "none":
|
|
3401
|
+
return 0;
|
|
3402
|
+
case "spinner":
|
|
3403
|
+
return 1;
|
|
3404
|
+
case "custom":
|
|
3405
|
+
case "skeleton": {
|
|
3406
|
+
const isInitialLoading = !pagination?.pageInfo;
|
|
3407
|
+
const initialCount = loadingIndicator.initialLoadingCount ?? 10;
|
|
3408
|
+
const scrollCount = loadingIndicator.scrollLoadingCount ?? 3;
|
|
3409
|
+
return isInitialLoading ? initialCount : scrollCount;
|
|
3410
|
+
}
|
|
3411
|
+
default: {
|
|
3412
|
+
throw new Error(`${loadingIndicatorType} is not known`);
|
|
3413
|
+
}
|
|
3128
3414
|
}
|
|
3129
|
-
}, [
|
|
3130
|
-
const
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
pagination.isLoading === true
|
|
3138
|
-
? count + 1
|
|
3139
|
-
: count,
|
|
3140
|
-
rowHeight,
|
|
3141
|
-
};
|
|
3142
|
-
}, [pagination, scrollParent, count, rowHeight]);
|
|
3143
|
-
const { fetchMoreOnBottomReached, getVirtualItems, getTotalSize, measureElement } = useInfiniteScroll(infiniteScrollProps);
|
|
3144
|
-
useEffect(() => {
|
|
3145
|
-
if (scrollParent) {
|
|
3146
|
-
const handleScroll = () => {
|
|
3147
|
-
fetchMoreOnBottomReached(scrollParent);
|
|
3148
|
-
};
|
|
3149
|
-
scrollParent.addEventListener("scroll", handleScroll);
|
|
3150
|
-
return () => {
|
|
3151
|
-
scrollParent.removeEventListener("scroll", handleScroll);
|
|
3152
|
-
};
|
|
3415
|
+
}, [loadingIndicator, pagination]);
|
|
3416
|
+
const estimateSize = useCallback((index) => {
|
|
3417
|
+
// Check if this is a loading row
|
|
3418
|
+
if (index >= actualCount) {
|
|
3419
|
+
const loaderIndex = index - actualCount;
|
|
3420
|
+
const shouldShowLoader = pagination?.isLoading === true && loaderIndex < getLoadingRowsCount();
|
|
3421
|
+
// Empty loader rows should be estimated at 0 height to prevent blank space
|
|
3422
|
+
return shouldShowLoader ? DEFAULT_ROW_HEIGHT : 0;
|
|
3153
3423
|
}
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3424
|
+
// For data rows (including header), use custom estimator if provided
|
|
3425
|
+
return estimateItemSize ? estimateItemSize(index) : DEFAULT_ROW_HEIGHT;
|
|
3426
|
+
}, [estimateItemSize, actualCount, pagination?.isLoading, getLoadingRowsCount]);
|
|
3427
|
+
const { getVirtualItems, getTotalSize, measureElement, scrollOffset } = useInfiniteScroll({
|
|
3428
|
+
pagination: pagination || noPagination,
|
|
3429
|
+
containerRef: parentRef,
|
|
3430
|
+
count: actualCount + (pagination?.isLoading === true ? getLoadingRowsCount() : 0),
|
|
3431
|
+
estimateSize,
|
|
3432
|
+
onChange: virtualizer => onScrollStateChange?.(virtualizer.scrollOffset ?? 0, virtualizer.isScrolling),
|
|
3433
|
+
});
|
|
3434
|
+
const isAtTop = scrollOffset <= 0;
|
|
3435
|
+
return (jsx("div", { className: cvaListContainer({
|
|
3436
|
+
withTopSeparator: topSeparatorOnScroll && !isAtTop,
|
|
3437
|
+
className,
|
|
3438
|
+
}), "data-testid": dataTestId, ref: parentRef, children: jsx("ul", { className: cvaList(), style: { height: `${getTotalSize()}px` }, children: getVirtualItems().map(virtualRow => {
|
|
3439
|
+
const isLoaderRow = virtualRow.index >= actualCount;
|
|
3440
|
+
const isHeaderRow = Boolean(header) && virtualRow.index === 0;
|
|
3441
|
+
// Calculate data index: if header exists, subtract 1 from index for data items
|
|
3442
|
+
const dataIndex = Boolean(header) && !isHeaderRow ? virtualRow.index - 1 : virtualRow.index;
|
|
3443
|
+
// Calculate which loading indicator this is (for multiple loading indicators)
|
|
3444
|
+
const loaderIndex = isLoaderRow ? virtualRow.index - actualCount : 0;
|
|
3445
|
+
// Props required by the virtualizer for proper positioning and behavior
|
|
3446
|
+
const listItemProps = {
|
|
3447
|
+
className: cvaListItem({ separator }), // List styling (separators, spacing)
|
|
3448
|
+
"data-index": isLoaderRow || isHeaderRow ? virtualRow.index : dataIndex, // For accessibility and debugging
|
|
3449
|
+
onClick: onRowClick && !isLoaderRow && !isHeaderRow
|
|
3450
|
+
? () => {
|
|
3451
|
+
const clickedItem = getItemAtIndex(dataIndex);
|
|
3452
|
+
onRowClick(clickedItem, dataIndex);
|
|
3453
|
+
}
|
|
3454
|
+
: undefined, // Row-level click handling (skip header)
|
|
3455
|
+
ref: measureElement, // Required for virtualizer to measure item dimensions
|
|
3456
|
+
style: {
|
|
3457
|
+
transform: `translateY(${virtualRow.start}px)`, // Critical: positions item in virtual scroll
|
|
3458
|
+
},
|
|
3459
|
+
tabIndex: -1, // Keyboard navigation support
|
|
3460
|
+
};
|
|
3461
|
+
// Handle loading rows
|
|
3462
|
+
if (isLoaderRow) {
|
|
3463
|
+
return (jsx("li", { ...listItemProps, children: pagination?.isLoading === true && loaderIndex < getLoadingRowsCount() ? (jsx(ListLoadingIndicator, { ...loadingIndicator })) : null }, virtualRow.index));
|
|
3464
|
+
}
|
|
3465
|
+
// Handle header row
|
|
3466
|
+
if (isHeaderRow && header) {
|
|
3467
|
+
return (jsx("li", { ...listItemProps, children: header }, "header"));
|
|
3468
|
+
}
|
|
3469
|
+
// For regular children, call the children function with virtualization props and item data
|
|
3470
|
+
const item = getItemAtIndex(dataIndex);
|
|
3471
|
+
return children(listItemProps, item, dataIndex);
|
|
3161
3472
|
}) }) }));
|
|
3162
3473
|
};
|
|
3163
3474
|
|
|
@@ -3222,35 +3533,16 @@ const cvaInteractableItem = cvaMerge("", {
|
|
|
3222
3533
|
},
|
|
3223
3534
|
});
|
|
3224
3535
|
|
|
3225
|
-
const cvaListItem = cvaMerge(["py-3", "px-4", "min-h-14", "w-full", "flex", "justify-between", "items-center"]);
|
|
3226
|
-
const cvaMainInformationClass = cvaMerge(["grid", "items-center", "text-sm", "gap-2"], {
|
|
3227
|
-
variants: {
|
|
3228
|
-
hasThumbnail: {
|
|
3229
|
-
true: "grid-cols-min-fr",
|
|
3230
|
-
false: "grid-cols-1",
|
|
3231
|
-
},
|
|
3232
|
-
},
|
|
3233
|
-
});
|
|
3234
|
-
const cvaThumbnailContainer = cvaMerge([
|
|
3235
|
-
"flex",
|
|
3236
|
-
"h-8",
|
|
3237
|
-
"w-8",
|
|
3238
|
-
"items-center",
|
|
3239
|
-
"justify-center",
|
|
3240
|
-
"overflow-hidden",
|
|
3241
|
-
"rounded-md",
|
|
3242
|
-
]);
|
|
3243
|
-
|
|
3244
3536
|
/**
|
|
3245
3537
|
* The ListItem is designed to present a concise set of items for quick scanning and navigation. It supports multiple content types and actions, and its flexible layout allows for customization based on the type of data being shown - assets, events, users, etc.
|
|
3246
3538
|
*
|
|
3247
3539
|
* @param { ListItemProps} props - The props for the ListItem component
|
|
3248
3540
|
* @returns {Element} ListItem component
|
|
3249
3541
|
*/
|
|
3250
|
-
const ListItem = ({ className, dataTestId, onClick, details, title, description, meta, thumbnail, thumbnailColor = "info-600", thumbnailBackground = "info-100", }) => {
|
|
3251
|
-
const baseClass = cvaListItem({ className });
|
|
3542
|
+
const ListItem = ({ className, dataTestId, onClick, details, title, description, meta, thumbnail, thumbnailColor = "info-600", thumbnailBackground = "info-100", ...rest }) => {
|
|
3543
|
+
const baseClass = cvaListItem$1({ className });
|
|
3252
3544
|
const interactableItemClass = onClick ? twMerge(baseClass, cvaInteractableItem({ cursor: "pointer" })) : baseClass;
|
|
3253
|
-
return (jsxs("
|
|
3545
|
+
return (jsxs("li", { className: interactableItemClass, "data-testid": dataTestId, onClick: onClick, ...rest, children: [jsxs("div", { className: cvaMainInformationClass({ hasThumbnail: !!thumbnail }), children: [thumbnail ? (jsx("div", { className: cvaThumbnailContainer({
|
|
3254
3546
|
className: `text-${thumbnailColor} bg-${thumbnailBackground}`,
|
|
3255
3547
|
}), children: thumbnail })) : null, jsxs("div", { className: "grid-rows-min-fr grid items-center text-sm", children: [jsx("div", { className: "gap-responsive-space-sm flex w-full min-w-0 items-center text-sm", children: typeof title === "string" ? (jsx(Text, { className: "truncate", dataTestId: dataTestId ? `${dataTestId}-title` : undefined, weight: "bold", children: title })) : (cloneElement(title, {
|
|
3256
3548
|
className: twMerge(title.props.className, "neutral-900 text-sm font-medium truncate"),
|
|
@@ -3258,7 +3550,7 @@ const ListItem = ({ className, dataTestId, onClick, details, title, description,
|
|
|
3258
3550
|
})) }), description !== undefined && description !== "" ? (jsx("div", { className: "gap-responsive-space-sm flex w-full min-w-0 items-center", children: typeof description === "string" ? (jsx(Text, { className: "truncate text-xs text-neutral-500", dataTestId: dataTestId ? `${dataTestId}-description` : undefined, weight: "bold", children: description })) : (cloneElement(description, {
|
|
3259
3551
|
className: twMerge(description.props.className, "text-neutral-500 text-xs font-medium truncate"),
|
|
3260
3552
|
dataTestId: !description.props.dataTestId && dataTestId ? `${dataTestId}-description` : undefined,
|
|
3261
|
-
})) })) : null, meta ? (jsx("div", { className: "gap-responsive-space-sm flex w-full min-w-0 items-center pt-0.5", children: jsx(Text, { className: "truncate text-xs text-neutral-400", dataTestId: dataTestId ? `${dataTestId}-meta` : undefined, weight: "bold", children: meta }) })) : null] })] }), jsxs("div", { className: "flex items-center gap-0.5 pl-2", children: [details, onClick ? jsx(Icon, { color: "neutral", name: "ChevronRight", size: "medium" }) : null] })] }));
|
|
3553
|
+
})) })) : null, meta ? (jsx("div", { className: "gap-responsive-space-sm flex w-full min-w-0 items-center pt-0.5", children: jsx(Text, { className: "truncate text-xs text-neutral-400", dataTestId: dataTestId ? `${dataTestId}-meta` : undefined, weight: "bold", children: meta }) })) : null] })] }), jsxs("div", { className: "flex items-center gap-0.5 text-nowrap pl-2", children: [details, onClick ? jsx(Icon, { color: "neutral", name: "ChevronRight", size: "medium" }) : null] })] }));
|
|
3262
3554
|
};
|
|
3263
3555
|
|
|
3264
3556
|
const cvaMenuList = cvaMerge([
|
|
@@ -3783,7 +4075,7 @@ const Pagination = ({ previousPage, nextPage, canPreviousPage = false, canNextPa
|
|
|
3783
4075
|
if (loading) {
|
|
3784
4076
|
return (jsx("div", { className: cvaPagination({ className }), children: jsx(SkeletonLines, { height: 16, width: 150 }) }));
|
|
3785
4077
|
}
|
|
3786
|
-
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, { 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
|
|
4078
|
+
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
|
|
3787
4079
|
? !canNextPage || false
|
|
3788
4080
|
: page !== undefined && pageCount !== undefined && pageCount !== null && page >= pageCount - 1, icon: jsx(Icon, { name: "ChevronRight", size: "small" }), onClick: () => handlePageChange("next"), size: "small", variant: "ghost-neutral" })] }));
|
|
3789
4081
|
};
|
|
@@ -4064,7 +4356,7 @@ const cvaTab = cvaMerge([
|
|
|
4064
4356
|
* We add a custom implementation of the asChild prop to make it easy to make the child element look like other tabs.
|
|
4065
4357
|
*/
|
|
4066
4358
|
const Tab = ({ value, isFullWidth = false, iconName = undefined, dataTestId, className, children, suffix, asChild = false, appendTabStylesToChildIfAsChild = true, ...rest }) => {
|
|
4067
|
-
const renderContent = () => (jsxs(Fragment, { children: [iconName !== undefined ? jsx(Icon, { name: iconName, size: "small" }) : null, isValidElement(children) ? children.props.children : children, suffix] }));
|
|
4359
|
+
const renderContent = () => (jsxs(Fragment$1, { children: [iconName !== undefined ? jsx(Icon, { name: iconName, size: "small" }) : null, isValidElement(children) ? children.props.children : children, suffix] }));
|
|
4068
4360
|
const commonProps = {
|
|
4069
4361
|
className: appendTabStylesToChildIfAsChild ? cvaTab({ className, isFullWidth }) : className,
|
|
4070
4362
|
...rest,
|
|
@@ -4283,7 +4575,7 @@ const ToggleButton = ({ title, size, children, dataTestId, className, icon, icon
|
|
|
4283
4575
|
large: "p-2 text-base",
|
|
4284
4576
|
};
|
|
4285
4577
|
const paddingClasses = isIconOnly ? iconOnlySizeClasses[size] : sizeClasses[size];
|
|
4286
|
-
return (jsx("button", { className: twMerge("flex items-center justify-center gap-1 self-stretch", paddingClasses, className), "data-testid": dataTestId, title: isIconOnly ? title : undefined, type: "button", ...rest, children: isIconOnly ? (icon) : (jsxs(Fragment, { children: [iconPrefix, children] })) }));
|
|
4578
|
+
return (jsx("button", { className: twMerge("flex items-center justify-center gap-1 self-stretch", paddingClasses, className), "data-testid": dataTestId, title: isIconOnly ? title : undefined, type: "button", ...rest, children: isIconOnly ? (icon) : (jsxs(Fragment$1, { children: [iconPrefix, children] })) }));
|
|
4287
4579
|
};
|
|
4288
4580
|
|
|
4289
4581
|
const cvaValueBar = cvaMerge([
|
|
@@ -4399,28 +4691,6 @@ const ValueBar = ({ value, min = 0, max = 100, unit, size = "small", levelColors
|
|
|
4399
4691
|
return (jsxs("span", { className: "relative flex items-center gap-2", "data-testid": dataTestId, children: [jsx("progress", { "aria-label": valueText, className: cvaValueBar({ className, size }), max: 100, style: { color: barFillColor }, value: score * 100 }), showValue && (size === "small" || size === "large") ? (jsx(Text, { className: cvaValueBarText({ size }), dataTestId: dataTestId ? `${dataTestId}-value` : undefined, children: jsx("span", { style: valueColor ? { color: valueColor } : undefined, children: valueText }) })) : null] }));
|
|
4400
4692
|
};
|
|
4401
4693
|
|
|
4402
|
-
const cvaZStackContainer = cvaMerge(["grid", "grid-cols-1", "grid-rows-1"]);
|
|
4403
|
-
const cvaZStackItem = cvaMerge(["col-start-1", "col-end-1", "row-start-1", "row-end-2"]);
|
|
4404
|
-
|
|
4405
|
-
/**
|
|
4406
|
-
* ZStack is a component that stacks its children on the z-axis.
|
|
4407
|
-
* Is a good alternative to "position: absolute" that avoids some of the unfortunate side effects of absolute positioning.
|
|
4408
|
-
*
|
|
4409
|
-
* @param { ZStackProps} props - The props for the ZStack component
|
|
4410
|
-
* @returns {Element} ZStack component
|
|
4411
|
-
*/
|
|
4412
|
-
const ZStack = ({ children, className, dataTestId }) => {
|
|
4413
|
-
return (jsx("div", { className: cvaZStackContainer({ className }), "data-testid": dataTestId, children: Children.map(children, (child, index) => {
|
|
4414
|
-
if (!isValidElement(child)) {
|
|
4415
|
-
return child;
|
|
4416
|
-
}
|
|
4417
|
-
return cloneElement(child, {
|
|
4418
|
-
className: cvaZStackItem({ className: child.props.className }),
|
|
4419
|
-
key: index,
|
|
4420
|
-
});
|
|
4421
|
-
}) }));
|
|
4422
|
-
};
|
|
4423
|
-
|
|
4424
4694
|
const cvaClickable = cvaMerge([
|
|
4425
4695
|
"shadow-lg",
|
|
4426
4696
|
"rounded-lg",
|
|
@@ -4445,4 +4715,4 @@ const cvaClickable = cvaMerge([
|
|
|
4445
4715
|
},
|
|
4446
4716
|
});
|
|
4447
4717
|
|
|
4448
|
-
export { ActionRenderer, Alert, Badge, Breadcrumb, BreadcrumbContainer, Button, Card, CardBody, CardFooter, CardHeader, Collapse, CompletionStatusIndicator, CopyableText, DetailsList, EmptyState, EmptyValue, ExternalLink, Heading, Highlight, 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, Prompt, ROLE_CARD, SectionHeader, Sidebar, SkeletonLines, Spacer, Spinner, StarButton, Tab, TabContent, TabList, Tabs, Tag, Text, ToggleGroup, Tooltip, ValueBar, ZStack, cvaButton, cvaButtonPrefixSuffix, cvaButtonSpinner, cvaButtonSpinnerContainer, cvaClickable, cvaContainerStyles, cvaIconButton, cvaImgStyles, cvaIndicator, cvaIndicatorIcon, cvaIndicatorIconBackground, cvaIndicatorLabel, cvaIndicatorPing, cvaInteractableItem, cvaList, cvaListContainer, cvaListItem
|
|
4718
|
+
export { ActionRenderer, Alert, Badge, Breadcrumb, BreadcrumbContainer, Button, Card, CardBody, CardFooter, CardHeader, Collapse, CompletionStatusIndicator, CopyableText, DetailsList, EmptyState, EmptyValue, ExternalLink, 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, Prompt, ROLE_CARD, SectionHeader, Sidebar, SkeletonLines, Spacer, Spinner, StarButton, Tab, TabContent, TabList, Tabs, Tag, Text, ToggleGroup, Tooltip, ValueBar, ZStack, cvaButton, cvaButtonPrefixSuffix, cvaButtonSpinner, cvaButtonSpinnerContainer, cvaClickable, cvaContainerStyles, cvaIconButton, cvaImgStyles, cvaIndicator, cvaIndicatorIcon, cvaIndicatorIconBackground, cvaIndicatorLabel, cvaIndicatorPing, cvaInteractableItem, cvaList, cvaListContainer, cvaListItem, cvaMenuItem, cvaMenuItemLabel, cvaMenuItemPrefix, cvaMenuItemStyle, cvaMenuItemSuffix, cvaPageHeader, cvaPageHeaderContainer, cvaPageHeaderHeading, cvaToggleGroup, cvaToggleGroupWithSlidingBackground, cvaToggleItem, cvaToggleItemContent, cvaToggleItemText, cvaZStackContainer, cvaZStackItem, docs, getDevicePixelRatio, getResponsiveRandomWidthPercentage, getValueBarColorByValue, iconColorNames, iconPalette, useClickOutside, useContainerBreakpoints, useContinuousTimeout, useDebounce, useDevicePixelRatio, useElevatedReducer, useElevatedState, useGeometry, useHover, useIsFirstRender, useIsFullscreen, useIsTextTruncated, useModifierKey, useOverflowItems, usePopoverContext, usePrompt, useResize, useScrollDetection, useSelfUpdatingRef, useStable, useTimeout, useViewportBreakpoints, useWatch, useWindowActivity };
|