@trackunit/react-components 0.1.13 → 0.1.16

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.js CHANGED
@@ -99,7 +99,7 @@ function __awaiter(thisArg, _arguments, P, generator) {
99
99
  * @returns {JSX.Element} Spinner component
100
100
  */
101
101
  const Spinner = ({ mode = "light", size = "medium", centered = false, className, containerClassName, dataTestId, }) => {
102
- return (jsx(Container$8, Object.assign({ "$mode": mode, "$centered": centered, className: containerClassName }, { children: jsx(StyledSpinner$1, { "$size": size, "$mode": mode, role: "spinbutton", className: className, "data-testid": dataTestId }) })));
102
+ return (jsx(Container$9, Object.assign({ "$mode": mode, "$centered": centered, className: containerClassName }, { children: jsx(StyledSpinner$1, { "$size": size, "$mode": mode, role: "spinbutton", className: className, "data-testid": dataTestId }) })));
103
103
  };
104
104
  const getSpinnerStyles = () => ({ $size }) => {
105
105
  switch ($size) {
@@ -117,7 +117,7 @@ const StyledSpinner$1 = tw.span `
117
117
  ${p => (p.$mode === "dark" ? tws `tu-spinner--dark` : null)}
118
118
  ${getSpinnerStyles()}
119
119
  `;
120
- const Container$8 = tw.div `
120
+ const Container$9 = tw.div `
121
121
  tu-spinner--container
122
122
  ${p => (p.$centered ? tws `tu-spinner--container-centered` : null)}
123
123
  `;
@@ -226,12 +226,12 @@ const Icon$1 = ({ name, size = "large", className, dataTestId, color, onClick, f
226
226
  if (!IconComponent) {
227
227
  return null;
228
228
  }
229
- return (jsx(React.Suspense, Object.assign({ fallback: jsx(LoadingDot, { className: iconSizeClassName }) }, { children: jsx(Root$7, Object.assign({ "$color": color, onClick: onClick, "data-testid": dataTestId, ref: forwardedRef }, { children: jsx(IconComponent, { "data-testid": dataTestId ? `${dataTestId}-${name}` : name, className: `${iconSizeClassName} ${colorClassNames} ${className !== null && className !== void 0 ? className : ""} ${name} aspect-square`, style: fillColor ? Object.assign(Object.assign({}, style), { fill: fillColor }) : Object.assign({}, style) }) })) })));
229
+ return (jsx(React.Suspense, Object.assign({ fallback: jsx(LoadingDot, { className: iconSizeClassName }) }, { children: jsx(Root$9, Object.assign({ "$color": color, onClick: onClick, "data-testid": dataTestId, ref: forwardedRef }, { children: jsx(IconComponent, { "data-testid": dataTestId ? `${dataTestId}-${name}` : name, className: `${iconSizeClassName} ${colorClassNames} ${className !== null && className !== void 0 ? className : ""} ${name} aspect-square`, style: fillColor ? Object.assign(Object.assign({}, style), { fill: fillColor }) : Object.assign({}, style) }) })) })));
230
230
  };
231
231
  const LoadingDot = tw.span `
232
232
  tu-loading-dot
233
233
  `;
234
- const Root$7 = tw.span `
234
+ const Root$9 = tw.span `
235
235
  tu-icon
236
236
  ${({ $color }) => {
237
237
  switch ($color) {
@@ -428,7 +428,7 @@ const Alert = ({ color = "info", title, className, children, primaryAction, seco
428
428
  ref && ref.current && ref.current.scrollIntoView && ref.current.scrollIntoView();
429
429
  }
430
430
  }, [ref, autoScroll]);
431
- return (jsxs(Container$7, Object.assign({ "$color": color, className: className, "data-testid": dataTestId, "$inline": inline, ref: ref, role: "alert" }, { children: [jsx(IconContainer$1, Object.assign({ "$inline": inline }, { children: getIconFromColor() })), jsxs(Content$1, Object.assign({ "$inline": inline }, { children: [jsxs(ContentDetails, { children: [jsx(ContentBody, { children: body }), onClose && !inline ? (jsx(CloseButtonContainer, Object.assign({ "$inline": inline }, { children: jsx(IconButton, { size: "small", variant: "transparent", color: "secondary", onClick: onClose, icon: jsx(Icon$1, { name: "XIcon", size: "small" }), title: "Close Alert", circular: true }) }))) : null] }), (primaryAction || secondaryAction || onClose) && (jsxs(RightAligned, Object.assign({ "$inline": inline }, { children: [secondaryAction && (jsx(Button, Object.assign({ size: "small", variant: "transparent", color: getButtonColor(), onClick: secondaryAction.onClick, loading: secondaryAction === null || secondaryAction === void 0 ? void 0 : secondaryAction.loading }, { children: secondaryAction.label }))), primaryAction && (jsx(Button, Object.assign({ size: "small", color: getButtonColor(), onClick: primaryAction.onClick, loading: primaryAction === null || primaryAction === void 0 ? void 0 : primaryAction.loading }, { children: primaryAction.label }))), onClose && inline ? (jsx(CloseButtonContainer, Object.assign({ "$inline": inline }, { children: jsx(IconButton, { size: "small", variant: "transparent", color: "secondary", onClick: onClose, icon: jsx(Icon$1, { name: "XIcon", size: "small" }), title: "Close Alert", circular: true }) }))) : null] })))] }))] })));
431
+ return (jsxs(Container$8, Object.assign({ "$color": color, className: className, "data-testid": dataTestId, "$inline": inline, ref: ref, role: "alert" }, { children: [jsx(IconContainer$1, Object.assign({ "$inline": inline }, { children: getIconFromColor() })), jsxs(Content$1, Object.assign({ "$inline": inline }, { children: [jsxs(ContentDetails, { children: [jsx(ContentBody, { children: body }), onClose && !inline ? (jsx(CloseButtonContainer, Object.assign({ "$inline": inline }, { children: jsx(IconButton, { size: "small", variant: "transparent", color: "secondary", onClick: onClose, icon: jsx(Icon$1, { name: "XIcon", size: "small" }), title: "Close Alert", circular: true }) }))) : null] }), (primaryAction || secondaryAction || onClose) && (jsxs(RightAligned, Object.assign({ "$inline": inline }, { children: [secondaryAction && (jsx(Button, Object.assign({ size: "small", variant: "transparent", color: getButtonColor(), onClick: secondaryAction.onClick, loading: secondaryAction === null || secondaryAction === void 0 ? void 0 : secondaryAction.loading }, { children: secondaryAction.label }))), primaryAction && (jsx(Button, Object.assign({ size: "small", color: getButtonColor(), onClick: primaryAction.onClick, loading: primaryAction === null || primaryAction === void 0 ? void 0 : primaryAction.loading }, { children: primaryAction.label }))), onClose && inline ? (jsx(CloseButtonContainer, Object.assign({ "$inline": inline }, { children: jsx(IconButton, { size: "small", variant: "transparent", color: "secondary", onClick: onClose, icon: jsx(Icon$1, { name: "XIcon", size: "small" }), title: "Close Alert", circular: true }) }))) : null] })))] }))] })));
432
432
  };
433
433
  const CloseButtonContainer = tw.div `
434
434
  ${p => (p.$inline ? tws `tu-alert__close-button-container--inline` : tws `tu-alert__close-button-container`)}
@@ -442,7 +442,7 @@ const getBackgroundColor = () => ({ $color }) => {
442
442
  };
443
443
  return colors[$color];
444
444
  };
445
- const Container$7 = tw.div `
445
+ const Container$8 = tw.div `
446
446
  tu-alert
447
447
  ${p => (p.$inline ? tws `tu-alert--inline` : null)}
448
448
  ${getBackgroundColor()}
@@ -655,11 +655,12 @@ const StyledDensityContainer = twx(DensityContainer) `
655
655
  * @param {CardFooterProps} props - The props for the CardFooter component
656
656
  * @returns {JSX.Element} CardFooter component
657
657
  */
658
- const CardFooter = ({ dataTestId, className, children, density }) => {
659
- return (jsx(TopBorderedDensityContainer, Object.assign({ dataTestId: dataTestId, className: className, "$density": density }, { children: jsx(Body, { children: children }) })));
658
+ const CardFooter = ({ dataTestId, className, children, density, hideSeparator = false }) => {
659
+ return (jsx(Container$7, Object.assign({ dataTestId: dataTestId, className: className, "$hideSeparator": hideSeparator, "$density": density }, { children: jsx(Body, { children: children }) })));
660
660
  };
661
- const TopBorderedDensityContainer = twx(DensityContainer) `
661
+ const Container$7 = twx(DensityContainer) `
662
662
  tu-card__footer-density-container
663
+ ${p => p.$hideSeparator && tws `tu-card__footer-density-container--borderless`}
663
664
  `;
664
665
  const Body = tw.div `
665
666
  tu-card__footer
@@ -706,11 +707,12 @@ const StyledHeading = tw.div `
706
707
  * @param {CardHeaderProps} props - The props for the CardHeader component
707
708
  * @returns {JSX.Element} CardHeader component
708
709
  */
709
- const CardHeader = ({ heading, headingVariant = "primary", subHeading, onClose, dataTestId, className, children, accessories, density, extraClassName, }) => {
710
- return (jsx(BottomBorderedDensityContainer, Object.assign({ dataTestId: dataTestId, className: className, "$density": density }, { children: jsxs(Container$6, { children: [jsxs("div", Object.assign({ className: extraClassName }, { children: [jsxs(HeadingContainer$1, { children: [jsx(Heading$1, Object.assign({ variant: headingVariant }, { children: heading })), accessories] }), subHeading && (jsx(Heading$1, Object.assign({ subtle: true, variant: "subtitle" }, { children: subHeading }))), children] })), onClose && (jsx(IconButton, { dataTestId: "card-header-close-button", variant: "transparent", color: "secondary", onClick: onClose, icon: jsx(Icon$1, { name: "XIcon", size: "small" }) }))] }) })));
710
+ const CardHeader = ({ heading, headingVariant = "primary", subHeading, onClose, dataTestId, className, children, accessories, density, extraClassName, hideSeparator = false, }) => {
711
+ return (jsx(Root$8, Object.assign({ dataTestId: dataTestId, className: className, "$hideSeparator": hideSeparator, "$density": density }, { children: jsxs(Container$6, { children: [jsxs("div", Object.assign({ className: extraClassName }, { children: [jsxs(HeadingContainer$1, { children: [jsx(Heading$1, Object.assign({ variant: headingVariant }, { children: heading })), accessories] }), subHeading && (jsx(Heading$1, Object.assign({ subtle: true, variant: "subtitle" }, { children: subHeading }))), children] })), onClose && (jsx(IconButton, { dataTestId: "card-header-close-button", variant: "transparent", color: "secondary", onClick: onClose, icon: jsx(Icon$1, { name: "XIcon", size: "small" }) }))] }) })));
711
712
  };
712
- const BottomBorderedDensityContainer = twx(DensityContainer) `
713
- tu-card__header-density-container
713
+ const Root$8 = twx(DensityContainer) `
714
+ tu-card__header-density-container
715
+ ${p => p.$hideSeparator && tws `tu-card__header-density-container--borderless`}
714
716
  `;
715
717
  const Container$6 = tw.div `
716
718
  tu-card__header
@@ -783,9 +785,9 @@ const ChartContainer = tw.div `
783
785
  * @returns {JSX.Element} Label component
784
786
  */
785
787
  const Label$2 = ({ id, htmlFor, children, className, dataTestId, disabled, isInvalid, }) => {
786
- return (jsx(Root$6, Object.assign({ id: id || "", htmlFor: htmlFor || "", className: className, "data-testid": dataTestId, "$disabled": disabled, "$isInvalid": isInvalid }, { children: children })));
788
+ return (jsx(Root$7, Object.assign({ id: id || "", htmlFor: htmlFor || "", className: className, "data-testid": dataTestId, "$disabled": disabled, "$isInvalid": isInvalid }, { children: children })));
787
789
  };
788
- const Root$6 = tw.label `
790
+ const Root$7 = tw.label `
789
791
  tu-label
790
792
  ${({ $disabled }) => $disabled && tws `tu-label--disabled`}
791
793
  ${({ $isInvalid }) => $isInvalid && tws `tu-label--invalid`}
@@ -1085,7 +1087,7 @@ const TipContainer = tw.text `
1085
1087
  * @returns {JSX.Element} FormGroup component
1086
1088
  */
1087
1089
  const FormGroup = ({ isInvalid, helpText, helpAddon, tip, className, dataTestId, label, children, disabled = false, }) => {
1088
- return (jsxs(FormGroupRoot, Object.assign({ className: className, "data-testid": dataTestId, "$disabled": disabled }, { children: [jsxs(ContainerBefore, { children: [jsx(Label$2, Object.assign({ dataTestId: `${dataTestId}-label` }, { children: label })), tip && jsx(Tip, Object.assign({ dataTestId: `${dataTestId}-tip` }, { children: tip }))] }), children, jsxs(ContainerAfter, Object.assign({ "$isInvalid": isInvalid }, { children: [helpText && jsx("span", Object.assign({ "data-testid": `${dataTestId}-helpText` }, { children: helpText })), helpAddon && jsx(HelpAddon, Object.assign({ "data-testid": `${dataTestId}-helpAddon` }, { children: helpAddon }))] }))] })));
1090
+ return (jsxs(FormGroupRoot, Object.assign({ className: className, "data-testid": dataTestId, "$disabled": disabled }, { children: [jsxs(ContainerBefore, { children: [jsx(FormGroupLabel, Object.assign({ dataTestId: `${dataTestId}-label` }, { children: label })), tip && jsx(Tip, Object.assign({ dataTestId: `${dataTestId}-tip` }, { children: tip }))] }), children, jsxs(ContainerAfter, Object.assign({ "$isInvalid": isInvalid }, { children: [helpText && jsx("span", Object.assign({ "data-testid": `${dataTestId}-helpText` }, { children: helpText })), helpAddon && jsx(HelpAddon, Object.assign({ "data-testid": `${dataTestId}-helpAddon` }, { children: helpAddon }))] }))] })));
1089
1091
  };
1090
1092
  const FormGroupRoot = tw.label `
1091
1093
  tu-form-group
@@ -1096,6 +1098,9 @@ const FormGroupRoot = tw.label `
1096
1098
  const ContainerBefore = tw.div `
1097
1099
  tu-form-group__container-before
1098
1100
  `;
1101
+ const FormGroupLabel = twx(Label$2) `
1102
+ component-formGroup-font
1103
+ `;
1099
1104
  const ContainerAfter = tw.div `
1100
1105
  tu-form-group__container-after
1101
1106
  ${({ $isInvalid }) => ($isInvalid ? tws `tu-form-group__container-after--invalid` : null)}
@@ -1213,7 +1218,7 @@ const Drawer = ({ open, onClose = () => { }, children, disableOverlay = false, p
1213
1218
  window.removeEventListener("keyup", onKeyPress);
1214
1219
  };
1215
1220
  }, [open, onClose]);
1216
- return (jsx(FloatingFocusManager, Object.assign({ context: context }, { children: open ? (jsxs(Root$5, Object.assign({ id: "drawer" }, { children: [!disableOverlay && jsx(Overlay, { "aria-hidden": open, "$open": open, onClick: onClose, id: "drawer__overlay" }), jsx(StyledCard$1, Object.assign({ "aria-hidden": open, "data-testId": dataTestId, "$open": open, "$position": position, id: "drawer__card", className: className }, { children: children }))] }))) : (
1221
+ return (jsx(FloatingFocusManager, Object.assign({ context: context }, { children: open ? (jsxs(Root$6, Object.assign({ id: "drawer" }, { children: [!disableOverlay && jsx(Overlay, { "aria-hidden": open, "$open": open, onClick: onClose, id: "drawer__overlay" }), jsx(StyledCard$1, Object.assign({ "aria-hidden": open, "data-testId": dataTestId, "$open": open, "$position": position, id: "drawer__card", className: className }, { children: children }))] }))) : (
1217
1222
  // eslint-disable-next-line react/jsx-no-useless-fragment
1218
1223
  jsx(Fragment, {})) })));
1219
1224
  };
@@ -1235,7 +1240,7 @@ const StyledCard$1 = twx(Card) `
1235
1240
  ${({ $open }) => $open && tws `tu-drawer__card--open`}
1236
1241
  ${({ $position }) => getPositionStyles($position)}
1237
1242
  `;
1238
- const Root$5 = tw.div `
1243
+ const Root$6 = tw.div `
1239
1244
  tu-drawer
1240
1245
  `;
1241
1246
  const Overlay = tw.div `
@@ -1346,9 +1351,9 @@ const Ping = tw.span `
1346
1351
  */
1347
1352
  const LegendItem = ({ className, label, disabled, selected, onClick, color, dataTestId }) => {
1348
1353
  const handleOnClick = onClick && !disabled ? () => onClick() : undefined;
1349
- return (jsxs(Root$4, Object.assign({ "data-testid": dataTestId, "$disabled": disabled, className: className, "$selected": selected, onClick: handleOnClick }, { children: [jsx(Indicator, { style: { backgroundColor: color }, "$selected": selected }), label && jsx("p", { children: label })] })));
1354
+ return (jsxs(Root$5, Object.assign({ "data-testid": dataTestId, "$disabled": disabled, className: className, "$selected": selected, onClick: handleOnClick }, { children: [jsx(Indicator, { style: { backgroundColor: color }, "$selected": selected }), label && jsx("p", { children: label })] })));
1350
1355
  };
1351
- const Root$4 = tw.div `
1356
+ const Root$5 = tw.div `
1352
1357
  inline-flex
1353
1358
  items-center
1354
1359
  transition-all
@@ -1441,11 +1446,11 @@ const MoreMenu = ({ className, dataTestId, dotsDirection = "horizontal", menuPla
1441
1446
  useClickOutside(actionMenuRef, () => {
1442
1447
  setActionMenuIsOpen(false);
1443
1448
  });
1444
- return (jsx(Root$3, Object.assign({ ref: actionMenuRef, className: className, "data-testid": dataTestId }, { children: jsx(Popover, Object.assign({ render: () => children, placement: menuPlacement }, { children: jsx(IconButton, Object.assign({}, iconButtonProps, { icon: jsx(Icon$1, Object.assign({ name: dotsName }, iconProps)), onClick: () => {
1449
+ return (jsx(Root$4, Object.assign({ ref: actionMenuRef, className: className, "data-testid": dataTestId }, { children: jsx(Popover, Object.assign({ render: () => children, placement: menuPlacement }, { children: jsx(IconButton, Object.assign({}, iconButtonProps, { icon: jsx(Icon$1, Object.assign({ name: dotsName }, iconProps)), onClick: () => {
1445
1450
  setActionMenuIsOpen(!actionMenuIsOpen);
1446
1451
  } })) })) })));
1447
1452
  };
1448
- const Root$3 = tw.div `
1453
+ const Root$4 = tw.div `
1449
1454
  tu-more-menu
1450
1455
  ${({ variant }) => (variant === "primary" ? tws `tu-more-menu--primary` : "")}
1451
1456
  `;
@@ -1619,7 +1624,7 @@ const IconContainer = tw.div `
1619
1624
  * @returns {JSX.Element} PageHeader component
1620
1625
  */
1621
1626
  const PageHeader = ({ className, dataTestId, action, showLoading, description, link, title, tag, backTo, children, }) => {
1622
- return (jsxs(StyledPageHeader, Object.assign({ className: className, "data-testid": dataTestId }, { children: [jsxs(HeadingAndAccessoriesContainer, Object.assign({ "data-testid": `${dataTestId}-heading-container` }, { children: [jsxs(HeadingContainer, { children: [backTo && (jsx(IconButton, { to: backTo, color: "tertiary", variant: "transparent", size: "small", icon: jsx(Icon$1, { name: "ArrowLeftIcon" }) })), jsx(Heading, Object.assign({ "data-testid": `${dataTestId}-heading` }, { children: title })), description && (jsx(StyledTip, Object.assign({ link: isValidHttpUrl(link) ? link : undefined, dataTestId: `${dataTestId}-tip`, iconProps: { name: "QuestionMarkCircleIcon" }, renderAs: link ? "a" : "span" }, { children: description })))] }), tag && jsx(Tag, Object.assign({ dataTestId: dataTestId }, { children: tag })), showLoading && jsx(Spinner, { dataTestId: `${dataTestId}-spinner`, centered: true }), action] })), jsx(ChildContainer, { children: children })] })));
1627
+ return (jsxs(StyledPageHeader, Object.assign({ className: className, "data-testid": dataTestId }, { children: [jsxs(HeadingAndAccessoriesContainer, Object.assign({ "data-testid": `${dataTestId}-heading-container` }, { children: [jsxs(HeadingContainer, { children: [backTo && (jsx(IconButton, { to: backTo, color: "tertiary", variant: "transparent", size: "small", icon: jsx(Icon$1, { name: "ArrowLeftIcon" }) })), jsx(Heading, Object.assign({ "data-testid": `${dataTestId}-heading` }, { children: title })), description && (jsx(StyledTip, Object.assign({ link: isValidHttpUrl(link) ? link : undefined, dataTestId: `${dataTestId}-tip`, iconProps: { name: "QuestionMarkCircleIcon" }, renderAs: link ? "a" : "span" }, { children: description })))] }), tag && jsx(Tag, Object.assign({ dataTestId: dataTestId }, { children: tag })), showLoading && jsx(Spinner, { dataTestId: `${dataTestId}-spinner`, centered: true }), action] })), jsx(ChildContainer$1, { children: children })] })));
1623
1628
  };
1624
1629
  function isValidHttpUrl(link) {
1625
1630
  if (!link) {
@@ -1646,7 +1651,7 @@ const HeadingContainer = tw.div `
1646
1651
  const Heading = tw.h1 `
1647
1652
  tu-page-header__heading
1648
1653
  `;
1649
- const ChildContainer = tw.div `
1654
+ const ChildContainer$1 = tw.div `
1650
1655
  tu-page-header__child-container
1651
1656
  `;
1652
1657
  const StyledTip = twx(Tip) `
@@ -1754,15 +1759,15 @@ const Pagination = ({ gotoPage, previousPage, nextPage, pageCount, pageIndex, ca
1754
1759
  handlePageChange("manual");
1755
1760
  }, [handlePageChange]);
1756
1761
  if (loading) {
1757
- return (jsx(Root$2, Object.assign({ className: className }, { children: jsx(SkeletonLines, { height: 16, width: 150 }) })));
1762
+ return (jsx(Root$3, Object.assign({ className: className }, { children: jsx(SkeletonLines, { height: 16, width: 150 }) })));
1758
1763
  }
1759
- return (jsxs(Root$2, Object.assign({ "data-testid": dataTestId, className: className }, { children: [jsx(IconButton, { onClick: () => handlePageChange("prev"), disabled: cursorBase ? false : page !== undefined && page <= 0, "data-testid": "prev-page", icon: jsx(Icon$1, { name: "ChevronLeftIcon", size: "small" }), size: "small", color: "secondary", variant: "transparent" }), !cursorBase && (jsxs(Fragment, { children: [jsx(Text$1, { children: getTranslatedPage() }), jsx(PageInput, { type: "number", min: 1,
1764
+ return (jsxs(Root$3, Object.assign({ "data-testid": dataTestId, className: className }, { children: [jsx(IconButton, { onClick: () => handlePageChange("prev"), disabled: cursorBase ? false : page !== undefined && page <= 0, "data-testid": "prev-page", icon: jsx(Icon$1, { name: "ChevronLeftIcon", size: "small" }), size: "small", color: "secondary", variant: "transparent" }), !cursorBase && (jsxs(Fragment, { children: [jsx(Text$1, { children: getTranslatedPage() }), jsx(PageInput, { type: "number", min: 1,
1760
1765
  // max={amountOfPages}
1761
1766
  value: pageInputValue, onFocus: handleOnFocus, onBlur: handleOnBlur, onChange: handleOnChange, onKeyPress: handleOnKeyPress, "data-testid": "page-input" }), jsx(TextStyled, Object.assign({ dataTestId: "page-count" }, { children: pageCount !== null && pageCount !== undefined && getTranslatedCount(pageCount) }))] })), jsx(IconButton, { onClick: () => handlePageChange("next"), disabled: cursorBase
1762
1767
  ? false
1763
1768
  : page !== undefined && pageCount !== undefined && pageCount !== null && page >= pageCount - 1, "data-testid": "next-page", icon: jsx(Icon$1, { name: "ChevronRightIcon", size: "small" }), size: "small", color: "secondary", variant: "transparent" })] })));
1764
1769
  };
1765
- const Root$2 = tw.div `
1770
+ const Root$3 = tw.div `
1766
1771
  tu-pagination
1767
1772
  `;
1768
1773
  const TextStyled = twx(Text$1) `
@@ -2451,6 +2456,111 @@ const SelectContainer = tw.div `
2451
2456
  `;
2452
2457
  Select.displayName = "Select";
2453
2458
 
2459
+ /**
2460
+ * A hook used to detect what children are overflowing a container.
2461
+ *
2462
+ * @param {OverflowItemsOptions} root0 The options for the hook.
2463
+ * @param {OverflowItemsOptions.threshold} root0.threshold The threshold for the intersection observer.
2464
+ * @returns {object} The overflow items and the ref to the container.
2465
+ */
2466
+ const useOverflowItems = ({ threshold = 1, childUniqueIdentifierAttribute = "id", children, }) => {
2467
+ const [itemOverflowMap, setItemOverflowMap] = useState({});
2468
+ const overflowContainerRef = useRef(null); // TODO: Handle other elements than div
2469
+ const observerRef = useRef(null);
2470
+ const handleIntersection = useCallback(entries => {
2471
+ const updatedEntries = {};
2472
+ entries.forEach(entry => {
2473
+ const targetElementId = entry.target[childUniqueIdentifierAttribute];
2474
+ if (targetElementId) {
2475
+ updatedEntries[targetElementId] = entry.isIntersecting ? false : true;
2476
+ }
2477
+ });
2478
+ setItemOverflowMap(prev => (Object.assign(Object.assign({}, prev), updatedEntries)));
2479
+ }, [childUniqueIdentifierAttribute]);
2480
+ const observe = useCallback(() => {
2481
+ const node = overflowContainerRef.current;
2482
+ if (node && node.children) {
2483
+ const root = overflowContainerRef.current;
2484
+ const options = { root, threshold };
2485
+ const observer = new IntersectionObserver(handleIntersection, options);
2486
+ Array.from(node.children).forEach(child => {
2487
+ observer.observe(child);
2488
+ });
2489
+ observer.observe(node);
2490
+ observerRef.current = observer;
2491
+ }
2492
+ }, [handleIntersection, threshold]);
2493
+ const unobserve = useCallback(() => {
2494
+ const currentObserver = observerRef.current;
2495
+ currentObserver === null || currentObserver === void 0 ? void 0 : currentObserver.disconnect();
2496
+ observerRef.current = null;
2497
+ }, []);
2498
+ const initializeObserver = useCallback(() => {
2499
+ unobserve();
2500
+ observe();
2501
+ }, [observe, unobserve]);
2502
+ useEffect(() => {
2503
+ initializeObserver();
2504
+ return () => {
2505
+ unobserve();
2506
+ };
2507
+ }, [initializeObserver, unobserve, children]);
2508
+ return { overflowContainerRef, itemOverflowMap };
2509
+ };
2510
+
2511
+ /**
2512
+ * The sidebar components is used to render a responsive sidebar.
2513
+ * - The sidebar will be rendered horizontally until a given breakpoint where it will be stacked vertically.
2514
+ * - The sidebar hides items that does not fit in the sidebar and show them in a more menu.
2515
+ * - **_The sidebar is just a wrapper. You are responsible for styling the children._**
2516
+ *
2517
+ * When using this component make sure to add `setupIntersectionObserver();` to your jest.setup.ts file.
2518
+ *
2519
+ * @param {SidebarProps} props - The props for the Sidebar component
2520
+ * @returns {JSX.Element} Sidebar component
2521
+ */
2522
+ const Sidebar = ({ childContainerClassName, children, breakpoint = "lg", className, dataTestId = "sidebar", moreMenuProps, menuListProps, overflowThreshold, }) => {
2523
+ const { overflowContainerRef, itemOverflowMap } = useOverflowItems({
2524
+ children,
2525
+ childUniqueIdentifierAttribute: "id",
2526
+ threshold: overflowThreshold !== null && overflowThreshold !== void 0 ? overflowThreshold : 0.8,
2527
+ });
2528
+ const overflowItemCount = Object.values(itemOverflowMap).filter(isOverflowing => isOverflowing).length;
2529
+ const itemVisibilityClassName = (id) => {
2530
+ if (itemOverflowMap[id]) {
2531
+ return tws `invisible`;
2532
+ }
2533
+ return tws `visible`;
2534
+ };
2535
+ return (jsxs(Root$2, Object.assign({ className: className, "data-testid": dataTestId }, { children: [jsx(ChildContainer, Object.assign({ "$breakpoint": breakpoint, ref: overflowContainerRef, className: childContainerClassName, "data-testid": `${dataTestId}-child-container` }, { children: React__default.Children.map(children, child => {
2536
+ return React__default.cloneElement(child, {
2537
+ className: `${child.props.className} ${itemVisibilityClassName(child.props.id)}`,
2538
+ });
2539
+ }) })), overflowItemCount > 0 ? (jsx(MoreMenu, Object.assign({ iconButtonProps: {
2540
+ circular: false,
2541
+ variant: "transparent",
2542
+ color: "secondary",
2543
+ }, menuPlacement: "bottom-end" }, moreMenuProps, { className: `${(moreMenuProps === null || moreMenuProps === void 0 ? void 0 : moreMenuProps.className) || ""} !p-0`, dataTestId: `${dataTestId}-more-menu` }, { children: jsx(MenuList, Object.assign({}, menuListProps, { dataTestId: `${dataTestId}-menu-list` }, { children: React__default.Children.map(children, child => {
2544
+ return itemOverflowMap[child.props.id] ? child : null;
2545
+ }) })) }))) : null] })));
2546
+ };
2547
+ const Root$2 = tw.div `
2548
+ tu-sidebar
2549
+ `;
2550
+ const breakpointClasses = {
2551
+ xs: tws `tu-sidebar__child-container--xs`,
2552
+ sm: tws `tu-sidebar__child-container--sm`,
2553
+ md: tws `tu-sidebar__child-container--md`,
2554
+ lg: tws `tu-sidebar__child-container--lg`,
2555
+ xl: tws `tu-sidebar__child-container--xl`,
2556
+ "2xl": tws `tu-sidebar__child-container--2xl`,
2557
+ "3xl": tws `tu-sidebar__child-container--3xl`,
2558
+ };
2559
+ const ChildContainer = tw.div `
2560
+ tu-sidebar__child-container
2561
+ ${({ $breakpoint }) => breakpointClasses[$breakpoint]}
2562
+ `;
2563
+
2454
2564
  /**
2455
2565
  * The TextArea is a base component, and should not be used very often.
2456
2566
  * For most cases the TextAreaField is the correct component.
@@ -2961,4 +3071,4 @@ const Layout = tw.div `
2961
3071
  tu-layout-TwoColumnLayout
2962
3072
  `;
2963
3073
 
2964
- export { ActionButton, Alert, Badge, BaseInput, Button, Card, CardBody, CardFooter, CardHeader, Chart, Checkbox, Collapse, ContentGridArea, CopyableText, CreatableSelect, DateField, DateInput, DayPicker, DayPickerPopover, DayRangePicker, DensityContainer, Drawer, ExternalLink, FilterListMapLayout, FormGroup, FullWidthRepeatingLayout, GridArea, Heading$1 as Heading, Icon$1 as Icon, IconButton, Indicator$1 as Indicator, IntrinsicallyResponsiveGrid, Label$2 as Label, LegendItem, MenuItem, MenuList, MinTopFullContentLayout, Modal, ModalBackdrop, MoreMenu, MultiSelectMenuItem, NavGridArea, NumberField, NumberInput, PageHeader, Pagination, PieChart, Popover, PopoverList, PopoverListItem, PopoverTitle, Prompt, ROLE_CARD, RadioGroup, RadioItem, Search, SectionHeader, Select, SelectPrefix, SingleSelectMenuItem, SkeletonLines, Spacer, Spinner, StarButton, Tag, Text$1 as Text, TextArea, TextAreaField, TextField, TextInput, Timeline, TimelineElement, Tip, TitleGridArea, TitleNavContentLayout, Toggle, Tooltip, TwoColumnLayout, ValueBar, docs, getOrderedOptions, getValueBarColorByValue, isMultiValue, useContainerProps, useCustomComponents, useDebounce, useLocalStorage, useModal, usePrompt, useTimeout };
3074
+ export { ActionButton, Alert, Badge, BaseInput, Button, Card, CardBody, CardFooter, CardHeader, Chart, Checkbox, Collapse, ContentGridArea, CopyableText, CreatableSelect, DateField, DateInput, DayPicker, DayPickerPopover, DayRangePicker, DensityContainer, Drawer, ExternalLink, FilterListMapLayout, FormGroup, FullWidthRepeatingLayout, GridArea, Heading$1 as Heading, Icon$1 as Icon, IconButton, Indicator$1 as Indicator, IntrinsicallyResponsiveGrid, Label$2 as Label, LegendItem, MenuItem, MenuList, MinTopFullContentLayout, Modal, ModalBackdrop, MoreMenu, MultiSelectMenuItem, NavGridArea, NumberField, NumberInput, PageHeader, Pagination, PieChart, Popover, PopoverList, PopoverListItem, PopoverTitle, Prompt, ROLE_CARD, RadioGroup, RadioItem, Search, SectionHeader, Select, SelectPrefix, Sidebar, SingleSelectMenuItem, SkeletonLines, Spacer, Spinner, StarButton, Tag, Text$1 as Text, TextArea, TextAreaField, TextField, TextInput, Timeline, TimelineElement, Tip, TitleGridArea, TitleNavContentLayout, Toggle, Tooltip, TwoColumnLayout, ValueBar, docs, getOrderedOptions, getValueBarColorByValue, isMultiValue, useContainerProps, useCustomComponents, useDebounce, useLocalStorage, useModal, usePrompt, useTimeout };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trackunit/react-components",
3
- "version": "0.1.13",
3
+ "version": "0.1.16",
4
4
  "repository": "https://github.com/Trackunit/manager",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "dependencies": {
@@ -9,7 +9,7 @@
9
9
  "@trackunit/tailwind-styled-components": "0.0.51",
10
10
  "react": "18.2.0",
11
11
  "@trackunit/ui-design-tokens": "0.0.53",
12
- "@trackunit/react-core-contexts-test": "0.1.14",
12
+ "@trackunit/react-core-contexts-test": "0.1.15",
13
13
  "echarts-for-react": "3.0.2",
14
14
  "echarts": "5.4.0",
15
15
  "uuid": "8.3.2",
@@ -9,6 +9,10 @@ export interface CardFooterProps extends CommonProps {
9
9
  * Element to render in the footer
10
10
  */
11
11
  children?: React.ReactNode | null;
12
+ /**
13
+ * Wether or not a seperator line should be rendered
14
+ */
15
+ hideSeparator?: boolean;
12
16
  }
13
17
  /**
14
18
  * A simple footer intended to use with Cards and Modals.
@@ -18,4 +22,4 @@ export interface CardFooterProps extends CommonProps {
18
22
  * @param {CardFooterProps} props - The props for the CardFooter component
19
23
  * @returns {JSX.Element} CardFooter component
20
24
  */
21
- export declare const CardFooter: ({ dataTestId, className, children, density }: CardFooterProps) => JSX.Element;
25
+ export declare const CardFooter: ({ dataTestId, className, children, density, hideSeparator }: CardFooterProps) => JSX.Element;
@@ -5,7 +5,7 @@ export interface CardHeaderProps extends CommonProps {
5
5
  /**
6
6
  * The text to show in the Cards main heading
7
7
  */
8
- heading: string;
8
+ heading?: string;
9
9
  /**
10
10
  * The variant/size of the heading.
11
11
  * Same as the `Heading` component.
@@ -26,7 +26,7 @@ export interface CardHeaderProps extends CommonProps {
26
26
  /**
27
27
  * Wether or not a seperator line should be rendered
28
28
  */
29
- showSeparator?: boolean;
29
+ hideSeparator?: boolean;
30
30
  /** Function to call when the close button is clicked */
31
31
  onClose?: () => void;
32
32
  /**
@@ -44,4 +44,4 @@ export interface CardHeaderProps extends CommonProps {
44
44
  * @param {CardHeaderProps} props - The props for the CardHeader component
45
45
  * @returns {JSX.Element} CardHeader component
46
46
  */
47
- export declare const CardHeader: ({ heading, headingVariant, subHeading, onClose, dataTestId, className, children, accessories, density, extraClassName, }: CardHeaderProps) => JSX.Element;
47
+ export declare const CardHeader: ({ heading, headingVariant, subHeading, onClose, dataTestId, className, children, accessories, density, extraClassName, hideSeparator, }: CardHeaderProps) => JSX.Element;
@@ -0,0 +1,55 @@
1
+ import React from "react";
2
+ import { CommonProps } from "../../common/CommonProps";
3
+ import { MenuList, MoreMenu } from "../Menu";
4
+ export interface SidebarItemProps extends CommonProps {
5
+ /**
6
+ * The id prop is used to identify the sidebar item in the dom.
7
+ * This is needed as it is used for the intersection observer to determine which items are visible and which are not.
8
+ */
9
+ id: string;
10
+ }
11
+ export declare type Breakpoints = "xs" | "sm" | "md" | "lg" | "xl" | "2xl" | "3xl";
12
+ export interface SidebarProps extends CommonProps {
13
+ /**
14
+ * The breakpoint at which the menu will go from Horizontal to vertical stacked
15
+ *
16
+ * @default "lg"
17
+ */
18
+ breakpoint?: Breakpoints;
19
+ /**
20
+ * The overflowThreshold is used to determine when an item is considered to be overflowing.
21
+ */
22
+ overflowThreshold?: number;
23
+ /**
24
+ * The child elements of the sidebar.
25
+ * Usually this will be a list of SidebarItems, bottons or links.
26
+ *
27
+ * **For the overflow functionality to take effect use _"min-w-[*]"_ or _"flex-shrink-0"_ on the elements.**
28
+ */
29
+ children: React.ReactElement<SidebarItemProps>[] | React.ReactElement<SidebarItemProps>;
30
+ /**
31
+ * The childContainerClassName is applied to the element that contains the children.
32
+ * This can be used to change gap or similar styling.
33
+ */
34
+ childContainerClassName?: string;
35
+ /**
36
+ * Pass custom props to the MoreMenu component used to render overflow items
37
+ */
38
+ moreMenuProps?: Omit<React.ComponentProps<typeof MoreMenu>, "children">;
39
+ /**
40
+ * Pass custom props to the MenuList component used to render overflow items
41
+ */
42
+ menuListProps?: Omit<React.ComponentProps<typeof MenuList>, "children">;
43
+ }
44
+ /**
45
+ * The sidebar components is used to render a responsive sidebar.
46
+ * - The sidebar will be rendered horizontally until a given breakpoint where it will be stacked vertically.
47
+ * - The sidebar hides items that does not fit in the sidebar and show them in a more menu.
48
+ * - **_The sidebar is just a wrapper. You are responsible for styling the children._**
49
+ *
50
+ * When using this component make sure to add `setupIntersectionObserver();` to your jest.setup.ts file.
51
+ *
52
+ * @param {SidebarProps} props - The props for the Sidebar component
53
+ * @returns {JSX.Element} Sidebar component
54
+ */
55
+ export declare const Sidebar: ({ childContainerClassName, children, breakpoint, className, dataTestId, moreMenuProps, menuListProps, overflowThreshold, }: SidebarProps) => JSX.Element;
@@ -0,0 +1,39 @@
1
+ import { HTMLAttributes } from "react";
2
+ import { SidebarItemProps } from "./Sidebar";
3
+ export interface OverflowItemsOptions {
4
+ /**
5
+ * If the child elements of the container are not available at the time of the hook initialization,
6
+ * you can pass the children as a prop, and the hook will initialize the observer when the children are available.
7
+ */
8
+ children?: React.ReactElement<SidebarItemProps>[] | React.ReactElement<SidebarItemProps>;
9
+ /**
10
+ * The threshold for the intersection observer.
11
+ * Learn more: https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#creating_an_intersection_observer
12
+ *
13
+ * @default 1
14
+ */
15
+ threshold?: number | number[];
16
+ /**
17
+ * The unique identifier attribute for the child elements.
18
+ * The identifier is the value that will be returned in the visibleItemsIds array.
19
+ *
20
+ * **ATTENTION: The identifier must be a HTMLAttribute that is available in the DOM, you cannot use react key.**
21
+ *
22
+ * @default "id"
23
+ */
24
+ childUniqueIdentifierAttribute?: keyof HTMLAttributes<HTMLElement> | `data-${string}`;
25
+ }
26
+ export declare type OverflowMap = {
27
+ [key: string]: boolean;
28
+ };
29
+ /**
30
+ * A hook used to detect what children are overflowing a container.
31
+ *
32
+ * @param {OverflowItemsOptions} root0 The options for the hook.
33
+ * @param {OverflowItemsOptions.threshold} root0.threshold The threshold for the intersection observer.
34
+ * @returns {object} The overflow items and the ref to the container.
35
+ */
36
+ export declare const useOverflowItems: ({ threshold, childUniqueIdentifierAttribute, children, }: OverflowItemsOptions) => {
37
+ overflowContainerRef: import("react").MutableRefObject<HTMLDivElement | null>;
38
+ itemOverflowMap: OverflowMap;
39
+ };
@@ -34,6 +34,10 @@ export interface ButtonCommonProps extends CommonProps {
34
34
  * The link to be used for the button.
35
35
  */
36
36
  to?: string;
37
+ /**
38
+ * The id of the button.
39
+ */
40
+ id?: string;
37
41
  title?: string;
38
42
  tabIndex?: number;
39
43
  role?: string;
@@ -33,6 +33,7 @@ export * from "./RadioGroup";
33
33
  export * from "./Search";
34
34
  export * from "./SectionHeader";
35
35
  export * from "./Select";
36
+ export * from "./Sidebar/Sidebar";
36
37
  export * from "./SkeletonLines";
37
38
  export * from "./Spacer";
38
39
  export * from "./Spinner";