Skip to content

Commit 0eb109c

Browse files
Merge pull request #429 from laratran/add-enable-cell-hover-reveal
Add enableCellHoverReveal column option
2 parents 508659c + 5999ffd commit 0eb109c

File tree

7 files changed

+6012
-4696
lines changed

7 files changed

+6012
-4696
lines changed

apps/mantine-react-table-docs/components/prop-tables/columnOptions.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,17 @@ export const columnOptions: ColumnOption[] = [
136136
required: false,
137137
type: "'select' | 'text' | 'multi-select'",
138138
},
139+
{
140+
columnOption: 'enableCellHoverReveal',
141+
defaultValue: '',
142+
description:
143+
"Enable or disable cell hover reveal for this column. There are some limitations to this feature. Passing props through mantineTableBodyCellProps might not work as expected. For example, passing align:'right' to mantineTableBodyCellProps would not be applied.",
144+
link: '',
145+
linkText: '',
146+
source: 'MRT',
147+
required: false,
148+
type: 'boolean',
149+
},
139150
{
140151
columnOption: 'enableClickToCopy',
141152
defaultValue: '',

packages/mantine-react-table/src/components/body/MRT_TableBodyCell.module.css

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,34 @@
7474
outline: 1px solid var(--mantine-color-gray-7);
7575
}
7676
}
77+
78+
.root-cell-hover-reveal {
79+
overflow: visible;
80+
}
81+
82+
.cell-hover-reveal {
83+
white-space: nowrap;
84+
overflow: hidden;
85+
text-overflow: ellipsis;
86+
text-align: var(--mrt-cell-align);
87+
}
88+
89+
.cell-hover-reveal.overflowing:hover{
90+
overflow: visible;
91+
white-space: normal;
92+
position: absolute;
93+
z-index: 2;
94+
padding: var(--table-vertical-spacing)
95+
var(--table-horizontal-spacing, var(--mantine-spacing-xs));
96+
background-color: var(--mrt-base-background-color);
97+
box-shadow: var(--mantine-shadow-sm);
98+
border: 1px solid var(--mantine-primary-color-filled);
99+
text-indent: -1px;
100+
width: max-content;
101+
top: 0;
102+
left: 0;
103+
height: 100%;
104+
display: flex;
105+
justify-content: center;
106+
align-items: center;
107+
}

packages/mantine-react-table/src/components/body/MRT_TableBodyCell.tsx

Lines changed: 87 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
type MouseEvent,
1010
type RefObject,
1111
useEffect,
12+
useRef,
1213
useState,
1314
} from 'react';
1415

@@ -194,6 +195,64 @@ export const MRT_TableBodyCell = <TData extends MRT_RowData>({
194195
table,
195196
};
196197

198+
const cellHoverRevealDivRef = useRef<any>(null);
199+
const [isCellContentOverflowing, setIsCellContentOverflowing] =
200+
useState(false);
201+
202+
const onMouseEnter = () => {
203+
if (!columnDef.enableCellHoverReveal) return;
204+
const div = cellHoverRevealDivRef.current;
205+
if (div) {
206+
const isOverflow = div.scrollWidth > div.clientWidth;
207+
setIsCellContentOverflowing(isOverflow);
208+
}
209+
};
210+
211+
const onMouseLeave = () => {
212+
if (!columnDef.enableCellHoverReveal) return;
213+
setIsCellContentOverflowing(false);
214+
};
215+
216+
const renderCellContent = () => {
217+
if (cell.getIsPlaceholder()) {
218+
return columnDef.PlaceholderCell?.({ cell, column, row, table }) ?? null;
219+
}
220+
221+
if (showSkeletons !== false && (isLoading || showSkeletons)) {
222+
return <Skeleton height={20} width={skeletonWidth} {...skeletonProps} />;
223+
}
224+
225+
if (
226+
columnDefType === 'display' &&
227+
(['mrt-row-expand', 'mrt-row-numbers', 'mrt-row-select'].includes(
228+
column.id,
229+
) ||
230+
!row.getIsGrouped())
231+
) {
232+
return columnDef.Cell?.({
233+
column,
234+
renderedCellValue: cell.renderValue() as any,
235+
row,
236+
rowRef,
237+
...cellValueProps,
238+
});
239+
}
240+
241+
if (isCreating || isEditing) {
242+
return <MRT_EditCellTextInput cell={cell} table={table} />;
243+
}
244+
245+
if (showClickToCopyButton && columnDef.enableClickToCopy !== false) {
246+
return (
247+
<MRT_CopyButton cell={cell} table={table}>
248+
<MRT_TableBodyCellValue {...cellValueProps} />
249+
</MRT_CopyButton>
250+
);
251+
}
252+
253+
return <MRT_TableBodyCellValue {...cellValueProps} />;
254+
};
255+
197256
return (
198257
<TableTd
199258
data-column-pinned={isColumnPinned || undefined}
@@ -243,47 +302,44 @@ export const MRT_TableBodyCell = <TData extends MRT_RowData>({
243302
classes['root-editable-hover'],
244303
columnDefType === 'data' && classes['root-data-col'],
245304
density === 'xs' && classes['root-nowrap'],
305+
columnDef.enableCellHoverReveal && classes['root-cell-hover-reveal'],
246306
tableCellProps?.className,
247307
)}
248308
onDoubleClick={handleDoubleClick}
249309
onDragEnter={handleDragEnter}
310+
onMouseEnter={onMouseEnter}
311+
onMouseLeave={onMouseLeave}
250312
style={(theme) => ({
251313
...widthStyles,
252314
...parseFromValuesOrFunc(tableCellProps.style, theme),
253315
})}
254316
>
255-
{tableCellProps.children ?? (
256-
<>
257-
{cell.getIsPlaceholder() ? (
258-
(columnDef.PlaceholderCell?.({ cell, column, row, table }) ?? null)
259-
) : showSkeletons !== false && (isLoading || showSkeletons) ? (
260-
<Skeleton height={20} width={skeletonWidth} {...skeletonProps} />
261-
) : columnDefType === 'display' &&
262-
(['mrt-row-expand', 'mrt-row-numbers', 'mrt-row-select'].includes(
263-
column.id,
264-
) ||
265-
!row.getIsGrouped()) ? (
266-
columnDef.Cell?.({
267-
column,
268-
renderedCellValue: cell.renderValue() as any,
269-
row,
270-
rowRef,
271-
...cellValueProps,
272-
})
273-
) : isCreating || isEditing ? (
274-
<MRT_EditCellTextInput cell={cell} table={table} />
275-
) : showClickToCopyButton && columnDef.enableClickToCopy !== false ? (
276-
<MRT_CopyButton cell={cell} table={table}>
277-
<MRT_TableBodyCellValue {...cellValueProps} />
278-
</MRT_CopyButton>
317+
<>
318+
{tableCellProps.children ??
319+
(columnDef.enableCellHoverReveal ? (
320+
<div
321+
className={clsx(
322+
columnDef.enableCellHoverReveal &&
323+
!(isCreating || isEditing) &&
324+
classes['cell-hover-reveal'],
325+
isCellContentOverflowing && classes['overflowing'],
326+
)}
327+
ref={cellHoverRevealDivRef}
328+
>
329+
{renderCellContent()}
330+
{cell.getIsGrouped() && !columnDef.GroupedCell && (
331+
<> ({row.subRows?.length})</>
332+
)}
333+
</div>
279334
) : (
280-
<MRT_TableBodyCellValue {...cellValueProps} />
281-
)}
282-
{cell.getIsGrouped() && !columnDef.GroupedCell && (
283-
<> ({row.subRows?.length})</>
284-
)}
285-
</>
286-
)}
335+
<>
336+
{renderCellContent()}
337+
{cell.getIsGrouped() && !columnDef.GroupedCell && (
338+
<> ({row.subRows?.length})</>
339+
)}
340+
</>
341+
))}
342+
</>
287343
</TableTd>
288344
);
289345
};

packages/mantine-react-table/src/components/inputs/MRT_GlobalFilterTextInput.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export const MRT_GlobalFilterTextInput = <TData extends MRT_RowData>({
3434
localization,
3535
mantineSearchTextInputProps,
3636
manualFiltering,
37-
positionGlobalFilter
37+
positionGlobalFilter,
3838
},
3939
refs: { searchInputRef },
4040
setGlobalFilter,

packages/mantine-react-table/src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,7 @@ export type MRT_ColumnDef<TData extends MRT_RowData, TValue = unknown> = {
447447
table: MRT_TableInstance<TData>;
448448
}) => ReactNode;
449449
editVariant?: 'multi-select' | 'select' | 'text';
450+
enableCellHoverReveal?: boolean;
450451
enableClickToCopy?: ((cell: MRT_Cell<TData>) => boolean) | boolean;
451452
enableColumnActions?: boolean;
452453
enableColumnDragging?: boolean;
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { MantineReactTable, type MRT_ColumnDef } from '../../src';
2+
3+
import { faker } from '@faker-js/faker';
4+
import { type Meta } from '@storybook/react';
5+
6+
const meta: Meta = {
7+
title: 'Features/Cell Hover Reveal Examples',
8+
};
9+
10+
export default meta;
11+
12+
const columns: MRT_ColumnDef<(typeof data)[0]>[] = [
13+
{
14+
accessorKey: 'firstName',
15+
header: 'First Name',
16+
},
17+
{
18+
accessorKey: 'lastName',
19+
header: 'Last Name',
20+
},
21+
{
22+
accessorKey: 'address',
23+
header: 'Address',
24+
},
25+
{
26+
accessorKey: 'state',
27+
header: 'State',
28+
},
29+
{
30+
accessorKey: 'phoneNumber',
31+
header: 'Phone Number',
32+
},
33+
{
34+
accessorKey: 'job',
35+
enableCellHoverReveal: true,
36+
header: 'Job',
37+
size: 75,
38+
},
39+
{
40+
accessorKey: 'jobDescriptor',
41+
header: 'Job Descriptor',
42+
},
43+
{
44+
accessorKey: 'company',
45+
header: 'Company',
46+
},
47+
];
48+
49+
const data = [...Array(100)].map(() => ({
50+
address: faker.location.streetAddress(),
51+
company: faker.company.name(),
52+
firstName: faker.person.firstName(),
53+
job: faker.person.jobTitle(),
54+
jobDescriptor: faker.person.jobDescriptor(),
55+
lastName: faker.person.lastName(),
56+
phoneNumber: faker.phone.number(),
57+
state: faker.location.state(),
58+
}));
59+
60+
export const CellHoverReveal = () => (
61+
<MantineReactTable columns={columns} data={data} layoutMode="grid" />
62+
);

0 commit comments

Comments
 (0)