@@ -13,6 +13,14 @@ import type {
13
13
} from '../interface' ;
14
14
import { getPathValue , validateValue } from '../utils/valueUtil' ;
15
15
import StickyContext from '../context/StickyContext' ;
16
+ import HoverContext from '../context/HoverContext' ;
17
+ import type { HoverContextProps } from '../context/HoverContext' ;
18
+
19
+ /** Check if cell is in hover range */
20
+ function inHoverRange ( cellStartRow : number , cellRowSpan : number , startRow : number , endRow : number ) {
21
+ const cellEndRow = cellStartRow + cellRowSpan - 1 ;
22
+ return cellStartRow <= endRow && cellEndRow >= startRow ;
23
+ }
16
24
17
25
function isRenderCell < RecordType > (
18
26
data : React . ReactNode | RenderedCell < RecordType > ,
@@ -28,7 +36,7 @@ function isRefComponent(component: CustomizeComponent) {
28
36
return supportRef ( component ) ;
29
37
}
30
38
31
- export interface CellProps < RecordType extends DefaultRecordType > {
39
+ interface InternalCellProps < RecordType extends DefaultRecordType > extends HoverContextProps {
32
40
prefixCls ?: string ;
33
41
className ?: string ;
34
42
record ?: RecordType ;
@@ -53,7 +61,7 @@ export interface CellProps<RecordType extends DefaultRecordType> {
53
61
firstFixRight ?: boolean ;
54
62
lastFixRight ?: boolean ;
55
63
56
- // Additional
64
+ // ====================== Private Props ======================
57
65
/** @private Used for `expandable` with nest tree */
58
66
appendNode ?: React . ReactNode ;
59
67
additionalProps ?: React . HTMLAttributes < HTMLElement > ;
@@ -65,6 +73,11 @@ export interface CellProps<RecordType extends DefaultRecordType> {
65
73
isSticky ?: boolean ;
66
74
}
67
75
76
+ export type CellProps < RecordType extends DefaultRecordType > = Omit <
77
+ InternalCellProps < RecordType > ,
78
+ keyof HoverContextProps
79
+ > ;
80
+
68
81
function Cell < RecordType extends DefaultRecordType > (
69
82
{
70
83
prefixCls,
@@ -76,7 +89,7 @@ function Cell<RecordType extends DefaultRecordType>(
76
89
children,
77
90
component : Component = 'td' ,
78
91
colSpan,
79
- rowSpan,
92
+ rowSpan, // This is already merged on WrapperCell
80
93
fixLeft,
81
94
fixRight,
82
95
firstFixLeft,
@@ -89,7 +102,12 @@ function Cell<RecordType extends DefaultRecordType>(
89
102
align,
90
103
rowType,
91
104
isSticky,
92
- } : CellProps < RecordType > ,
105
+
106
+ // Hover
107
+ startRow,
108
+ endRow,
109
+ onHover,
110
+ } : InternalCellProps < RecordType > ,
93
111
ref : React . Ref < any > ,
94
112
) : React . ReactElement {
95
113
const cellPrefixCls = `${ prefixCls } -cell` ;
@@ -139,8 +157,8 @@ function Cell<RecordType extends DefaultRecordType>(
139
157
className : cellClassName ,
140
158
...restCellProps
141
159
} = cellProps || { } ;
142
- const mergedColSpan = cellColSpan !== undefined ? cellColSpan : colSpan ;
143
- const mergedRowSpan = cellRowSpan !== undefined ? cellRowSpan : rowSpan ;
160
+ const mergedColSpan = ( cellColSpan !== undefined ? cellColSpan : colSpan ) ?? 1 ;
161
+ const mergedRowSpan = ( cellRowSpan !== undefined ? cellRowSpan : rowSpan ) ?? 1 ;
144
162
145
163
if ( mergedColSpan === 0 || mergedRowSpan === 0 ) {
146
164
return null ;
@@ -167,6 +185,25 @@ function Cell<RecordType extends DefaultRecordType>(
167
185
alignStyle . textAlign = align ;
168
186
}
169
187
188
+ // ====================== Hover =======================
189
+ const hovering = inHoverRange ( index , mergedRowSpan , startRow , endRow ) ;
190
+
191
+ const onMouseEnter : React . MouseEventHandler < HTMLElement > = event => {
192
+ if ( record ) {
193
+ onHover ( index , index + mergedRowSpan - 1 ) ;
194
+ }
195
+
196
+ additionalProps ?. onMouseEnter ?.( event ) ;
197
+ } ;
198
+
199
+ const onMouseLeave : React . MouseEventHandler < HTMLElement > = event => {
200
+ if ( record ) {
201
+ onHover ( - 1 , - 1 ) ;
202
+ }
203
+
204
+ additionalProps ?. onMouseLeave ?.( event ) ;
205
+ } ;
206
+
170
207
// ====================== Render ======================
171
208
let title : string ;
172
209
const ellipsisConfig : CellEllipsisType = ellipsis === true ? { showTitle : true } : ellipsis ;
@@ -178,12 +215,14 @@ function Cell<RecordType extends DefaultRecordType>(
178
215
}
179
216
}
180
217
181
- const componentProps = {
218
+ const componentProps : React . TdHTMLAttributes < HTMLTableCellElement > & {
219
+ ref : React . Ref < any > ;
220
+ } = {
182
221
title,
183
222
...restCellProps ,
184
223
...additionalProps ,
185
- colSpan : mergedColSpan && mergedColSpan !== 1 ? mergedColSpan : null ,
186
- rowSpan : mergedRowSpan && mergedRowSpan !== 1 ? mergedRowSpan : null ,
224
+ colSpan : mergedColSpan !== 1 ? mergedColSpan : null ,
225
+ rowSpan : mergedRowSpan !== 1 ? mergedRowSpan : null ,
187
226
className : classNames (
188
227
cellPrefixCls ,
189
228
className ,
@@ -197,11 +236,14 @@ function Cell<RecordType extends DefaultRecordType>(
197
236
[ `${ cellPrefixCls } -ellipsis` ] : ellipsis ,
198
237
[ `${ cellPrefixCls } -with-append` ] : appendNode ,
199
238
[ `${ cellPrefixCls } -fix-sticky` ] : ( isFixLeft || isFixRight ) && isSticky && supportSticky ,
239
+ [ `${ cellPrefixCls } -row-hover` ] : hovering ,
200
240
} ,
201
241
additionalProps . className ,
202
242
cellClassName ,
203
243
) ,
204
244
style : { ...additionalProps . style , ...alignStyle , ...fixedStyle , ...cellStyle } ,
245
+ onMouseEnter,
246
+ onMouseLeave,
205
247
ref : isRefComponent ( Component ) ? ref : null ,
206
248
} ;
207
249
@@ -213,22 +255,33 @@ function Cell<RecordType extends DefaultRecordType>(
213
255
) ;
214
256
}
215
257
216
- const RefCell = React . forwardRef < any , CellProps < any > > ( Cell ) ;
258
+ const RefCell = React . forwardRef < any , InternalCellProps < any > > ( Cell ) ;
217
259
RefCell . displayName = 'Cell' ;
218
260
219
- const comparePropList : ( keyof CellProps < any > ) [ ] = [ 'expanded' , 'className' ] ;
261
+ const comparePropList : ( keyof InternalCellProps < any > ) [ ] = [ 'expanded' , 'className' ] ;
220
262
221
- const MemoCell = React . memo ( RefCell , ( prev : CellProps < any > , next : CellProps < any > ) => {
222
- if ( next . shouldCellUpdate ) {
223
- return (
224
- // Additional handle of expanded logic
225
- comparePropList . every ( propName => prev [ propName ] === next [ propName ] ) &&
226
- // User control update logic
227
- ! next . shouldCellUpdate ( next . record , prev . record )
228
- ) ;
229
- }
263
+ const MemoCell = React . memo (
264
+ RefCell ,
265
+ ( prev : InternalCellProps < any > , next : InternalCellProps < any > ) => {
266
+ if ( next . shouldCellUpdate ) {
267
+ return (
268
+ // Additional handle of expanded logic
269
+ comparePropList . every ( propName => prev [ propName ] === next [ propName ] ) &&
270
+ // User control update logic
271
+ ! next . shouldCellUpdate ( next . record , prev . record )
272
+ ) ;
273
+ }
274
+
275
+ return false ;
276
+ } ,
277
+ ) ;
278
+
279
+ /** Inject hover data here, we still wish MemoCell keep simple `shouldCellUpdate` logic */
280
+ const WrappedCell = React . forwardRef ( ( props : CellProps < any > , ref : React . Ref < any > ) => {
281
+ const { onHover, startRow, endRow } = React . useContext ( HoverContext ) ;
230
282
231
- return false ;
283
+ return < MemoCell { ... props } ref = { ref } onHover = { onHover } startRow = { startRow } endRow = { endRow } /> ;
232
284
} ) ;
285
+ WrappedCell . displayName = 'WrappedCell' ;
233
286
234
- export default MemoCell ;
287
+ export default WrappedCell ;
0 commit comments