@bioturing/components 0.36.0 → 0.37.0

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.
@@ -1,112 +1,121 @@
1
1
  "use client";
2
- import { jsx as T } from "react/jsx-runtime";
3
- import { useRef as M, useState as L, useCallback as h, useLayoutEffect as z } from "react";
4
- import { calculateAvailableHeight as A, getMiddleTruncatedString as F } from "./helpers.js";
2
+ import { jsx as C } from "react/jsx-runtime";
3
+ import { useRef as A, useState as F, useCallback as p, useLayoutEffect as R } from "react";
4
+ import { calculateAvailableHeight as k, getMiddleTruncatedString as q } from "./helpers.js";
5
5
  import { useOverflowDetection as g } from "./useOverflowDetection.js";
6
6
  import './style.css';/* empty css */
7
- import { useCls as R } from "../utils/antdUtils.js";
8
- import { clsx as k } from "../utils/cn.js";
9
- const J = ({
7
+ import { useCls as B } from "../utils/antdUtils.js";
8
+ import { clsx as j } from "../utils/cn.js";
9
+ const Q = ({
10
10
  children: w,
11
11
  position: i = "end",
12
12
  lines: e = 1,
13
- className: b,
14
- style: f,
13
+ className: O,
14
+ style: v,
15
15
  container: o,
16
- ...x
16
+ renderText: d,
17
+ ...S
17
18
  }) => {
18
- const s = R(), l = M(null), [m, p] = L(w), r = w, O = g({
19
+ const f = B(), l = A(null), [m, y] = F(w), a = w, x = g({
19
20
  container: l,
20
21
  additionalElements: [l.current?.parentElement].filter(Boolean),
21
22
  type: "vertical",
22
- text: r,
23
+ text: a,
23
24
  enabled: e === "auto"
24
- }), C = g({
25
+ }), E = g({
25
26
  container: l,
26
27
  type: "vertical",
27
- text: r,
28
+ text: a,
28
29
  enabled: typeof e == "number" && e > 1
29
- }), E = g({
30
+ }), H = g({
30
31
  container: l,
31
32
  type: "horizontal",
32
33
  enabled: i === "end" && e === 1
33
- }), S = e === "auto" && O.isOverflowing || typeof e == "number" && e > 1 && C.isOverflowing || i === "end" && e === 1 && E.isOverflowing || i === "middle" && r !== m, H = h(() => e !== "auto" ? {
34
- ...f,
34
+ }), M = e === "auto" && x.isOverflowing || typeof e == "number" && e > 1 && E.isOverflowing || i === "end" && e === 1 && H.isOverflowing || i === "middle" && a !== m, L = p(() => e !== "auto" ? {
35
+ ...v,
35
36
  ...e > 1 ? { "--ds-line-clamp": e } : {}
36
37
  } : {
37
- ...f,
38
+ ...v,
38
39
  // Set a temporary high value to ensure it fits the parent
39
40
  "--ds-line-clamp": 999
40
- }, [e, f]), y = h(() => {
41
+ }, [e, v]), b = p(() => {
41
42
  const t = l.current;
42
43
  if (!t) return;
43
- const n = window.getComputedStyle(t), a = parseFloat(n.fontSize), u = n.lineHeight === "normal" ? a * 1.2 : parseFloat(n.lineHeight), c = A(t), d = Math.max(1, Math.floor(c / u));
44
- t.style.setProperty("--ds-line-clamp", String(d));
45
- }, [r]), v = h(
46
- (t, n, a) => {
44
+ const n = window.getComputedStyle(t), r = parseFloat(n.fontSize), u = n.lineHeight === "normal" ? r * 1.2 : parseFloat(n.lineHeight), c = k(t), s = Math.max(1, Math.floor(c / u));
45
+ t.style.setProperty("--ds-line-clamp", String(s));
46
+ }, []), h = p(
47
+ (t, n, r) => {
47
48
  const u = t.filter(Boolean);
48
49
  if (u.length === 0) return;
49
50
  window.document.fonts?.ready?.then(n);
50
51
  const c = new ResizeObserver(() => {
51
52
  window.requestAnimationFrame(n);
52
53
  });
53
- return u.forEach((d) => c.observe(d)), () => {
54
- c.disconnect(), a && a();
54
+ return u.forEach((s) => c.observe(s)), () => {
55
+ c.disconnect(), r && r();
55
56
  };
56
57
  },
57
58
  []
58
59
  );
59
- return z(() => {
60
+ R(() => {
60
61
  const t = l.current;
61
62
  let n = o instanceof HTMLElement ? o : o && "current" in o ? o.current : null;
62
63
  if (t && (n || (n = t.closest("[data-truncate-container]")), !(typeof o < "u" && !n))) {
63
- if (p(r), e === "auto")
64
- return v(
64
+ if (y(a), e === "auto")
65
+ return h(
65
66
  [t, t.parentElement],
66
- y
67
+ b
67
68
  );
68
69
  if (i === "middle") {
69
- let a = { cancelled: !1 };
70
- return v(
70
+ let r = { cancelled: !1 };
71
+ return h(
71
72
  [n || t],
72
73
  () => {
73
- a && (a.cancelled = !0);
74
+ r && (r.cancelled = !0);
74
75
  const c = { cancelled: !1 };
75
- a = c;
76
- const d = F(r, "…", t);
77
- c.cancelled || p(d);
76
+ r = c;
77
+ const s = q(
78
+ a,
79
+ "…",
80
+ t,
81
+ d
82
+ );
83
+ c.cancelled || y(s);
78
84
  },
79
85
  () => {
80
- a.cancelled = !0;
86
+ r.cancelled = !0;
81
87
  }
82
88
  );
83
89
  }
84
90
  }
85
91
  }, [
86
- r,
92
+ a,
87
93
  i,
88
94
  e,
89
95
  o,
90
- y,
91
- v
92
- ]), /* @__PURE__ */ T(
96
+ b,
97
+ h,
98
+ d
99
+ ]);
100
+ const T = d ? d(m) : m, z = a !== m;
101
+ return /* @__PURE__ */ C(
93
102
  "span",
94
103
  {
95
104
  ref: l,
96
- style: H(),
97
- className: k(
98
- s(e === 1 ? "truncate" : "truncate-multiline"),
99
- s(i === "middle" ? "truncate-middle" : "truncate-end"),
100
- b
105
+ style: L(),
106
+ className: j(
107
+ f(e === 1 ? "truncate" : "truncate-multiline"),
108
+ f(i === "middle" ? "truncate-middle" : "truncate-end"),
109
+ O
101
110
  ),
102
- title: S ? r : void 0,
111
+ title: M ? a : void 0,
103
112
  "data-truncate-text-node": !0,
104
- ...x,
105
- children: r === m ? r : /* @__PURE__ */ T("span", { children: m })
113
+ ...S,
114
+ children: z ? /* @__PURE__ */ C("span", { children: T }) : T
106
115
  }
107
116
  );
108
117
  };
109
118
  export {
110
- J as Truncate
119
+ Q as Truncate
111
120
  };
112
121
  //# sourceMappingURL=component.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"component.js","sources":["../../../src/components/truncate/component.tsx"],"sourcesContent":["\"use client\";\nimport React, { useCallback, useLayoutEffect, useRef, useState } from \"react\";\nimport { clsx, useCls } from \"../utils\";\nimport { calculateAvailableHeight, getMiddleTruncatedString } from \"./helpers\";\nimport { useOverflowDetection } from \"./useOverflowDetection\";\n\nimport \"./style.css\";\n\nexport type TruncatePosition = \"middle\" | \"end\";\n\nexport interface TruncateProps\n extends Omit<React.ComponentPropsWithoutRef<\"span\">, \"title\"> {\n /** Text content to be truncated */\n children: string;\n /** Position where the truncation should occur */\n position?: TruncatePosition;\n /**\n * Number of lines to display before truncating\n * @default 1\n * @remarks Use 'auto' to fit parent height\n */\n lines?: number | \"auto\";\n /*\n * Ref to the container element of the truncated text. It will have resize observer attached to it.\n */\n container?: HTMLElement | React.RefObject<HTMLElement>;\n}\n\nexport const Truncate: React.FC<TruncateProps> = ({\n children,\n position = \"end\",\n lines = 1,\n className,\n style,\n container: containerProp,\n ...rest\n}) => {\n const cls = useCls();\n const containerRef = useRef<HTMLElement>(null);\n const [truncatedText, setTruncatedText] = useState(children);\n\n const text = children;\n\n // Overflow detection for different scenarios\n const autoLineOverflow = useOverflowDetection({\n container: containerRef,\n additionalElements: [containerRef.current?.parentElement].filter(Boolean),\n type: \"vertical\",\n text,\n enabled: lines === \"auto\",\n });\n\n const multilineOverflow = useOverflowDetection({\n container: containerRef,\n type: \"vertical\",\n text,\n enabled: typeof lines === \"number\" && lines > 1,\n });\n\n const endTruncateOverflow = useOverflowDetection({\n container: containerRef,\n type: \"horizontal\",\n enabled: position === \"end\" && lines === 1,\n });\n\n // Determine if content is truncated based on the active mode\n const isTruncated =\n (lines === \"auto\" && autoLineOverflow.isOverflowing) ||\n (typeof lines === \"number\" &&\n lines > 1 &&\n multilineOverflow.isOverflowing) ||\n (position === \"end\" && lines === 1 && endTruncateOverflow.isOverflowing) ||\n (position === \"middle\" && text !== truncatedText);\n\n // Get appropriate container styles based on position and lines\n const getContainerStyles = useCallback(() => {\n // For explicit line numbers or single line\n if (lines !== \"auto\") {\n return {\n ...style,\n ...(lines > 1 ? { \"--ds-line-clamp\": lines } : {}),\n } as React.CSSProperties;\n }\n\n // For auto line calculation, we'll let the useLayoutEffect handle it\n return {\n ...style,\n // Set a temporary high value to ensure it fits the parent\n \"--ds-line-clamp\": 999,\n } as React.CSSProperties;\n }, [lines, style]);\n\n // Handle auto line calculation\n const handleAutoLineCalculation = useCallback(() => {\n const container = containerRef.current;\n if (!container) return;\n\n const styles = window.getComputedStyle(container);\n const fontSize = parseFloat(styles.fontSize);\n const lineHeight =\n styles.lineHeight === \"normal\"\n ? fontSize * 1.2\n : parseFloat(styles.lineHeight);\n\n const availableHeight = calculateAvailableHeight(container);\n const maxLines = Math.max(1, Math.floor(availableHeight / lineHeight));\n\n container.style.setProperty(\"--ds-line-clamp\", String(maxLines));\n }, [text]);\n\n const observe = useCallback(\n (\n elements: (HTMLElement | null)[],\n callback: () => void,\n cleanup?: () => void\n ) => {\n const validElements = elements.filter(Boolean) as HTMLElement[];\n if (validElements.length === 0) return;\n\n window.document.fonts?.ready?.then(callback);\n\n const observer = new ResizeObserver(() => {\n window.requestAnimationFrame(callback);\n });\n\n validElements.forEach((el) => observer.observe(el));\n return () => {\n observer.disconnect();\n if (cleanup) cleanup();\n };\n },\n []\n );\n\n useLayoutEffect(() => {\n const container = containerRef.current;\n let flexContainer =\n containerProp instanceof HTMLElement\n ? containerProp\n : containerProp && \"current\" in containerProp\n ? containerProp.current\n : null;\n if (!container) return;\n if (!flexContainer) {\n flexContainer = container.closest(\"[data-truncate-container]\");\n }\n if (typeof containerProp !== \"undefined\" && !flexContainer) return;\n\n // reset\n setTruncatedText(text);\n\n // Handle auto-height calculation\n if (lines === \"auto\") {\n return observe(\n [container, container.parentElement],\n handleAutoLineCalculation\n );\n }\n\n // Handle middle truncation with resize observer\n if (position === \"middle\") {\n let cancellationToken = { cancelled: false };\n\n const handleMiddleTruncate = () => {\n if (cancellationToken) cancellationToken.cancelled = true;\n\n const requestCancellationToken = { cancelled: false };\n cancellationToken = requestCancellationToken;\n\n const truncated = getMiddleTruncatedString(text, \"\\u2026\", container);\n\n if (requestCancellationToken.cancelled) return;\n\n setTruncatedText(truncated);\n };\n\n return observe(\n [flexContainer ? flexContainer : container],\n handleMiddleTruncate,\n () => {\n cancellationToken.cancelled = true;\n }\n );\n }\n }, [\n text,\n position,\n lines,\n containerProp,\n handleAutoLineCalculation,\n observe,\n ]);\n\n return (\n <span\n ref={containerRef}\n style={getContainerStyles()}\n className={clsx(\n lines === 1 ? cls(\"truncate\") : cls(\"truncate-multiline\"),\n position === \"middle\" ? cls(\"truncate-middle\") : cls(\"truncate-end\"),\n className\n )}\n title={isTruncated ? text : undefined}\n data-truncate-text-node\n {...rest}\n >\n {text === truncatedText ? text : <span>{truncatedText}</span>}\n </span>\n );\n};\n"],"names":["Truncate","children","position","lines","className","style","containerProp","rest","cls","useCls","containerRef","useRef","truncatedText","setTruncatedText","useState","text","autoLineOverflow","useOverflowDetection","multilineOverflow","endTruncateOverflow","isTruncated","getContainerStyles","useCallback","handleAutoLineCalculation","container","styles","fontSize","lineHeight","availableHeight","calculateAvailableHeight","maxLines","observe","elements","callback","cleanup","validElements","observer","el","useLayoutEffect","flexContainer","cancellationToken","requestCancellationToken","truncated","getMiddleTruncatedString","jsx","clsx"],"mappings":"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"}
1
+ {"version":3,"file":"component.js","sources":["../../../src/components/truncate/component.tsx"],"sourcesContent":["\"use client\";\nimport React, { useCallback, useLayoutEffect, useRef, useState } from \"react\";\nimport { clsx, useCls } from \"../utils\";\nimport { calculateAvailableHeight, getMiddleTruncatedString } from \"./helpers\";\nimport { useOverflowDetection } from \"./useOverflowDetection\";\n\nimport \"./style.css\";\n\nexport type TruncatePosition = \"middle\" | \"end\";\n\nexport interface TruncateProps\n extends Omit<React.ComponentPropsWithoutRef<\"span\">, \"title\"> {\n /** Text content to be truncated */\n children: string;\n /** Position where the truncation should occur */\n position?: TruncatePosition;\n /**\n * Number of lines to display before truncating\n * @default 1\n * @remarks Use 'auto' to fit parent height\n */\n lines?: number | \"auto\";\n /*\n * Ref to the container element of the truncated text. It will have resize observer attached to it.\n */\n container?: HTMLElement | React.RefObject<HTMLElement>;\n /**\n * Custom render function to transform text before display\n * Useful for keyword highlighting, text transformations, etc.\n * @param text - The text to render (may be truncated)\n * @returns React node to render\n */\n renderText?: (text: string) => React.ReactNode;\n}\n\nexport const Truncate: React.FC<TruncateProps> = ({\n children,\n position = \"end\",\n lines = 1,\n className,\n style,\n container: containerProp,\n renderText,\n ...rest\n}) => {\n const cls = useCls();\n const containerRef = useRef<HTMLElement>(null);\n const [truncatedText, setTruncatedText] = useState(children);\n\n const text = children;\n\n // Overflow detection for different scenarios\n const autoLineOverflow = useOverflowDetection({\n container: containerRef,\n additionalElements: [containerRef.current?.parentElement].filter(Boolean),\n type: \"vertical\",\n text,\n enabled: lines === \"auto\",\n });\n\n const multilineOverflow = useOverflowDetection({\n container: containerRef,\n type: \"vertical\",\n text,\n enabled: typeof lines === \"number\" && lines > 1,\n });\n\n const endTruncateOverflow = useOverflowDetection({\n container: containerRef,\n type: \"horizontal\",\n enabled: position === \"end\" && lines === 1,\n });\n\n // Determine if content is truncated based on the active mode\n const isTruncated =\n (lines === \"auto\" && autoLineOverflow.isOverflowing) ||\n (typeof lines === \"number\" &&\n lines > 1 &&\n multilineOverflow.isOverflowing) ||\n (position === \"end\" && lines === 1 && endTruncateOverflow.isOverflowing) ||\n (position === \"middle\" && text !== truncatedText);\n\n // Get appropriate container styles based on position and lines\n const getContainerStyles = useCallback(() => {\n // For explicit line numbers or single line\n if (lines !== \"auto\") {\n return {\n ...style,\n ...(lines > 1 ? { \"--ds-line-clamp\": lines } : {}),\n } as React.CSSProperties;\n }\n\n // For auto line calculation, we'll let the useLayoutEffect handle it\n return {\n ...style,\n // Set a temporary high value to ensure it fits the parent\n \"--ds-line-clamp\": 999,\n } as React.CSSProperties;\n }, [lines, style]);\n\n // Handle auto line calculation\n const handleAutoLineCalculation = useCallback(() => {\n const container = containerRef.current;\n if (!container) return;\n\n const styles = window.getComputedStyle(container);\n const fontSize = parseFloat(styles.fontSize);\n const lineHeight =\n styles.lineHeight === \"normal\"\n ? fontSize * 1.2\n : parseFloat(styles.lineHeight);\n\n const availableHeight = calculateAvailableHeight(container);\n const maxLines = Math.max(1, Math.floor(availableHeight / lineHeight));\n\n container.style.setProperty(\"--ds-line-clamp\", String(maxLines));\n }, []);\n\n const observe = useCallback(\n (\n elements: (HTMLElement | null)[],\n callback: () => void,\n cleanup?: () => void\n ) => {\n const validElements = elements.filter(Boolean) as HTMLElement[];\n if (validElements.length === 0) return;\n\n window.document.fonts?.ready?.then(callback);\n\n const observer = new ResizeObserver(() => {\n window.requestAnimationFrame(callback);\n });\n\n validElements.forEach((el) => observer.observe(el));\n return () => {\n observer.disconnect();\n if (cleanup) cleanup();\n };\n },\n []\n );\n\n useLayoutEffect(() => {\n const container = containerRef.current;\n let flexContainer =\n containerProp instanceof HTMLElement\n ? containerProp\n : containerProp && \"current\" in containerProp\n ? containerProp.current\n : null;\n if (!container) return;\n if (!flexContainer) {\n flexContainer = container.closest(\"[data-truncate-container]\");\n }\n if (typeof containerProp !== \"undefined\" && !flexContainer) return;\n\n // reset\n setTruncatedText(text);\n\n // Handle auto-height calculation\n if (lines === \"auto\") {\n return observe(\n [container, container.parentElement],\n handleAutoLineCalculation\n );\n }\n\n // Handle middle truncation with resize observer\n if (position === \"middle\") {\n let cancellationToken = { cancelled: false };\n\n const handleMiddleTruncate = () => {\n if (cancellationToken) cancellationToken.cancelled = true;\n\n const requestCancellationToken = { cancelled: false };\n cancellationToken = requestCancellationToken;\n\n // Pass renderText function for styled content measurement\n const truncated = getMiddleTruncatedString(\n text,\n \"\\u2026\",\n container,\n renderText\n );\n\n if (requestCancellationToken.cancelled) return;\n\n setTruncatedText(truncated);\n };\n\n return observe(\n [flexContainer ? flexContainer : container],\n handleMiddleTruncate,\n () => {\n cancellationToken.cancelled = true;\n }\n );\n }\n }, [\n text,\n position,\n lines,\n containerProp,\n handleAutoLineCalculation,\n observe,\n renderText,\n ]);\n\n // Determine content to display\n const displayContent = renderText ? renderText(truncatedText) : truncatedText;\n const shouldWrapInSpan = text !== truncatedText;\n\n return (\n <span\n ref={containerRef}\n style={getContainerStyles()}\n className={clsx(\n lines === 1 ? cls(\"truncate\") : cls(\"truncate-multiline\"),\n position === \"middle\" ? cls(\"truncate-middle\") : cls(\"truncate-end\"),\n className\n )}\n title={isTruncated ? text : undefined}\n data-truncate-text-node\n {...rest}\n >\n {shouldWrapInSpan ? <span>{displayContent}</span> : displayContent}\n </span>\n );\n};\n"],"names":["Truncate","children","position","lines","className","style","containerProp","renderText","rest","cls","useCls","containerRef","useRef","truncatedText","setTruncatedText","useState","text","autoLineOverflow","useOverflowDetection","multilineOverflow","endTruncateOverflow","isTruncated","getContainerStyles","useCallback","handleAutoLineCalculation","container","styles","fontSize","lineHeight","availableHeight","calculateAvailableHeight","maxLines","observe","elements","callback","cleanup","validElements","observer","el","useLayoutEffect","flexContainer","cancellationToken","requestCancellationToken","truncated","getMiddleTruncatedString","displayContent","shouldWrapInSpan","jsx","clsx"],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { default as React } from 'react';
1
2
  /**
2
3
  * Finds the closest parent element that has inline flex display
3
4
  * @param element - The starting HTMLElement
@@ -5,11 +6,16 @@
5
6
  */
6
7
  export declare function findClosestInlineFlexParent(element: HTMLElement): HTMLElement | null;
7
8
  export declare function getFlexRemainingSpace(flexContainer: HTMLElement, ignoreElement?: HTMLElement | ((e: HTMLElement) => boolean)): number;
8
- export declare const measureText: (container: HTMLElement) => {
9
+ /**
10
+ * Measures text width with optional render function for styled content
11
+ * @param container - The container element to clone styles from
12
+ * @param renderText - Optional render function to apply styling to text before measurement
13
+ */
14
+ export declare const measureText: (container: HTMLElement, renderText?: ((text: string) => React.ReactNode) | null) => {
9
15
  measure: (text: string) => number;
10
16
  destroy: () => void;
11
17
  };
12
- export declare const getMiddleTruncatedString: (text: string, ellipsis: string, container: HTMLElement) => string;
18
+ export declare const getMiddleTruncatedString: (text: string, ellipsis: string, container: HTMLElement, renderText?: ((text: string) => React.ReactNode) | null) => string;
13
19
  export declare const createMeasurementClone: (text: string, container: HTMLElement) => HTMLSpanElement;
14
20
  export declare const checkIfTextTruncated: (text: string, container: HTMLElement, availableHeight?: number) => boolean;
15
21
  export declare const calculateAvailableHeight: (container: HTMLElement) => number;
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/components/truncate/helpers.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,WAAW,GACnB,WAAW,GAAG,IAAI,CAqBpB;AAED,wBAAgB,qBAAqB,CACnC,aAAa,EAAE,WAAW,EAC1B,aAAa,GAAE,WAAW,GAAG,CAAC,CAAC,CAAC,EAAE,WAAW,KAAK,OAAO,CAAQ,UA2ClE;AAED,eAAO,MAAM,WAAW,GAAI,WAAW,WAAW;oBAuB9B,MAAM;;CASzB,CAAC;AAEF,eAAO,MAAM,wBAAwB,GACnC,MAAM,MAAM,EACZ,UAAU,MAAM,EAChB,WAAW,WAAW,KACrB,MAgEF,CAAC;AAGF,eAAO,MAAM,sBAAsB,GACjC,MAAM,MAAM,EACZ,WAAW,WAAW,oBAavB,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,MAAM,MAAM,EACZ,WAAW,WAAW,EACtB,kBAAkB,MAAM,YAQzB,CAAC;AAEF,eAAO,MAAM,wBAAwB,GAAI,WAAW,WAAW,WAiB9D,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAAI,WAAW,WAAW,WAiB7D,CAAC;AAGF,MAAM,MAAM,YAAY,GAAG,YAAY,GAAG,UAAU,GAAG,MAAM,CAAC;AAE9D,MAAM,WAAW,oBAAoB;IACnC,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,eAAO,MAAM,uBAAuB,GAAI,WAAW,WAAW,KAAG,OAEhE,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAChC,WAAW,WAAW,EACtB,kBAAkB,MAAM,KACvB,OAGF,CAAC;AAEF,eAAO,MAAM,aAAa,GACxB,WAAW,WAAW,EACtB,UAAS,oBAAyB,KACjC,OAyCF,CAAC"}
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/components/truncate/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,WAAW,GACnB,WAAW,GAAG,IAAI,CAqBpB;AAED,wBAAgB,qBAAqB,CACnC,aAAa,EAAE,WAAW,EAC1B,aAAa,GAAE,WAAW,GAAG,CAAC,CAAC,CAAC,EAAE,WAAW,KAAK,OAAO,CAAQ,UA2ClE;AAKD;;;;GAIG;AACH,eAAO,MAAM,WAAW,GACtB,WAAW,WAAW,EACtB,aAAa,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,KAAK,CAAC,SAAS,CAAC,GAAG,IAAI;oBAwBrC,MAAM;;CA0DzB,CAAC;AAEF,eAAO,MAAM,wBAAwB,GACnC,MAAM,MAAM,EACZ,UAAU,MAAM,EAChB,WAAW,WAAW,EACtB,aAAa,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,KAAK,CAAC,SAAS,CAAC,GAAG,IAAI,KACtD,MAgEF,CAAC;AAGF,eAAO,MAAM,sBAAsB,GACjC,MAAM,MAAM,EACZ,WAAW,WAAW,oBAavB,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,MAAM,MAAM,EACZ,WAAW,WAAW,EACtB,kBAAkB,MAAM,YAQzB,CAAC;AAEF,eAAO,MAAM,wBAAwB,GAAI,WAAW,WAAW,WAiB9D,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAAI,WAAW,WAAW,WAiB7D,CAAC;AAGF,MAAM,MAAM,YAAY,GAAG,YAAY,GAAG,UAAU,GAAG,MAAM,CAAC;AAE9D,MAAM,WAAW,oBAAoB;IACnC,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,eAAO,MAAM,uBAAuB,GAAI,WAAW,WAAW,KAAG,OAEhE,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAChC,WAAW,WAAW,EACtB,kBAAkB,MAAM,KACvB,OAGF,CAAC;AAEF,eAAO,MAAM,aAAa,GACxB,WAAW,WAAW,EACtB,UAAS,oBAAyB,KACjC,OAyCF,CAAC"}
@@ -1,83 +1,111 @@
1
- const b = (t) => {
2
- const o = document.createElement("span");
3
- o.style.opacity = "0", o.style.position = "absolute", o.style.top = "-1000px", o.style.left = "-1000px", o.style.whiteSpace = "nowrap", o.style.pointerEvents = "none";
4
- const l = t.getBoundingClientRect(), e = t.cloneNode(!0);
5
- return e.style.maxWidth = `${l.width}px`, e.style.position = "absolute", e.style.pointerEvents = "none", e.style.top = "-99px", e.style.left = "-99px", e.style.zIndex = "-1", t.parentElement?.appendChild(e), e.appendChild(o), {
6
- measure: (r) => (o.innerText = r, o.clientWidth),
1
+ import v from "react";
2
+ import { renderToStaticMarkup as w } from "react-dom/server";
3
+ const d = /* @__PURE__ */ new Map(), W = (e, n) => {
4
+ const t = document.createElement("span");
5
+ t.style.opacity = "0", t.style.position = "absolute", t.style.top = "-1000px", t.style.left = "-1000px", t.style.whiteSpace = "nowrap", t.style.pointerEvents = "none";
6
+ const o = e.getBoundingClientRect(), s = e.cloneNode(!0);
7
+ return s.style.maxWidth = `${o.width}px`, s.style.position = "absolute", s.style.pointerEvents = "none", s.style.top = "-99px", s.style.left = "-99px", s.style.zIndex = "-1", e.parentElement?.appendChild(s), s.appendChild(t), {
8
+ measure: (l) => {
9
+ if (n) {
10
+ const r = `${l}-styled`;
11
+ if (d.has(r))
12
+ return d.get(r);
13
+ t.innerHTML = "";
14
+ const c = n(l);
15
+ if (typeof c == "string")
16
+ t.innerHTML = c;
17
+ else if (v.isValidElement(c))
18
+ try {
19
+ const i = w(c);
20
+ t.innerHTML = i;
21
+ } catch {
22
+ t.innerText = l;
23
+ }
24
+ else
25
+ t.innerText = l;
26
+ const h = t.clientWidth;
27
+ if (d.set(r, h), d.size > 100) {
28
+ const i = d.keys().next().value;
29
+ d.delete(i);
30
+ }
31
+ return h;
32
+ }
33
+ return t.innerText = l, t.clientWidth;
34
+ },
7
35
  destroy: () => {
8
- e.removeChild(o), t.parentElement?.removeChild(e);
36
+ s.removeChild(t), e.parentElement?.removeChild(s);
9
37
  }
10
38
  };
11
- }, H = (t, o, l) => {
12
- if (!t) return t;
13
- const { measure: e, destroy: r } = b(l), s = e(t), n = l.clientWidth, f = Math.floor(
14
- n / s * t.length
39
+ }, M = (e, n, t, o) => {
40
+ if (!e) return e;
41
+ const { measure: s, destroy: l } = W(t, o), r = s(e), c = t.clientWidth, h = Math.floor(
42
+ c / r * e.length
15
43
  );
16
- if (s <= n)
17
- return r(), t;
18
- let i = f;
19
- const c = {}, g = 20, m = 4;
20
- for (; Object.values(c).length <= g && !c[i]; ) {
44
+ if (r <= c)
45
+ return l(), e;
46
+ let i = h;
47
+ const a = {}, g = 20, b = 4;
48
+ for (; Object.values(a).length <= g && !a[i]; ) {
21
49
  if (i <= 1) {
22
- c[0] = [0, o];
50
+ a[0] = [0, n];
23
51
  break;
24
52
  }
25
- const d = t.slice(0, Math.ceil((i - o.length) / 2 - 1)).trimEnd(), y = t.slice(Math.floor((i - o.length) / 2) - i).trimStart(), p = d + o + y, a = e(p);
26
- if (c[i] = [a, p], a >= n)
53
+ const p = e.slice(0, Math.ceil((i - n.length) / 2 - 1)).trimEnd(), H = e.slice(Math.floor((i - n.length) / 2) - i).trimStart(), m = p + n + H, f = s(m);
54
+ if (a[i] = [f, m], f >= c)
27
55
  i = i - 2;
28
56
  else {
29
- if (n - a < m) break;
57
+ if (c - f < b) break;
30
58
  i = i + 2;
31
59
  }
32
60
  }
33
- return r(), Object.values(c).reverse().find(([d]) => d < n)?.[1] ?? Object.values(c)[0][1];
34
- }, v = (t, o) => {
35
- const l = window.getComputedStyle(o), e = document.createElement("span");
36
- return e.style.visibility = "hidden", e.style.position = "absolute", e.style.width = `${o.clientWidth}px`, e.style.fontSize = l.fontSize, e.style.fontFamily = l.fontFamily, e.style.lineHeight = l.lineHeight, e.textContent = t, document.body.appendChild(e), e;
37
- }, W = (t) => {
38
- const o = t.parentElement;
39
- if (!o) return 0;
40
- const l = window.getComputedStyle(o), e = parseFloat(l.paddingTop) || 0, r = parseFloat(l.paddingBottom) || 0, s = parseFloat(l.borderTopWidth) || 0, n = parseFloat(l.borderBottomWidth) || 0;
41
- return o.clientHeight - e - r - s - n;
42
- }, h = (t) => t.scrollWidth > t.clientWidth, u = (t, o) => {
43
- const l = o ?? (t.clientHeight || t.offsetHeight);
44
- return t.scrollHeight > l;
45
- }, w = (t, o = {}) => {
46
- const { type: l = "horizontal", availableHeight: e, text: r } = o;
47
- if (r) {
48
- const s = v(r, t);
49
- let n = !1;
50
- switch (l) {
61
+ return l(), Object.values(a).reverse().find(([p]) => p < c)?.[1] ?? Object.values(a)[0][1];
62
+ }, C = (e, n) => {
63
+ const t = window.getComputedStyle(n), o = document.createElement("span");
64
+ return o.style.visibility = "hidden", o.style.position = "absolute", o.style.width = `${n.clientWidth}px`, o.style.fontSize = t.fontSize, o.style.fontFamily = t.fontFamily, o.style.lineHeight = t.lineHeight, o.textContent = e, document.body.appendChild(o), o;
65
+ }, S = (e) => {
66
+ const n = e.parentElement;
67
+ if (!n) return 0;
68
+ const t = window.getComputedStyle(n), o = parseFloat(t.paddingTop) || 0, s = parseFloat(t.paddingBottom) || 0, l = parseFloat(t.borderTopWidth) || 0, r = parseFloat(t.borderBottomWidth) || 0;
69
+ return n.clientHeight - o - s - l - r;
70
+ }, u = (e) => e.scrollWidth > e.clientWidth, y = (e, n) => {
71
+ const t = n ?? (e.clientHeight || e.offsetHeight);
72
+ return e.scrollHeight > t;
73
+ }, E = (e, n = {}) => {
74
+ const { type: t = "horizontal", availableHeight: o, text: s } = n;
75
+ if (s) {
76
+ const l = C(s, e);
77
+ let r = !1;
78
+ switch (t) {
51
79
  case "horizontal":
52
- n = s.scrollWidth > t.clientWidth;
80
+ r = l.scrollWidth > e.clientWidth;
53
81
  break;
54
82
  case "vertical":
55
- n = e ? s.scrollHeight > e : s.scrollHeight > (t.clientHeight || t.offsetHeight);
83
+ r = o ? l.scrollHeight > o : l.scrollHeight > (e.clientHeight || e.offsetHeight);
56
84
  break;
57
85
  case "both":
58
- n = s.scrollWidth > t.clientWidth || (e ? s.scrollHeight > e : s.scrollHeight > (t.clientHeight || t.offsetHeight));
86
+ r = l.scrollWidth > e.clientWidth || (o ? l.scrollHeight > o : l.scrollHeight > (e.clientHeight || e.offsetHeight));
59
87
  break;
60
88
  }
61
- return document.body.removeChild(s), n;
89
+ return document.body.removeChild(l), r;
62
90
  }
63
- switch (l) {
91
+ switch (t) {
64
92
  case "horizontal":
65
- return h(t);
93
+ return u(e);
66
94
  case "vertical":
67
- return u(t, e);
95
+ return y(e, o);
68
96
  case "both":
69
- return h(t) || u(t, e);
97
+ return u(e) || y(e, o);
70
98
  default:
71
- return h(t);
99
+ return u(e);
72
100
  }
73
101
  };
74
102
  export {
75
- W as calculateAvailableHeight,
76
- h as checkHorizontalOverflow,
77
- w as checkOverflow,
78
- u as checkVerticalOverflow,
79
- v as createMeasurementClone,
80
- H as getMiddleTruncatedString,
81
- b as measureText
103
+ S as calculateAvailableHeight,
104
+ u as checkHorizontalOverflow,
105
+ E as checkOverflow,
106
+ y as checkVerticalOverflow,
107
+ C as createMeasurementClone,
108
+ M as getMiddleTruncatedString,
109
+ W as measureText
82
110
  };
83
111
  //# sourceMappingURL=helpers.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.js","sources":["../../../src/components/truncate/helpers.ts"],"sourcesContent":["/**\n * Finds the closest parent element that has inline flex display\n * @param element - The starting HTMLElement\n * @returns The closest inline flex parent or null if not found\n */\nexport function findClosestInlineFlexParent(\n element: HTMLElement\n): HTMLElement | null {\n if (!element) return null;\n\n let currentElement = element.parentElement;\n let attempts = 0;\n const maxAttempts = 10;\n\n while (currentElement && attempts < maxAttempts) {\n const computedStyle = window.getComputedStyle(currentElement);\n\n // Check if display is inline-flex\n if (computedStyle.display === \"inline-flex\") {\n return currentElement;\n }\n\n // Move to the next parent\n currentElement = currentElement.parentElement;\n attempts++;\n }\n\n return null;\n}\n\nexport function getFlexRemainingSpace(\n flexContainer: HTMLElement,\n ignoreElement: HTMLElement | ((e: HTMLElement) => boolean) = null\n) {\n const style = window.getComputedStyle(flexContainer);\n const isRow =\n style.flexDirection === \"row\" || style.flexDirection === \"row-reverse\";\n const containerSize = isRow\n ? flexContainer.offsetWidth\n : flexContainer.offsetHeight;\n const gap = parseFloat(style.gap) || 0;\n const paddingStart =\n parseFloat(isRow ? style.paddingLeft : style.paddingTop) || 0;\n const paddingEnd =\n parseFloat(isRow ? style.paddingRight : style.paddingBottom) || 0;\n\n let totalItemSize = 0;\n const items: HTMLElement[] = (\n [...flexContainer.children] as HTMLElement[]\n ).filter(\n (child) =>\n child instanceof HTMLElement &&\n (typeof ignoreElement === \"function\"\n ? !ignoreElement(child)\n : child !== ignoreElement)\n );\n for (let i = 0; i < items.length; i++) {\n const itemStyle = window.getComputedStyle(items[i]);\n const marginStart =\n parseFloat(isRow ? itemStyle.marginLeft : itemStyle.marginTop) || 0;\n const marginEnd =\n parseFloat(isRow ? itemStyle.marginRight : itemStyle.marginBottom) || 0;\n totalItemSize +=\n (isRow ? items[i].offsetWidth : items[i].offsetHeight) +\n marginStart +\n marginEnd;\n }\n\n // Add gaps (n-1 gaps for n items)\n totalItemSize += gap * (items.length - 1);\n\n // Calculate remaining space\n const remainingSpace =\n containerSize - totalItemSize - paddingStart - paddingEnd;\n return Math.max(0, remainingSpace);\n}\n\nexport const measureText = (container: HTMLElement) => {\n const span = document.createElement(\"span\");\n span.style.opacity = \"0\";\n span.style.position = \"absolute\";\n span.style.top = \"-1000px\";\n span.style.left = \"-1000px\";\n span.style.whiteSpace = \"nowrap\";\n span.style.pointerEvents = \"none\";\n\n const nodeRect = container.getBoundingClientRect();\n\n const containerClone = container.cloneNode(true) as HTMLElement;\n containerClone.style.maxWidth = `${nodeRect.width}px`;\n containerClone.style.position = \"absolute\";\n containerClone.style.pointerEvents = \"none\";\n containerClone.style.top = \"-99px\";\n containerClone.style.left = \"-99px\";\n containerClone.style.zIndex = \"-1\";\n\n container.parentElement?.appendChild(containerClone);\n containerClone.appendChild(span);\n\n return {\n measure: (text: string) => {\n span.innerText = text;\n return span.clientWidth;\n },\n destroy: () => {\n containerClone.removeChild(span);\n container.parentElement?.removeChild(containerClone);\n },\n };\n};\n\nexport const getMiddleTruncatedString = (\n text: string,\n ellipsis: string,\n container: HTMLElement\n): string => {\n if (!text) return text;\n\n const { measure: getTextWidth, destroy: destroyMeasure } =\n measureText(container);\n\n const textWidth = getTextWidth(text);\n\n const containerWidthToCompare = container.clientWidth;\n\n const initialOffset = Math.floor(\n (containerWidthToCompare / textWidth) * text.length\n );\n\n if (textWidth <= containerWidthToCompare) {\n destroyMeasure();\n return text;\n }\n\n let offset = initialOffset;\n const attempts: Record<number, [number, string]> = {};\n const maxAttempts = 20;\n const buffer = 4;\n\n while (Object.values(attempts).length <= maxAttempts) {\n // If we have already tried this offset, stop\n if (attempts[offset]) break;\n\n // If we are at the beginning of the string, just return the ellipsis\n if (offset <= 1) {\n attempts[0] = [0, ellipsis];\n break;\n }\n\n const start = text\n .slice(0, Math.ceil((offset - ellipsis.length) / 2 - 1))\n .trimEnd();\n const end = text\n .slice(Math.floor((offset - ellipsis.length) / 2) - offset)\n .trimStart();\n const truncatedStr = start + ellipsis + end;\n const width = getTextWidth(truncatedStr);\n\n attempts[offset] = [width, truncatedStr];\n\n if (width >= containerWidthToCompare) {\n offset = offset - 2;\n } else {\n // If we are close to the container width, stop\n if (containerWidthToCompare - width < buffer) break;\n offset = offset + 2;\n }\n }\n\n // Remove the span element used for measuring text\n destroyMeasure();\n\n // Find the closest attempt that is smaller than the container width\n return (\n Object.values(attempts)\n .reverse()\n .find(([width]) => width < containerWidthToCompare)?.[1] ??\n Object.values(attempts)[0][1]\n );\n};\n\n// Utility functions for measurements\nexport const createMeasurementClone = (\n text: string,\n container: HTMLElement\n) => {\n const styles = window.getComputedStyle(container);\n const clone = document.createElement(\"span\");\n clone.style.visibility = \"hidden\";\n clone.style.position = \"absolute\";\n clone.style.width = `${container.clientWidth}px`;\n clone.style.fontSize = styles.fontSize;\n clone.style.fontFamily = styles.fontFamily;\n clone.style.lineHeight = styles.lineHeight;\n clone.textContent = text;\n document.body.appendChild(clone);\n return clone;\n};\n\nexport const checkIfTextTruncated = (\n text: string,\n container: HTMLElement,\n availableHeight?: number\n) => {\n // Use the new checkOverflow utility for consistency\n return checkOverflow(container, {\n type: \"vertical\",\n availableHeight,\n text,\n });\n};\n\nexport const calculateAvailableHeight = (container: HTMLElement) => {\n const parent = container.parentElement;\n if (!parent) return 0;\n\n const parentStyles = window.getComputedStyle(parent);\n const paddingTop = parseFloat(parentStyles.paddingTop) || 0;\n const paddingBottom = parseFloat(parentStyles.paddingBottom) || 0;\n const borderTopWidth = parseFloat(parentStyles.borderTopWidth) || 0;\n const borderBottomWidth = parseFloat(parentStyles.borderBottomWidth) || 0;\n\n return (\n parent.clientHeight -\n paddingTop -\n paddingBottom -\n borderTopWidth -\n borderBottomWidth\n );\n};\n\nexport const calculateAvailableWidth = (container: HTMLElement) => {\n const parent = container.parentElement;\n if (!parent) return 0;\n\n const parentStyles = window.getComputedStyle(parent);\n const paddingLeft = parseFloat(parentStyles.paddingLeft) || 0;\n const paddingRight = parseFloat(parentStyles.paddingRight) || 0;\n const borderLeftWidth = parseFloat(parentStyles.borderLeftWidth) || 0;\n const borderRightWidth = parseFloat(parentStyles.borderRightWidth) || 0;\n\n return (\n parent.clientWidth -\n paddingLeft -\n paddingRight -\n borderLeftWidth -\n borderRightWidth\n );\n};\n\n// Overflow detection utilities\nexport type OverflowType = \"horizontal\" | \"vertical\" | \"both\";\n\nexport interface OverflowCheckOptions {\n type?: OverflowType;\n availableHeight?: number;\n text?: string;\n}\n\nexport const checkHorizontalOverflow = (container: HTMLElement): boolean => {\n return container.scrollWidth > container.clientWidth;\n};\n\nexport const checkVerticalOverflow = (\n container: HTMLElement,\n availableHeight?: number\n): boolean => {\n const compareHeight = availableHeight ?? (container.clientHeight || container.offsetHeight);\n return container.scrollHeight > compareHeight;\n};\n\nexport const checkOverflow = (\n container: HTMLElement,\n options: OverflowCheckOptions = {}\n): boolean => {\n const { type = \"horizontal\", availableHeight, text } = options;\n\n // If text is provided, use measurement clone for more accurate detection\n if (text) {\n const clone = createMeasurementClone(text, container);\n let isOverflowing = false;\n\n switch (type) {\n case \"horizontal\":\n isOverflowing = clone.scrollWidth > container.clientWidth;\n break;\n case \"vertical\":\n isOverflowing = availableHeight\n ? clone.scrollHeight > availableHeight\n : clone.scrollHeight > (container.clientHeight || container.offsetHeight);\n break;\n case \"both\":\n isOverflowing =\n clone.scrollWidth > container.clientWidth ||\n (availableHeight\n ? clone.scrollHeight > availableHeight\n : clone.scrollHeight > (container.clientHeight || container.offsetHeight));\n break;\n }\n\n document.body.removeChild(clone);\n return isOverflowing;\n }\n\n // Use direct container measurements\n switch (type) {\n case \"horizontal\":\n return checkHorizontalOverflow(container);\n case \"vertical\":\n return checkVerticalOverflow(container, availableHeight);\n case \"both\":\n return checkHorizontalOverflow(container) || checkVerticalOverflow(container, availableHeight);\n default:\n return checkHorizontalOverflow(container);\n }\n};\n"],"names":["measureText","container","span","nodeRect","containerClone","text","getMiddleTruncatedString","ellipsis","getTextWidth","destroyMeasure","textWidth","containerWidthToCompare","initialOffset","offset","attempts","maxAttempts","buffer","start","end","truncatedStr","width","createMeasurementClone","styles","clone","calculateAvailableHeight","parent","parentStyles","paddingTop","paddingBottom","borderTopWidth","borderBottomWidth","checkHorizontalOverflow","checkVerticalOverflow","availableHeight","compareHeight","checkOverflow","options","type","isOverflowing"],"mappings":"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"}
1
+ {"version":3,"file":"helpers.js","sources":["../../../src/components/truncate/helpers.ts"],"sourcesContent":["import React from \"react\";\nimport { renderToStaticMarkup } from \"react-dom/server\";\n\n/**\n * Finds the closest parent element that has inline flex display\n * @param element - The starting HTMLElement\n * @returns The closest inline flex parent or null if not found\n */\nexport function findClosestInlineFlexParent(\n element: HTMLElement\n): HTMLElement | null {\n if (!element) return null;\n\n let currentElement = element.parentElement;\n let attempts = 0;\n const maxAttempts = 10;\n\n while (currentElement && attempts < maxAttempts) {\n const computedStyle = window.getComputedStyle(currentElement);\n\n // Check if display is inline-flex\n if (computedStyle.display === \"inline-flex\") {\n return currentElement;\n }\n\n // Move to the next parent\n currentElement = currentElement.parentElement;\n attempts++;\n }\n\n return null;\n}\n\nexport function getFlexRemainingSpace(\n flexContainer: HTMLElement,\n ignoreElement: HTMLElement | ((e: HTMLElement) => boolean) = null\n) {\n const style = window.getComputedStyle(flexContainer);\n const isRow =\n style.flexDirection === \"row\" || style.flexDirection === \"row-reverse\";\n const containerSize = isRow\n ? flexContainer.offsetWidth\n : flexContainer.offsetHeight;\n const gap = parseFloat(style.gap) || 0;\n const paddingStart =\n parseFloat(isRow ? style.paddingLeft : style.paddingTop) || 0;\n const paddingEnd =\n parseFloat(isRow ? style.paddingRight : style.paddingBottom) || 0;\n\n let totalItemSize = 0;\n const items: HTMLElement[] = (\n [...flexContainer.children] as HTMLElement[]\n ).filter(\n (child) =>\n child instanceof HTMLElement &&\n (typeof ignoreElement === \"function\"\n ? !ignoreElement(child)\n : child !== ignoreElement)\n );\n for (let i = 0; i < items.length; i++) {\n const itemStyle = window.getComputedStyle(items[i]);\n const marginStart =\n parseFloat(isRow ? itemStyle.marginLeft : itemStyle.marginTop) || 0;\n const marginEnd =\n parseFloat(isRow ? itemStyle.marginRight : itemStyle.marginBottom) || 0;\n totalItemSize +=\n (isRow ? items[i].offsetWidth : items[i].offsetHeight) +\n marginStart +\n marginEnd;\n }\n\n // Add gaps (n-1 gaps for n items)\n totalItemSize += gap * (items.length - 1);\n\n // Calculate remaining space\n const remainingSpace =\n containerSize - totalItemSize - paddingStart - paddingEnd;\n return Math.max(0, remainingSpace);\n}\n\n// Cache for measurement results to optimize performance (only used with renderText)\nconst measurementCache = new Map<string, number>();\n\n/**\n * Measures text width with optional render function for styled content\n * @param container - The container element to clone styles from\n * @param renderText - Optional render function to apply styling to text before measurement\n */\nexport const measureText = (\n container: HTMLElement,\n renderText?: ((text: string) => React.ReactNode) | null\n) => {\n const span = document.createElement(\"span\");\n span.style.opacity = \"0\";\n span.style.position = \"absolute\";\n span.style.top = \"-1000px\";\n span.style.left = \"-1000px\";\n span.style.whiteSpace = \"nowrap\";\n span.style.pointerEvents = \"none\";\n\n const nodeRect = container.getBoundingClientRect();\n\n const containerClone = container.cloneNode(true) as HTMLElement;\n containerClone.style.maxWidth = `${nodeRect.width}px`;\n containerClone.style.position = \"absolute\";\n containerClone.style.pointerEvents = \"none\";\n containerClone.style.top = \"-99px\";\n containerClone.style.left = \"-99px\";\n containerClone.style.zIndex = \"-1\";\n\n container.parentElement?.appendChild(containerClone);\n containerClone.appendChild(span);\n\n return {\n measure: (text: string) => {\n // Only use cache when measuring styled content (renderText case)\n // This preserves original behavior for default case\n if (renderText) {\n const cacheKey = `${text}-styled`;\n\n // Check cache first\n if (measurementCache.has(cacheKey)) {\n return measurementCache.get(cacheKey)!;\n }\n\n // Clear previous content\n span.innerHTML = \"\";\n\n // Render the content using renderText\n const rendered = renderText(text);\n\n // Handle different types of rendered content\n if (typeof rendered === \"string\") {\n // Simple string - just set innerHTML\n span.innerHTML = rendered;\n } else if (React.isValidElement(rendered)) {\n // React element - use renderToStaticMarkup for synchronous rendering\n try {\n const html = renderToStaticMarkup(rendered);\n span.innerHTML = html;\n } catch (_e) {\n // Fallback to plain text if renderToStaticMarkup fails\n span.innerText = text;\n }\n } else {\n // Fallback to plain text\n span.innerText = text;\n }\n\n const width = span.clientWidth;\n\n // Cache the result\n measurementCache.set(cacheKey, width);\n\n // Limit cache size to prevent memory leaks\n if (measurementCache.size > 100) {\n const firstKey = measurementCache.keys().next().value;\n measurementCache.delete(firstKey);\n }\n\n return width;\n }\n\n // Original behavior when renderText is not provided\n span.innerText = text;\n return span.clientWidth;\n },\n destroy: () => {\n containerClone.removeChild(span);\n container.parentElement?.removeChild(containerClone);\n },\n };\n};\n\nexport const getMiddleTruncatedString = (\n text: string,\n ellipsis: string,\n container: HTMLElement,\n renderText?: ((text: string) => React.ReactNode) | null\n): string => {\n if (!text) return text;\n\n const { measure: getTextWidth, destroy: destroyMeasure } =\n measureText(container, renderText);\n\n const textWidth = getTextWidth(text);\n\n const containerWidthToCompare = container.clientWidth;\n\n const initialOffset = Math.floor(\n (containerWidthToCompare / textWidth) * text.length\n );\n\n if (textWidth <= containerWidthToCompare) {\n destroyMeasure();\n return text;\n }\n\n let offset = initialOffset;\n const attempts: Record<number, [number, string]> = {};\n const maxAttempts = 20;\n const buffer = 4;\n\n while (Object.values(attempts).length <= maxAttempts) {\n // If we have already tried this offset, stop\n if (attempts[offset]) break;\n\n // If we are at the beginning of the string, just return the ellipsis\n if (offset <= 1) {\n attempts[0] = [0, ellipsis];\n break;\n }\n\n const start = text\n .slice(0, Math.ceil((offset - ellipsis.length) / 2 - 1))\n .trimEnd();\n const end = text\n .slice(Math.floor((offset - ellipsis.length) / 2) - offset)\n .trimStart();\n const truncatedStr = start + ellipsis + end;\n const width = getTextWidth(truncatedStr);\n\n attempts[offset] = [width, truncatedStr];\n\n if (width >= containerWidthToCompare) {\n offset = offset - 2;\n } else {\n // If we are close to the container width, stop\n if (containerWidthToCompare - width < buffer) break;\n offset = offset + 2;\n }\n }\n\n // Remove the span element used for measuring text\n destroyMeasure();\n\n // Find the closest attempt that is smaller than the container width\n return (\n Object.values(attempts)\n .reverse()\n .find(([width]) => width < containerWidthToCompare)?.[1] ??\n Object.values(attempts)[0][1]\n );\n};\n\n// Utility functions for measurements\nexport const createMeasurementClone = (\n text: string,\n container: HTMLElement\n) => {\n const styles = window.getComputedStyle(container);\n const clone = document.createElement(\"span\");\n clone.style.visibility = \"hidden\";\n clone.style.position = \"absolute\";\n clone.style.width = `${container.clientWidth}px`;\n clone.style.fontSize = styles.fontSize;\n clone.style.fontFamily = styles.fontFamily;\n clone.style.lineHeight = styles.lineHeight;\n clone.textContent = text;\n document.body.appendChild(clone);\n return clone;\n};\n\nexport const checkIfTextTruncated = (\n text: string,\n container: HTMLElement,\n availableHeight?: number\n) => {\n // Use the new checkOverflow utility for consistency\n return checkOverflow(container, {\n type: \"vertical\",\n availableHeight,\n text,\n });\n};\n\nexport const calculateAvailableHeight = (container: HTMLElement) => {\n const parent = container.parentElement;\n if (!parent) return 0;\n\n const parentStyles = window.getComputedStyle(parent);\n const paddingTop = parseFloat(parentStyles.paddingTop) || 0;\n const paddingBottom = parseFloat(parentStyles.paddingBottom) || 0;\n const borderTopWidth = parseFloat(parentStyles.borderTopWidth) || 0;\n const borderBottomWidth = parseFloat(parentStyles.borderBottomWidth) || 0;\n\n return (\n parent.clientHeight -\n paddingTop -\n paddingBottom -\n borderTopWidth -\n borderBottomWidth\n );\n};\n\nexport const calculateAvailableWidth = (container: HTMLElement) => {\n const parent = container.parentElement;\n if (!parent) return 0;\n\n const parentStyles = window.getComputedStyle(parent);\n const paddingLeft = parseFloat(parentStyles.paddingLeft) || 0;\n const paddingRight = parseFloat(parentStyles.paddingRight) || 0;\n const borderLeftWidth = parseFloat(parentStyles.borderLeftWidth) || 0;\n const borderRightWidth = parseFloat(parentStyles.borderRightWidth) || 0;\n\n return (\n parent.clientWidth -\n paddingLeft -\n paddingRight -\n borderLeftWidth -\n borderRightWidth\n );\n};\n\n// Overflow detection utilities\nexport type OverflowType = \"horizontal\" | \"vertical\" | \"both\";\n\nexport interface OverflowCheckOptions {\n type?: OverflowType;\n availableHeight?: number;\n text?: string;\n}\n\nexport const checkHorizontalOverflow = (container: HTMLElement): boolean => {\n return container.scrollWidth > container.clientWidth;\n};\n\nexport const checkVerticalOverflow = (\n container: HTMLElement,\n availableHeight?: number\n): boolean => {\n const compareHeight = availableHeight ?? (container.clientHeight || container.offsetHeight);\n return container.scrollHeight > compareHeight;\n};\n\nexport const checkOverflow = (\n container: HTMLElement,\n options: OverflowCheckOptions = {}\n): boolean => {\n const { type = \"horizontal\", availableHeight, text } = options;\n\n // If text is provided, use measurement clone for more accurate detection\n if (text) {\n const clone = createMeasurementClone(text, container);\n let isOverflowing = false;\n\n switch (type) {\n case \"horizontal\":\n isOverflowing = clone.scrollWidth > container.clientWidth;\n break;\n case \"vertical\":\n isOverflowing = availableHeight\n ? clone.scrollHeight > availableHeight\n : clone.scrollHeight > (container.clientHeight || container.offsetHeight);\n break;\n case \"both\":\n isOverflowing =\n clone.scrollWidth > container.clientWidth ||\n (availableHeight\n ? clone.scrollHeight > availableHeight\n : clone.scrollHeight > (container.clientHeight || container.offsetHeight));\n break;\n }\n\n document.body.removeChild(clone);\n return isOverflowing;\n }\n\n // Use direct container measurements\n switch (type) {\n case \"horizontal\":\n return checkHorizontalOverflow(container);\n case \"vertical\":\n return checkVerticalOverflow(container, availableHeight);\n case \"both\":\n return checkHorizontalOverflow(container) || checkVerticalOverflow(container, availableHeight);\n default:\n return checkHorizontalOverflow(container);\n }\n};\n"],"names":["measurementCache","measureText","container","renderText","span","nodeRect","containerClone","text","cacheKey","rendered","React","html","renderToStaticMarkup","width","firstKey","getMiddleTruncatedString","ellipsis","getTextWidth","destroyMeasure","textWidth","containerWidthToCompare","initialOffset","offset","attempts","maxAttempts","buffer","start","end","truncatedStr","createMeasurementClone","styles","clone","calculateAvailableHeight","parent","parentStyles","paddingTop","paddingBottom","borderTopWidth","borderBottomWidth","checkHorizontalOverflow","checkVerticalOverflow","availableHeight","compareHeight","checkOverflow","options","type","isOverflowing"],"mappings":"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"}