@trackunit/react-components 0.5.33 → 0.5.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.cjs.js CHANGED
@@ -287,6 +287,24 @@ const PackageNameStoryComponent = ({ packageJSON }) => {
287
287
  return (jsxRuntime.jsxs("div", { className: "flex gap-2", children: [jsxRuntime.jsx(Tag, { color: "secondary", children: packageJSON === null || packageJSON === void 0 ? void 0 : packageJSON.name }), jsxRuntime.jsxs(Tag, { color: "secondary", children: ["v", packageJSON === null || packageJSON === void 0 ? void 0 : packageJSON.version] })] }));
288
288
  };
289
289
 
290
+ /**
291
+ * Hook to detect if text content is wrapping to multiple lines
292
+ *
293
+ * @param {HTMLElement | null} element - The element to check for text wrapping
294
+ * @returns {boolean} True if the text spans multiple lines
295
+ */
296
+ const useIsTextWrapping = (element) => {
297
+ const [isWrapping, setIsWrapping] = React.useState(false);
298
+ React.useEffect(() => {
299
+ if (!element) {
300
+ setIsWrapping(false);
301
+ return;
302
+ }
303
+ setIsWrapping(element.clientHeight > element.scrollHeight / 2);
304
+ }, [element]);
305
+ return isWrapping;
306
+ };
307
+
290
308
  const cvaText = cssClassVarianceUtilities.cvaMerge(["text-black", "m-0", "relative", "text-sm", "font-normal"], {
291
309
  variants: {
292
310
  align: {
@@ -350,8 +368,9 @@ const cvaText = cssClassVarianceUtilities.cvaMerge(["text-black", "m-0", "relati
350
368
  * @param {TextProps} props - The props for the Text component
351
369
  * @returns {JSX.Element} Text component
352
370
  */
353
- const Text = ({ children, type = "p", size = "medium", align = "left", weight = "normal", underline = false, inverted = false, subtle = false, italicize = false, uppercase = false, disabled = false, className, dataTestId, ...rest }) => {
371
+ const Text = React__namespace.forwardRef(({ children, type = "p", size = "medium", align = "left", weight = "normal", underline = false, inverted = false, subtle = false, italicize = false, uppercase = false, disabled = false, className, dataTestId, ...rest }, ref) => {
354
372
  return React__namespace.createElement(type, {
373
+ ref,
355
374
  className: cvaText({
356
375
  inverted,
357
376
  uppercase,
@@ -367,7 +386,8 @@ const Text = ({ children, type = "p", size = "medium", align = "left", weight =
367
386
  "data-testid": dataTestId,
368
387
  ...rest,
369
388
  }, children);
370
- };
389
+ });
390
+ Text.displayName = "Text";
371
391
 
372
392
  const cvaSpinner = cssClassVarianceUtilities.cvaMerge(["bg-transparent", "rounded-full", "border-2", "border-solid", "inline-block", "animate-spin", "cursor-wait"], {
373
393
  variants: {
@@ -726,9 +746,16 @@ const cvaAlert = cssClassVarianceUtilities.cvaMerge(["flex", "flex-col", "gap-3"
726
746
  });
727
747
  const cvaAlertActionsContainer = cssClassVarianceUtilities.cvaMerge(["flex", "justify-end", "gap-2"]);
728
748
  const cvaContent = cssClassVarianceUtilities.cvaMerge(["flex-grow"]);
729
- const cvaAlertContentContainer = cssClassVarianceUtilities.cvaMerge(["flex", "flex-row", "h-full", "gap-2"]);
749
+ const cvaAlertContentContainer = cssClassVarianceUtilities.cvaMerge(["flex", "flex-row", "h-full", "gap-2"], {
750
+ variants: {
751
+ inline: {
752
+ true: ["items-center"],
753
+ false: ["items-start"],
754
+ },
755
+ },
756
+ });
730
757
  const cvaAlertCloseButtonContainer = cssClassVarianceUtilities.cvaMerge(["flex", "self-start", "w-min", "shrink-0"]);
731
- const cvaAlertIconContainer = cssClassVarianceUtilities.cvaMerge(["self-start", "shrink-0", "grid", "w-min", "hidden", "@2xs:block"]);
758
+ const cvaAlertIconContainer = cssClassVarianceUtilities.cvaMerge(["self-start", "shrink-0", "grid", "w-min", "hidden", "@2xs:flex"]);
732
759
 
733
760
  /**
734
761
  * The Alert component should be used to inform the user of important information.
@@ -738,20 +765,8 @@ const cvaAlertIconContainer = cssClassVarianceUtilities.cvaMerge(["self-start",
738
765
  */
739
766
  const Alert = ({ color = "info", title, className, children, primaryAction, secondaryAction, onClose, dataTestId, autoScroll, }) => {
740
767
  const ref = React__namespace.useRef(null);
741
- const getIconFromColor = () => {
742
- switch (color) {
743
- case "danger":
744
- return jsxRuntime.jsx(Icon, { color: color, name: "ExclamationTriangle" });
745
- case "warning":
746
- return jsxRuntime.jsx(Icon, { color: color, name: "ExclamationCircle" });
747
- case "success":
748
- return jsxRuntime.jsx(Icon, { color: color, name: "CheckCircle" });
749
- case "info":
750
- return jsxRuntime.jsx(Icon, { color: color, name: "InformationCircle" });
751
- default:
752
- return jsxRuntime.jsx(Icon, { name: "InformationCircle" });
753
- }
754
- };
768
+ const titleRef = React__namespace.useRef(null);
769
+ const isWrapping = useIsTextWrapping(titleRef.current);
755
770
  React__namespace.useEffect(() => {
756
771
  var _a, _b;
757
772
  if (autoScroll) {
@@ -759,7 +774,22 @@ const Alert = ({ color = "info", title, className, children, primaryAction, seco
759
774
  (_b = (_a = ref.current) === null || _a === void 0 ? void 0 : _a.scrollIntoView) === null || _b === void 0 ? void 0 : _b.call(_a);
760
775
  }
761
776
  }, [ref, autoScroll]);
762
- return (jsxRuntime.jsxs("div", { className: cvaAlert({ color, className }), "data-testid": dataTestId, ref: ref, role: "alert", children: [jsxRuntime.jsxs("div", { className: cvaAlertContentContainer(), children: [jsxRuntime.jsx("div", { className: cvaAlertIconContainer(), children: getIconFromColor() }), jsxRuntime.jsxs("div", { className: cvaContent(), children: [title ? (jsxRuntime.jsx(Text, { dataTestId: `${dataTestId}-title`, weight: "bold", children: title })) : null, children ? (jsxRuntime.jsx(Text, { type: typeof children === "string" || typeof children === "number" ? "p" : "span", weight: !title ? "bold" : "normal", children: children })) : null] }), onClose ? (jsxRuntime.jsx("div", { className: cvaAlertCloseButtonContainer(), children: jsxRuntime.jsx(IconButton, { circular: true, icon: jsxRuntime.jsx(Icon, { name: "XMark", size: "small" }), onClick: onClose, size: "small", title: "Close Alert", variant: "ghost-neutral" }) })) : null] }), primaryAction || secondaryAction ? (jsxRuntime.jsxs("div", { className: cvaAlertActionsContainer(), children: [secondaryAction ? (jsxRuntime.jsx(Button, { loading: secondaryAction.loading, onClick: secondaryAction.onClick, size: "small", variant: "ghost-neutral", children: secondaryAction.label })) : null, primaryAction ? (jsxRuntime.jsx(Button, { loading: primaryAction.loading, onClick: primaryAction.onClick, size: "small", variant: color === "danger" ? "primary-danger" : "primary", children: primaryAction.label })) : null] })) : null] }));
777
+ return (jsxRuntime.jsxs("div", { className: cvaAlert({ color, className }), "data-testid": dataTestId, ref: ref, role: "alert", children: [jsxRuntime.jsxs("div", { className: cvaAlertContentContainer({ inline: !isWrapping && !children }), children: [jsxRuntime.jsx("div", { className: cvaAlertIconContainer(), children: jsxRuntime.jsx(Icon, { color: color, name: getIconName(color) }) }), jsxRuntime.jsxs("div", { className: cvaContent(), children: [title ? (jsxRuntime.jsx(Text, { dataTestId: `${dataTestId}-title`, ref: titleRef, weight: "bold", children: title })) : null, children ? (jsxRuntime.jsx(Text, { type: typeof children === "string" || typeof children === "number" ? "p" : "span", weight: !title ? "bold" : "normal", children: children })) : null] }), onClose ? (jsxRuntime.jsx("div", { className: cvaAlertCloseButtonContainer(), children: jsxRuntime.jsx(IconButton, { circular: true, icon: jsxRuntime.jsx(Icon, { name: "XMark", size: "small" }), onClick: onClose, size: "small", title: "Close Alert", variant: "ghost-neutral" }) })) : null] }), primaryAction || secondaryAction ? (jsxRuntime.jsxs("div", { className: cvaAlertActionsContainer(), children: [secondaryAction ? (jsxRuntime.jsx(Button, { loading: secondaryAction.loading, onClick: secondaryAction.onClick, size: "small", variant: "ghost-neutral", children: secondaryAction.label })) : null, primaryAction ? (jsxRuntime.jsx(Button, { loading: primaryAction.loading, onClick: primaryAction.onClick, size: "small", variant: color === "danger" ? "primary-danger" : "primary", children: primaryAction.label })) : null] })) : null] }));
778
+ };
779
+ const getIconName = (color) => {
780
+ switch (color) {
781
+ case "danger":
782
+ return "ExclamationTriangle";
783
+ case "warning":
784
+ return "ExclamationCircle";
785
+ case "success":
786
+ return "CheckCircle";
787
+ case "info":
788
+ return "InformationCircle";
789
+ default: {
790
+ throw new Error(`${color} is not known`);
791
+ }
792
+ }
763
793
  };
764
794
 
765
795
  const cvaBadge = cssClassVarianceUtilities.cvaMerge([
package/index.esm.js CHANGED
@@ -267,6 +267,24 @@ const PackageNameStoryComponent = ({ packageJSON }) => {
267
267
  return (jsxs("div", { className: "flex gap-2", children: [jsx(Tag, { color: "secondary", children: packageJSON === null || packageJSON === void 0 ? void 0 : packageJSON.name }), jsxs(Tag, { color: "secondary", children: ["v", packageJSON === null || packageJSON === void 0 ? void 0 : packageJSON.version] })] }));
268
268
  };
269
269
 
270
+ /**
271
+ * Hook to detect if text content is wrapping to multiple lines
272
+ *
273
+ * @param {HTMLElement | null} element - The element to check for text wrapping
274
+ * @returns {boolean} True if the text spans multiple lines
275
+ */
276
+ const useIsTextWrapping = (element) => {
277
+ const [isWrapping, setIsWrapping] = useState(false);
278
+ useEffect(() => {
279
+ if (!element) {
280
+ setIsWrapping(false);
281
+ return;
282
+ }
283
+ setIsWrapping(element.clientHeight > element.scrollHeight / 2);
284
+ }, [element]);
285
+ return isWrapping;
286
+ };
287
+
270
288
  const cvaText = cvaMerge(["text-black", "m-0", "relative", "text-sm", "font-normal"], {
271
289
  variants: {
272
290
  align: {
@@ -330,8 +348,9 @@ const cvaText = cvaMerge(["text-black", "m-0", "relative", "text-sm", "font-norm
330
348
  * @param {TextProps} props - The props for the Text component
331
349
  * @returns {JSX.Element} Text component
332
350
  */
333
- const Text = ({ children, type = "p", size = "medium", align = "left", weight = "normal", underline = false, inverted = false, subtle = false, italicize = false, uppercase = false, disabled = false, className, dataTestId, ...rest }) => {
351
+ const Text = React.forwardRef(({ children, type = "p", size = "medium", align = "left", weight = "normal", underline = false, inverted = false, subtle = false, italicize = false, uppercase = false, disabled = false, className, dataTestId, ...rest }, ref) => {
334
352
  return React.createElement(type, {
353
+ ref,
335
354
  className: cvaText({
336
355
  inverted,
337
356
  uppercase,
@@ -347,7 +366,8 @@ const Text = ({ children, type = "p", size = "medium", align = "left", weight =
347
366
  "data-testid": dataTestId,
348
367
  ...rest,
349
368
  }, children);
350
- };
369
+ });
370
+ Text.displayName = "Text";
351
371
 
352
372
  const cvaSpinner = cvaMerge(["bg-transparent", "rounded-full", "border-2", "border-solid", "inline-block", "animate-spin", "cursor-wait"], {
353
373
  variants: {
@@ -706,9 +726,16 @@ const cvaAlert = cvaMerge(["flex", "flex-col", "gap-3", "relative", "p-3", "roun
706
726
  });
707
727
  const cvaAlertActionsContainer = cvaMerge(["flex", "justify-end", "gap-2"]);
708
728
  const cvaContent = cvaMerge(["flex-grow"]);
709
- const cvaAlertContentContainer = cvaMerge(["flex", "flex-row", "h-full", "gap-2"]);
729
+ const cvaAlertContentContainer = cvaMerge(["flex", "flex-row", "h-full", "gap-2"], {
730
+ variants: {
731
+ inline: {
732
+ true: ["items-center"],
733
+ false: ["items-start"],
734
+ },
735
+ },
736
+ });
710
737
  const cvaAlertCloseButtonContainer = cvaMerge(["flex", "self-start", "w-min", "shrink-0"]);
711
- const cvaAlertIconContainer = cvaMerge(["self-start", "shrink-0", "grid", "w-min", "hidden", "@2xs:block"]);
738
+ const cvaAlertIconContainer = cvaMerge(["self-start", "shrink-0", "grid", "w-min", "hidden", "@2xs:flex"]);
712
739
 
713
740
  /**
714
741
  * The Alert component should be used to inform the user of important information.
@@ -718,20 +745,8 @@ const cvaAlertIconContainer = cvaMerge(["self-start", "shrink-0", "grid", "w-min
718
745
  */
719
746
  const Alert = ({ color = "info", title, className, children, primaryAction, secondaryAction, onClose, dataTestId, autoScroll, }) => {
720
747
  const ref = React.useRef(null);
721
- const getIconFromColor = () => {
722
- switch (color) {
723
- case "danger":
724
- return jsx(Icon, { color: color, name: "ExclamationTriangle" });
725
- case "warning":
726
- return jsx(Icon, { color: color, name: "ExclamationCircle" });
727
- case "success":
728
- return jsx(Icon, { color: color, name: "CheckCircle" });
729
- case "info":
730
- return jsx(Icon, { color: color, name: "InformationCircle" });
731
- default:
732
- return jsx(Icon, { name: "InformationCircle" });
733
- }
734
- };
748
+ const titleRef = React.useRef(null);
749
+ const isWrapping = useIsTextWrapping(titleRef.current);
735
750
  React.useEffect(() => {
736
751
  var _a, _b;
737
752
  if (autoScroll) {
@@ -739,7 +754,22 @@ const Alert = ({ color = "info", title, className, children, primaryAction, seco
739
754
  (_b = (_a = ref.current) === null || _a === void 0 ? void 0 : _a.scrollIntoView) === null || _b === void 0 ? void 0 : _b.call(_a);
740
755
  }
741
756
  }, [ref, autoScroll]);
742
- return (jsxs("div", { className: cvaAlert({ color, className }), "data-testid": dataTestId, ref: ref, role: "alert", children: [jsxs("div", { className: cvaAlertContentContainer(), children: [jsx("div", { className: cvaAlertIconContainer(), children: getIconFromColor() }), jsxs("div", { className: cvaContent(), children: [title ? (jsx(Text, { dataTestId: `${dataTestId}-title`, weight: "bold", children: title })) : null, children ? (jsx(Text, { type: typeof children === "string" || typeof children === "number" ? "p" : "span", weight: !title ? "bold" : "normal", children: children })) : null] }), onClose ? (jsx("div", { className: cvaAlertCloseButtonContainer(), children: jsx(IconButton, { circular: true, icon: jsx(Icon, { name: "XMark", size: "small" }), onClick: onClose, size: "small", title: "Close Alert", variant: "ghost-neutral" }) })) : null] }), primaryAction || secondaryAction ? (jsxs("div", { className: cvaAlertActionsContainer(), children: [secondaryAction ? (jsx(Button, { loading: secondaryAction.loading, onClick: secondaryAction.onClick, size: "small", variant: "ghost-neutral", children: secondaryAction.label })) : null, primaryAction ? (jsx(Button, { loading: primaryAction.loading, onClick: primaryAction.onClick, size: "small", variant: color === "danger" ? "primary-danger" : "primary", children: primaryAction.label })) : null] })) : null] }));
757
+ return (jsxs("div", { className: cvaAlert({ color, className }), "data-testid": dataTestId, ref: ref, role: "alert", children: [jsxs("div", { className: cvaAlertContentContainer({ inline: !isWrapping && !children }), children: [jsx("div", { className: cvaAlertIconContainer(), children: jsx(Icon, { color: color, name: getIconName(color) }) }), jsxs("div", { className: cvaContent(), children: [title ? (jsx(Text, { dataTestId: `${dataTestId}-title`, ref: titleRef, weight: "bold", children: title })) : null, children ? (jsx(Text, { type: typeof children === "string" || typeof children === "number" ? "p" : "span", weight: !title ? "bold" : "normal", children: children })) : null] }), onClose ? (jsx("div", { className: cvaAlertCloseButtonContainer(), children: jsx(IconButton, { circular: true, icon: jsx(Icon, { name: "XMark", size: "small" }), onClick: onClose, size: "small", title: "Close Alert", variant: "ghost-neutral" }) })) : null] }), primaryAction || secondaryAction ? (jsxs("div", { className: cvaAlertActionsContainer(), children: [secondaryAction ? (jsx(Button, { loading: secondaryAction.loading, onClick: secondaryAction.onClick, size: "small", variant: "ghost-neutral", children: secondaryAction.label })) : null, primaryAction ? (jsx(Button, { loading: primaryAction.loading, onClick: primaryAction.onClick, size: "small", variant: color === "danger" ? "primary-danger" : "primary", children: primaryAction.label })) : null] })) : null] }));
758
+ };
759
+ const getIconName = (color) => {
760
+ switch (color) {
761
+ case "danger":
762
+ return "ExclamationTriangle";
763
+ case "warning":
764
+ return "ExclamationCircle";
765
+ case "success":
766
+ return "CheckCircle";
767
+ case "info":
768
+ return "InformationCircle";
769
+ default: {
770
+ throw new Error(`${color} is not known`);
771
+ }
772
+ }
743
773
  };
744
774
 
745
775
  const cvaBadge = cvaMerge([
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trackunit/react-components",
3
- "version": "0.5.33",
3
+ "version": "0.5.35",
4
4
  "repository": "https://github.com/Trackunit/manager",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "engines": {
@@ -3,6 +3,8 @@ export declare const cvaAlert: (props?: ({
3
3
  } & import("class-variance-authority/dist/types").ClassProp) | undefined) => string;
4
4
  export declare const cvaAlertActionsContainer: (props?: import("class-variance-authority/dist/types").ClassProp | undefined) => string;
5
5
  export declare const cvaContent: (props?: import("class-variance-authority/dist/types").ClassProp | undefined) => string;
6
- export declare const cvaAlertContentContainer: (props?: import("class-variance-authority/dist/types").ClassProp | undefined) => string;
6
+ export declare const cvaAlertContentContainer: (props?: ({
7
+ inline?: boolean | null | undefined;
8
+ } & import("class-variance-authority/dist/types").ClassProp) | undefined) => string;
7
9
  export declare const cvaAlertCloseButtonContainer: (props?: import("class-variance-authority/dist/types").ClassProp | undefined) => string;
8
10
  export declare const cvaAlertIconContainer: (props?: import("class-variance-authority/dist/types").ClassProp | undefined) => string;
@@ -63,4 +63,4 @@ export interface TextProps extends CommonProps {
63
63
  * @param {TextProps} props - The props for the Text component
64
64
  * @returns {JSX.Element} Text component
65
65
  */
66
- export declare const Text: ({ children, type, size, align, weight, underline, inverted, subtle, italicize, uppercase, disabled, className, dataTestId, ...rest }: TextProps) => JSX.Element;
66
+ export declare const Text: React.ForwardRefExoticComponent<TextProps & React.RefAttributes<HTMLElement>>;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Hook to detect if text content is wrapping to multiple lines
3
+ *
4
+ * @param {HTMLElement | null} element - The element to check for text wrapping
5
+ * @returns {boolean} True if the text spans multiple lines
6
+ */
7
+ export declare const useIsTextWrapping: (element: HTMLElement | null) => boolean;