@trackunit/react-table-base-components 2.6.54 → 2.7.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.
package/index.cjs.js CHANGED
@@ -1152,24 +1152,50 @@ const cvaTd = cssClassVarianceUtilities.cva({
1152
1152
  ],
1153
1153
  });
1154
1154
 
1155
- const cvaTextCell = cssClassVarianceUtilities.cva({ base: ["truncate", "text-sm"] });
1155
+ // The clamp is `!important` (and the variant adds no display utility of its own) because a display
1156
+ // class like `block` would otherwise win the cascade over line-clamp's `display: -webkit-box` —
1157
+ // in this workspace's compiled Tailwind output, `.block` etc. are declared after `.line-clamp-2` —
1158
+ // silently disabling the two-line truncation. tailwind-merge does not treat display and line-clamp
1159
+ // as conflicting, so a consumer-passed display utility survives the merge; the important modifier
1160
+ // makes the clamp win regardless.
1161
+ const cvaTextCell = cssClassVarianceUtilities.cva({
1162
+ base: ["text-sm"],
1163
+ variants: {
1164
+ variant: {
1165
+ default: ["truncate"],
1166
+ multiline: ["!line-clamp-2", "overflow-hidden", "whitespace-normal"],
1167
+ },
1168
+ },
1169
+ defaultVariants: {
1170
+ variant: "default",
1171
+ },
1172
+ });
1156
1173
  const cvaTextCellTooltip = cssClassVarianceUtilities.cva({ base: ["grid"] });
1157
1174
 
1158
1175
  /**
1159
1176
  * The `<TextCell>` component is used for displaying text in a table cell.
1160
- * The text is not editable and will be truncated with an ellipsis if the cell is too narrow.
1177
+ * The text is not editable and will be truncated if the cell is too small:
1178
+ * by default with an ellipsis on a single line, or clamped after two lines with `variant="multiline"`.
1161
1179
  * When truncated, a tooltip automatically appears on hover showing the full content.
1180
+ * Truncation is re-evaluated when the cell is resized (e.g. a resizable table column).
1162
1181
  *
1163
1182
  * ### When to use
1164
1183
  * - Display simple text values in table columns (names, descriptions, statuses)
1165
1184
  * - When text content might be longer than the column width
1166
1185
  * - For read-only text display in data grids
1186
+ * - For longer free-form text (notes, descriptions) - use `variant="multiline"` to show two lines before clamping
1167
1187
  *
1168
1188
  * ### When not to use
1169
1189
  * - For numeric values - use `NumberCell` instead
1170
1190
  * - For dates and times - use `DateTimeCell` instead
1171
1191
  * - For clickable links - use `LinkCell` or `ButtonCell` instead
1172
- * - For multi-line content - consider `IdentityCell` with details
1192
+ * - For structured multi-row content - consider `IdentityCell` or `MultiRowTableCell` instead
1193
+ *
1194
+ * ### Caveats
1195
+ * - The multiline clamp declarations are marked important, so a display utility (`block`, `flex`, ...)
1196
+ * passed through `className` cannot disable the clamp — but still avoid passing display utilities.
1197
+ * - The multiline clamp only just fits a default `Table` row (`rowHeight` 50 with `Td`'s
1198
+ * `py-table-spacing`). Prefer not to increase font size via `className`.
1173
1199
  *
1174
1200
  * @example Basic usage
1175
1201
  * ```tsx
@@ -1195,16 +1221,28 @@ const cvaTextCellTooltip = cssClassVarianceUtilities.cva({ base: ["grid"] });
1195
1221
  * // ReactElement content
1196
1222
  * <TextCell content={<span className="font-bold">Bold text</span>} />
1197
1223
  * ```
1224
+ * @example Multiline clamp for longer text
1225
+ * ```tsx
1226
+ * import { TextCell } from "@trackunit/react-table-base-components";
1227
+ *
1228
+ * // Wraps onto two lines, then clamps; tooltip shows the full note on hover
1229
+ * <TextCell content={note} variant="multiline" />
1230
+ * ```
1198
1231
  * @param {TextCellProps} props - The props for the TextCell component
1199
1232
  * @returns {ReactElement} TextCell component
1200
1233
  */
1201
- const TextCell = ({ content = "", className, "data-testid": dataTestId, ref, style }) => {
1202
- const [isTooltipVisible, setIsTooltipVisible] = react.useState(false);
1203
- const updateTooltipVisibility = (element) => {
1204
- setIsTooltipVisible(element ? element.scrollWidth > element.clientWidth : false);
1205
- };
1206
- const mergedRef = reactComponents.useMergeRefs([ref, (elementRef) => updateTooltipVisibility(elementRef)]);
1207
- return (jsxRuntime.jsx(reactComponents.Tooltip, { disabled: !isTooltipVisible, label: content, placement: "bottom", children: jsxRuntime.jsx("div", { className: cvaTextCellTooltip(), children: jsxRuntime.jsx("span", { className: cvaTextCell({ className }), "data-testid": dataTestId, ref: mergedRef, style: style, children: content }) }) }));
1234
+ const TextCell = ({ content = "", className, "data-testid": dataTestId, ref, style, variant = "default", }) => {
1235
+ const { element, geometry, ref: measureRef } = reactComponents.useMeasure();
1236
+ // Reading scroll/client metrics during render keeps the tooltip in sync with resizes:
1237
+ // useMeasure's ResizeObserver re-renders on geometry changes, so overflow introduced by
1238
+ // narrowing a resizable column is picked up (a mount-only ref measurement would not be).
1239
+ const isTooltipVisible = element !== null &&
1240
+ geometry !== undefined &&
1241
+ (variant === "multiline"
1242
+ ? element.scrollHeight > element.clientHeight
1243
+ : element.scrollWidth > element.clientWidth);
1244
+ const mergedRef = reactComponents.useMergeRefs([ref, measureRef]);
1245
+ return (jsxRuntime.jsx(reactComponents.Tooltip, { disabled: !isTooltipVisible, label: content, placement: "bottom", children: jsxRuntime.jsx("div", { className: cvaTextCellTooltip(), children: jsxRuntime.jsx("span", { className: cvaTextCell({ className, variant }), "data-testid": dataTestId, ref: mergedRef, style: style, children: content }) }) }));
1208
1246
  };
1209
1247
 
1210
1248
  /**
package/index.esm.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { jsx, jsxs } from 'react/jsx-runtime';
2
2
  import { registerTranslations, useNamespaceTranslation } from '@trackunit/i18n-library-translation';
3
- import { Button, Icon, CopyableText, Highlight, Heading, ExternalLink, DetailsList, Indicator, Spinner, Tooltip, Text, Tag, Notice, IconButton, MoreMenu, MenuContent, MenuItem, MenuDivider, useMergeRefs } from '@trackunit/react-components';
3
+ import { Button, Icon, CopyableText, Highlight, Heading, ExternalLink, DetailsList, Indicator, Spinner, Tooltip, Text, Tag, Notice, IconButton, MoreMenu, MenuContent, MenuItem, MenuDivider, useMeasure, useMergeRefs } from '@trackunit/react-components';
4
4
  import { twMerge } from 'tailwind-merge';
5
5
  import { Checkbox } from '@trackunit/react-form-components';
6
6
  import { cva } from '@trackunit/css-class-variance-utilities';
@@ -1150,24 +1150,50 @@ const cvaTd = cva({
1150
1150
  ],
1151
1151
  });
1152
1152
 
1153
- const cvaTextCell = cva({ base: ["truncate", "text-sm"] });
1153
+ // The clamp is `!important` (and the variant adds no display utility of its own) because a display
1154
+ // class like `block` would otherwise win the cascade over line-clamp's `display: -webkit-box` —
1155
+ // in this workspace's compiled Tailwind output, `.block` etc. are declared after `.line-clamp-2` —
1156
+ // silently disabling the two-line truncation. tailwind-merge does not treat display and line-clamp
1157
+ // as conflicting, so a consumer-passed display utility survives the merge; the important modifier
1158
+ // makes the clamp win regardless.
1159
+ const cvaTextCell = cva({
1160
+ base: ["text-sm"],
1161
+ variants: {
1162
+ variant: {
1163
+ default: ["truncate"],
1164
+ multiline: ["!line-clamp-2", "overflow-hidden", "whitespace-normal"],
1165
+ },
1166
+ },
1167
+ defaultVariants: {
1168
+ variant: "default",
1169
+ },
1170
+ });
1154
1171
  const cvaTextCellTooltip = cva({ base: ["grid"] });
1155
1172
 
1156
1173
  /**
1157
1174
  * The `<TextCell>` component is used for displaying text in a table cell.
1158
- * The text is not editable and will be truncated with an ellipsis if the cell is too narrow.
1175
+ * The text is not editable and will be truncated if the cell is too small:
1176
+ * by default with an ellipsis on a single line, or clamped after two lines with `variant="multiline"`.
1159
1177
  * When truncated, a tooltip automatically appears on hover showing the full content.
1178
+ * Truncation is re-evaluated when the cell is resized (e.g. a resizable table column).
1160
1179
  *
1161
1180
  * ### When to use
1162
1181
  * - Display simple text values in table columns (names, descriptions, statuses)
1163
1182
  * - When text content might be longer than the column width
1164
1183
  * - For read-only text display in data grids
1184
+ * - For longer free-form text (notes, descriptions) - use `variant="multiline"` to show two lines before clamping
1165
1185
  *
1166
1186
  * ### When not to use
1167
1187
  * - For numeric values - use `NumberCell` instead
1168
1188
  * - For dates and times - use `DateTimeCell` instead
1169
1189
  * - For clickable links - use `LinkCell` or `ButtonCell` instead
1170
- * - For multi-line content - consider `IdentityCell` with details
1190
+ * - For structured multi-row content - consider `IdentityCell` or `MultiRowTableCell` instead
1191
+ *
1192
+ * ### Caveats
1193
+ * - The multiline clamp declarations are marked important, so a display utility (`block`, `flex`, ...)
1194
+ * passed through `className` cannot disable the clamp — but still avoid passing display utilities.
1195
+ * - The multiline clamp only just fits a default `Table` row (`rowHeight` 50 with `Td`'s
1196
+ * `py-table-spacing`). Prefer not to increase font size via `className`.
1171
1197
  *
1172
1198
  * @example Basic usage
1173
1199
  * ```tsx
@@ -1193,16 +1219,28 @@ const cvaTextCellTooltip = cva({ base: ["grid"] });
1193
1219
  * // ReactElement content
1194
1220
  * <TextCell content={<span className="font-bold">Bold text</span>} />
1195
1221
  * ```
1222
+ * @example Multiline clamp for longer text
1223
+ * ```tsx
1224
+ * import { TextCell } from "@trackunit/react-table-base-components";
1225
+ *
1226
+ * // Wraps onto two lines, then clamps; tooltip shows the full note on hover
1227
+ * <TextCell content={note} variant="multiline" />
1228
+ * ```
1196
1229
  * @param {TextCellProps} props - The props for the TextCell component
1197
1230
  * @returns {ReactElement} TextCell component
1198
1231
  */
1199
- const TextCell = ({ content = "", className, "data-testid": dataTestId, ref, style }) => {
1200
- const [isTooltipVisible, setIsTooltipVisible] = useState(false);
1201
- const updateTooltipVisibility = (element) => {
1202
- setIsTooltipVisible(element ? element.scrollWidth > element.clientWidth : false);
1203
- };
1204
- const mergedRef = useMergeRefs([ref, (elementRef) => updateTooltipVisibility(elementRef)]);
1205
- return (jsx(Tooltip, { disabled: !isTooltipVisible, label: content, placement: "bottom", children: jsx("div", { className: cvaTextCellTooltip(), children: jsx("span", { className: cvaTextCell({ className }), "data-testid": dataTestId, ref: mergedRef, style: style, children: content }) }) }));
1232
+ const TextCell = ({ content = "", className, "data-testid": dataTestId, ref, style, variant = "default", }) => {
1233
+ const { element, geometry, ref: measureRef } = useMeasure();
1234
+ // Reading scroll/client metrics during render keeps the tooltip in sync with resizes:
1235
+ // useMeasure's ResizeObserver re-renders on geometry changes, so overflow introduced by
1236
+ // narrowing a resizable column is picked up (a mount-only ref measurement would not be).
1237
+ const isTooltipVisible = element !== null &&
1238
+ geometry !== undefined &&
1239
+ (variant === "multiline"
1240
+ ? element.scrollHeight > element.clientHeight
1241
+ : element.scrollWidth > element.clientWidth);
1242
+ const mergedRef = useMergeRefs([ref, measureRef]);
1243
+ return (jsx(Tooltip, { disabled: !isTooltipVisible, label: content, placement: "bottom", children: jsx("div", { className: cvaTextCellTooltip(), children: jsx("span", { className: cvaTextCell({ className, variant }), "data-testid": dataTestId, ref: mergedRef, style: style, children: content }) }) }));
1206
1244
  };
1207
1245
 
1208
1246
  /**
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entry.js","sourceRoot":"","sources":["../../../../../libs/react/table-base-components/migrations/entry.ts"],"names":[],"mappings":"","sourcesContent":["export {};\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trackunit/react-table-base-components",
3
- "version": "2.6.54",
3
+ "version": "2.7.0",
4
4
  "repository": "https://github.com/Trackunit/manager",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "engines": {
@@ -7,22 +7,38 @@ export interface TextCellProps extends CommonProps, Refable<HTMLSpanElement>, St
7
7
  * and a tooltip will appear on hover showing the full content.
8
8
  */
9
9
  content?: string | ReactElement;
10
+ /**
11
+ * Visual variant of the cell. Defaults to `"default"`.
12
+ * - `"default"`: single line, truncated with an ellipsis on horizontal overflow.
13
+ * - `"multiline"`: wraps and clamps after two lines on vertical overflow.
14
+ * Either way, a tooltip with the full content appears on hover when truncated.
15
+ */
16
+ variant?: "default" | "multiline";
10
17
  }
11
18
  /**
12
19
  * The `<TextCell>` component is used for displaying text in a table cell.
13
- * The text is not editable and will be truncated with an ellipsis if the cell is too narrow.
20
+ * The text is not editable and will be truncated if the cell is too small:
21
+ * by default with an ellipsis on a single line, or clamped after two lines with `variant="multiline"`.
14
22
  * When truncated, a tooltip automatically appears on hover showing the full content.
23
+ * Truncation is re-evaluated when the cell is resized (e.g. a resizable table column).
15
24
  *
16
25
  * ### When to use
17
26
  * - Display simple text values in table columns (names, descriptions, statuses)
18
27
  * - When text content might be longer than the column width
19
28
  * - For read-only text display in data grids
29
+ * - For longer free-form text (notes, descriptions) - use `variant="multiline"` to show two lines before clamping
20
30
  *
21
31
  * ### When not to use
22
32
  * - For numeric values - use `NumberCell` instead
23
33
  * - For dates and times - use `DateTimeCell` instead
24
34
  * - For clickable links - use `LinkCell` or `ButtonCell` instead
25
- * - For multi-line content - consider `IdentityCell` with details
35
+ * - For structured multi-row content - consider `IdentityCell` or `MultiRowTableCell` instead
36
+ *
37
+ * ### Caveats
38
+ * - The multiline clamp declarations are marked important, so a display utility (`block`, `flex`, ...)
39
+ * passed through `className` cannot disable the clamp — but still avoid passing display utilities.
40
+ * - The multiline clamp only just fits a default `Table` row (`rowHeight` 50 with `Td`'s
41
+ * `py-table-spacing`). Prefer not to increase font size via `className`.
26
42
  *
27
43
  * @example Basic usage
28
44
  * ```tsx
@@ -48,7 +64,14 @@ export interface TextCellProps extends CommonProps, Refable<HTMLSpanElement>, St
48
64
  * // ReactElement content
49
65
  * <TextCell content={<span className="font-bold">Bold text</span>} />
50
66
  * ```
67
+ * @example Multiline clamp for longer text
68
+ * ```tsx
69
+ * import { TextCell } from "@trackunit/react-table-base-components";
70
+ *
71
+ * // Wraps onto two lines, then clamps; tooltip shows the full note on hover
72
+ * <TextCell content={note} variant="multiline" />
73
+ * ```
51
74
  * @param {TextCellProps} props - The props for the TextCell component
52
75
  * @returns {ReactElement} TextCell component
53
76
  */
54
- export declare const TextCell: ({ content, className, "data-testid": dataTestId, ref, style }: TextCellProps) => import("react/jsx-runtime").JSX.Element;
77
+ export declare const TextCell: ({ content, className, "data-testid": dataTestId, ref, style, variant, }: TextCellProps) => import("react/jsx-runtime").JSX.Element;
@@ -1,9 +1,30 @@
1
1
  export declare const cvaTextCell: import("cva").CVAComponent<Omit<{
2
2
  base: string[];
3
+ variants: {
4
+ variant: {
5
+ default: string[];
6
+ multiline: string[];
7
+ };
8
+ };
9
+ defaultVariants: {
10
+ variant: "default";
11
+ };
3
12
  }, "defaultVariants"> & {
4
- variants: unknown;
5
- defaultVariants: Omit<{}, never>;
6
- }, unknown>;
13
+ variants: {
14
+ variant: {
15
+ default: string[];
16
+ multiline: string[];
17
+ };
18
+ };
19
+ defaultVariants: Omit<{}, "variant"> & {
20
+ variant: "default";
21
+ };
22
+ }, {
23
+ variant: {
24
+ default: string[];
25
+ multiline: string[];
26
+ };
27
+ }>;
7
28
  export declare const cvaTextCellTooltip: import("cva").CVAComponent<Omit<{
8
29
  base: string[];
9
30
  }, "defaultVariants"> & {