@trackunit/react-components 1.17.22 → 1.17.23
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 +1309 -185
- package/index.esm.js +1309 -185
- package/package.json +1 -1
- package/src/components/Badge/Badge.d.ts +13 -6
- package/src/components/Card/CardBody.d.ts +38 -3
- package/src/components/Card/CardFooter.d.ts +44 -3
- package/src/components/Card/CardHeader.d.ts +43 -2
- package/src/components/CompletionStatusIndicator/CompletionStatusIndicator.d.ts +19 -10
- package/src/components/CopyableText/CopyableText.d.ts +28 -2
- package/src/components/DetailsList/DetailsList.d.ts +28 -7
- package/src/components/EmptyState/EmptyState.d.ts +24 -0
- package/src/components/EmptyValue/EmptyValue.d.ts +19 -1
- package/src/components/ExternalLink/ExternalLink.d.ts +35 -4
- package/src/components/Heading/Heading.d.ts +22 -1
- package/src/components/Highlight/Highlight.d.ts +32 -7
- package/src/components/HorizontalOverflowScroller/HorizontalOverflowScroller.d.ts +24 -9
- package/src/components/Icon/Icon.d.ts +19 -2
- package/src/components/KPI/KPISkeleton.d.ts +23 -2
- package/src/components/KPICard/KPICardSkeleton.d.ts +23 -2
- package/src/components/ListItem/ListItem.d.ts +25 -3
- package/src/components/ListItem/ListItemSkeleton.d.ts +24 -2
- package/src/components/Menu/MenuDivider/MenuDivider.d.ts +22 -1
- package/src/components/Menu/MenuItem/MenuItem.d.ts +30 -1
- package/src/components/Menu/MoreMenu/MoreMenu.d.ts +56 -2
- package/src/components/Notice/Notice.d.ts +33 -3
- package/src/components/Page/Page.d.ts +38 -2
- package/src/components/Page/PageContent.d.ts +34 -5
- package/src/components/PageHeader/PageHeader.d.ts +8 -4
- package/src/components/Pagination/Pagination.d.ts +31 -2
- package/src/components/Polygon/Polygon.d.ts +38 -3
- package/src/components/Popover/Popover.d.ts +8 -6
- package/src/components/Popover/PopoverContent.d.ts +29 -3
- package/src/components/Popover/PopoverTitle.d.ts +36 -2
- package/src/components/Popover/PopoverTrigger.d.ts +35 -4
- package/src/components/Portal/Portal.d.ts +35 -4
- package/src/components/PreferenceCard/PreferenceCard.d.ts +25 -3
- package/src/components/PreferenceCard/PreferenceCardSkeleton.d.ts +23 -2
- package/src/components/Prompt/Prompt.d.ts +42 -8
- package/src/components/SectionHeader/SectionHeader.d.ts +23 -3
- package/src/components/Sidebar/Sidebar.d.ts +12 -5
- package/src/components/Skeleton/SkeletonBlock/SkeletonBlock.d.ts +21 -5
- package/src/components/Skeleton/SkeletonLabel/SkeletonLabel.d.ts +21 -5
- package/src/components/SkeletonLines/SkeletonLines.d.ts +8 -6
- package/src/components/Spacer/Spacer.d.ts +17 -10
- package/src/components/Spinner/Spinner.d.ts +27 -6
- package/src/components/Tabs/Tab.d.ts +29 -2
- package/src/components/Tabs/TabContent.d.ts +46 -1
- package/src/components/Tabs/TabList.d.ts +29 -1
- package/src/components/Tabs/Tabs.d.ts +14 -1
- package/src/components/Tag/Tag.d.ts +13 -6
- package/src/components/Text/Text.d.ts +28 -3
- package/src/components/ToggleGroup/ToggleGroup.d.ts +30 -1
- package/src/components/ValueBar/ValueBar.d.ts +38 -2
- package/src/components/ZStack/ZStack.d.ts +23 -4
- package/src/components/buttons/Button/Button.d.ts +1 -1
- package/src/components/buttons/StarButton/StarButton.d.ts +24 -1
package/index.cjs.js
CHANGED
|
@@ -120,10 +120,27 @@ const isSafari = () => {
|
|
|
120
120
|
return ua.includes("safari") && !ua.includes("chrome");
|
|
121
121
|
};
|
|
122
122
|
/**
|
|
123
|
-
*
|
|
123
|
+
* Icon renders SVG icons from the icon sprite sheets. All [HeroIcons](https://heroicons.com/) as well as custom Trackunit icons are available.
|
|
124
|
+
* Icons support three sizes (small=16px, medium=20px, large=24px) and theme-based colors.
|
|
124
125
|
*
|
|
125
|
-
*
|
|
126
|
+
* ### When to use
|
|
127
|
+
* Use Icon to provide visual cues for actions, statuses, or navigation. Icons can be used standalone or paired with text (e.g., inside Buttons, MenuItems, Tags).
|
|
128
|
+
*
|
|
129
|
+
* ### When not to use
|
|
130
|
+
* Do not use Icon alone for critical actions without an accessible label. Always provide `ariaLabel` or pair with visible text.
|
|
131
|
+
*
|
|
132
|
+
* @example Icons at different sizes
|
|
133
|
+
* ```tsx
|
|
134
|
+
* import { Icon } from "@trackunit/react-components";
|
|
126
135
|
*
|
|
136
|
+
* const IconExamples = () => (
|
|
137
|
+
* <div className="flex items-center gap-4">
|
|
138
|
+
* <Icon name="MapPin" size="small" color="danger" />
|
|
139
|
+
* <Icon name="Truck" size="medium" color="primary" />
|
|
140
|
+
* <Icon name="Cog6Tooth" size="large" type="outline" />
|
|
141
|
+
* </div>
|
|
142
|
+
* );
|
|
143
|
+
* ```
|
|
127
144
|
* @param {IconProps} props - The props for the Icon component
|
|
128
145
|
* @returns {ReactElement} Icon component
|
|
129
146
|
*/
|
|
@@ -247,14 +264,21 @@ const cvaTagIcon = cssClassVarianceUtilities.cvaMerge(["cursor-pointer", "transi
|
|
|
247
264
|
|
|
248
265
|
const TAG_TEXT_MIN_WIDTH_PX = sharedUtils.parseTailwindArbitraryValue(TAG_TEXT_MIN_WIDTH_CLASS);
|
|
249
266
|
/**
|
|
250
|
-
*
|
|
251
|
-
*
|
|
252
|
-
*
|
|
267
|
+
* Tag is used for labeling or categorizing items in the UI. Common use cases include indicating asset status,
|
|
268
|
+
* marking features as Beta, or displaying selected options in multi-select inputs.
|
|
269
|
+
* Tags support dismissal (close button), icons, and multiple color variants.
|
|
253
270
|
*
|
|
254
|
-
*
|
|
271
|
+
* ### When to use
|
|
272
|
+
* Use Tag to label statuses, categories, or user selections. It supports colors for intent (success, warning, danger) and activity states.
|
|
273
|
+
*
|
|
274
|
+
* ### When not to use
|
|
275
|
+
* Do not use Tag for numeric counts — use `Badge`.
|
|
276
|
+
* Do not use Tag for highlighting data values — use `Highlight`.
|
|
277
|
+
*
|
|
278
|
+
* How to choose between Tag, `Badge` and `Highlight`?
|
|
255
279
|
* - Use a Tag for labeling statuses, categories, or selections.
|
|
256
|
-
* - Use a
|
|
257
|
-
* - Use a
|
|
280
|
+
* - Use a `Badge` to indicate notifications or counts of applied elements, such as filters.
|
|
281
|
+
* - Use a `Highlight` to draw attention to values in plain text that require special attention or have crossed a threshold.
|
|
258
282
|
*
|
|
259
283
|
* @example Status tags with different colors
|
|
260
284
|
* ```tsx
|
|
@@ -501,12 +525,37 @@ const cvaText = cssClassVarianceUtilities.cvaMerge(["text-black", "m-0", "relati
|
|
|
501
525
|
});
|
|
502
526
|
|
|
503
527
|
/**
|
|
504
|
-
*
|
|
528
|
+
* Text applies Trackunit default typography styles to body text. It renders as a `<p>`, `<span>`, or `<div>` element
|
|
529
|
+
* and supports size, weight, alignment, and style variants (subtle, inverted, uppercase, etc.).
|
|
530
|
+
*
|
|
531
|
+
* ### When to use
|
|
532
|
+
* Use Text for body content, descriptions, labels, and any non-heading text. It ensures consistent typography across the application.
|
|
533
|
+
*
|
|
534
|
+
* ### When not to use
|
|
535
|
+
* Do not use Text for page or section headings — use `Heading` instead.
|
|
505
536
|
*
|
|
506
|
-
*
|
|
537
|
+
* @example Text with different sizes and weights
|
|
538
|
+
* ```tsx
|
|
539
|
+
* import { Text } from "@trackunit/react-components";
|
|
507
540
|
*
|
|
508
|
-
*
|
|
541
|
+
* const TextExamples = () => (
|
|
542
|
+
* <div>
|
|
543
|
+
* <Text size="large" weight="bold">Large bold text</Text>
|
|
544
|
+
* <Text size="medium">Default body text</Text>
|
|
545
|
+
* <Text size="small" subtle>Small subtle caption</Text>
|
|
546
|
+
* </div>
|
|
547
|
+
* );
|
|
548
|
+
* ```
|
|
549
|
+
* @example Inline text using span
|
|
550
|
+
* ```tsx
|
|
551
|
+
* import { Text } from "@trackunit/react-components";
|
|
509
552
|
*
|
|
553
|
+
* const InlineExample = () => (
|
|
554
|
+
* <Text>
|
|
555
|
+
* Asset status: <Text type="span" weight="bold">Active</Text>
|
|
556
|
+
* </Text>
|
|
557
|
+
* );
|
|
558
|
+
* ```
|
|
510
559
|
* @param {TextProps} props - The props for the Text component
|
|
511
560
|
* @returns {ReactElement} Text component
|
|
512
561
|
*/
|
|
@@ -569,16 +618,37 @@ const cvaSpinnerContainer = cssClassVarianceUtilities.cvaMerge(["box-border", "p
|
|
|
569
618
|
const cvaSpinnerLabel = cssClassVarianceUtilities.cvaMerge(["self-center", "text-center", "text-current"]);
|
|
570
619
|
|
|
571
620
|
/**
|
|
572
|
-
*
|
|
621
|
+
* Spinner provides visual feedback that data is being processed. It reassures users that their action is being handled
|
|
622
|
+
* during short operations (1-5 seconds) such as saving, loading, or refreshing data.
|
|
623
|
+
*
|
|
624
|
+
* ### When to use
|
|
625
|
+
* Use Spinner for short-duration loading states: button actions, table refreshes, inline data fetches, or modal content loading.
|
|
573
626
|
*
|
|
574
|
-
*
|
|
627
|
+
* ### When not to use
|
|
628
|
+
* Do not use Spinner for long loading states where content structure is known — use `SkeletonBlock` or `SkeletonLabel` instead.
|
|
629
|
+
* Do not use Spinner for full-page loading — use `EmptyState` with `loading` prop.
|
|
575
630
|
*
|
|
576
|
-
*
|
|
631
|
+
* @example Centered spinner during data load
|
|
632
|
+
* ```tsx
|
|
633
|
+
* import { Spinner } from "@trackunit/react-components";
|
|
577
634
|
*
|
|
578
|
-
*
|
|
635
|
+
* const LoadingContent = () => (
|
|
636
|
+
* <div className="h-64">
|
|
637
|
+
* <Spinner centering="centered" label="Loading assets..." />
|
|
638
|
+
* </div>
|
|
639
|
+
* );
|
|
640
|
+
* ```
|
|
641
|
+
* @example Small inline spinner
|
|
642
|
+
* ```tsx
|
|
643
|
+
* import { Spinner } from "@trackunit/react-components";
|
|
579
644
|
*
|
|
580
|
-
*
|
|
581
|
-
|
|
645
|
+
* const InlineLoading = () => (
|
|
646
|
+
* <div className="flex items-center gap-2">
|
|
647
|
+
* <Spinner size="small" centering="vertically" />
|
|
648
|
+
* <span>Saving...</span>
|
|
649
|
+
* </div>
|
|
650
|
+
* );
|
|
651
|
+
* ```
|
|
582
652
|
* @param {SpinnerProps} props - The props for the Spinner component
|
|
583
653
|
* @returns {ReactElement} Spinner component
|
|
584
654
|
*/
|
|
@@ -864,7 +934,7 @@ const cvaIconButton = cssClassVarianceUtilities.cvaMerge([], {
|
|
|
864
934
|
* Use buttons to communicate actions users can take and to allow users to interact with the page. Each page should have one primary button, and any remaining calls to action should be represented as lower emphasis buttons.
|
|
865
935
|
*
|
|
866
936
|
* ### When not to use
|
|
867
|
-
* Do not use buttons as navigational elements. Instead, use
|
|
937
|
+
* Do not use buttons as navigational elements. Instead, use `Links` when the desired action is to take the user to a new page.
|
|
868
938
|
*
|
|
869
939
|
* @example Basic button with click handler
|
|
870
940
|
* ```tsx
|
|
@@ -1214,14 +1284,21 @@ const cvaBadge = cssClassVarianceUtilities.cvaMerge([
|
|
|
1214
1284
|
});
|
|
1215
1285
|
|
|
1216
1286
|
/**
|
|
1217
|
-
*
|
|
1218
|
-
* It
|
|
1287
|
+
* Badge displays a numeric count or a small colored dot to indicate notifications or applied elements.
|
|
1288
|
+
* It is typically placed on buttons, tabs, or filters to draw attention to new or pending items.
|
|
1289
|
+
*
|
|
1290
|
+
* ### When to use
|
|
1291
|
+
* Use Badge to indicate a count (e.g., unread notifications, active filters) or as a compact status dot.
|
|
1292
|
+
*
|
|
1293
|
+
* ### When not to use
|
|
1294
|
+
* Do not use Badge for labeling statuses or categories — use `Tag` instead.
|
|
1295
|
+
* Do not use Badge for highlighting data values — use `Highlight`.
|
|
1219
1296
|
*
|
|
1220
|
-
* How to choose between Badge and Tag
|
|
1297
|
+
* How to choose between Badge and `Tag`?
|
|
1221
1298
|
* - Use a Badge to indicate notifications or counts of applied elements.
|
|
1222
|
-
* - Use a
|
|
1299
|
+
* - Use a `Tag` for labeling statuses, categories, or selections.
|
|
1223
1300
|
*
|
|
1224
|
-
* @example Badge with count and max
|
|
1301
|
+
* @example Badge with count and max — Use `count` and `max` to show a capped notification count. Values above `max` display as "9+".
|
|
1225
1302
|
* ```tsx
|
|
1226
1303
|
* import { Badge, Button } from "@trackunit/react-components";
|
|
1227
1304
|
*
|
|
@@ -1231,7 +1308,7 @@ const cvaBadge = cssClassVarianceUtilities.cvaMerge([
|
|
|
1231
1308
|
* </Button>
|
|
1232
1309
|
* );
|
|
1233
1310
|
* ```
|
|
1234
|
-
* @example Compact
|
|
1311
|
+
* @example Compact status dot — Set `compact` to render a small colored dot without any count, useful for online/offline indicators.
|
|
1235
1312
|
* ```tsx
|
|
1236
1313
|
* import { Badge } from "@trackunit/react-components";
|
|
1237
1314
|
*
|
|
@@ -1480,8 +1557,25 @@ const BreadcrumbContainer = ({ "data-testid": dataTestId, breadcrumbItems, }) =>
|
|
|
1480
1557
|
};
|
|
1481
1558
|
|
|
1482
1559
|
/**
|
|
1483
|
-
*
|
|
1560
|
+
* StarButton renders a clickable star icon for toggling favorite/starred state.
|
|
1561
|
+
* The star appears in the primary color when starred and neutral when unstarred.
|
|
1484
1562
|
*
|
|
1563
|
+
* ### When to use
|
|
1564
|
+
* Use StarButton for favorite or bookmark actions on list items, cards, or tables.
|
|
1565
|
+
*
|
|
1566
|
+
* ### When not to use
|
|
1567
|
+
* Do not use StarButton for primary actions — use `Button` or `IconButton`.
|
|
1568
|
+
*
|
|
1569
|
+
* @example Star button for favoriting an asset
|
|
1570
|
+
* ```tsx
|
|
1571
|
+
* import { StarButton } from "@trackunit/react-components";
|
|
1572
|
+
* import { useState } from "react";
|
|
1573
|
+
*
|
|
1574
|
+
* const FavoriteToggle = () => {
|
|
1575
|
+
* const [starred, setStarred] = useState(false);
|
|
1576
|
+
* return <StarButton starred={starred} onClick={() => setStarred(s => !s)} />;
|
|
1577
|
+
* };
|
|
1578
|
+
* ```
|
|
1485
1579
|
* @param {StarButtonProps} props - The props for the StarButton component
|
|
1486
1580
|
* @returns {ReactElement} StarButton component
|
|
1487
1581
|
*/
|
|
@@ -1629,10 +1723,45 @@ const Card = ({ children, onClick, fullHeight = false, onMouseEnter, onMouseLeav
|
|
|
1629
1723
|
Card.displayName = "Card";
|
|
1630
1724
|
|
|
1631
1725
|
/**
|
|
1632
|
-
*
|
|
1726
|
+
* CardBody is the main content area of a Card. It provides consistent padding and gap between child elements.
|
|
1727
|
+
* Padding is applied here rather than on the Card itself to avoid insetting the scrollbar when content overflows.
|
|
1728
|
+
*
|
|
1729
|
+
* Use CardBody inside a Card to wrap the primary content.
|
|
1730
|
+
* It works alongside CardHeader and CardFooter.
|
|
1731
|
+
*
|
|
1732
|
+
* ### When to use
|
|
1733
|
+
* Use CardBody to wrap the main content area of a `Card`. It handles padding, gap, and flex direction so content is laid out consistently.
|
|
1633
1734
|
*
|
|
1634
|
-
*
|
|
1635
|
-
*
|
|
1735
|
+
* ### When not to use
|
|
1736
|
+
* Do not use CardBody outside of a `Card`. For standalone content layout, use standard flex or grid containers.
|
|
1737
|
+
*
|
|
1738
|
+
* @example Basic card body inside a card
|
|
1739
|
+
* ```tsx
|
|
1740
|
+
* import { Card, CardHeader, CardBody, Text } from "@trackunit/react-components";
|
|
1741
|
+
*
|
|
1742
|
+
* const AssetInfo = () => (
|
|
1743
|
+
* <Card>
|
|
1744
|
+
* <CardHeader heading="Asset Details" />
|
|
1745
|
+
* <CardBody>
|
|
1746
|
+
* <Text>Operating hours: 1,234</Text>
|
|
1747
|
+
* <Text>Status: Active</Text>
|
|
1748
|
+
* </CardBody>
|
|
1749
|
+
* </Card>
|
|
1750
|
+
* );
|
|
1751
|
+
* ```
|
|
1752
|
+
* @example Card body with custom direction and no gap
|
|
1753
|
+
* ```tsx
|
|
1754
|
+
* import { Card, CardBody } from "@trackunit/react-components";
|
|
1755
|
+
*
|
|
1756
|
+
* const HorizontalLayout = () => (
|
|
1757
|
+
* <Card>
|
|
1758
|
+
* <CardBody direction="row" gap="none" padding="none">
|
|
1759
|
+
* <div>Left section</div>
|
|
1760
|
+
* <div>Right section</div>
|
|
1761
|
+
* </CardBody>
|
|
1762
|
+
* </Card>
|
|
1763
|
+
* );
|
|
1764
|
+
* ```
|
|
1636
1765
|
* @param {CardBodyProps} props - The props for the CardBody component
|
|
1637
1766
|
* @returns {ReactElement} CardBody component
|
|
1638
1767
|
*/
|
|
@@ -1646,10 +1775,51 @@ const CardBody = ({ children, "data-testid": dataTestId, className, direction =
|
|
|
1646
1775
|
};
|
|
1647
1776
|
|
|
1648
1777
|
/**
|
|
1649
|
-
*
|
|
1650
|
-
*
|
|
1651
|
-
*
|
|
1778
|
+
* CardFooter provides a consistent footer section for a Card, typically containing action buttons.
|
|
1779
|
+
* Buttons are right-justified by default. To push a button to the left, apply `margin-right: auto` to it.
|
|
1780
|
+
*
|
|
1781
|
+
* Use CardFooter as the last child inside a Card, below CardBody.
|
|
1782
|
+
*
|
|
1783
|
+
* ### When to use
|
|
1784
|
+
* Use CardFooter to add action buttons (e.g., Save, Cancel, View Details) at the bottom of a `Card` or Modal.
|
|
1785
|
+
*
|
|
1786
|
+
* ### When not to use
|
|
1787
|
+
* Do not use CardFooter for content display. It is designed specifically for action buttons and controls.
|
|
1652
1788
|
*
|
|
1789
|
+
* @example Card with footer actions
|
|
1790
|
+
* ```tsx
|
|
1791
|
+
* import { Card, CardHeader, CardBody, CardFooter, Button, Text } from "@trackunit/react-components";
|
|
1792
|
+
*
|
|
1793
|
+
* const ConfirmationCard = () => (
|
|
1794
|
+
* <Card>
|
|
1795
|
+
* <CardHeader heading="Confirm Action" />
|
|
1796
|
+
* <CardBody>
|
|
1797
|
+
* <Text>Are you sure you want to proceed?</Text>
|
|
1798
|
+
* </CardBody>
|
|
1799
|
+
* <CardFooter>
|
|
1800
|
+
* <Button variant="secondary">Cancel</Button>
|
|
1801
|
+
* <Button variant="primary">Confirm</Button>
|
|
1802
|
+
* </CardFooter>
|
|
1803
|
+
* </Card>
|
|
1804
|
+
* );
|
|
1805
|
+
* ```
|
|
1806
|
+
* @example Footer with left-aligned and right-aligned buttons
|
|
1807
|
+
* ```tsx
|
|
1808
|
+
* import { Card, CardBody, CardFooter, Button, Text } from "@trackunit/react-components";
|
|
1809
|
+
*
|
|
1810
|
+
* const FormCard = () => (
|
|
1811
|
+
* <Card>
|
|
1812
|
+
* <CardBody>
|
|
1813
|
+
* <Text>Form content here</Text>
|
|
1814
|
+
* </CardBody>
|
|
1815
|
+
* <CardFooter>
|
|
1816
|
+
* <Button variant="secondary-danger" className="mr-auto">Delete</Button>
|
|
1817
|
+
* <Button variant="secondary">Cancel</Button>
|
|
1818
|
+
* <Button variant="primary">Save</Button>
|
|
1819
|
+
* </CardFooter>
|
|
1820
|
+
* </Card>
|
|
1821
|
+
* );
|
|
1822
|
+
* ```
|
|
1653
1823
|
* @param {CardFooterProps} props - The props for the CardFooter component
|
|
1654
1824
|
* @returns {ReactElement} CardFooter component
|
|
1655
1825
|
*/
|
|
@@ -1693,8 +1863,29 @@ const cvaHeading = cssClassVarianceUtilities.cvaMerge(["m-0", "leading-normal",
|
|
|
1693
1863
|
});
|
|
1694
1864
|
|
|
1695
1865
|
/**
|
|
1696
|
-
*
|
|
1866
|
+
* Heading renders semantic heading elements (h1, h2, h3, h4) with Trackunit typography styles.
|
|
1867
|
+
* The `variant` prop maps to the semantic heading level: "primary" = h1, "secondary" = h2, "tertiary" = h3, "subtitle" = h4.
|
|
1868
|
+
*
|
|
1869
|
+
* ### When to use
|
|
1870
|
+
* Use Heading for page titles, section titles, and any hierarchical heading. Choose the variant based on the semantic level of the heading.
|
|
1871
|
+
*
|
|
1872
|
+
* ### When not to use
|
|
1873
|
+
* Do not use Heading for body text — use `Text` instead.
|
|
1874
|
+
* Do not use Heading for page-level headers with actions — use `PageHeader` or `SectionHeader`.
|
|
1697
1875
|
*
|
|
1876
|
+
* @example Heading variants
|
|
1877
|
+
* ```tsx
|
|
1878
|
+
* import { Heading } from "@trackunit/react-components";
|
|
1879
|
+
*
|
|
1880
|
+
* const HeadingExamples = () => (
|
|
1881
|
+
* <div>
|
|
1882
|
+
* <Heading variant="primary">Page Title (h1)</Heading>
|
|
1883
|
+
* <Heading variant="secondary">Section Title (h2)</Heading>
|
|
1884
|
+
* <Heading variant="tertiary">Subsection (h3)</Heading>
|
|
1885
|
+
* <Heading variant="subtitle" subtle>Subtitle (h4)</Heading>
|
|
1886
|
+
* </div>
|
|
1887
|
+
* );
|
|
1888
|
+
* ```
|
|
1698
1889
|
* @param {HeadingProps} props - The props for the Heading component
|
|
1699
1890
|
* @returns {ReactElement} Heading component
|
|
1700
1891
|
*/
|
|
@@ -1714,8 +1905,49 @@ const Heading = ({ variant = "primary", inverted = false, subtle = false, classN
|
|
|
1714
1905
|
};
|
|
1715
1906
|
|
|
1716
1907
|
/**
|
|
1717
|
-
*
|
|
1908
|
+
* CardHeader provides a consistent header section for a Card, including a heading, optional subheading, accessories, and action buttons.
|
|
1909
|
+
* It is designed to be used as the first child inside a Card, above CardBody.
|
|
1910
|
+
*
|
|
1911
|
+
* ### When to use
|
|
1912
|
+
* Use CardHeader when a `Card` needs a title and optional actions (e.g., close button, edit button). It handles heading layout, separator lines, and action placement.
|
|
1913
|
+
*
|
|
1914
|
+
* ### When not to use
|
|
1915
|
+
* Do not use CardHeader outside of a `Card`. For standalone section titles, use `SectionHeader` or `Heading` instead.
|
|
1718
1916
|
*
|
|
1917
|
+
* @example Card header with heading and actions
|
|
1918
|
+
* ```tsx
|
|
1919
|
+
* import { Card, CardHeader, CardBody, Button, Text } from "@trackunit/react-components";
|
|
1920
|
+
*
|
|
1921
|
+
* const AssetCard = () => (
|
|
1922
|
+
* <Card>
|
|
1923
|
+
* <CardHeader
|
|
1924
|
+
* heading="Excavator #1234"
|
|
1925
|
+
* subHeading="Last updated: 2 hours ago"
|
|
1926
|
+
* actions={<Button variant="ghost" size="small">Edit</Button>}
|
|
1927
|
+
* />
|
|
1928
|
+
* <CardBody>
|
|
1929
|
+
* <Text>Operating hours: 1,234</Text>
|
|
1930
|
+
* </CardBody>
|
|
1931
|
+
* </Card>
|
|
1932
|
+
* );
|
|
1933
|
+
* ```
|
|
1934
|
+
* @example Card header with accessories and no separator
|
|
1935
|
+
* ```tsx
|
|
1936
|
+
* import { Card, CardHeader, CardBody, Badge, Text } from "@trackunit/react-components";
|
|
1937
|
+
*
|
|
1938
|
+
* const NotificationsCard = () => (
|
|
1939
|
+
* <Card>
|
|
1940
|
+
* <CardHeader
|
|
1941
|
+
* heading="Notifications"
|
|
1942
|
+
* accessories={<Badge count={5} color="primary" />}
|
|
1943
|
+
* hideSeparator
|
|
1944
|
+
* />
|
|
1945
|
+
* <CardBody>
|
|
1946
|
+
* <Text>Notification list here</Text>
|
|
1947
|
+
* </CardBody>
|
|
1948
|
+
* </Card>
|
|
1949
|
+
* );
|
|
1950
|
+
* ```
|
|
1719
1951
|
* @param {CardHeaderProps} props - The props for the CardHeader component
|
|
1720
1952
|
* @returns {ReactElement} CardHeader component
|
|
1721
1953
|
*/
|
|
@@ -2012,17 +2244,26 @@ const Collapsible = ({ children, expanded, id, variant, extraPadding }) => {
|
|
|
2012
2244
|
};
|
|
2013
2245
|
|
|
2014
2246
|
/**
|
|
2015
|
-
*
|
|
2247
|
+
* CompletionStatusIndicator displays a visual icon or spinner based on a process completion status:
|
|
2248
|
+
* loading (spinner), success (green check circle), or error (red X circle). Returns null if no status flag is set.
|
|
2016
2249
|
*
|
|
2017
|
-
*
|
|
2018
|
-
*
|
|
2019
|
-
*
|
|
2020
|
-
*
|
|
2021
|
-
*
|
|
2022
|
-
*
|
|
2023
|
-
*
|
|
2024
|
-
* @
|
|
2025
|
-
*
|
|
2250
|
+
* ### When to use
|
|
2251
|
+
* Use CompletionStatusIndicator to show inline feedback for async operations (e.g., form submissions, save actions, data syncs).
|
|
2252
|
+
*
|
|
2253
|
+
* ### When not to use
|
|
2254
|
+
* Do not use for page-level loading — use `Spinner`.
|
|
2255
|
+
* Do not use for persistent status labels — use `Tag` or `Notice`.
|
|
2256
|
+
*
|
|
2257
|
+
* @example Completion status for a save action
|
|
2258
|
+
* ```tsx
|
|
2259
|
+
* import { CompletionStatusIndicator } from "@trackunit/react-components";
|
|
2260
|
+
*
|
|
2261
|
+
* const SaveStatus = ({ saving, saved, failed }: { saving: boolean; saved: boolean; failed: boolean }) => (
|
|
2262
|
+
* <CompletionStatusIndicator loading={saving} success={saved} error={failed} />
|
|
2263
|
+
* );
|
|
2264
|
+
* ```
|
|
2265
|
+
* @param {CompletionStatusIndicatorProps} props - The props for the CompletionStatusIndicator component
|
|
2266
|
+
* @returns {ReactElement | null} CompletionStatusIndicator component, or null when no status flag is set
|
|
2026
2267
|
*/
|
|
2027
2268
|
const CompletionStatusIndicator = ({ loading = false, error, success, ...rest }) => {
|
|
2028
2269
|
if (loading) {
|
|
@@ -2131,8 +2372,34 @@ const cvaCopyableText = cssClassVarianceUtilities.cvaMerge([
|
|
|
2131
2372
|
});
|
|
2132
2373
|
|
|
2133
2374
|
/**
|
|
2134
|
-
*
|
|
2135
|
-
|
|
2375
|
+
* CopyableText displays a text value that the user can click to copy to the clipboard.
|
|
2376
|
+
* It shows a brief animation on copy to provide visual feedback. The copied value can differ from the displayed text via `alternativeText`.
|
|
2377
|
+
*
|
|
2378
|
+
* ### When to use
|
|
2379
|
+
* Use CopyableText for identifiers, serial numbers, URLs, or any value the user may want to copy (e.g., asset IDs, error codes).
|
|
2380
|
+
*
|
|
2381
|
+
* ### When not to use
|
|
2382
|
+
* Do not use CopyableText for long paragraphs or content that doesn't need to be copied.
|
|
2383
|
+
*
|
|
2384
|
+
* @example Copyable serial number
|
|
2385
|
+
* ```tsx
|
|
2386
|
+
* import { CopyableText } from "@trackunit/react-components";
|
|
2387
|
+
*
|
|
2388
|
+
* const AssetSerial = () => (
|
|
2389
|
+
* <CopyableText text="SN-2024-00142" data-testid="serial-number" />
|
|
2390
|
+
* );
|
|
2391
|
+
* ```
|
|
2392
|
+
* @example Copyable text with alternative clipboard value
|
|
2393
|
+
* ```tsx
|
|
2394
|
+
* import { CopyableText } from "@trackunit/react-components";
|
|
2395
|
+
*
|
|
2396
|
+
* const AssetLink = () => (
|
|
2397
|
+
* <CopyableText
|
|
2398
|
+
* text="Excavator #1234"
|
|
2399
|
+
* alternativeText="https://app.trackunit.com/assets/1234"
|
|
2400
|
+
* />
|
|
2401
|
+
* );
|
|
2402
|
+
* ```
|
|
2136
2403
|
* @param {CopyableTextProps} props - The props for the CopyableText component
|
|
2137
2404
|
* @returns {ReactElement} CopyableText component
|
|
2138
2405
|
*/
|
|
@@ -2160,14 +2427,35 @@ const cvaDetailsList = cssClassVarianceUtilities.cvaMerge(["flex", "w-full", "mi
|
|
|
2160
2427
|
const cvaDetailsListItem = cssClassVarianceUtilities.cvaMerge(["last:truncate"]);
|
|
2161
2428
|
|
|
2162
2429
|
/**
|
|
2163
|
-
*
|
|
2430
|
+
* DetailsList renders a one-line list of text values separated by slash icons.
|
|
2431
|
+
* It is used to display compact metadata such as model, serial number, or location in a single row.
|
|
2432
|
+
*
|
|
2433
|
+
* ### When to use
|
|
2434
|
+
* Use DetailsList to display a compact, horizontal list of metadata values (e.g., in a card subtitle or list item description).
|
|
2435
|
+
*
|
|
2436
|
+
* ### When not to use
|
|
2437
|
+
* Do not use DetailsList for key-value pairs — use a standard layout with labels. Do not use for long text — values should be short strings.
|
|
2438
|
+
*
|
|
2439
|
+
* @example Displaying asset metadata
|
|
2440
|
+
* ```tsx
|
|
2441
|
+
* import { DetailsList } from "@trackunit/react-components";
|
|
2442
|
+
*
|
|
2443
|
+
* const AssetMeta = () => (
|
|
2444
|
+
* <DetailsList details={["Excavator", "CAT 320", "SN-00142"]} />
|
|
2445
|
+
* );
|
|
2446
|
+
* ```
|
|
2447
|
+
* @example DetailsList inside a linked context
|
|
2448
|
+
* ```tsx
|
|
2449
|
+
* import { DetailsList } from "@trackunit/react-components";
|
|
2164
2450
|
*
|
|
2165
|
-
*
|
|
2166
|
-
*
|
|
2167
|
-
*
|
|
2168
|
-
*
|
|
2169
|
-
*
|
|
2170
|
-
*
|
|
2451
|
+
* const LinkedAssetMeta = () => (
|
|
2452
|
+
* <a href="/assets/123">
|
|
2453
|
+
* <DetailsList details={["Site: Oslo", "Group: Heavy"]} hasLink />
|
|
2454
|
+
* </a>
|
|
2455
|
+
* );
|
|
2456
|
+
* ```
|
|
2457
|
+
* @param {DetailsListProps} props - The props for the DetailsList component
|
|
2458
|
+
* @returns {ReactElement} DetailsList component
|
|
2171
2459
|
*/
|
|
2172
2460
|
const DetailsList = ({ details, className, hasLink = false, ref }) => {
|
|
2173
2461
|
return (jsxRuntime.jsx("div", { className: cvaDetailsList({ className, hasLink }), ref: ref, children: details.map((value, index, array) => (jsxRuntime.jsxs(react.Fragment, { children: [jsxRuntime.jsx("span", { className: cvaDetailsListItem({ className }), children: value }), index < array.length - 1 && (jsxRuntime.jsx("div", { className: "mx-0.5 flex items-center", children: jsxRuntime.jsx(Icon, { className: "w-4 text-neutral-300", color: "neutral", name: "Slash", size: "small" }) }))] }, index))) }));
|
|
@@ -2303,13 +2591,29 @@ const cvaSkeleton = cssClassVarianceUtilities.cvaMerge([
|
|
|
2303
2591
|
});
|
|
2304
2592
|
|
|
2305
2593
|
/**
|
|
2306
|
-
*
|
|
2594
|
+
* SkeletonLabel renders a single animated placeholder line for text content. It uses text-size keys (text-xs, text-sm, text-base, etc.)
|
|
2595
|
+
* to match the visual cap-height of actual text, with appropriate vertical margins to maintain line-height alignment.
|
|
2596
|
+
*
|
|
2597
|
+
* ### When to use
|
|
2598
|
+
* Use SkeletonLabel as a loading placeholder for single text lines: labels, titles, descriptions, values.
|
|
2599
|
+
*
|
|
2600
|
+
* ### When not to use
|
|
2601
|
+
* For multiple text lines, use `SkeletonLines`.
|
|
2602
|
+
* For shape-based elements (images, icons, avatars), use `SkeletonBlock`.
|
|
2307
2603
|
*
|
|
2308
|
-
*
|
|
2309
|
-
*
|
|
2604
|
+
* @example Skeleton label for a title and description
|
|
2605
|
+
* ```tsx
|
|
2606
|
+
* import { SkeletonLabel } from "@trackunit/react-components";
|
|
2310
2607
|
*
|
|
2311
|
-
*
|
|
2312
|
-
*
|
|
2608
|
+
* const TextSkeleton = () => (
|
|
2609
|
+
* <div className="flex flex-col gap-1">
|
|
2610
|
+
* <SkeletonLabel textSize="text-lg" width={200} />
|
|
2611
|
+
* <SkeletonLabel textSize="text-sm" width="80%" />
|
|
2612
|
+
* </div>
|
|
2613
|
+
* );
|
|
2614
|
+
* ```
|
|
2615
|
+
* @param {SkeletonLabelProps} props - The props for the SkeletonLabel component
|
|
2616
|
+
* @returns {ReactElement} SkeletonLabel component
|
|
2313
2617
|
*/
|
|
2314
2618
|
const SkeletonLabel = react.memo((props) => {
|
|
2315
2619
|
const { width = "100%", textSize = "text-base", flexibleWidth = true, className, "data-testid": dataTestId, children, ref, } = props;
|
|
@@ -2459,8 +2763,26 @@ const EmptyState = ({ description, altText, image = "SEARCH_DOCUMENT", customIma
|
|
|
2459
2763
|
const cvaEmptyValue = cssClassVarianceUtilities.cvaMerge(["text-neutral-400"]);
|
|
2460
2764
|
|
|
2461
2765
|
/**
|
|
2462
|
-
*
|
|
2766
|
+
* EmptyValue renders a consistent dash symbol ("–") to represent missing, null, undefined, or not applicable values.
|
|
2767
|
+
* It is primarily used in tables and detail lists to maintain visual consistency when data is absent.
|
|
2768
|
+
*
|
|
2769
|
+
* ### When to use
|
|
2770
|
+
* Use EmptyValue as a placeholder in table cells, detail lists, or any data display where values may be missing.
|
|
2771
|
+
*
|
|
2772
|
+
* ### When not to use
|
|
2773
|
+
* Do not use EmptyValue for empty pages or sections — use `EmptyState` instead.
|
|
2774
|
+
*
|
|
2775
|
+
* @example Empty value in a details row
|
|
2776
|
+
* ```tsx
|
|
2777
|
+
* import { EmptyValue, Text } from "@trackunit/react-components";
|
|
2463
2778
|
*
|
|
2779
|
+
* const AssetDetail = ({ value }: { value?: string }) => (
|
|
2780
|
+
* <div className="flex justify-between">
|
|
2781
|
+
* <Text weight="bold">Serial Number</Text>
|
|
2782
|
+
* {value ? <Text>{value}</Text> : <EmptyValue />}
|
|
2783
|
+
* </div>
|
|
2784
|
+
* );
|
|
2785
|
+
* ```
|
|
2464
2786
|
* @param {EmptyValueProps} props - The props for the EmptyValue component
|
|
2465
2787
|
* @returns {ReactElement} EmptyValue component
|
|
2466
2788
|
*/
|
|
@@ -2500,10 +2822,41 @@ const cvaExternalLink = cssClassVarianceUtilities.cvaMerge(["underline", "decora
|
|
|
2500
2822
|
});
|
|
2501
2823
|
|
|
2502
2824
|
/**
|
|
2503
|
-
*
|
|
2504
|
-
|
|
2505
|
-
*
|
|
2506
|
-
*
|
|
2825
|
+
* ExternalLink renders an anchor element for navigating to external URLs. It opens in a new tab by default with `rel="noreferrer"` for security.
|
|
2826
|
+
* If no children are provided, the href URL is displayed as the link text.
|
|
2827
|
+
*
|
|
2828
|
+
* ### When to use
|
|
2829
|
+
* Use ExternalLink for any links that navigate to external resources outside the application (e.g., documentation, support pages, vendor sites).
|
|
2830
|
+
*
|
|
2831
|
+
* ### When not to use
|
|
2832
|
+
* Do not use ExternalLink for in-app navigation. Use `Link` from `@tanstack/react-router` for internal routes.
|
|
2833
|
+
*
|
|
2834
|
+
* @example Basic external link
|
|
2835
|
+
* ```tsx
|
|
2836
|
+
* import { ExternalLink } from "@trackunit/react-components";
|
|
2837
|
+
*
|
|
2838
|
+
* const SupportLink = () => (
|
|
2839
|
+
* <ExternalLink href="https://support.trackunit.com">
|
|
2840
|
+
* Visit Support Center
|
|
2841
|
+
* </ExternalLink>
|
|
2842
|
+
* );
|
|
2843
|
+
* ```
|
|
2844
|
+
* @example Neutral colored link opening in same window
|
|
2845
|
+
* ```tsx
|
|
2846
|
+
* import { ExternalLink } from "@trackunit/react-components";
|
|
2847
|
+
*
|
|
2848
|
+
* const DocumentLink = () => (
|
|
2849
|
+
* <ExternalLink
|
|
2850
|
+
* href="https://docs.trackunit.com/api"
|
|
2851
|
+
* color="neutral"
|
|
2852
|
+
* target="_self"
|
|
2853
|
+
* >
|
|
2854
|
+
* API Documentation
|
|
2855
|
+
* </ExternalLink>
|
|
2856
|
+
* );
|
|
2857
|
+
* ```
|
|
2858
|
+
* @param {ExternalLinkProps} props - The props for the ExternalLink component
|
|
2859
|
+
* @returns {ReactElement} ExternalLink component
|
|
2507
2860
|
*/
|
|
2508
2861
|
const ExternalLink = ({ rel = "noreferrer", target = "_blank", href, className, children = href, title = href, "data-testid": dataTestId, onClick, color = "primary", ref, }) => {
|
|
2509
2862
|
return (jsxRuntime.jsx("a", { className: cvaExternalLink({ className, color }), "data-testid": dataTestId, href: href, onClick: onClick, ref: ref, rel: rel, target: target, title: title, children: children }));
|
|
@@ -3295,15 +3648,40 @@ const cvaHighlight = cssClassVarianceUtilities.cvaMerge([
|
|
|
3295
3648
|
const cvaHighlightText = cssClassVarianceUtilities.cvaMerge(["truncate"]);
|
|
3296
3649
|
|
|
3297
3650
|
/**
|
|
3298
|
-
*
|
|
3299
|
-
* It
|
|
3651
|
+
* Highlight draws visual attention to data values that may require user action, monitoring, or investigation.
|
|
3652
|
+
* It uses color cues (e.g., danger, warning, success) to emphasize out-of-range or critical values for quick scanning.
|
|
3653
|
+
*
|
|
3654
|
+
* ### When to use
|
|
3655
|
+
* Use Highlight to emphasize numeric or text values inline that have crossed a threshold or need attention (e.g., temperature warnings, low battery).
|
|
3656
|
+
*
|
|
3657
|
+
* ### When not to use
|
|
3658
|
+
* Do not use Highlight for labeling statuses or categories — use `Tag`.
|
|
3659
|
+
* Do not use Highlight for notification counts — use `Badge`.
|
|
3660
|
+
*
|
|
3661
|
+
* @example Highlighting a warning value
|
|
3662
|
+
* ```tsx
|
|
3663
|
+
* import { Highlight, Text } from "@trackunit/react-components";
|
|
3300
3664
|
*
|
|
3301
|
-
*
|
|
3302
|
-
*
|
|
3303
|
-
*
|
|
3665
|
+
* const TemperatureDisplay = () => (
|
|
3666
|
+
* <Text>
|
|
3667
|
+
* Engine temperature: <Highlight color="warning">92°C</Highlight>
|
|
3668
|
+
* </Text>
|
|
3669
|
+
* );
|
|
3670
|
+
* ```
|
|
3671
|
+
* @example Different highlight colors for thresholds
|
|
3672
|
+
* ```tsx
|
|
3673
|
+
* import { Highlight } from "@trackunit/react-components";
|
|
3304
3674
|
*
|
|
3305
|
-
*
|
|
3306
|
-
*
|
|
3675
|
+
* const BatteryStatus = () => (
|
|
3676
|
+
* <div className="flex gap-2">
|
|
3677
|
+
* <Highlight color="success">85%</Highlight>
|
|
3678
|
+
* <Highlight color="warning">32%</Highlight>
|
|
3679
|
+
* <Highlight color="danger">8%</Highlight>
|
|
3680
|
+
* </div>
|
|
3681
|
+
* );
|
|
3682
|
+
* ```
|
|
3683
|
+
* @param {HighlightProps} props - The props for the Highlight component
|
|
3684
|
+
* @returns {ReactElement} Highlight component
|
|
3307
3685
|
*/
|
|
3308
3686
|
const Highlight = ({ className, "data-testid": dataTestId, children, size = "small", color = "warning", ref, }) => {
|
|
3309
3687
|
return (jsxRuntime.jsx("div", { className: cvaHighlight({ className, size, color }), "data-testid": dataTestId, ref: ref, children: jsxRuntime.jsx("span", { className: cvaHighlightText(), children: children }) }));
|
|
@@ -3565,11 +3943,30 @@ const cvaZStackContainer = cssClassVarianceUtilities.cvaMerge(["grid", "grid-col
|
|
|
3565
3943
|
const cvaZStackItem = cssClassVarianceUtilities.cvaMerge(["col-start-1", "col-end-1", "row-start-1", "row-end-2"]);
|
|
3566
3944
|
|
|
3567
3945
|
/**
|
|
3568
|
-
* ZStack
|
|
3569
|
-
*
|
|
3946
|
+
* ZStack stacks its children on the z-axis (overlaying them on top of each other).
|
|
3947
|
+
* It is a CSS grid-based alternative to `position: absolute` that avoids side effects like elements being removed from the document flow.
|
|
3948
|
+
*
|
|
3949
|
+
* ### When to use
|
|
3950
|
+
* Use ZStack when you need to overlay elements on top of each other (e.g., an image with a badge overlay, or a scroll container with fade indicators).
|
|
3951
|
+
*
|
|
3952
|
+
* ### When not to use
|
|
3953
|
+
* Do not use ZStack for standard stacking/layout — use flex or grid containers instead.
|
|
3570
3954
|
*
|
|
3571
|
-
* @
|
|
3572
|
-
*
|
|
3955
|
+
* @example Overlaying a badge on a thumbnail
|
|
3956
|
+
* ```tsx
|
|
3957
|
+
* import { ZStack, Badge, Icon } from "@trackunit/react-components";
|
|
3958
|
+
*
|
|
3959
|
+
* const ThumbnailWithBadge = () => (
|
|
3960
|
+
* <ZStack>
|
|
3961
|
+
* <Icon name="Truck" size="large" />
|
|
3962
|
+
* <div className="self-start justify-self-end">
|
|
3963
|
+
* <Badge count={3} color="danger" />
|
|
3964
|
+
* </div>
|
|
3965
|
+
* </ZStack>
|
|
3966
|
+
* );
|
|
3967
|
+
* ```
|
|
3968
|
+
* @param {ZStackProps} props - The props for the ZStack component
|
|
3969
|
+
* @returns {ReactElement} ZStack component
|
|
3573
3970
|
*/
|
|
3574
3971
|
const ZStack = ({ children, className, "data-testid": dataTestId, ref }) => {
|
|
3575
3972
|
return (jsxRuntime.jsx("div", { className: cvaZStackContainer({ className }), "data-testid": dataTestId, ref: ref, children: react.Children.map(children, (child, index) => {
|
|
@@ -3635,16 +4032,31 @@ const OverflowIndicator = ({ className, "data-testid": dataTestId, direction, on
|
|
|
3635
4032
|
};
|
|
3636
4033
|
|
|
3637
4034
|
/**
|
|
3638
|
-
*
|
|
3639
|
-
*
|
|
4035
|
+
* HorizontalOverflowScroller displays child elements in a horizontal row with automatic overflow detection.
|
|
4036
|
+
* When content overflows, it shows left/right scroll indicators with click-to-scroll functionality.
|
|
3640
4037
|
*
|
|
3641
|
-
*
|
|
3642
|
-
*
|
|
3643
|
-
*
|
|
3644
|
-
*
|
|
3645
|
-
*
|
|
3646
|
-
*
|
|
3647
|
-
* @
|
|
4038
|
+
* ### When to use
|
|
4039
|
+
* Use HorizontalOverflowScroller to display a row of items (cards, tags, buttons) that may overflow on smaller screens, with visual cues that more content is available.
|
|
4040
|
+
*
|
|
4041
|
+
* ### When not to use
|
|
4042
|
+
* Do not use HorizontalOverflowScroller for tab navigation — use `TabList` which has its own scroll handling.
|
|
4043
|
+
*
|
|
4044
|
+
* @example Horizontal scroller for KPI cards
|
|
4045
|
+
* ```tsx
|
|
4046
|
+
* import { HorizontalOverflowScroller, Tag } from "@trackunit/react-components";
|
|
4047
|
+
*
|
|
4048
|
+
* const TagRow = () => (
|
|
4049
|
+
* <HorizontalOverflowScroller>
|
|
4050
|
+
* <Tag color="success">Active</Tag>
|
|
4051
|
+
* <Tag color="warning">Idle</Tag>
|
|
4052
|
+
* <Tag color="danger">Offline</Tag>
|
|
4053
|
+
* <Tag color="info">Maintenance</Tag>
|
|
4054
|
+
* <Tag color="neutral">Archived</Tag>
|
|
4055
|
+
* </HorizontalOverflowScroller>
|
|
4056
|
+
* );
|
|
4057
|
+
* ```
|
|
4058
|
+
* @param {HorizontalOverflowScrollerProps} props - The props for the HorizontalOverflowScroller component
|
|
4059
|
+
* @returns {ReactElement} HorizontalOverflowScroller component
|
|
3648
4060
|
*/
|
|
3649
4061
|
const HorizontalOverflowScroller = ({ className, "data-testid": dataTestId, children, onScrollStateChange, ref, }) => {
|
|
3650
4062
|
const childrenArray = react.Children.toArray(children);
|
|
@@ -3888,14 +4300,16 @@ const usePopoverContext = () => {
|
|
|
3888
4300
|
return context;
|
|
3889
4301
|
};
|
|
3890
4302
|
/**
|
|
3891
|
-
*
|
|
3892
|
-
*
|
|
3893
|
-
*
|
|
3894
|
-
* - The context is used to share the state of the popover, such as the open state and the reference element.
|
|
4303
|
+
* Popover is a floating overlay that appears relative to a trigger element. It provides a context for
|
|
4304
|
+
* PopoverTrigger and PopoverContent children,
|
|
4305
|
+
* managing open/close state, positioning, and focus management.
|
|
3895
4306
|
*
|
|
3896
|
-
*
|
|
4307
|
+
* ### When to use
|
|
4308
|
+
* Use Popover for contextual information, menus, or forms that should appear near a trigger element without navigating away.
|
|
3897
4309
|
*
|
|
3898
|
-
*
|
|
4310
|
+
* ### When not to use
|
|
4311
|
+
* Do not use Popover for simple text hints — use `Tooltip` instead.
|
|
4312
|
+
* For full-screen overlays or blocking dialogs, use a Modal.
|
|
3899
4313
|
*
|
|
3900
4314
|
* @example Basic popover with trigger
|
|
3901
4315
|
* ```tsx
|
|
@@ -3964,10 +4378,41 @@ const getDefaultPortalContainer = () => {
|
|
|
3964
4378
|
};
|
|
3965
4379
|
|
|
3966
4380
|
/**
|
|
3967
|
-
*
|
|
3968
|
-
* By default
|
|
3969
|
-
*
|
|
3970
|
-
*
|
|
4381
|
+
* Portal renders its children into a separate DOM node, outside the normal React tree hierarchy.
|
|
4382
|
+
* By default, content is portalled into a z-index-isolated `div#portal-container` in the document body.
|
|
4383
|
+
* This is used internally by Popover, Tooltip, and other overlay components.
|
|
4384
|
+
*
|
|
4385
|
+
* ### When to use
|
|
4386
|
+
* Use Portal when you need to render content (modals, popovers, tooltips) outside its parent's DOM hierarchy to avoid z-index or overflow clipping issues.
|
|
4387
|
+
*
|
|
4388
|
+
* ### When not to use
|
|
4389
|
+
* Do not use Portal for regular content rendering. Most overlay components (`Popover`, `Tooltip`) already use Portal internally.
|
|
4390
|
+
*
|
|
4391
|
+
* @example Rendering into a specific container
|
|
4392
|
+
* ```tsx
|
|
4393
|
+
* import { Portal } from "@trackunit/react-components";
|
|
4394
|
+
*
|
|
4395
|
+
* const PortalledContent = () => (
|
|
4396
|
+
* <Portal root={document.getElementById('sidebar-container')}>
|
|
4397
|
+
* <div className="p-4">This content renders in #sidebar-container</div>
|
|
4398
|
+
* </Portal>
|
|
4399
|
+
* );
|
|
4400
|
+
* ```
|
|
4401
|
+
* @example Default portal into document body
|
|
4402
|
+
* ```tsx
|
|
4403
|
+
* import { Portal } from "@trackunit/react-components";
|
|
4404
|
+
*
|
|
4405
|
+
* const FloatingContent = ({ isVisible }: { isVisible: boolean }) => (
|
|
4406
|
+
* isVisible ? (
|
|
4407
|
+
* <Portal>
|
|
4408
|
+
* <div className="fixed bottom-4 right-4 bg-white p-4 rounded-lg shadow-lg">
|
|
4409
|
+
* Toast notification content
|
|
4410
|
+
* </div>
|
|
4411
|
+
* </Portal>
|
|
4412
|
+
* ) : null
|
|
4413
|
+
* );
|
|
4414
|
+
* @param {PortalProps} props - The props for the Portal component
|
|
4415
|
+
* @returns {ReactElement} Portal component
|
|
3971
4416
|
*/
|
|
3972
4417
|
const Portal = (props) => {
|
|
3973
4418
|
return jsxRuntime.jsx(react$1.FloatingPortal, { ...props, root: props.root ?? getDefaultPortalContainer() });
|
|
@@ -3985,11 +4430,37 @@ const cvaPopoverTitleContainer = cssClassVarianceUtilities.cvaMerge(["flex", "it
|
|
|
3985
4430
|
const cvaPopoverTitleText = cssClassVarianceUtilities.cvaMerge(["flex-1", "text-neutral-500"]);
|
|
3986
4431
|
|
|
3987
4432
|
/**
|
|
3988
|
-
*
|
|
4433
|
+
* PopoverContent displays the floating content inside a Popover.
|
|
4434
|
+
* It renders in a portal and manages focus, positioning, and accessibility. Must be a child of a Popover.
|
|
3989
4435
|
*
|
|
3990
|
-
*
|
|
3991
|
-
* in a portal and manages focus, positioning, and accessibility features.
|
|
4436
|
+
* The `children` prop can be a ReactNode or a render function that receives a `close` callback to programmatically dismiss the popover.
|
|
3992
4437
|
*
|
|
4438
|
+
* ### When to use
|
|
4439
|
+
* Use PopoverContent inside a `Popover` to define what appears in the floating overlay.
|
|
4440
|
+
*
|
|
4441
|
+
* ### When not to use
|
|
4442
|
+
* Do not use PopoverContent outside of a `Popover` context. It relies on `Popover`'s context for positioning and state.
|
|
4443
|
+
*
|
|
4444
|
+
* @example PopoverContent with close callback
|
|
4445
|
+
* ```tsx
|
|
4446
|
+
* import { Popover, PopoverTrigger, PopoverContent, Button, Text } from "@trackunit/react-components";
|
|
4447
|
+
*
|
|
4448
|
+
* const DismissablePopover = () => (
|
|
4449
|
+
* <Popover>
|
|
4450
|
+
* <PopoverTrigger>
|
|
4451
|
+
* <Button variant="secondary">Open</Button>
|
|
4452
|
+
* </PopoverTrigger>
|
|
4453
|
+
* <PopoverContent>
|
|
4454
|
+
* {(close) => (
|
|
4455
|
+
* <div className="p-4">
|
|
4456
|
+
* <Text>Popover content here</Text>
|
|
4457
|
+
* <Button onClick={close} size="small">Done</Button>
|
|
4458
|
+
* </div>
|
|
4459
|
+
* )}
|
|
4460
|
+
* </PopoverContent>
|
|
4461
|
+
* </Popover>
|
|
4462
|
+
* );
|
|
4463
|
+
* ```
|
|
3993
4464
|
* @param {PopoverContentProps} props - The props for the PopoverContent component
|
|
3994
4465
|
* @returns {ReactElement} The popover content element
|
|
3995
4466
|
*/
|
|
@@ -4006,8 +4477,34 @@ const PopoverContent = function PopoverContent({ className, "data-testid": dataT
|
|
|
4006
4477
|
};
|
|
4007
4478
|
|
|
4008
4479
|
/**
|
|
4009
|
-
*
|
|
4480
|
+
* PopoverTrigger is the clickable element that opens or closes a Popover.
|
|
4481
|
+
* It must be used as a direct child of a Popover component. By default, it clones the child element and attaches popover behavior.
|
|
4482
|
+
* Set `renderButton` to true to wrap children in a default Button.
|
|
4483
|
+
*
|
|
4484
|
+
* ### When to use
|
|
4485
|
+
* Use PopoverTrigger inside a `Popover` to designate which element opens the popover overlay.
|
|
4486
|
+
*
|
|
4487
|
+
* ### When not to use
|
|
4488
|
+
* Do not use PopoverTrigger outside of a `Popover` context. It relies on `Popover`'s context for state management.
|
|
4489
|
+
*
|
|
4490
|
+
* @example PopoverTrigger with a custom button
|
|
4491
|
+
* ```tsx
|
|
4492
|
+
* import { Popover, PopoverTrigger, PopoverContent, IconButton, Icon } from "@trackunit/react-components";
|
|
4010
4493
|
*
|
|
4494
|
+
* const IconPopover = () => (
|
|
4495
|
+
* <Popover placement="bottom">
|
|
4496
|
+
* <PopoverTrigger>
|
|
4497
|
+
* <IconButton
|
|
4498
|
+
* icon={<Icon name="InformationCircle" size="small" />}
|
|
4499
|
+
* variant="ghost"
|
|
4500
|
+
* />
|
|
4501
|
+
* </PopoverTrigger>
|
|
4502
|
+
* <PopoverContent>
|
|
4503
|
+
* <div className="p-3">Helpful information</div>
|
|
4504
|
+
* </PopoverContent>
|
|
4505
|
+
* </Popover>
|
|
4506
|
+
* );
|
|
4507
|
+
* ```
|
|
4011
4508
|
* @param {PopoverTriggerProps} props - The props for the PopoverTrigger component
|
|
4012
4509
|
* @returns {ReactElement} PopoverTrigger component
|
|
4013
4510
|
*/
|
|
@@ -4464,8 +4961,29 @@ const KPI = ({ title, value, unit, className, "data-testid": dataTestId, tooltip
|
|
|
4464
4961
|
};
|
|
4465
4962
|
|
|
4466
4963
|
/**
|
|
4467
|
-
*
|
|
4468
|
-
*
|
|
4964
|
+
* KPISkeleton is a loading placeholder that mimics the layout of a KPI component.
|
|
4965
|
+
* It renders skeleton lines for the title and value with randomized widths for a natural appearance.
|
|
4966
|
+
*
|
|
4967
|
+
* ### When to use
|
|
4968
|
+
* Use KPISkeleton while `KPI` data is loading to maintain layout stability and reduce perceived load time.
|
|
4969
|
+
*
|
|
4970
|
+
* ### When not to use
|
|
4971
|
+
* Do not use KPISkeleton for generic text loading — use `SkeletonLabel`.
|
|
4972
|
+
*
|
|
4973
|
+
* @example KPI skeleton in a dashboard
|
|
4974
|
+
* ```tsx
|
|
4975
|
+
* import { KPISkeleton } from "@trackunit/react-components";
|
|
4976
|
+
*
|
|
4977
|
+
* const LoadingDashboard = () => (
|
|
4978
|
+
* <div className="flex gap-4">
|
|
4979
|
+
* <KPISkeleton variant="default" />
|
|
4980
|
+
* <KPISkeleton variant="default" />
|
|
4981
|
+
* <KPISkeleton variant="small" />
|
|
4982
|
+
* </div>
|
|
4983
|
+
* );
|
|
4984
|
+
* ```
|
|
4985
|
+
* @param {KPISkeletonProps} props - The props for the KPISkeleton component
|
|
4986
|
+
* @returns {ReactElement} KPISkeleton component
|
|
4469
4987
|
*/
|
|
4470
4988
|
const KPISkeleton = ({ variant = "default", className, "data-testid": dataTestId, style, ref, ...rest }) => {
|
|
4471
4989
|
const isSmallVariant = variant === "small";
|
|
@@ -4595,16 +5113,52 @@ const getValueBarColorByValue = (value, min, max, levelColors) => {
|
|
|
4595
5113
|
};
|
|
4596
5114
|
|
|
4597
5115
|
/**
|
|
4598
|
-
* ValueBar
|
|
4599
|
-
|
|
4600
|
-
*
|
|
4601
|
-
*
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
|
|
5116
|
+
* ValueBar displays a numeric value as a colored progress bar within a defined range.
|
|
5117
|
+
* The bar color changes based on the score (value relative to min/max) using either default or custom level colors.
|
|
5118
|
+
* It can optionally display the numeric value and unit alongside the bar.
|
|
5119
|
+
*
|
|
5120
|
+
* ### When to use
|
|
5121
|
+
* Use ValueBar to visualize a metric relative to a range (e.g., battery level, fuel percentage, utilization rate, temperature).
|
|
5122
|
+
*
|
|
5123
|
+
* ### When not to use
|
|
5124
|
+
* Do not use ValueBar for progress through a multi-step process. Use a stepper or progress indicator instead.
|
|
5125
|
+
*
|
|
5126
|
+
* @example Basic value bar with percentage
|
|
5127
|
+
* ```tsx
|
|
5128
|
+
* import { ValueBar } from "@trackunit/react-components";
|
|
5129
|
+
*
|
|
5130
|
+
* const BatteryLevel = () => (
|
|
5131
|
+
* <ValueBar value={72} min={0} max={100} unit="%" showValue />
|
|
5132
|
+
* );
|
|
5133
|
+
* ```
|
|
5134
|
+
* @example Value bar with custom level colors
|
|
5135
|
+
* ```tsx
|
|
5136
|
+
* import { ValueBar } from "@trackunit/react-components";
|
|
5137
|
+
*
|
|
5138
|
+
* const TemperatureBar = () => (
|
|
5139
|
+
* <ValueBar
|
|
5140
|
+
* value={85}
|
|
5141
|
+
* min={0}
|
|
5142
|
+
* max={120}
|
|
5143
|
+
* unit="°C"
|
|
5144
|
+
* showValue
|
|
5145
|
+
* size="large"
|
|
5146
|
+
* levelColors={[
|
|
5147
|
+
* { min: 0, max: 0.5, color: "#22c55e" },
|
|
5148
|
+
* { min: 0.5, max: 0.75, color: "#f59e0b" },
|
|
5149
|
+
* { min: 0.75, max: 1, color: "#ef4444" },
|
|
5150
|
+
* ]}
|
|
5151
|
+
* />
|
|
5152
|
+
* );
|
|
5153
|
+
* ```
|
|
5154
|
+
* @param {ValueBarProps} props - The props for the ValueBar component
|
|
5155
|
+
* @returns {ReactElement} ValueBar component
|
|
5156
|
+
*/
|
|
5157
|
+
const ValueBar = ({ value, min = 0, max = 100, unit, size = "small", levelColors, valueColor, showValue = false, className, "data-testid": dataTestId, zeroScoreAllowed = false, ref, }) => {
|
|
5158
|
+
const score = getScore(value, min, max, zeroScoreAllowed);
|
|
5159
|
+
const barFillColor = levelColors ? getFillColor(score, levelColors) : getDefaultFillColor(score);
|
|
5160
|
+
const valueText = `${Number(value.toFixed(1))}${sharedUtils.nonNullable(unit) ? unit : ""}`;
|
|
5161
|
+
return (jsxRuntime.jsxs("span", { className: "relative flex items-center gap-2", "data-testid": dataTestId, ref: ref, children: [jsxRuntime.jsx("progress", { "aria-label": valueText, className: cvaValueBar({ className, size }), max: 100, style: { color: barFillColor }, value: score * 100 }), showValue && (size === "small" || size === "large") ? (jsxRuntime.jsx(Text, { className: cvaValueBarText({ size }), "data-testid": dataTestId ? `${dataTestId}-value` : undefined, children: jsxRuntime.jsx("span", { style: valueColor ? { color: valueColor } : undefined, children: valueText }) })) : null] }));
|
|
4608
5162
|
};
|
|
4609
5163
|
|
|
4610
5164
|
const cvaKPICard = cssClassVarianceUtilities.cvaMerge([
|
|
@@ -4705,13 +5259,29 @@ const KPICard = ({ isActive = false, onClick, className, "data-testid": dataTest
|
|
|
4705
5259
|
};
|
|
4706
5260
|
|
|
4707
5261
|
/**
|
|
4708
|
-
*
|
|
5262
|
+
* SkeletonBlock renders a single animated placeholder block for shape-based elements (images, icons, buttons, avatars)
|
|
5263
|
+
* before data is loaded. It uses exact height and width values to match the element it replaces.
|
|
4709
5264
|
*
|
|
4710
|
-
*
|
|
4711
|
-
*
|
|
5265
|
+
* ### When to use
|
|
5266
|
+
* Use SkeletonBlock for loading placeholders of shape-based UI elements: images, icons, badges, buttons, avatars, thumbnails.
|
|
4712
5267
|
*
|
|
4713
|
-
*
|
|
4714
|
-
* For
|
|
5268
|
+
* ### When not to use
|
|
5269
|
+
* For text content, use `SkeletonLabel` which accounts for text line-height margins.
|
|
5270
|
+
* For multiple text lines, use `SkeletonLines`.
|
|
5271
|
+
*
|
|
5272
|
+
* @example Skeleton block for an avatar and button
|
|
5273
|
+
* ```tsx
|
|
5274
|
+
* import { SkeletonBlock } from "@trackunit/react-components";
|
|
5275
|
+
*
|
|
5276
|
+
* const AvatarSkeleton = () => (
|
|
5277
|
+
* <div className="flex items-center gap-3">
|
|
5278
|
+
* <SkeletonBlock height={40} width={40} className="rounded-full" />
|
|
5279
|
+
* <SkeletonBlock height={32} width={120} className="rounded-md" />
|
|
5280
|
+
* </div>
|
|
5281
|
+
* );
|
|
5282
|
+
* ```
|
|
5283
|
+
* @param {SkeletonBlockProps} props - The props for the SkeletonBlock component
|
|
5284
|
+
* @returns {ReactElement} SkeletonBlock component
|
|
4715
5285
|
*/
|
|
4716
5286
|
const SkeletonBlock = react.memo((props) => {
|
|
4717
5287
|
const { width = "100%", height = 16, flexibleWidth = false, className, "data-testid": dataTestId, children, ref, } = props;
|
|
@@ -4726,8 +5296,29 @@ const SkeletonBlock = react.memo((props) => {
|
|
|
4726
5296
|
SkeletonBlock.displayName = "SkeletonBlock";
|
|
4727
5297
|
|
|
4728
5298
|
/**
|
|
4729
|
-
*
|
|
4730
|
-
*
|
|
5299
|
+
* KPICardSkeleton is a loading placeholder that mimics the layout of a KPICard.
|
|
5300
|
+
* It renders skeleton lines for the KPI title/value, and optional icon, trends, value bar, and notice slots.
|
|
5301
|
+
*
|
|
5302
|
+
* ### When to use
|
|
5303
|
+
* Use KPICardSkeleton while `KPICard` data is loading to maintain layout stability in dashboards and metric displays.
|
|
5304
|
+
*
|
|
5305
|
+
* ### When not to use
|
|
5306
|
+
* Do not use KPICardSkeleton for generic card loading — use a `Card` with `SkeletonLabel` inside.
|
|
5307
|
+
*
|
|
5308
|
+
* @example KPICard skeletons in a dashboard row
|
|
5309
|
+
* ```tsx
|
|
5310
|
+
* import { KPICardSkeleton } from "@trackunit/react-components";
|
|
5311
|
+
*
|
|
5312
|
+
* const LoadingMetrics = () => (
|
|
5313
|
+
* <div className="grid grid-cols-3 gap-4">
|
|
5314
|
+
* <KPICardSkeleton hasIcon hasTrends />
|
|
5315
|
+
* <KPICardSkeleton hasIcon hasValueBar />
|
|
5316
|
+
* <KPICardSkeleton hasNotice />
|
|
5317
|
+
* </div>
|
|
5318
|
+
* );
|
|
5319
|
+
* ```
|
|
5320
|
+
* @param {KPICardSkeletonProps} props - The props for the KPICardSkeleton component
|
|
5321
|
+
* @returns {ReactElement} KPICardSkeleton component
|
|
4731
5322
|
*/
|
|
4732
5323
|
const KPICardSkeleton = ({ hasIcon = false, hasTrends = false, hasValueBar = false, hasNotice = false, children, className, "data-testid": dataTestId, style, ref, ...rest }) => {
|
|
4733
5324
|
return (jsxRuntime.jsx(Card, { className: cvaKPICard({ className }), "data-testid": dataTestId, ref: ref, style: style, ...rest, children: jsxRuntime.jsxs(CardBody, { className: cvaKPICardBody(), gap: "none", padding: "none", children: [jsxRuntime.jsxs("div", { className: cvaKPICardHeader(), children: [jsxRuntime.jsx(KPISkeleton, { className: "p-0", "data-testid": dataTestId ? `${dataTestId}-kpi` : undefined }), hasIcon ? (jsxRuntime.jsx(SkeletonBlock, { "data-testid": dataTestId ? `${dataTestId}-icon-loading` : undefined, height: 28, width: 28 })) : null] }), hasTrends ? (jsxRuntime.jsx(SkeletonLabel, { "data-testid": dataTestId ? `${dataTestId}-trend-indicator-loading` : undefined, textSize: "text-xs", width: "100%" })) : null, hasValueBar ? (jsxRuntime.jsx(SkeletonLabel, { "data-testid": dataTestId ? `${dataTestId}-value-bar-loading` : undefined, textSize: "text-xs", width: "100%" })) : null, hasNotice ? (jsxRuntime.jsx(SkeletonLabel, { "data-testid": dataTestId ? `${dataTestId}-notice-loading` : undefined, textSize: "text-xs", width: "100%" })) : null, children] }) }));
|
|
@@ -4807,8 +5398,30 @@ const DEFAULT_SKELETON_LIST_ITEM_PROPS = {
|
|
|
4807
5398
|
hasDetails: false,
|
|
4808
5399
|
};
|
|
4809
5400
|
/**
|
|
4810
|
-
*
|
|
4811
|
-
*
|
|
5401
|
+
* ListItemSkeleton is a loading placeholder that mimics the layout of a ListItem.
|
|
5402
|
+
* It renders skeleton lines for the title, description, meta, thumbnail, and details slots.
|
|
5403
|
+
* Use it inside a List to indicate that list data is loading.
|
|
5404
|
+
*
|
|
5405
|
+
* ### When to use
|
|
5406
|
+
* Use ListItemSkeleton as the loading state inside a `List` when data is being fetched. Configure it to match the shape of your actual ListItems.
|
|
5407
|
+
*
|
|
5408
|
+
* ### When not to use
|
|
5409
|
+
* Do not use ListItemSkeleton for non-list loading states. Use `SkeletonBlock` or `SkeletonLabel` for generic loading placeholders.
|
|
5410
|
+
*
|
|
5411
|
+
* @example ListItemSkeleton matching a typical asset list item
|
|
5412
|
+
* ```tsx
|
|
5413
|
+
* import { ListItemSkeleton } from "@trackunit/react-components";
|
|
5414
|
+
*
|
|
5415
|
+
* const LoadingList = () => (
|
|
5416
|
+
* <div>
|
|
5417
|
+
* <ListItemSkeleton hasThumbnail hasDescription hasMeta={false} />
|
|
5418
|
+
* <ListItemSkeleton hasThumbnail hasDescription hasMeta={false} />
|
|
5419
|
+
* <ListItemSkeleton hasThumbnail hasDescription hasMeta={false} />
|
|
5420
|
+
* </div>
|
|
5421
|
+
* );
|
|
5422
|
+
* ```
|
|
5423
|
+
* @param {ListItemSkeletonProps} props - The props for the ListItemSkeleton component
|
|
5424
|
+
* @returns {ReactElement} ListItemSkeleton component
|
|
4812
5425
|
*/
|
|
4813
5426
|
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, }) => {
|
|
4814
5427
|
// Generate stable random widths once and never change them
|
|
@@ -5294,10 +5907,32 @@ const getResolvedLoadingIndicator = (loadingIndicator) => {
|
|
|
5294
5907
|
};
|
|
5295
5908
|
|
|
5296
5909
|
/**
|
|
5297
|
-
*
|
|
5910
|
+
* ListItem presents a concise row of information for quick scanning and navigation. It supports a title, description, meta text, thumbnail,
|
|
5911
|
+
* and a details slot. When `onClick` is provided, the item becomes interactive with a chevron indicator.
|
|
5298
5912
|
*
|
|
5299
|
-
*
|
|
5300
|
-
*
|
|
5913
|
+
* ### When to use
|
|
5914
|
+
* Use ListItem inside a `List` to display items such as assets, events, users, or notifications with consistent layout.
|
|
5915
|
+
*
|
|
5916
|
+
* ### When not to use
|
|
5917
|
+
* Do not use ListItem outside of a `List` component for standalone clickable elements — use `Card` or `Button`.
|
|
5918
|
+
*
|
|
5919
|
+
* @example ListItem with thumbnail and details
|
|
5920
|
+
* ```tsx
|
|
5921
|
+
* import { ListItem, Icon, Tag } from "@trackunit/react-components";
|
|
5922
|
+
*
|
|
5923
|
+
* const AssetListItem = () => (
|
|
5924
|
+
* <ListItem
|
|
5925
|
+
* title="Excavator CAT 320"
|
|
5926
|
+
* description="Serial: SN-2024-00142"
|
|
5927
|
+
* meta="Last seen: 2 hours ago"
|
|
5928
|
+
* thumbnail={<Icon name="Truck" size="small" />}
|
|
5929
|
+
* details={<Tag color="success" size="small">Active</Tag>}
|
|
5930
|
+
* onClick={() => console.log("Navigate to asset")}
|
|
5931
|
+
* />
|
|
5932
|
+
* );
|
|
5933
|
+
* ```
|
|
5934
|
+
* @param {ListItemProps} props - The props for the ListItem component
|
|
5935
|
+
* @returns {ReactElement} ListItem component
|
|
5301
5936
|
*/
|
|
5302
5937
|
const ListItem = ({ className, "data-testid": dataTestId, onClick, details, title, description, meta, thumbnail, thumbnailColor = "info-600", thumbnailBackground = "info-100", ...rest }) => {
|
|
5303
5938
|
const baseClass = cvaListItem({ className });
|
|
@@ -5401,7 +6036,28 @@ const cvaMenuListMultiSelect = cssClassVarianceUtilities.cvaMerge([
|
|
|
5401
6036
|
const cvaMenuListItem = cssClassVarianceUtilities.cvaMerge("max-w-full");
|
|
5402
6037
|
|
|
5403
6038
|
/**
|
|
5404
|
-
*
|
|
6039
|
+
* MenuDivider renders a horizontal line to visually separate groups of items within a MenuList.
|
|
6040
|
+
*
|
|
6041
|
+
* ### When to use
|
|
6042
|
+
* Use MenuDivider between groups of related `MenuItem` elements to create logical sections within a menu.
|
|
6043
|
+
*
|
|
6044
|
+
* ### When not to use
|
|
6045
|
+
* Do not use MenuDivider outside of a `MenuList`. For general-purpose dividers, use `Spacer` with `border`.
|
|
6046
|
+
*
|
|
6047
|
+
* @example Menu with grouped sections
|
|
6048
|
+
* ```tsx
|
|
6049
|
+
* import { MenuList, MenuItem, MenuDivider, Icon } from "@trackunit/react-components";
|
|
6050
|
+
*
|
|
6051
|
+
* const GroupedMenu = () => (
|
|
6052
|
+
* <MenuList>
|
|
6053
|
+
* <MenuItem id="edit" label="Edit" prefix={<Icon name="PencilSquare" size="small" />} />
|
|
6054
|
+
* <MenuItem id="duplicate" label="Duplicate" prefix={<Icon name="DocumentDuplicate" size="small" />} />
|
|
6055
|
+
* <MenuDivider />
|
|
6056
|
+
* <MenuItem id="delete" label="Delete" variant="danger" prefix={<Icon name="Trash" size="small" />} />
|
|
6057
|
+
* </MenuList>
|
|
6058
|
+
* );
|
|
6059
|
+
* ```
|
|
6060
|
+
* @returns {ReactElement} MenuDivider component
|
|
5405
6061
|
*/
|
|
5406
6062
|
const MenuDivider = () => {
|
|
5407
6063
|
return jsxRuntime.jsx("div", { className: cvaMenuListDivider(), "data-testid": "menu-divider" });
|
|
@@ -5527,8 +6183,37 @@ const cvaMenuItemSuffix = cssClassVarianceUtilities.cvaMerge(["text-neutral-400"
|
|
|
5527
6183
|
});
|
|
5528
6184
|
|
|
5529
6185
|
/**
|
|
5530
|
-
*
|
|
6186
|
+
* MenuItem represents a single actionable item within a MenuList.
|
|
6187
|
+
* It supports labels, icons (prefix/suffix), selected and focused states, and danger variants.
|
|
5531
6188
|
*
|
|
6189
|
+
* ### When to use
|
|
6190
|
+
* Use MenuItem inside a `MenuList` for individual actions (edit, delete, duplicate) or selectable options.
|
|
6191
|
+
*
|
|
6192
|
+
* ### When not to use
|
|
6193
|
+
* Do not use MenuItem outside of a `MenuList` context. For standalone clickable items, use `Button` or `ListItem`.
|
|
6194
|
+
*
|
|
6195
|
+
* @example MenuItem with icon prefix
|
|
6196
|
+
* ```tsx
|
|
6197
|
+
* import { MenuList, MenuItem, Icon } from "@trackunit/react-components";
|
|
6198
|
+
*
|
|
6199
|
+
* const ActionMenu = () => (
|
|
6200
|
+
* <MenuList>
|
|
6201
|
+
* <MenuItem
|
|
6202
|
+
* id="edit"
|
|
6203
|
+
* label="Edit asset"
|
|
6204
|
+
* prefix={<Icon name="PencilSquare" size="small" />}
|
|
6205
|
+
* onClick={() => console.log("Edit clicked")}
|
|
6206
|
+
* />
|
|
6207
|
+
* <MenuItem
|
|
6208
|
+
* id="delete"
|
|
6209
|
+
* label="Delete asset"
|
|
6210
|
+
* prefix={<Icon name="Trash" size="small" />}
|
|
6211
|
+
* variant="danger"
|
|
6212
|
+
* onClick={() => console.log("Delete clicked")}
|
|
6213
|
+
* />
|
|
6214
|
+
* </MenuList>
|
|
6215
|
+
* );
|
|
6216
|
+
* ```
|
|
5532
6217
|
* @param {MenuItemProps} props - The props for the MenuItem component
|
|
5533
6218
|
* @returns {ReactElement} MenuItem component
|
|
5534
6219
|
*/
|
|
@@ -5658,9 +6343,45 @@ const MenuList = ({ "data-testid": dataTestId, className, children, isMulti = fa
|
|
|
5658
6343
|
const cvaMoreMenu = cssClassVarianceUtilities.cvaMerge(["p-0"]);
|
|
5659
6344
|
|
|
5660
6345
|
/**
|
|
5661
|
-
*
|
|
5662
|
-
*
|
|
6346
|
+
* MoreMenu (kebab menu) renders a three-dot button that opens a popover with a list of actions.
|
|
6347
|
+
* It is typically filled with a MenuList containing MenuItem elements.
|
|
6348
|
+
*
|
|
6349
|
+
* ### When to use
|
|
6350
|
+
* Use MoreMenu when you have overflow actions that don't fit in the main UI. Common for row-level actions in tables, card headers, or list items.
|
|
6351
|
+
*
|
|
6352
|
+
* ### When not to use
|
|
6353
|
+
* Do not use MoreMenu for primary actions that should always be visible. Use `Button` instead.
|
|
6354
|
+
*
|
|
6355
|
+
* @example Action items with render prop — Pass a function as `children` to receive the `close` callback and dismiss the menu after an action.
|
|
6356
|
+
* ```tsx
|
|
6357
|
+
* import { MoreMenu, MenuList, MenuItem, Icon } from "@trackunit/react-components";
|
|
6358
|
+
*
|
|
6359
|
+
* const AssetActions = () => (
|
|
6360
|
+
* <MoreMenu>
|
|
6361
|
+
* {(close) => (
|
|
6362
|
+
* <MenuList onClick={close}>
|
|
6363
|
+
* <MenuItem id="edit" label="Edit" prefix={<Icon name="PencilSquare" size="small" />} />
|
|
6364
|
+
* <MenuItem id="delete" label="Delete" variant="danger" prefix={<Icon name="Trash" size="small" />} />
|
|
6365
|
+
* </MenuList>
|
|
6366
|
+
* )}
|
|
6367
|
+
* </MoreMenu>
|
|
6368
|
+
* );
|
|
6369
|
+
* ```
|
|
6370
|
+
* @example Custom trigger button — Use `customButton` to replace the default kebab icon with any element, like a labeled Button.
|
|
6371
|
+
* ```tsx
|
|
6372
|
+
* import { MoreMenu, MenuList, MenuItem, Button } from "@trackunit/react-components";
|
|
5663
6373
|
*
|
|
6374
|
+
* const CustomTriggerMenu = () => (
|
|
6375
|
+
* <MoreMenu customButton={<Button variant="secondary" size="small">Actions</Button>}>
|
|
6376
|
+
* {(close) => (
|
|
6377
|
+
* <MenuList onClick={close}>
|
|
6378
|
+
* <MenuItem id="export" label="Export" />
|
|
6379
|
+
* <MenuItem id="archive" label="Archive" />
|
|
6380
|
+
* </MenuList>
|
|
6381
|
+
* )}
|
|
6382
|
+
* </MoreMenu>
|
|
6383
|
+
* );
|
|
6384
|
+
* ```
|
|
5664
6385
|
* @param {MoreMenuProps} props - The props for the MoreMenu component
|
|
5665
6386
|
* @returns {ReactElement} MoreMenu component
|
|
5666
6387
|
*/
|
|
@@ -5713,12 +6434,42 @@ const cvaNoticeIcon = cssClassVarianceUtilities.cvaMerge(["rounded-full", "items
|
|
|
5713
6434
|
});
|
|
5714
6435
|
|
|
5715
6436
|
/**
|
|
5716
|
-
*
|
|
6437
|
+
* Notice is a non-interactive element that communicates informational messages the user should see, without prompting an action.
|
|
6438
|
+
* It displays an optional icon and text label, with optional tooltip support.
|
|
6439
|
+
*
|
|
6440
|
+
* ### When to use
|
|
6441
|
+
* Use Notice to communicate non-essential, contextual information that does not require action (e.g., status notes, informational hints).
|
|
5717
6442
|
*
|
|
5718
|
-
*
|
|
6443
|
+
* ### When not to use
|
|
6444
|
+
* Do not use Notice for essential information that requires user action — use `Alert` instead.
|
|
6445
|
+
* Do not use Notice for asset state indicators — use `Indicator` instead.
|
|
5719
6446
|
*
|
|
5720
|
-
*
|
|
6447
|
+
* @example Notice with icon and label
|
|
6448
|
+
* ```tsx
|
|
6449
|
+
* import { Notice } from "@trackunit/react-components";
|
|
5721
6450
|
*
|
|
6451
|
+
* const InfoNotice = () => (
|
|
6452
|
+
* <Notice
|
|
6453
|
+
* iconName="InformationCircle"
|
|
6454
|
+
* label="Last synced 5 minutes ago"
|
|
6455
|
+
* color="info"
|
|
6456
|
+
* />
|
|
6457
|
+
* );
|
|
6458
|
+
* ```
|
|
6459
|
+
* @example Warning notice with tooltip
|
|
6460
|
+
* ```tsx
|
|
6461
|
+
* import { Notice } from "@trackunit/react-components";
|
|
6462
|
+
*
|
|
6463
|
+
* const WarningNotice = () => (
|
|
6464
|
+
* <Notice
|
|
6465
|
+
* iconName="ExclamationTriangle"
|
|
6466
|
+
* label="Low battery"
|
|
6467
|
+
* color="warning"
|
|
6468
|
+
* withTooltip
|
|
6469
|
+
* tooltipLabel="Battery level is below 20%"
|
|
6470
|
+
* />
|
|
6471
|
+
* );
|
|
6472
|
+
* ```
|
|
5722
6473
|
* @param {NoticeProps} props - The props for the Notice component
|
|
5723
6474
|
* @returns {ReactElement} Notice component
|
|
5724
6475
|
*/
|
|
@@ -5753,18 +6504,68 @@ const cvaPageContent = cssClassVarianceUtilities.cvaMerge(["overflow-auto", "pag
|
|
|
5753
6504
|
});
|
|
5754
6505
|
|
|
5755
6506
|
/**
|
|
5756
|
-
*
|
|
6507
|
+
* Page is the top-level layout container that applies consistent padding and layout to page content.
|
|
6508
|
+
* Use it in combination with PageContent and PageHeader.
|
|
6509
|
+
*
|
|
6510
|
+
* ### When to use
|
|
6511
|
+
* Use Page as the outermost wrapper for a page view. It provides the base layout structure (padding, spacing) for the page.
|
|
6512
|
+
*
|
|
6513
|
+
* ### When not to use
|
|
6514
|
+
* Do not use Page for card-level or section-level layout. Use `Card` or standard flex/grid containers instead.
|
|
6515
|
+
*
|
|
6516
|
+
* @example Page with header and content
|
|
6517
|
+
* ```tsx
|
|
6518
|
+
* import { Page, PageContent, PageHeader } from "@trackunit/react-components";
|
|
6519
|
+
*
|
|
6520
|
+
* const AssetListPage = () => (
|
|
6521
|
+
* <Page layout="default">
|
|
6522
|
+
* <PageHeader
|
|
6523
|
+
* title="Assets"
|
|
6524
|
+
* accessoryType="actions"
|
|
6525
|
+
* primaryAction={{ actionText: "Add Asset", actionCallback: () => {} }}
|
|
6526
|
+
* />
|
|
6527
|
+
* <PageContent layout="default">
|
|
6528
|
+
* <p>Asset list content goes here</p>
|
|
6529
|
+
* </PageContent>
|
|
6530
|
+
* </Page>
|
|
6531
|
+
* );
|
|
6532
|
+
* ```
|
|
6533
|
+
* @param {PageProps} props - The props for the Page component
|
|
6534
|
+
* @returns {ReactElement} Page component
|
|
5757
6535
|
*/
|
|
5758
6536
|
const Page = ({ layout, className, children, "data-testid": dataTestId, ref }) => {
|
|
5759
6537
|
return (jsxRuntime.jsx("div", { className: cvaPage({ className, layout }), "data-testid": dataTestId ? dataTestId : "page", ref: ref, children: children }));
|
|
5760
6538
|
};
|
|
5761
6539
|
|
|
5762
6540
|
/**
|
|
5763
|
-
*
|
|
5764
|
-
*
|
|
6541
|
+
* PageContent is the main content area inside a Page.
|
|
6542
|
+
* It applies consistent padding to the content section. Must be used within a Page component.
|
|
5765
6543
|
*
|
|
5766
|
-
*
|
|
5767
|
-
*
|
|
6544
|
+
* ### When to use
|
|
6545
|
+
* Use PageContent to wrap the main content of a page, below a `PageHeader`. It provides consistent padding and layout.
|
|
6546
|
+
*
|
|
6547
|
+
* ### When not to use
|
|
6548
|
+
* Do not use PageContent outside of a `Page`. For standalone content wrappers, use `Card` or `CardBody`.
|
|
6549
|
+
*
|
|
6550
|
+
* @example PageContent inside a Page
|
|
6551
|
+
* ```tsx
|
|
6552
|
+
* import { Page, PageContent, PageHeader, Card, CardBody, Text } from "@trackunit/react-components";
|
|
6553
|
+
*
|
|
6554
|
+
* const DetailPage = () => (
|
|
6555
|
+
* <Page layout="default">
|
|
6556
|
+
* <PageHeader title="Asset Details" />
|
|
6557
|
+
* <PageContent layout="default">
|
|
6558
|
+
* <Card>
|
|
6559
|
+
* <CardBody>
|
|
6560
|
+
* <Text>Asset information goes here</Text>
|
|
6561
|
+
* </CardBody>
|
|
6562
|
+
* </Card>
|
|
6563
|
+
* </PageContent>
|
|
6564
|
+
* </Page>
|
|
6565
|
+
* );
|
|
6566
|
+
* ```
|
|
6567
|
+
* @param {PageContentProps} props - The props for the PageContent component
|
|
6568
|
+
* @returns {ReactElement} PageContent component
|
|
5768
6569
|
*/
|
|
5769
6570
|
const PageContent = ({ className, children, "data-testid": dataTestId, layout, ref, }) => {
|
|
5770
6571
|
return (jsxRuntime.jsx("div", { className: cvaPageContent({ className, layout }), "data-testid": dataTestId ? dataTestId : "page-content", ref: ref, children: children }));
|
|
@@ -5848,14 +6649,16 @@ cssClassVarianceUtilities.cvaMerge([
|
|
|
5848
6649
|
]);
|
|
5849
6650
|
|
|
5850
6651
|
/**
|
|
5851
|
-
*
|
|
6652
|
+
* SkeletonLines renders multiple animated placeholder text lines before data loads.
|
|
6653
|
+
* It supports three modes: uniform (identical lines), custom (per-line config), and preset (common patterns like paragraphs or articles).
|
|
6654
|
+
* Built on top of SkeletonLabel for text-specific margins and sizing.
|
|
5852
6655
|
*
|
|
5853
|
-
*
|
|
5854
|
-
* -
|
|
5855
|
-
* - **Custom mode**: Display customized lines with per-line configuration using `variant="custom"` and `lines` prop
|
|
5856
|
-
* - **Preset mode**: Display common patterns using `variant="preset"` and `preset` name
|
|
6656
|
+
* ### When to use
|
|
6657
|
+
* Use SkeletonLines for loading placeholders of multi-line text content: paragraphs, descriptions, articles, or any text block.
|
|
5857
6658
|
*
|
|
5858
|
-
*
|
|
6659
|
+
* ### When not to use
|
|
6660
|
+
* For single text lines, use `SkeletonLabel`.
|
|
6661
|
+
* For shape-based elements, use `SkeletonBlock`.
|
|
5859
6662
|
*
|
|
5860
6663
|
* @example
|
|
5861
6664
|
* // Uniform lines (simple mode)
|
|
@@ -6064,11 +6867,15 @@ const PageHeaderTitle = ({ title, "data-testid": dataTestId, className, ref: for
|
|
|
6064
6867
|
};
|
|
6065
6868
|
|
|
6066
6869
|
/**
|
|
6067
|
-
*
|
|
6870
|
+
* PageHeader displays the header of a page, providing context about the current location within the application.
|
|
6871
|
+
* It supports a title, optional description tooltip, tag, action buttons, KPI metrics, and tabs for in-page navigation.
|
|
6068
6872
|
*
|
|
6069
|
-
*
|
|
6070
|
-
* PageHeader
|
|
6071
|
-
*
|
|
6873
|
+
* ### When to use
|
|
6874
|
+
* Use PageHeader at the top of a `Page` to display the page title, description, and primary/secondary actions.
|
|
6875
|
+
*
|
|
6876
|
+
* ### When not to use
|
|
6877
|
+
* Do not use PageHeader for section-level headings within a page. Use `SectionHeader` instead.
|
|
6878
|
+
* Do not use for card-level headers — use `CardHeader`.
|
|
6072
6879
|
*
|
|
6073
6880
|
* @example Page header with actions
|
|
6074
6881
|
* ```tsx
|
|
@@ -6137,8 +6944,37 @@ const cvaPagination = cssClassVarianceUtilities.cvaMerge(["flex", "items-center"
|
|
|
6137
6944
|
const cvaPaginationText = cssClassVarianceUtilities.cvaMerge("whitespace-nowrap");
|
|
6138
6945
|
|
|
6139
6946
|
/**
|
|
6140
|
-
* Pagination
|
|
6141
|
-
|
|
6947
|
+
* Pagination provides previous/next page navigation with an optional page counter and jump-to-page input.
|
|
6948
|
+
* It supports both offset-based pagination (with `pageIndex` and `pageCount`) and cursor-based pagination (with `cursorBase`).
|
|
6949
|
+
*
|
|
6950
|
+
* ### When to use
|
|
6951
|
+
* Use Pagination below a table or list when data is split across multiple pages and the user needs to navigate between them.
|
|
6952
|
+
*
|
|
6953
|
+
* ### When not to use
|
|
6954
|
+
* Do not use Pagination for infinite scroll patterns. Use the `useInfiniteScroll` hook instead.
|
|
6955
|
+
*
|
|
6956
|
+
* @example Basic offset-based pagination
|
|
6957
|
+
* ```tsx
|
|
6958
|
+
* import { Pagination } from "@trackunit/react-components";
|
|
6959
|
+
* import { useState } from "react";
|
|
6960
|
+
*
|
|
6961
|
+
* const PaginatedList = () => {
|
|
6962
|
+
* const [page, setPage] = useState(0);
|
|
6963
|
+
* const totalPages = 10;
|
|
6964
|
+
*
|
|
6965
|
+
* return (
|
|
6966
|
+
* <Pagination
|
|
6967
|
+
* pageIndex={page}
|
|
6968
|
+
* pageCount={totalPages}
|
|
6969
|
+
* canPreviousPage={page > 0}
|
|
6970
|
+
* canNextPage={page < totalPages - 1}
|
|
6971
|
+
* previousPage={() => setPage(p => p - 1)}
|
|
6972
|
+
* nextPage={() => setPage(p => p + 1)}
|
|
6973
|
+
* getTranslatedCount={(count) => `of ${count}`}
|
|
6974
|
+
* />
|
|
6975
|
+
* );
|
|
6976
|
+
* };
|
|
6977
|
+
* ```
|
|
6142
6978
|
* @param {PaginationProps} props - The props for the Pagination component
|
|
6143
6979
|
* @returns {ReactElement} Pagination component
|
|
6144
6980
|
*/
|
|
@@ -6194,10 +7030,29 @@ const Pagination = ({ previousPage, nextPage, canPreviousPage = false, canNextPa
|
|
|
6194
7030
|
|
|
6195
7031
|
const STROKE_WIDTH_THRESHOLD = 32;
|
|
6196
7032
|
/**
|
|
6197
|
-
*
|
|
7033
|
+
* Polygon renders an SVG polygon from a set of coordinate points. Points are automatically normalized to fit within the specified size.
|
|
7034
|
+
* It supports fill/stroke color options and optional opacity for overlay effects.
|
|
6198
7035
|
*
|
|
6199
|
-
*
|
|
6200
|
-
*
|
|
7036
|
+
* ### When to use
|
|
7037
|
+
* Use Polygon for rendering geofence boundaries, map overlays, or any custom shape defined by coordinate points.
|
|
7038
|
+
*
|
|
7039
|
+
* ### When not to use
|
|
7040
|
+
* Do not use Polygon for standard UI elements — use the design system components instead.
|
|
7041
|
+
*
|
|
7042
|
+
* @example Rendering a triangle
|
|
7043
|
+
* ```tsx
|
|
7044
|
+
* import { Polygon } from "@trackunit/react-components";
|
|
7045
|
+
*
|
|
7046
|
+
* const Triangle = () => (
|
|
7047
|
+
* <Polygon
|
|
7048
|
+
* points={[[0, 100], [50, 0], [100, 100]]}
|
|
7049
|
+
* size={64}
|
|
7050
|
+
* color="black"
|
|
7051
|
+
* />
|
|
7052
|
+
* );
|
|
7053
|
+
* ```
|
|
7054
|
+
* @param {PolygonProps} props - The props for the Polygon component
|
|
7055
|
+
* @returns {ReactElement} Polygon component
|
|
6201
7056
|
*/
|
|
6202
7057
|
const Polygon = ({ points, size, color = "black", opaque = true, className, "data-testid": dataTestId, ref, }) => {
|
|
6203
7058
|
// Calculate the bounds of the points
|
|
@@ -6227,8 +7082,39 @@ const Polygon = ({ points, size, color = "black", opaque = true, className, "dat
|
|
|
6227
7082
|
const normalize = ({ value, min, max, size }) => ((value - min) / (max - min)) * size;
|
|
6228
7083
|
|
|
6229
7084
|
/**
|
|
6230
|
-
*
|
|
7085
|
+
* PopoverTitle renders a styled title bar at the top of a PopoverContent panel.
|
|
7086
|
+
* It displays uppercase bold text with an optional action element and divider.
|
|
6231
7087
|
*
|
|
7088
|
+
* ### When to use
|
|
7089
|
+
* Use PopoverTitle inside `PopoverContent` when the popover needs a labeled header, optionally with an action (e.g., a close button).
|
|
7090
|
+
*
|
|
7091
|
+
* ### When not to use
|
|
7092
|
+
* Do not use PopoverTitle outside of a `Popover` context. For standalone headings, use `Heading`.
|
|
7093
|
+
*
|
|
7094
|
+
* @example Popover with titled content
|
|
7095
|
+
* ```tsx
|
|
7096
|
+
* import { Popover, PopoverTrigger, PopoverContent, PopoverTitle, Button, Text } from "@trackunit/react-components";
|
|
7097
|
+
*
|
|
7098
|
+
* const TitledPopover = () => (
|
|
7099
|
+
* <Popover placement="bottom-start">
|
|
7100
|
+
* <PopoverTrigger>
|
|
7101
|
+
* <Button variant="secondary">Details</Button>
|
|
7102
|
+
* </PopoverTrigger>
|
|
7103
|
+
* <PopoverContent>
|
|
7104
|
+
* {(close) => (
|
|
7105
|
+
* <div className="w-64">
|
|
7106
|
+
* <PopoverTitle divider action={<Button variant="ghost" size="small" onClick={close}>Close</Button>}>
|
|
7107
|
+
* Asset Info
|
|
7108
|
+
* </PopoverTitle>
|
|
7109
|
+
* <div className="p-3">
|
|
7110
|
+
* <Text size="small">Additional information about this asset.</Text>
|
|
7111
|
+
* </div>
|
|
7112
|
+
* </div>
|
|
7113
|
+
* )}
|
|
7114
|
+
* </PopoverContent>
|
|
7115
|
+
* </Popover>
|
|
7116
|
+
* );
|
|
7117
|
+
* ```
|
|
6232
7118
|
* @param {PopoverTitleProps} props - The props for the PopoverTitle component
|
|
6233
7119
|
* @returns {ReactElement} PopoverTitle component
|
|
6234
7120
|
*/
|
|
@@ -6371,9 +7257,31 @@ const preferenceCardGrid = createGrid()
|
|
|
6371
7257
|
})
|
|
6372
7258
|
.build();
|
|
6373
7259
|
/**
|
|
6374
|
-
* PreferenceCard is a flexible component for displaying add-
|
|
6375
|
-
* with
|
|
6376
|
-
*
|
|
7260
|
+
* PreferenceCard is a flexible component for displaying add-on options or settings with an optional input control (checkbox, radio, toggle).
|
|
7261
|
+
* It supports icons/images, titles with tags, descriptions, and custom child content for advanced layouts.
|
|
7262
|
+
*
|
|
7263
|
+
* ### When to use
|
|
7264
|
+
* Use PreferenceCard for settings pages, feature toggles, or plan selection where each option needs a title, description, and an input control.
|
|
7265
|
+
*
|
|
7266
|
+
* ### When not to use
|
|
7267
|
+
* Do not use PreferenceCard for data display — use `Card`.
|
|
7268
|
+
* Do not use for simple toggle settings — use a standalone toggle switch.
|
|
7269
|
+
*
|
|
7270
|
+
* @example PreferenceCard with checkbox input
|
|
7271
|
+
* ```tsx
|
|
7272
|
+
* import { PreferenceCard } from "@trackunit/react-components";
|
|
7273
|
+
*
|
|
7274
|
+
* const FeatureToggle = () => (
|
|
7275
|
+
* <PreferenceCard
|
|
7276
|
+
* title="Email Notifications"
|
|
7277
|
+
* description="Receive email alerts for asset status changes"
|
|
7278
|
+
* icon={{ type: "icon", name: "EnvelopeOpen", containerClassName: "bg-primary-100" }}
|
|
7279
|
+
* input={<input type="checkbox" />}
|
|
7280
|
+
* />
|
|
7281
|
+
* );
|
|
7282
|
+
* ```
|
|
7283
|
+
* @param {PreferenceCardProps} props - The props for the PreferenceCard component
|
|
7284
|
+
* @returns {ReactNode} PreferenceCard component
|
|
6377
7285
|
*/
|
|
6378
7286
|
const PreferenceCard = ({ title, description, icon, input, titleTag, cardTag, disabled = false, className, "data-testid": dataTestId, children, ref: forwardedRef, }) => {
|
|
6379
7287
|
const { ref: measureRef, geometry } = useMeasure();
|
|
@@ -6423,8 +7331,29 @@ const getRandomWidth = (min, max) => {
|
|
|
6423
7331
|
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
6424
7332
|
};
|
|
6425
7333
|
/**
|
|
6426
|
-
*
|
|
6427
|
-
*
|
|
7334
|
+
* PreferenceCardSkeleton is a loading placeholder that mimics the layout of a PreferenceCard.
|
|
7335
|
+
* It renders skeleton lines for the title, description, and optional slots (icon, tags, input).
|
|
7336
|
+
* Configure the boolean flags to match the shape of your actual PreferenceCards.
|
|
7337
|
+
*
|
|
7338
|
+
* ### When to use
|
|
7339
|
+
* Use PreferenceCardSkeleton while preference/settings data is loading to maintain layout stability and reduce perceived load time.
|
|
7340
|
+
*
|
|
7341
|
+
* ### When not to use
|
|
7342
|
+
* Do not use PreferenceCardSkeleton for generic loading — use `SkeletonBlock` or `SkeletonLabel`.
|
|
7343
|
+
*
|
|
7344
|
+
* @example PreferenceCard skeleton with icon and input
|
|
7345
|
+
* ```tsx
|
|
7346
|
+
* import { PreferenceCardSkeleton } from "@trackunit/react-components";
|
|
7347
|
+
*
|
|
7348
|
+
* const LoadingSettings = () => (
|
|
7349
|
+
* <div className="flex flex-col gap-3">
|
|
7350
|
+
* <PreferenceCardSkeleton hasIcon hasInput />
|
|
7351
|
+
* <PreferenceCardSkeleton hasIcon hasInput />
|
|
7352
|
+
* </div>
|
|
7353
|
+
* );
|
|
7354
|
+
* ```
|
|
7355
|
+
* @param {PreferenceCardSkeletonProps} props - The props for the PreferenceCardSkeleton component
|
|
7356
|
+
* @returns {ReactElement} PreferenceCardSkeleton component
|
|
6428
7357
|
*/
|
|
6429
7358
|
const PreferenceCardSkeleton = ({ hasIcon = DEFAULT_SKELETON_PREFERENCE_CARD_PROPS.hasIcon, hasTitleTag = DEFAULT_SKELETON_PREFERENCE_CARD_PROPS.hasTitleTag, hasCardTag = DEFAULT_SKELETON_PREFERENCE_CARD_PROPS.hasCardTag, hasInput = DEFAULT_SKELETON_PREFERENCE_CARD_PROPS.hasInput, ref, }) => {
|
|
6430
7359
|
const gridAreas = useGridAreas(preferenceCardGrid);
|
|
@@ -6451,11 +7380,27 @@ function useConfirmExit(confirmExit, when = true) {
|
|
|
6451
7380
|
reactRouter.useBlocker(confirmExit, when);
|
|
6452
7381
|
}
|
|
6453
7382
|
/**
|
|
6454
|
-
*
|
|
6455
|
-
|
|
6456
|
-
*
|
|
6457
|
-
*
|
|
6458
|
-
*
|
|
7383
|
+
* usePrompt displays a browser confirmation dialog when the user tries to navigate away from the current page.
|
|
7384
|
+
* It blocks both in-app navigation (via TanStack Router's `useBlocker`) and browser-level navigation (via `beforeunload`).
|
|
7385
|
+
*
|
|
7386
|
+
* ### When to use
|
|
7387
|
+
* Use usePrompt when the user has unsaved changes (e.g., in a form) and should be warned before navigating away.
|
|
7388
|
+
*
|
|
7389
|
+
* ### When not to use
|
|
7390
|
+
* Do not use usePrompt for non-destructive navigation. Only use when there is genuinely unsaved state that would be lost.
|
|
7391
|
+
*
|
|
7392
|
+
* @example Warn user about unsaved form changes
|
|
7393
|
+
* ```tsx
|
|
7394
|
+
* import { usePrompt } from "@trackunit/react-components";
|
|
7395
|
+
*
|
|
7396
|
+
* const EditForm = ({ isDirty }: { isDirty: boolean }) => {
|
|
7397
|
+
* usePrompt("You have unsaved changes. Are you sure you want to leave?", isDirty);
|
|
7398
|
+
* return <form>...</form>;
|
|
7399
|
+
* };
|
|
7400
|
+
* ```
|
|
7401
|
+
* @param {string} message - The confirmation message shown to the user
|
|
7402
|
+
* @param {boolean} when - Whether the prompt should be active. Defaults to true.
|
|
7403
|
+
* @returns {void}
|
|
6459
7404
|
*/
|
|
6460
7405
|
const usePrompt = (message, when = true) => {
|
|
6461
7406
|
react.useEffect(() => {
|
|
@@ -6478,10 +7423,28 @@ const usePrompt = (message, when = true) => {
|
|
|
6478
7423
|
useConfirmExit(confirmExit, when);
|
|
6479
7424
|
};
|
|
6480
7425
|
/**
|
|
6481
|
-
*
|
|
7426
|
+
* Prompt is a declarative component wrapper around the `usePrompt` hook. It displays a browser confirmation dialog
|
|
7427
|
+
* when the user tries to navigate away while `when` is true.
|
|
6482
7428
|
*
|
|
6483
|
-
*
|
|
6484
|
-
*
|
|
7429
|
+
* ### When to use
|
|
7430
|
+
* Use Prompt when you prefer a declarative approach to navigation blocking (instead of the `usePrompt` hook).
|
|
7431
|
+
*
|
|
7432
|
+
* ### When not to use
|
|
7433
|
+
* Do not use Prompt when `when` is always false — simply omit it.
|
|
7434
|
+
*
|
|
7435
|
+
* @example Declarative prompt for unsaved changes
|
|
7436
|
+
* ```tsx
|
|
7437
|
+
* import { Prompt } from "@trackunit/react-components";
|
|
7438
|
+
*
|
|
7439
|
+
* const EditPage = ({ hasUnsavedChanges }: { hasUnsavedChanges: boolean }) => (
|
|
7440
|
+
* <>
|
|
7441
|
+
* <Prompt when={hasUnsavedChanges} message="Discard unsaved changes?" />
|
|
7442
|
+
* <form>...</form>
|
|
7443
|
+
* </>
|
|
7444
|
+
* );
|
|
7445
|
+
* ```
|
|
7446
|
+
* @param {PromptProps} props - The props for the Prompt component
|
|
7447
|
+
* @returns {null} Renders nothing; only activates the navigation prompt
|
|
6485
7448
|
*/
|
|
6486
7449
|
const Prompt = ({ when, message }) => {
|
|
6487
7450
|
usePrompt(message, when);
|
|
@@ -6507,18 +7470,25 @@ const cvaSpacer = cssClassVarianceUtilities.cvaMerge([], {
|
|
|
6507
7470
|
});
|
|
6508
7471
|
|
|
6509
7472
|
/**
|
|
6510
|
-
*
|
|
7473
|
+
* Spacer adds vertical whitespace between elements. It can optionally render a visible border line as a divider.
|
|
7474
|
+
*
|
|
7475
|
+
* ### When to use
|
|
7476
|
+
* Use Spacer to add consistent vertical gaps between sections or to render a horizontal divider line.
|
|
7477
|
+
*
|
|
7478
|
+
* ### When not to use
|
|
7479
|
+
* Do not use Spacer for horizontal gaps — use Tailwind flex/grid gap utilities. Do not use for structural layout — use CSS grid or flex.
|
|
6511
7480
|
*
|
|
6512
|
-
* @example
|
|
7481
|
+
* @example Spacer as a divider
|
|
6513
7482
|
* ```tsx
|
|
6514
|
-
* import { Spacer } from "@trackunit/react-components";
|
|
6515
|
-
*
|
|
6516
|
-
*
|
|
6517
|
-
*
|
|
6518
|
-
*
|
|
6519
|
-
*
|
|
6520
|
-
*
|
|
6521
|
-
*
|
|
7483
|
+
* import { Spacer, Text } from "@trackunit/react-components";
|
|
7484
|
+
*
|
|
7485
|
+
* const DividedSections = () => (
|
|
7486
|
+
* <div>
|
|
7487
|
+
* <Text>Section 1 content</Text>
|
|
7488
|
+
* <Spacer size="medium" border />
|
|
7489
|
+
* <Text>Section 2 content</Text>
|
|
7490
|
+
* </div>
|
|
7491
|
+
* );
|
|
6522
7492
|
* ```
|
|
6523
7493
|
* @param {SpacerProps} props - The props for the Spacer component
|
|
6524
7494
|
* @returns {ReactElement} Spacer component
|
|
@@ -6528,9 +7498,29 @@ const Spacer = ({ size = "medium", border = false, "data-testid": dataTestId, cl
|
|
|
6528
7498
|
};
|
|
6529
7499
|
|
|
6530
7500
|
/**
|
|
6531
|
-
*
|
|
6532
|
-
|
|
6533
|
-
*
|
|
7501
|
+
* SectionHeader renders a section title with an optional subtitle and addon elements (e.g., action buttons, status indicators).
|
|
7502
|
+
* It also sets the document title via React Helmet for SEO and browser tab labeling.
|
|
7503
|
+
*
|
|
7504
|
+
* ### When to use
|
|
7505
|
+
* Use SectionHeader to label a distinct section within a page, below a `PageHeader`. It provides consistent heading styles and an optional subtitle.
|
|
7506
|
+
*
|
|
7507
|
+
* ### When not to use
|
|
7508
|
+
* Do not use SectionHeader for the main page title — use `PageHeader`.
|
|
7509
|
+
* Do not use for card-level headers — use `CardHeader`.
|
|
7510
|
+
*
|
|
7511
|
+
* @example Section header with subtitle and addon
|
|
7512
|
+
* ```tsx
|
|
7513
|
+
* import { SectionHeader, Button } from "@trackunit/react-components";
|
|
7514
|
+
*
|
|
7515
|
+
* const AssetSection = () => (
|
|
7516
|
+
* <SectionHeader
|
|
7517
|
+
* title="Maintenance Schedule"
|
|
7518
|
+
* subtitle="Upcoming and past maintenance events"
|
|
7519
|
+
* addons={<Button variant="secondary" size="small">Add Event</Button>}
|
|
7520
|
+
* />
|
|
7521
|
+
* );
|
|
7522
|
+
* ```
|
|
7523
|
+
* @param {SectionHeaderProps} props - The props for the SectionHeader component
|
|
6534
7524
|
* @returns {ReactElement} SectionHeader component
|
|
6535
7525
|
*/
|
|
6536
7526
|
const SectionHeader = ({ title, subtitle, "data-testid": dataTestId, addons, ref, }) => {
|
|
@@ -6606,12 +7596,19 @@ const useOverflowItems = ({ threshold = 1, childUniqueIdentifierAttribute = "id"
|
|
|
6606
7596
|
};
|
|
6607
7597
|
|
|
6608
7598
|
/**
|
|
6609
|
-
*
|
|
6610
|
-
*
|
|
6611
|
-
* - The sidebar hides items that does not fit in the sidebar and show them in a more menu.
|
|
6612
|
-
* - **_The sidebar is just a wrapper. You are responsible for styling the children._**
|
|
7599
|
+
* Sidebar renders a responsive horizontal/vertical navigation bar that automatically collapses overflowing items into a MoreMenu.
|
|
7600
|
+
* It is rendered horizontally until a given breakpoint, then stacks vertically.
|
|
6613
7601
|
*
|
|
6614
|
-
*
|
|
7602
|
+
* **Important:** The Sidebar is just a layout wrapper. You are responsible for styling the children.
|
|
7603
|
+
* For the overflow functionality, use `min-w-[*]` or `flex-shrink-0` on child elements.
|
|
7604
|
+
*
|
|
7605
|
+
* When testing, add `setupIntersectionObserver();` to your `jest.setup.ts` file.
|
|
7606
|
+
*
|
|
7607
|
+
* ### When to use
|
|
7608
|
+
* Use Sidebar for secondary in-page navigation (e.g., switching between views within a page). Works well with `Tabs` for page-level navigation.
|
|
7609
|
+
*
|
|
7610
|
+
* ### When not to use
|
|
7611
|
+
* Do not use Sidebar for primary app navigation (the main menu). For top-level navigation, use the app shell navigation.
|
|
6615
7612
|
*
|
|
6616
7613
|
* @example Responsive sidebar with navigation items
|
|
6617
7614
|
* ```tsx
|
|
@@ -6717,8 +7714,35 @@ const cvaTab = cssClassVarianceUtilities.cvaMerge([
|
|
|
6717
7714
|
});
|
|
6718
7715
|
|
|
6719
7716
|
/**
|
|
6720
|
-
*
|
|
6721
|
-
*
|
|
7717
|
+
* Tab is an individual tab trigger within a TabList.
|
|
7718
|
+
* Each Tab requires a unique `value` prop that corresponds to a TabContent with the same value.
|
|
7719
|
+
* Supports optional icons, suffixes (e.g., badges), and rendering as a custom child element via `asChild`.
|
|
7720
|
+
*
|
|
7721
|
+
* ### When to use
|
|
7722
|
+
* Use Tab inside a `TabList` to create clickable tab triggers. Each Tab maps to a `TabContent` panel.
|
|
7723
|
+
*
|
|
7724
|
+
* ### When not to use
|
|
7725
|
+
* Do not use Tab outside of a `TabList`/`Tabs` context. For standalone buttons, use `Button`.
|
|
7726
|
+
*
|
|
7727
|
+
* @example Tab with icon and badge suffix
|
|
7728
|
+
* ```tsx
|
|
7729
|
+
* import { Tabs, TabList, Tab, TabContent, Badge } from "@trackunit/react-components";
|
|
7730
|
+
*
|
|
7731
|
+
* const TabsWithBadge = () => (
|
|
7732
|
+
* <Tabs defaultValue="alerts">
|
|
7733
|
+
* <TabList>
|
|
7734
|
+
* <Tab value="alerts" iconName="Bell" suffix={<Badge count={3} color="danger" />}>
|
|
7735
|
+
* Alerts
|
|
7736
|
+
* </Tab>
|
|
7737
|
+
* <Tab value="history" iconName="Clock">History</Tab>
|
|
7738
|
+
* </TabList>
|
|
7739
|
+
* <TabContent value="alerts">Active alerts</TabContent>
|
|
7740
|
+
* <TabContent value="history">Event history</TabContent>
|
|
7741
|
+
* </Tabs>
|
|
7742
|
+
* );
|
|
7743
|
+
* ```
|
|
7744
|
+
* @param {TabProps} props - The props for the Tab component
|
|
7745
|
+
* @returns {ReactElement} Tab component
|
|
6722
7746
|
*/
|
|
6723
7747
|
const Tab = ({ value, isFullWidth = false, iconName = undefined, "data-testid": dataTestId, className, children, suffix, asChild = false, appendTabStylesToChildIfAsChild = true, ref, ...rest }) => {
|
|
6724
7748
|
const renderContent = () => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [iconName !== undefined ? jsxRuntime.jsx(Icon, { name: iconName, size: "small" }) : null, react.isValidElement(children) ? children.props.children : children, suffix] }));
|
|
@@ -6733,14 +7757,72 @@ const Tab = ({ value, isFullWidth = false, iconName = undefined, "data-testid":
|
|
|
6733
7757
|
};
|
|
6734
7758
|
|
|
6735
7759
|
/**
|
|
6736
|
-
*
|
|
7760
|
+
* TabContent is the content panel displayed when its corresponding Tab is active.
|
|
7761
|
+
* Each TabContent must have a `value` prop that matches the `value` of its corresponding Tab trigger.
|
|
7762
|
+
* TabContent is only rendered when its tab is selected (unless `forceMount` is set).
|
|
7763
|
+
*
|
|
7764
|
+
* ### When to use
|
|
7765
|
+
* Use TabContent inside a `Tabs` component, one for each `Tab` trigger.
|
|
7766
|
+
*
|
|
7767
|
+
* ### When not to use
|
|
7768
|
+
* Do not use TabContent outside of a `Tabs` context. For conditionally shown content, use standard conditional rendering.
|
|
7769
|
+
*
|
|
7770
|
+
* @example Tabs with styled content panels
|
|
7771
|
+
* ```tsx
|
|
7772
|
+
* import { Tabs, TabList, Tab, TabContent, Text } from "@trackunit/react-components";
|
|
7773
|
+
*
|
|
7774
|
+
* const AssetTabs = () => (
|
|
7775
|
+
* <Tabs defaultValue="overview">
|
|
7776
|
+
* <TabList>
|
|
7777
|
+
* <Tab value="overview">Overview</Tab>
|
|
7778
|
+
* <Tab value="maintenance">Maintenance</Tab>
|
|
7779
|
+
* </TabList>
|
|
7780
|
+
* <TabContent value="overview">
|
|
7781
|
+
* <Text>Asset overview information</Text>
|
|
7782
|
+
* </TabContent>
|
|
7783
|
+
* <TabContent value="maintenance">
|
|
7784
|
+
* <Text>Maintenance schedule and history</Text>
|
|
7785
|
+
* </TabContent>
|
|
7786
|
+
* </Tabs>
|
|
7787
|
+
* );
|
|
7788
|
+
* ```
|
|
7789
|
+
* @param {TabContentProps} props - The props for the TabContent component
|
|
7790
|
+
* @returns {ReactElement} TabContent component
|
|
6737
7791
|
*/
|
|
6738
7792
|
const TabContent = ({ className, "data-testid": dataTestId, children, ref, ...rest }) => {
|
|
6739
7793
|
return (jsxRuntime.jsx(reactTabs.Content, { className: cvaTabContent({ className }), "data-testid": dataTestId ? `${dataTestId}-content` : undefined, ref: ref, ...rest, children: children }));
|
|
6740
7794
|
};
|
|
6741
7795
|
|
|
6742
7796
|
/**
|
|
6743
|
-
*
|
|
7797
|
+
* TabList is the container for Tab triggers inside a Tabs component.
|
|
7798
|
+
* It renders a horizontal row of tab triggers and handles horizontal scroll behavior when tabs overflow.
|
|
7799
|
+
* By default, it auto-scrolls the active tab into view.
|
|
7800
|
+
*
|
|
7801
|
+
* ### When to use
|
|
7802
|
+
* Use TabList as a direct child of `Tabs` to wrap the `Tab` triggers. It handles layout and scrolling.
|
|
7803
|
+
*
|
|
7804
|
+
* ### When not to use
|
|
7805
|
+
* Do not use TabList outside of a `Tabs` context. For horizontal navigation lists, use a standard nav element.
|
|
7806
|
+
*
|
|
7807
|
+
* @example TabList with auto-scroll disabled
|
|
7808
|
+
* ```tsx
|
|
7809
|
+
* import { Tabs, TabList, Tab, TabContent } from "@trackunit/react-components";
|
|
7810
|
+
*
|
|
7811
|
+
* const StaticTabs = () => (
|
|
7812
|
+
* <Tabs defaultValue="first">
|
|
7813
|
+
* <TabList autoScrollToActive={false}>
|
|
7814
|
+
* <Tab value="first">First</Tab>
|
|
7815
|
+
* <Tab value="second">Second</Tab>
|
|
7816
|
+
* <Tab value="third">Third</Tab>
|
|
7817
|
+
* </TabList>
|
|
7818
|
+
* <TabContent value="first">First panel</TabContent>
|
|
7819
|
+
* <TabContent value="second">Second panel</TabContent>
|
|
7820
|
+
* <TabContent value="third">Third panel</TabContent>
|
|
7821
|
+
* </Tabs>
|
|
7822
|
+
* );
|
|
7823
|
+
* ```
|
|
7824
|
+
* @param {TabListProps} props - The props for the TabList component
|
|
7825
|
+
* @returns {ReactElement} TabList component
|
|
6744
7826
|
*/
|
|
6745
7827
|
const TabList = ({ className, "data-testid": dataTestId, children, autoScrollToActive = true, ref, ...rest }) => {
|
|
6746
7828
|
const listRef = react.useRef(null);
|
|
@@ -6787,7 +7869,18 @@ const TabList = ({ className, "data-testid": dataTestId, children, autoScrollToA
|
|
|
6787
7869
|
};
|
|
6788
7870
|
|
|
6789
7871
|
/**
|
|
6790
|
-
* Tabs
|
|
7872
|
+
* Tabs group different but related content, allowing users to navigate views without leaving the page.
|
|
7873
|
+
* They always contain at least two items and one tab is active at a time.
|
|
7874
|
+
* Tabs can be used on full page layouts or in components such as modals or tables.
|
|
7875
|
+
*
|
|
7876
|
+
* Compose Tabs with TabList, Tab, and TabContent.
|
|
7877
|
+
*
|
|
7878
|
+
* ### When to use
|
|
7879
|
+
* Use tabs to switch between related content sections within the same context (e.g., different views of an asset, or settings categories).
|
|
7880
|
+
*
|
|
7881
|
+
* ### When not to use
|
|
7882
|
+
* Do not use tabs for primary navigation between unrelated pages. Use `Sidebar` or route-based navigation instead.
|
|
7883
|
+
* Avoid tabs when there is only one content panel — just show the content directly.
|
|
6791
7884
|
*
|
|
6792
7885
|
* @example Basic tabs with content panels
|
|
6793
7886
|
* ```tsx
|
|
@@ -6821,6 +7914,8 @@ const TabList = ({ className, "data-testid": dataTestId, children, autoScrollToA
|
|
|
6821
7914
|
* </Tabs>
|
|
6822
7915
|
* );
|
|
6823
7916
|
* ```
|
|
7917
|
+
* @param {TabsProps} props - The props for the Tabs component
|
|
7918
|
+
* @returns {ReactElement} Tabs component
|
|
6824
7919
|
*/
|
|
6825
7920
|
const Tabs = ({ children, forceRender, className, "data-testid": dataTestId, fullWidth, ref, ...rest }) => {
|
|
6826
7921
|
return (jsxRuntime.jsx(reactTabs.Root, { className: cvaTabsRoot({ className }), "data-testid": dataTestId, ref: ref, ...rest, children: children }));
|
|
@@ -6941,8 +8036,37 @@ const cvaToggleItemContent = cssClassVarianceUtilities.cvaMerge([], {
|
|
|
6941
8036
|
});
|
|
6942
8037
|
|
|
6943
8038
|
/**
|
|
6944
|
-
*
|
|
8039
|
+
* ToggleGroup allows users to toggle between two or more closely related options and immediately apply the selection.
|
|
8040
|
+
* It renders a segmented control with a sliding background indicator. Supports text-only, icon-only, and text+icon modes.
|
|
8041
|
+
*
|
|
8042
|
+
* ### When to use
|
|
8043
|
+
* Use ToggleGroup when the user needs to switch between 2-5 mutually exclusive options that take effect immediately (e.g., view modes, map/list toggle, time ranges).
|
|
8044
|
+
*
|
|
8045
|
+
* ### When not to use
|
|
8046
|
+
* Do not use ToggleGroup for navigation — use `Tabs`.
|
|
8047
|
+
* Do not use ToggleGroup for form selections with many options — use a Select or RadioGroup.
|
|
8048
|
+
*
|
|
8049
|
+
* @example ToggleGroup for view mode switching
|
|
8050
|
+
* ```tsx
|
|
8051
|
+
* import { ToggleGroup } from "@trackunit/react-components";
|
|
8052
|
+
* import { useState } from "react";
|
|
8053
|
+
*
|
|
8054
|
+
* const ViewToggle = () => {
|
|
8055
|
+
* const [selected, setSelected] = useState("list");
|
|
6945
8056
|
*
|
|
8057
|
+
* return (
|
|
8058
|
+
* <ToggleGroup
|
|
8059
|
+
* selected={selected}
|
|
8060
|
+
* setSelected={setSelected}
|
|
8061
|
+
* onChange={(id) => console.log("Selected:", id)}
|
|
8062
|
+
* list={[
|
|
8063
|
+
* { id: "list", title: "List", iconName: "Bars3" },
|
|
8064
|
+
* { id: "map", title: "Map", iconName: "MapPin" },
|
|
8065
|
+
* ]}
|
|
8066
|
+
* />
|
|
8067
|
+
* );
|
|
8068
|
+
* };
|
|
8069
|
+
* ```
|
|
6946
8070
|
* @param {ToggleGroupProps} props - The props for the ToggleGroup component
|
|
6947
8071
|
* @returns {ReactElement} ToggleGroup component
|
|
6948
8072
|
*/
|