@trackunit/react-components 1.9.21 → 1.9.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.cjs.js CHANGED
@@ -32,7 +32,6 @@ const cvaIcon = cssClassVarianceUtilities.cvaMerge(["aspect-square", "inline-gri
32
32
  variants: {
33
33
  color: {
34
34
  primary: "text-primary-600",
35
- secondary: "text-neutral-400",
36
35
  neutral: "text-neutral-400",
37
36
  info: "text-info-600",
38
37
  success: "text-success-600",
@@ -201,7 +200,6 @@ const cvaTag = cssClassVarianceUtilities.cvaMerge([
201
200
  },
202
201
  color: {
203
202
  primary: ["bg-primary-100", "text-primary-700"],
204
- secondary: ["bg-neutral-100", "text-neutral-700"],
205
203
  neutral: ["bg-neutral-200", "text-neutral-700"],
206
204
  black: ["bg-neutral-100", "text-neutral-700"],
207
205
  white: ["bg-white", "text-neutral-600"],
@@ -274,7 +272,7 @@ Tag.displayName = "Tag";
274
272
  * A component used to display the package name and version in the Storybook docs.
275
273
  */
276
274
  const PackageNameStoryComponent = ({ packageJSON }) => {
277
- return (jsxRuntime.jsxs("div", { className: "flex gap-2", children: [jsxRuntime.jsx(Tag, { color: "secondary", children: packageJSON?.name }), jsxRuntime.jsxs(Tag, { color: "secondary", children: ["v", packageJSON?.version] })] }));
275
+ return (jsxRuntime.jsxs("div", { className: "flex gap-2", children: [jsxRuntime.jsx(Tag, { color: "neutral", children: packageJSON?.name }), jsxRuntime.jsxs(Tag, { color: "neutral", children: ["v", packageJSON?.version] })] }));
278
276
  };
279
277
 
280
278
  /**
@@ -1937,7 +1935,7 @@ const BreadcrumbForMediumScreen = ({ dataTestId, breadcrumbItems }) => {
1937
1935
  const [expanded, setExpanded] = react.useState(false);
1938
1936
  const getReducedArray = react.useCallback(() => {
1939
1937
  let reducedArrayElements = [];
1940
- const collapsibleButton = (jsxRuntime.jsxs("div", { className: cvaBreadcrumbItem(), children: [jsxRuntime.jsx(IconButton, { dataTestId: `collapsibleButton-${dataTestId}`, icon: jsxRuntime.jsx(Icon, { name: "EllipsisHorizontal", size: "small" }), onClick: () => setExpanded(true), size: "small", variant: "ghost-neutral" }), jsxRuntime.jsx(Icon, { color: "secondary", name: "Slash", size: "small" })] }));
1938
+ const collapsibleButton = (jsxRuntime.jsxs("div", { className: cvaBreadcrumbItem(), children: [jsxRuntime.jsx(IconButton, { dataTestId: `collapsibleButton-${dataTestId}`, icon: jsxRuntime.jsx(Icon, { name: "EllipsisHorizontal", size: "small" }), onClick: () => setExpanded(true), size: "small", variant: "ghost-neutral" }), jsxRuntime.jsx(Icon, { color: "neutral", name: "Slash", size: "small" })] }));
1941
1939
  const firstBreadcrumbItem = breadcrumbItems[0];
1942
1940
  if (firstBreadcrumbItem && breadcrumbItems.length > 3) {
1943
1941
  const lastTwoBreadcrumbItem = breadcrumbItems.slice(-2);
@@ -3426,7 +3424,31 @@ const cvaCardBodyDensityContainer = cssClassVarianceUtilities.cvaMerge(["grid",
3426
3424
  },
3427
3425
  });
3428
3426
 
3429
- const cvaListItem$1 = cssClassVarianceUtilities.cvaMerge(["py-3", "px-4", "min-h-14", "w-full", "flex", "justify-between", "items-center"]);
3427
+ const cvaListContainer = cssClassVarianceUtilities.cvaMerge(["overflow-y-auto", "overflow-x-hidden", "h-full"], {
3428
+ variants: {
3429
+ withTopSeparator: {
3430
+ true: ["border-t", "border-neutral-200", "transition-colors duration-200 ease-in"],
3431
+ false: ["border-t", "border-transparent", "transition-colors duration-200 ease-in"],
3432
+ },
3433
+ },
3434
+ defaultVariants: {
3435
+ withTopSeparator: false,
3436
+ },
3437
+ });
3438
+ const cvaList = cssClassVarianceUtilities.cvaMerge(["relative"]);
3439
+ const cvaListItem$1 = cssClassVarianceUtilities.cvaMerge(["absolute", "top-0", "left-0", "w-full"], {
3440
+ variants: {
3441
+ separator: {
3442
+ line: ["border-b", "border-neutral-200"],
3443
+ none: "",
3444
+ },
3445
+ },
3446
+ defaultVariants: {
3447
+ separator: "none",
3448
+ },
3449
+ });
3450
+
3451
+ const cvaListItem = cssClassVarianceUtilities.cvaMerge(["py-3", "px-4", "min-h-14", "w-full", "flex", "justify-between", "items-center"]);
3430
3452
  const cvaMainInformationClass = cssClassVarianceUtilities.cvaMerge(["grid", "items-center", "text-sm", "gap-2"], {
3431
3453
  variants: {
3432
3454
  hasThumbnail: {
@@ -3466,33 +3488,9 @@ const ListItemSkeleton = ({ hasThumbnail = DEFAULT_SKELETON_LIST_ITEM_PROPS.hasT
3466
3488
  details: getResponsiveRandomWidthPercentage({ min: 25, max: 45 }),
3467
3489
  };
3468
3490
  });
3469
- return (jsxRuntime.jsxs("div", { className: cvaListItem$1({ 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: "grid-rows-min-fr grid w-full items-center gap-1 text-sm", children: [jsxRuntime.jsx(SkeletonLines, { height: "0.875em", lines: 1, width: lineWidths.title }), hasDescription ? jsxRuntime.jsx(SkeletonLines, { height: "0.75em", lines: 1, width: lineWidths.description }) : null, hasMeta ? jsxRuntime.jsx(SkeletonLines, { height: "0.75em", lines: 1, width: lineWidths.meta }) : null] })] }), hasDetails ? (jsxRuntime.jsx("div", { className: "pl-2 text-sm", children: jsxRuntime.jsx(SkeletonLines, { height: "0.875em", lines: 1, width: lineWidths.details }) })) : null] }));
3491
+ 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: "grid-rows-min-fr grid w-full items-center gap-1 text-sm", children: [jsxRuntime.jsx(SkeletonLines, { height: "0.875em", lines: 1, width: lineWidths.title }), hasDescription ? jsxRuntime.jsx(SkeletonLines, { height: "0.75em", lines: 1, width: lineWidths.description }) : null, hasMeta ? jsxRuntime.jsx(SkeletonLines, { height: "0.75em", lines: 1, width: lineWidths.meta }) : null] })] }), hasDetails ? (jsxRuntime.jsx("div", { className: "pl-2 text-sm", children: jsxRuntime.jsx(SkeletonLines, { height: "0.875em", lines: 1, width: lineWidths.details }) })) : null] }));
3470
3492
  };
3471
3493
 
3472
- const cvaListContainer = cssClassVarianceUtilities.cvaMerge(["overflow-y-auto", "overflow-x-hidden", "h-full"], {
3473
- variants: {
3474
- withTopSeparator: {
3475
- true: ["border-t", "border-neutral-200", "transition-colors duration-200 ease-in"],
3476
- false: ["border-t", "border-transparent", "transition-colors duration-200 ease-in"],
3477
- },
3478
- },
3479
- defaultVariants: {
3480
- withTopSeparator: false,
3481
- },
3482
- });
3483
- const cvaList = cssClassVarianceUtilities.cvaMerge(["relative"]);
3484
- const cvaListItem = cssClassVarianceUtilities.cvaMerge(["absolute", "top-0", "left-0", "w-full"], {
3485
- variants: {
3486
- separator: {
3487
- line: ["[&:not(:last-child)]:border-b", "border-neutral-200"],
3488
- none: "",
3489
- },
3490
- },
3491
- defaultVariants: {
3492
- separator: "none",
3493
- },
3494
- });
3495
-
3496
3494
  /**
3497
3495
  *
3498
3496
  */
@@ -3577,9 +3575,9 @@ const ListLoadingIndicator = ({ type, hasThumbnail, thumbnailShape, hasDescripti
3577
3575
  * );
3578
3576
  * ```
3579
3577
  */
3580
- const List = ({ children, className, dataTestId, topSeparatorOnScroll = false, separator = "line",
3578
+ const List = ({ children, className, dataTestId,
3581
3579
  // UseListResult properties
3582
- containerRef, listRef, rows, getListItemProps, header, loadingIndicator, shouldShowLoaderAtIndex, count, isScrolling, scrollOffset,
3580
+ containerRef, listRef, rows, getListItemProps, header, loadingIndicator, shouldShowLoaderAtIndex, count, isScrolling, scrollOffset, separator, topSeparatorOnScroll,
3583
3581
  // Unused but part of UseListResult interface
3584
3582
  getTotalSize: _getTotalSize, getVirtualItems: _getVirtualItems, scrollToOffset: _scrollToOffset, scrollToIndex: _scrollToIndex, measure: _measure, }) => {
3585
3583
  return (jsxRuntime.jsx("div", { className: cvaListContainer({
@@ -3588,22 +3586,18 @@ getTotalSize: _getTotalSize, getVirtualItems: _getVirtualItems, scrollToOffset:
3588
3586
  }), "data-is-scrolling": isScrolling, "data-testid": dataTestId, ref: containerRef, children: jsxRuntime.jsx("ul", { className: cvaList(), ref: listRef, children: rows.map(row => {
3589
3587
  // Generate list item props with separator styling
3590
3588
  const listItemProps = getListItemProps(row, {
3591
- className: cvaListItem({ separator }),
3589
+ className: cvaListItem$1({ separator }),
3592
3590
  });
3593
3591
  const key = row.virtualRow.key;
3594
3592
  // Render loading row
3595
3593
  if (row.type === "loader") {
3596
- const loadingConfig = loadingIndicator ?? {
3597
- type: "skeleton",
3598
- ...DEFAULT_SKELETON_LIST_ITEM_PROPS,
3599
- };
3600
3594
  // Use the total data count from useList, not visible rows (which are virtualized)
3601
3595
  const hasHeaderRow = !!header;
3602
3596
  const dataStartIndex = hasHeaderRow ? 1 : 0;
3603
3597
  const totalDataRows = dataStartIndex + count;
3604
3598
  const loaderIndex = row.virtualRow.index - totalDataRows;
3605
3599
  const shouldShowLoader = shouldShowLoaderAtIndex(loaderIndex);
3606
- return (jsxRuntime.jsx("li", { ...listItemProps, children: shouldShowLoader ? jsxRuntime.jsx(ListLoadingIndicator, { ...loadingConfig }) : null }, key));
3600
+ return (jsxRuntime.jsx("li", { ...listItemProps, children: shouldShowLoader ? jsxRuntime.jsx(ListLoadingIndicator, { ...loadingIndicator }) : null }, key));
3607
3601
  }
3608
3602
  // Render header row
3609
3603
  if (row.type === "header") {
@@ -3651,10 +3645,12 @@ const DEFAULT_LOADING_INDICATOR_CONFIG = {
3651
3645
  * );
3652
3646
  * ```
3653
3647
  */
3654
- const useList = ({ count, pagination, header, getItem, loadingIndicator = DEFAULT_LOADING_INDICATOR_CONFIG, onRowClick, onChange, estimateItemSize, estimateHeaderSize, overscan, }) => {
3648
+ const useList = ({ count, pagination, header, getItem, loadingIndicator = DEFAULT_LOADING_INDICATOR_CONFIG, onRowClick, onChange, estimateItemSize, estimateHeaderSize, overscan, separator = "line", topSeparatorOnScroll = false, }) => {
3655
3649
  const containerRef = react.useRef(null);
3656
3650
  const listRef = react.useRef(null);
3657
3651
  const rowRefsMap = react.useRef(new Map());
3652
+ // Resolve loading indicator once to avoid unnecessary re-renders
3653
+ const resolvedLoadingIndicator = react.useMemo(() => getResolvedLoadingIndicator(loadingIndicator), [loadingIndicator]);
3658
3654
  // Calculate total count including header
3659
3655
  const hasHeader = Boolean(header);
3660
3656
  const dataStartIndex = hasHeader ? 1 : 0;
@@ -3664,7 +3660,7 @@ const useList = ({ count, pagination, header, getItem, loadingIndicator = DEFAUL
3664
3660
  const getLoadingRowsCount = react.useCallback(() => {
3665
3661
  if (pagination?.isLoading === false)
3666
3662
  return 0;
3667
- const { type: loadingIndicatorType } = loadingIndicator;
3663
+ const { type: loadingIndicatorType } = resolvedLoadingIndicator;
3668
3664
  switch (loadingIndicatorType) {
3669
3665
  case "none":
3670
3666
  return 0;
@@ -3673,20 +3669,20 @@ const useList = ({ count, pagination, header, getItem, loadingIndicator = DEFAUL
3673
3669
  case "custom":
3674
3670
  case "skeleton": {
3675
3671
  const isInitialLoading = !pagination?.pageInfo;
3676
- const initialCount = loadingIndicator.initialLoadingCount ?? 10;
3677
- const scrollCount = loadingIndicator.scrollLoadingCount ?? 3;
3672
+ const initialCount = resolvedLoadingIndicator.initialLoadingCount ?? 10;
3673
+ const scrollCount = resolvedLoadingIndicator.scrollLoadingCount ?? 3;
3678
3674
  return isInitialLoading ? initialCount : scrollCount;
3679
3675
  }
3680
3676
  default: {
3681
3677
  throw new Error(`${loadingIndicatorType} is not known`);
3682
3678
  }
3683
3679
  }
3684
- }, [loadingIndicator, pagination?.isLoading, pagination?.pageInfo]);
3680
+ }, [resolvedLoadingIndicator, pagination?.isLoading, pagination?.pageInfo]);
3685
3681
  // Helper to determine if a specific loader index should be shown
3686
3682
  const shouldShowLoaderAtIndex = react.useCallback((loaderIndex) => {
3687
3683
  if (pagination?.isLoading === false)
3688
3684
  return false;
3689
- const { type: loadingIndicatorType } = loadingIndicator;
3685
+ const { type: loadingIndicatorType } = resolvedLoadingIndicator;
3690
3686
  let result;
3691
3687
  switch (loadingIndicatorType) {
3692
3688
  case "none":
@@ -3698,8 +3694,8 @@ const useList = ({ count, pagination, header, getItem, loadingIndicator = DEFAUL
3698
3694
  case "custom":
3699
3695
  case "skeleton": {
3700
3696
  const isInitialLoading = !pagination?.pageInfo;
3701
- const initialCount = loadingIndicator.initialLoadingCount ?? 10;
3702
- const scrollCount = loadingIndicator.scrollLoadingCount ?? 3;
3697
+ const initialCount = resolvedLoadingIndicator.initialLoadingCount ?? 10;
3698
+ const scrollCount = resolvedLoadingIndicator.scrollLoadingCount ?? 3;
3703
3699
  const maxCount = isInitialLoading ? initialCount : scrollCount;
3704
3700
  result = loaderIndex < maxCount;
3705
3701
  break;
@@ -3709,7 +3705,7 @@ const useList = ({ count, pagination, header, getItem, loadingIndicator = DEFAUL
3709
3705
  }
3710
3706
  }
3711
3707
  return result;
3712
- }, [loadingIndicator, pagination?.isLoading, pagination?.pageInfo]);
3708
+ }, [resolvedLoadingIndicator, pagination?.isLoading, pagination?.pageInfo]);
3713
3709
  const totalRowCount = react.useMemo(() => {
3714
3710
  // Only add the exact number of loading rows we want to show
3715
3711
  const loadingRows = pagination?.isLoading === true ? getLoadingRowsCount() : 0;
@@ -3833,11 +3829,19 @@ const useList = ({ count, pagination, header, getItem, loadingIndicator = DEFAUL
3833
3829
  rows,
3834
3830
  getListItemProps,
3835
3831
  header,
3836
- loadingIndicator,
3832
+ loadingIndicator: resolvedLoadingIndicator,
3837
3833
  shouldShowLoaderAtIndex,
3838
3834
  count,
3835
+ separator,
3836
+ topSeparatorOnScroll,
3839
3837
  };
3840
3838
  };
3839
+ const getResolvedLoadingIndicator = (loadingIndicator) => {
3840
+ if (typeof loadingIndicator === "function") {
3841
+ return loadingIndicator(DEFAULT_LOADING_INDICATOR_CONFIG);
3842
+ }
3843
+ return loadingIndicator;
3844
+ };
3841
3845
 
3842
3846
  // Height constants (in pixels) - based on ListItem.variants.ts styling
3843
3847
  const MIN_ITEM_HEIGHT = 56; // min-h-14 = 56px (minimum height for ListItem)
@@ -3955,7 +3959,7 @@ const cvaInteractableItem = cssClassVarianceUtilities.cvaMerge("", {
3955
3959
  * @returns {Element} ListItem component
3956
3960
  */
3957
3961
  const ListItem = ({ className, dataTestId, onClick, details, title, description, meta, thumbnail, thumbnailColor = "info-600", thumbnailBackground = "info-100", ...rest }) => {
3958
- const baseClass = cvaListItem$1({ className });
3962
+ const baseClass = cvaListItem({ className });
3959
3963
  const interactableItemClass = onClick ? tailwindMerge.twMerge(baseClass, cvaInteractableItem({ cursor: "pointer" })) : baseClass;
3960
3964
  return (jsxRuntime.jsxs("li", { className: interactableItemClass, "data-testid": dataTestId, onClick: onClick, ...rest, children: [jsxRuntime.jsxs("div", { className: cvaMainInformationClass({ hasThumbnail: !!thumbnail }), children: [thumbnail ? (jsxRuntime.jsx("div", { className: cvaThumbnailContainer({
3961
3965
  className: `text-${thumbnailColor} bg-${thumbnailBackground}`,
@@ -5229,7 +5233,7 @@ exports.cvaIndicatorPing = cvaIndicatorPing;
5229
5233
  exports.cvaInteractableItem = cvaInteractableItem;
5230
5234
  exports.cvaList = cvaList;
5231
5235
  exports.cvaListContainer = cvaListContainer;
5232
- exports.cvaListItem = cvaListItem;
5236
+ exports.cvaListItem = cvaListItem$1;
5233
5237
  exports.cvaMenuItem = cvaMenuItem;
5234
5238
  exports.cvaMenuItemLabel = cvaMenuItemLabel;
5235
5239
  exports.cvaMenuItemPrefix = cvaMenuItemPrefix;
package/index.esm.js CHANGED
@@ -30,7 +30,6 @@ const cvaIcon = cvaMerge(["aspect-square", "inline-grid", "relative", "shrink-0"
30
30
  variants: {
31
31
  color: {
32
32
  primary: "text-primary-600",
33
- secondary: "text-neutral-400",
34
33
  neutral: "text-neutral-400",
35
34
  info: "text-info-600",
36
35
  success: "text-success-600",
@@ -199,7 +198,6 @@ const cvaTag = cvaMerge([
199
198
  },
200
199
  color: {
201
200
  primary: ["bg-primary-100", "text-primary-700"],
202
- secondary: ["bg-neutral-100", "text-neutral-700"],
203
201
  neutral: ["bg-neutral-200", "text-neutral-700"],
204
202
  black: ["bg-neutral-100", "text-neutral-700"],
205
203
  white: ["bg-white", "text-neutral-600"],
@@ -272,7 +270,7 @@ Tag.displayName = "Tag";
272
270
  * A component used to display the package name and version in the Storybook docs.
273
271
  */
274
272
  const PackageNameStoryComponent = ({ packageJSON }) => {
275
- return (jsxs("div", { className: "flex gap-2", children: [jsx(Tag, { color: "secondary", children: packageJSON?.name }), jsxs(Tag, { color: "secondary", children: ["v", packageJSON?.version] })] }));
273
+ return (jsxs("div", { className: "flex gap-2", children: [jsx(Tag, { color: "neutral", children: packageJSON?.name }), jsxs(Tag, { color: "neutral", children: ["v", packageJSON?.version] })] }));
276
274
  };
277
275
 
278
276
  /**
@@ -1935,7 +1933,7 @@ const BreadcrumbForMediumScreen = ({ dataTestId, breadcrumbItems }) => {
1935
1933
  const [expanded, setExpanded] = useState(false);
1936
1934
  const getReducedArray = useCallback(() => {
1937
1935
  let reducedArrayElements = [];
1938
- const collapsibleButton = (jsxs("div", { className: cvaBreadcrumbItem(), children: [jsx(IconButton, { dataTestId: `collapsibleButton-${dataTestId}`, icon: jsx(Icon, { name: "EllipsisHorizontal", size: "small" }), onClick: () => setExpanded(true), size: "small", variant: "ghost-neutral" }), jsx(Icon, { color: "secondary", name: "Slash", size: "small" })] }));
1936
+ const collapsibleButton = (jsxs("div", { className: cvaBreadcrumbItem(), children: [jsx(IconButton, { dataTestId: `collapsibleButton-${dataTestId}`, icon: jsx(Icon, { name: "EllipsisHorizontal", size: "small" }), onClick: () => setExpanded(true), size: "small", variant: "ghost-neutral" }), jsx(Icon, { color: "neutral", name: "Slash", size: "small" })] }));
1939
1937
  const firstBreadcrumbItem = breadcrumbItems[0];
1940
1938
  if (firstBreadcrumbItem && breadcrumbItems.length > 3) {
1941
1939
  const lastTwoBreadcrumbItem = breadcrumbItems.slice(-2);
@@ -3424,7 +3422,31 @@ const cvaCardBodyDensityContainer = cvaMerge(["grid", "grid-cols-[1fr_auto]"], {
3424
3422
  },
3425
3423
  });
3426
3424
 
3427
- const cvaListItem$1 = cvaMerge(["py-3", "px-4", "min-h-14", "w-full", "flex", "justify-between", "items-center"]);
3425
+ const cvaListContainer = cvaMerge(["overflow-y-auto", "overflow-x-hidden", "h-full"], {
3426
+ variants: {
3427
+ withTopSeparator: {
3428
+ true: ["border-t", "border-neutral-200", "transition-colors duration-200 ease-in"],
3429
+ false: ["border-t", "border-transparent", "transition-colors duration-200 ease-in"],
3430
+ },
3431
+ },
3432
+ defaultVariants: {
3433
+ withTopSeparator: false,
3434
+ },
3435
+ });
3436
+ const cvaList = cvaMerge(["relative"]);
3437
+ const cvaListItem$1 = cvaMerge(["absolute", "top-0", "left-0", "w-full"], {
3438
+ variants: {
3439
+ separator: {
3440
+ line: ["border-b", "border-neutral-200"],
3441
+ none: "",
3442
+ },
3443
+ },
3444
+ defaultVariants: {
3445
+ separator: "none",
3446
+ },
3447
+ });
3448
+
3449
+ const cvaListItem = cvaMerge(["py-3", "px-4", "min-h-14", "w-full", "flex", "justify-between", "items-center"]);
3428
3450
  const cvaMainInformationClass = cvaMerge(["grid", "items-center", "text-sm", "gap-2"], {
3429
3451
  variants: {
3430
3452
  hasThumbnail: {
@@ -3464,33 +3486,9 @@ const ListItemSkeleton = ({ hasThumbnail = DEFAULT_SKELETON_LIST_ITEM_PROPS.hasT
3464
3486
  details: getResponsiveRandomWidthPercentage({ min: 25, max: 45 }),
3465
3487
  };
3466
3488
  });
3467
- 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] }));
3489
+ return (jsxs("div", { className: cvaListItem({ className: "w-full" }), children: [jsxs("div", { className: cvaMainInformationClass({ hasThumbnail, className: "w-full" }), children: [hasThumbnail ? (jsx("div", { className: cvaThumbnailContainer({ className: "bg-gray-200" }), children: jsx("div", { className: twMerge("bg-gray-300", thumbnailShape === "circle" ? "rounded-full" : "rounded"), style: { width: 20, height: 20 } }) })) : null, jsxs("div", { className: "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] }));
3468
3490
  };
3469
3491
 
3470
- const cvaListContainer = cvaMerge(["overflow-y-auto", "overflow-x-hidden", "h-full"], {
3471
- variants: {
3472
- withTopSeparator: {
3473
- true: ["border-t", "border-neutral-200", "transition-colors duration-200 ease-in"],
3474
- false: ["border-t", "border-transparent", "transition-colors duration-200 ease-in"],
3475
- },
3476
- },
3477
- defaultVariants: {
3478
- withTopSeparator: false,
3479
- },
3480
- });
3481
- const cvaList = cvaMerge(["relative"]);
3482
- const cvaListItem = cvaMerge(["absolute", "top-0", "left-0", "w-full"], {
3483
- variants: {
3484
- separator: {
3485
- line: ["[&:not(:last-child)]:border-b", "border-neutral-200"],
3486
- none: "",
3487
- },
3488
- },
3489
- defaultVariants: {
3490
- separator: "none",
3491
- },
3492
- });
3493
-
3494
3492
  /**
3495
3493
  *
3496
3494
  */
@@ -3575,9 +3573,9 @@ const ListLoadingIndicator = ({ type, hasThumbnail, thumbnailShape, hasDescripti
3575
3573
  * );
3576
3574
  * ```
3577
3575
  */
3578
- const List = ({ children, className, dataTestId, topSeparatorOnScroll = false, separator = "line",
3576
+ const List = ({ children, className, dataTestId,
3579
3577
  // UseListResult properties
3580
- containerRef, listRef, rows, getListItemProps, header, loadingIndicator, shouldShowLoaderAtIndex, count, isScrolling, scrollOffset,
3578
+ containerRef, listRef, rows, getListItemProps, header, loadingIndicator, shouldShowLoaderAtIndex, count, isScrolling, scrollOffset, separator, topSeparatorOnScroll,
3581
3579
  // Unused but part of UseListResult interface
3582
3580
  getTotalSize: _getTotalSize, getVirtualItems: _getVirtualItems, scrollToOffset: _scrollToOffset, scrollToIndex: _scrollToIndex, measure: _measure, }) => {
3583
3581
  return (jsx("div", { className: cvaListContainer({
@@ -3586,22 +3584,18 @@ getTotalSize: _getTotalSize, getVirtualItems: _getVirtualItems, scrollToOffset:
3586
3584
  }), "data-is-scrolling": isScrolling, "data-testid": dataTestId, ref: containerRef, children: jsx("ul", { className: cvaList(), ref: listRef, children: rows.map(row => {
3587
3585
  // Generate list item props with separator styling
3588
3586
  const listItemProps = getListItemProps(row, {
3589
- className: cvaListItem({ separator }),
3587
+ className: cvaListItem$1({ separator }),
3590
3588
  });
3591
3589
  const key = row.virtualRow.key;
3592
3590
  // Render loading row
3593
3591
  if (row.type === "loader") {
3594
- const loadingConfig = loadingIndicator ?? {
3595
- type: "skeleton",
3596
- ...DEFAULT_SKELETON_LIST_ITEM_PROPS,
3597
- };
3598
3592
  // Use the total data count from useList, not visible rows (which are virtualized)
3599
3593
  const hasHeaderRow = !!header;
3600
3594
  const dataStartIndex = hasHeaderRow ? 1 : 0;
3601
3595
  const totalDataRows = dataStartIndex + count;
3602
3596
  const loaderIndex = row.virtualRow.index - totalDataRows;
3603
3597
  const shouldShowLoader = shouldShowLoaderAtIndex(loaderIndex);
3604
- return (jsx("li", { ...listItemProps, children: shouldShowLoader ? jsx(ListLoadingIndicator, { ...loadingConfig }) : null }, key));
3598
+ return (jsx("li", { ...listItemProps, children: shouldShowLoader ? jsx(ListLoadingIndicator, { ...loadingIndicator }) : null }, key));
3605
3599
  }
3606
3600
  // Render header row
3607
3601
  if (row.type === "header") {
@@ -3649,10 +3643,12 @@ const DEFAULT_LOADING_INDICATOR_CONFIG = {
3649
3643
  * );
3650
3644
  * ```
3651
3645
  */
3652
- const useList = ({ count, pagination, header, getItem, loadingIndicator = DEFAULT_LOADING_INDICATOR_CONFIG, onRowClick, onChange, estimateItemSize, estimateHeaderSize, overscan, }) => {
3646
+ const useList = ({ count, pagination, header, getItem, loadingIndicator = DEFAULT_LOADING_INDICATOR_CONFIG, onRowClick, onChange, estimateItemSize, estimateHeaderSize, overscan, separator = "line", topSeparatorOnScroll = false, }) => {
3653
3647
  const containerRef = useRef(null);
3654
3648
  const listRef = useRef(null);
3655
3649
  const rowRefsMap = useRef(new Map());
3650
+ // Resolve loading indicator once to avoid unnecessary re-renders
3651
+ const resolvedLoadingIndicator = useMemo(() => getResolvedLoadingIndicator(loadingIndicator), [loadingIndicator]);
3656
3652
  // Calculate total count including header
3657
3653
  const hasHeader = Boolean(header);
3658
3654
  const dataStartIndex = hasHeader ? 1 : 0;
@@ -3662,7 +3658,7 @@ const useList = ({ count, pagination, header, getItem, loadingIndicator = DEFAUL
3662
3658
  const getLoadingRowsCount = useCallback(() => {
3663
3659
  if (pagination?.isLoading === false)
3664
3660
  return 0;
3665
- const { type: loadingIndicatorType } = loadingIndicator;
3661
+ const { type: loadingIndicatorType } = resolvedLoadingIndicator;
3666
3662
  switch (loadingIndicatorType) {
3667
3663
  case "none":
3668
3664
  return 0;
@@ -3671,20 +3667,20 @@ const useList = ({ count, pagination, header, getItem, loadingIndicator = DEFAUL
3671
3667
  case "custom":
3672
3668
  case "skeleton": {
3673
3669
  const isInitialLoading = !pagination?.pageInfo;
3674
- const initialCount = loadingIndicator.initialLoadingCount ?? 10;
3675
- const scrollCount = loadingIndicator.scrollLoadingCount ?? 3;
3670
+ const initialCount = resolvedLoadingIndicator.initialLoadingCount ?? 10;
3671
+ const scrollCount = resolvedLoadingIndicator.scrollLoadingCount ?? 3;
3676
3672
  return isInitialLoading ? initialCount : scrollCount;
3677
3673
  }
3678
3674
  default: {
3679
3675
  throw new Error(`${loadingIndicatorType} is not known`);
3680
3676
  }
3681
3677
  }
3682
- }, [loadingIndicator, pagination?.isLoading, pagination?.pageInfo]);
3678
+ }, [resolvedLoadingIndicator, pagination?.isLoading, pagination?.pageInfo]);
3683
3679
  // Helper to determine if a specific loader index should be shown
3684
3680
  const shouldShowLoaderAtIndex = useCallback((loaderIndex) => {
3685
3681
  if (pagination?.isLoading === false)
3686
3682
  return false;
3687
- const { type: loadingIndicatorType } = loadingIndicator;
3683
+ const { type: loadingIndicatorType } = resolvedLoadingIndicator;
3688
3684
  let result;
3689
3685
  switch (loadingIndicatorType) {
3690
3686
  case "none":
@@ -3696,8 +3692,8 @@ const useList = ({ count, pagination, header, getItem, loadingIndicator = DEFAUL
3696
3692
  case "custom":
3697
3693
  case "skeleton": {
3698
3694
  const isInitialLoading = !pagination?.pageInfo;
3699
- const initialCount = loadingIndicator.initialLoadingCount ?? 10;
3700
- const scrollCount = loadingIndicator.scrollLoadingCount ?? 3;
3695
+ const initialCount = resolvedLoadingIndicator.initialLoadingCount ?? 10;
3696
+ const scrollCount = resolvedLoadingIndicator.scrollLoadingCount ?? 3;
3701
3697
  const maxCount = isInitialLoading ? initialCount : scrollCount;
3702
3698
  result = loaderIndex < maxCount;
3703
3699
  break;
@@ -3707,7 +3703,7 @@ const useList = ({ count, pagination, header, getItem, loadingIndicator = DEFAUL
3707
3703
  }
3708
3704
  }
3709
3705
  return result;
3710
- }, [loadingIndicator, pagination?.isLoading, pagination?.pageInfo]);
3706
+ }, [resolvedLoadingIndicator, pagination?.isLoading, pagination?.pageInfo]);
3711
3707
  const totalRowCount = useMemo(() => {
3712
3708
  // Only add the exact number of loading rows we want to show
3713
3709
  const loadingRows = pagination?.isLoading === true ? getLoadingRowsCount() : 0;
@@ -3831,11 +3827,19 @@ const useList = ({ count, pagination, header, getItem, loadingIndicator = DEFAUL
3831
3827
  rows,
3832
3828
  getListItemProps,
3833
3829
  header,
3834
- loadingIndicator,
3830
+ loadingIndicator: resolvedLoadingIndicator,
3835
3831
  shouldShowLoaderAtIndex,
3836
3832
  count,
3833
+ separator,
3834
+ topSeparatorOnScroll,
3837
3835
  };
3838
3836
  };
3837
+ const getResolvedLoadingIndicator = (loadingIndicator) => {
3838
+ if (typeof loadingIndicator === "function") {
3839
+ return loadingIndicator(DEFAULT_LOADING_INDICATOR_CONFIG);
3840
+ }
3841
+ return loadingIndicator;
3842
+ };
3839
3843
 
3840
3844
  // Height constants (in pixels) - based on ListItem.variants.ts styling
3841
3845
  const MIN_ITEM_HEIGHT = 56; // min-h-14 = 56px (minimum height for ListItem)
@@ -3953,7 +3957,7 @@ const cvaInteractableItem = cvaMerge("", {
3953
3957
  * @returns {Element} ListItem component
3954
3958
  */
3955
3959
  const ListItem = ({ className, dataTestId, onClick, details, title, description, meta, thumbnail, thumbnailColor = "info-600", thumbnailBackground = "info-100", ...rest }) => {
3956
- const baseClass = cvaListItem$1({ className });
3960
+ const baseClass = cvaListItem({ className });
3957
3961
  const interactableItemClass = onClick ? twMerge(baseClass, cvaInteractableItem({ cursor: "pointer" })) : baseClass;
3958
3962
  return (jsxs("li", { className: interactableItemClass, "data-testid": dataTestId, onClick: onClick, ...rest, children: [jsxs("div", { className: cvaMainInformationClass({ hasThumbnail: !!thumbnail }), children: [thumbnail ? (jsx("div", { className: cvaThumbnailContainer({
3959
3963
  className: `text-${thumbnailColor} bg-${thumbnailBackground}`,
@@ -5147,4 +5151,4 @@ const cvaClickable = cvaMerge([
5147
5151
  },
5148
5152
  });
5149
5153
 
5150
- 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, defaultPageSize, docs, getDevicePixelRatio, getResponsiveRandomWidthPercentage, getValueBarColorByValue, iconColorNames, iconPalette, noPagination, useClickOutside, useContainerBreakpoints, useContinuousTimeout, useDebounce, useDevicePixelRatio, useElevatedReducer, useElevatedState, useGeometry, useHover, useInfiniteScroll, useIsFirstRender, useIsFullscreen, useIsTextTruncated, useList, useListItemHeight, useModifierKey, useOverflowItems, usePopoverContext, usePrompt, useRelayPagination, useResize, useScrollDetection, useSelfUpdatingRef, useStable, useTimeout, useViewportBreakpoints, useWatch, useWindowActivity };
5154
+ 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$1 as cvaListItem, cvaMenuItem, cvaMenuItemLabel, cvaMenuItemPrefix, cvaMenuItemStyle, cvaMenuItemSuffix, cvaPageHeader, cvaPageHeaderContainer, cvaPageHeaderHeading, cvaToggleGroup, cvaToggleGroupWithSlidingBackground, cvaToggleItem, cvaToggleItemContent, cvaToggleItemText, cvaZStackContainer, cvaZStackItem, defaultPageSize, docs, getDevicePixelRatio, getResponsiveRandomWidthPercentage, getValueBarColorByValue, iconColorNames, iconPalette, noPagination, useClickOutside, useContainerBreakpoints, useContinuousTimeout, useDebounce, useDevicePixelRatio, useElevatedReducer, useElevatedState, useGeometry, useHover, useInfiniteScroll, useIsFirstRender, useIsFullscreen, useIsTextTruncated, useList, useListItemHeight, useModifierKey, useOverflowItems, usePopoverContext, usePrompt, useRelayPagination, useResize, useScrollDetection, useSelfUpdatingRef, useStable, useTimeout, useViewportBreakpoints, useWatch, useWindowActivity };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trackunit/react-components",
3
- "version": "1.9.21",
3
+ "version": "1.9.24",
4
4
  "repository": "https://github.com/Trackunit/manager",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "engines": {
@@ -16,11 +16,11 @@
16
16
  "@floating-ui/react": "^0.26.25",
17
17
  "string-ts": "^2.0.0",
18
18
  "tailwind-merge": "^2.0.0",
19
- "@trackunit/ui-design-tokens": "1.7.12",
20
- "@trackunit/css-class-variance-utilities": "1.7.12",
21
- "@trackunit/shared-utils": "1.9.12",
22
- "@trackunit/ui-icons": "1.7.14",
23
- "@trackunit/react-test-setup": "1.4.12",
19
+ "@trackunit/ui-design-tokens": "1.7.15",
20
+ "@trackunit/css-class-variance-utilities": "1.7.14",
21
+ "@trackunit/shared-utils": "1.9.14",
22
+ "@trackunit/ui-icons": "1.7.16",
23
+ "@trackunit/react-test-setup": "1.4.14",
24
24
  "@tanstack/react-router": "1.114.29",
25
25
  "es-toolkit": "^1.39.10",
26
26
  "@tanstack/react-virtual": "3.13.12"
@@ -172,18 +172,6 @@ export declare const iconPalette: {
172
172
  readonly 800: "30 64 175";
173
173
  readonly 900: "30 58 138";
174
174
  };
175
- SECONDARY: {
176
- readonly 50: "248 250 252";
177
- readonly 100: "241 245 249";
178
- readonly 200: "226 232 240";
179
- readonly 300: "203 213 225";
180
- readonly 400: "148 163 184";
181
- readonly 500: "100 116 139";
182
- readonly 600: "71 85 105";
183
- readonly 700: "51 65 85";
184
- readonly 800: "30 41 59";
185
- readonly 900: "15 23 42";
186
- };
187
175
  NEUTRAL: {
188
176
  readonly 50: "249 250 251";
189
177
  readonly 100: "243 244 246";
@@ -251,7 +239,7 @@ export declare const iconPalette: {
251
239
  readonly 900: "127 29 29";
252
240
  };
253
241
  };
254
- export declare const iconColorNames: ("info" | "success" | "warning" | "danger" | "primary" | "secondary" | "neutral" | "black" | "white" | "good" | "low" | "critical" | "working" | "idle" | "stopped" | "unknown" | "moving" | "active" | "excessive_usage" | "unused" | "utilized" | "heavily_utilized" | "unknown_utilization" | "site_area" | "site_classic_poi" | "site_classic_zone" | "site_depot" | "site_work_place" | "site_construction_site" | "site_unknown" | "on_rent" | "returned" | "available" | "pickup_ready" | "transfer" | "in_repair" | "other_rental_status")[];
242
+ export declare const iconColorNames: ("info" | "success" | "warning" | "danger" | "primary" | "neutral" | "black" | "white" | "good" | "low" | "critical" | "working" | "idle" | "stopped" | "unknown" | "moving" | "active" | "excessive_usage" | "unused" | "utilized" | "heavily_utilized" | "unknown_utilization" | "site_area" | "site_classic_poi" | "site_classic_zone" | "site_depot" | "site_work_place" | "site_construction_site" | "site_unknown" | "on_rent" | "returned" | "available" | "pickup_ready" | "transfer" | "in_repair" | "other_rental_status")[];
255
243
  type IconPropsSmall = {
256
244
  size?: "small";
257
245
  type?: "solid";
@@ -1,5 +1,5 @@
1
1
  export declare const cvaIcon: (props?: ({
2
- color?: "default" | "primary" | "secondary" | "neutral" | "info" | "success" | "warning" | "danger" | "good" | "low" | "critical" | "working" | "idle" | "moving" | "active" | "excessive_usage" | "stopped" | "unknown" | "black" | "white" | "unused" | "utilized" | "heavily_utilized" | "unknown_utilization" | "site_area" | "site_classic_poi" | "site_classic_zone" | "site_depot" | "site_work_place" | "site_construction_site" | "site_unknown" | "on_rent" | "returned" | "available" | "pickup_ready" | "transfer" | "in_repair" | "other_rental_status" | null | undefined;
2
+ color?: "default" | "primary" | "neutral" | "info" | "success" | "warning" | "danger" | "good" | "low" | "critical" | "working" | "idle" | "moving" | "active" | "excessive_usage" | "stopped" | "unknown" | "black" | "white" | "unused" | "utilized" | "heavily_utilized" | "unknown_utilization" | "site_area" | "site_classic_poi" | "site_classic_zone" | "site_depot" | "site_work_place" | "site_construction_site" | "site_unknown" | "on_rent" | "returned" | "available" | "pickup_ready" | "transfer" | "in_repair" | "other_rental_status" | null | undefined;
3
3
  size?: "small" | "medium" | "large" | null | undefined;
4
4
  fontSize?: boolean | null | undefined;
5
5
  } & import("class-variance-authority/dist/types").ClassProp) | undefined) => string;
@@ -1,9 +1,6 @@
1
- import { VariantProps } from "@trackunit/css-class-variance-utilities";
2
1
  import { ReactElement } from "react";
3
2
  import { CommonProps } from "../../common/CommonProps";
4
- import { cvaListItem } from "./List.variants";
5
3
  import { UseListResult, VirtualizationListItemProps } from "./useList";
6
- type Separator = NonNullable<VariantProps<typeof cvaListItem>["separator"]>;
7
4
  export type { VirtualizationListItemProps } from "./useList";
8
5
  export interface ListProps<TItem = unknown> extends CommonProps, UseListResult<TItem> {
9
6
  /**
@@ -26,14 +23,6 @@ export interface ListProps<TItem = unknown> extends CommonProps, UseListResult<T
26
23
  item: TItem | undefined;
27
24
  index: number;
28
25
  }) => ReactElement | null;
29
- /**
30
- * Separator style between list items.
31
- */
32
- separator?: Separator;
33
- /**
34
- * Show a top separator when the list is scrolled.
35
- */
36
- topSeparatorOnScroll?: boolean;
37
26
  }
38
27
  /**
39
28
  * A performant virtualized list component with infinite scrolling support.
@@ -100,4 +89,4 @@ export interface ListProps<TItem = unknown> extends CommonProps, UseListResult<T
100
89
  * );
101
90
  * ```
102
91
  */
103
- export declare const List: <TItem = unknown>({ children, className, dataTestId, topSeparatorOnScroll, separator, containerRef, listRef, rows, getListItemProps, header, loadingIndicator, shouldShowLoaderAtIndex, count, isScrolling, scrollOffset, getTotalSize: _getTotalSize, getVirtualItems: _getVirtualItems, scrollToOffset: _scrollToOffset, scrollToIndex: _scrollToIndex, measure: _measure, }: ListProps<TItem>) => ReactElement;
92
+ export declare const List: <TItem = unknown>({ children, className, dataTestId, containerRef, listRef, rows, getListItemProps, header, loadingIndicator, shouldShowLoaderAtIndex, count, isScrolling, scrollOffset, separator, topSeparatorOnScroll, getTotalSize: _getTotalSize, getVirtualItems: _getVirtualItems, scrollToOffset: _scrollToOffset, scrollToIndex: _scrollToIndex, measure: _measure, }: ListProps<TItem>) => ReactElement;
@@ -2,6 +2,9 @@ import type { VirtualItem, Virtualizer } from "@tanstack/react-virtual";
2
2
  import { ReactElement, RefObject } from "react";
3
3
  import { RelayPagination } from "../../hooks";
4
4
  import { ListLoadingIndicatorProps } from "./ListLoadingIndicator";
5
+ declare const DEFAULT_LOADING_INDICATOR_CONFIG: Extract<ListLoadingIndicatorProps, {
6
+ type: "skeleton";
7
+ }>;
5
8
  /**
6
9
  * Props that must be spread onto list items for proper virtualization.
7
10
  * These handle positioning, measurement, accessibility, and interaction.
@@ -12,7 +15,7 @@ export interface VirtualizationListItemProps {
12
15
  /** CSS classes for list styling and separators */
13
16
  className: string;
14
17
  /** Element ref for virtualization measurement and setting positioning transform styles */
15
- ref?: (node: HTMLLIElement | null) => void;
18
+ ref: (node: HTMLLIElement | null) => void;
16
19
  /** Click handler for row-level interactions */
17
20
  onClick?: () => void;
18
21
  /** Data attribute for accessibility and debugging */
@@ -45,7 +48,7 @@ export interface UseListOptions<TItem = unknown> {
45
48
  /**
46
49
  * Loading indicator configuration for pagination loading.
47
50
  */
48
- loadingIndicator?: ListLoadingIndicatorProps;
51
+ loadingIndicator?: ListLoadingIndicatorProps | ((defaultLoadingIndicator: typeof DEFAULT_LOADING_INDICATOR_CONFIG) => ListLoadingIndicatorProps);
49
52
  /**
50
53
  * Callback fired when a row is clicked.
51
54
  */
@@ -97,6 +100,14 @@ export interface UseListOptions<TItem = unknown> {
97
100
  * Higher values reduce blank areas during fast scrolling but increase memory usage.
98
101
  */
99
102
  overscan?: number;
103
+ /**
104
+ * Separator style between list items.
105
+ */
106
+ separator?: "none" | "line";
107
+ /**
108
+ * Show a top separator when the list is scrolled.
109
+ */
110
+ topSeparatorOnScroll?: boolean;
100
111
  }
101
112
  export type ListRowType = "header" | "data" | "loader";
102
113
  export type ListRow<TItem> = {
@@ -130,11 +141,15 @@ export interface UseListResult<TItem> extends Pick<Virtualizer<HTMLDivElement, H
130
141
  /** Header element (if provided) */
131
142
  readonly header?: ReactElement;
132
143
  /** Loading indicator configuration */
133
- readonly loadingIndicator?: ListLoadingIndicatorProps;
144
+ readonly loadingIndicator: ListLoadingIndicatorProps;
134
145
  /** Helper to determine if a specific loader index should be shown */
135
146
  readonly shouldShowLoaderAtIndex: (loaderIndex: number) => boolean;
136
147
  /** Total number of data items (for consistent calculations) */
137
148
  readonly count: number;
149
+ /** Separator style between list items */
150
+ readonly separator: "none" | "line";
151
+ /** Show a top separator when the list is scrolled */
152
+ readonly topSeparatorOnScroll: boolean;
138
153
  }
139
154
  /**
140
155
  * A hook for managing virtualized list state and behavior.
@@ -168,4 +183,5 @@ export interface UseListResult<TItem> extends Pick<Virtualizer<HTMLDivElement, H
168
183
  * );
169
184
  * ```
170
185
  */
171
- export declare const useList: <TItem = unknown>({ count, pagination, header, getItem, loadingIndicator, onRowClick, onChange, estimateItemSize, estimateHeaderSize, overscan, }: UseListOptions<TItem>) => UseListResult<TItem>;
186
+ export declare const useList: <TItem = unknown>({ count, pagination, header, getItem, loadingIndicator, onRowClick, onChange, estimateItemSize, estimateHeaderSize, overscan, separator, topSeparatorOnScroll, }: UseListOptions<TItem>) => UseListResult<TItem>;
187
+ export {};
@@ -1,11 +1,21 @@
1
1
  import { VariantProps } from "@trackunit/css-class-variance-utilities";
2
2
  import { MappedOmit } from "@trackunit/shared-utils";
3
3
  import { tailwindPalette, ThemeColors } from "@trackunit/ui-design-tokens";
4
- import { CSSProperties, MouseEventHandler, ReactElement, ReactNode, Ref } from "react";
4
+ import { MouseEventHandler, ReactElement, ReactNode, Ref } from "react";
5
5
  import { CommonProps } from "../../common/CommonProps";
6
6
  import { cvaListItem } from "./ListItem.variants";
7
7
  type ThemeColorShades = `${keyof (typeof tailwindPalette)[keyof typeof tailwindPalette]}`;
8
- export interface ListItemProps extends CommonProps, MappedOmit<VariantProps<typeof cvaListItem>, "className"> {
8
+ export type ListItemVirtualizationProps = {
9
+ /** @internal */
10
+ ref?: Ref<HTMLLIElement>;
11
+ /** @internal */
12
+ tabIndex?: number;
13
+ /** @internal */
14
+ "data-index"?: number;
15
+ /** @internal */
16
+ className?: string;
17
+ };
18
+ export interface ListItemProps extends CommonProps, ListItemVirtualizationProps, MappedOmit<VariantProps<typeof cvaListItem>, "className"> {
9
19
  /**The main text line of the ListItem */
10
20
  title: string | ReactElement<CommonProps>;
11
21
  /**Optional description for the ListItem. Can be used for descriptions, metadata, or other important details. */
@@ -24,14 +34,6 @@ export interface ListItemProps extends CommonProps, MappedOmit<VariantProps<type
24
34
  * If asset image is chosen as thumbnail, make the thumbnailBackground "white".
25
35
  */
26
36
  thumbnailBackground?: `${ThemeColors}-${ThemeColorShades}` | `${ThemeColors}`;
27
- /** @internal */
28
- style?: CSSProperties;
29
- /** @internal */
30
- ref?: Ref<HTMLLIElement>;
31
- /** @internal */
32
- tabIndex?: number;
33
- /** @internal */
34
- "data-index"?: number;
35
37
  }
36
38
  /**
37
39
  * 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.
@@ -1,7 +1,7 @@
1
1
  export declare const cvaTag: (props?: ({
2
2
  size?: "small" | "medium" | null | undefined;
3
3
  layout?: "default" | "containsDismiss" | "containsIcon" | null | undefined;
4
- color?: "primary" | "secondary" | "neutral" | "black" | "white" | "info" | "success" | "warning" | "danger" | "good" | "low" | "critical" | "working" | "stopped" | "idle" | "unknown" | "moving" | "active" | "excessive_usage" | null | undefined;
4
+ color?: "primary" | "neutral" | "black" | "white" | "info" | "success" | "warning" | "danger" | "good" | "low" | "critical" | "working" | "stopped" | "idle" | "unknown" | "moving" | "active" | "excessive_usage" | null | undefined;
5
5
  border?: "default" | "none" | null | undefined;
6
6
  } & import("class-variance-authority/dist/types").ClassProp) | undefined) => string;
7
7
  export declare const cvaTagText: (props?: import("class-variance-authority/dist/types").ClassProp | undefined) => string;