-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathCell.tsx
More file actions
111 lines (103 loc) · 4.05 KB
/
Copy pathCell.tsx
File metadata and controls
111 lines (103 loc) · 4.05 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import type { KeyboardEvent, MouseEvent } from 'react'
import { useCallback, useContext, useEffect, useMemo, useRef } from 'react'
import { CellCallbacksContext, RenderCellContentContext, StringifyContext } from '../contexts/CellConfigurationContext.js'
import { ColumnWidthsContext } from '../contexts/ColumnWidthsContext.js'
import { useCellFocus } from '../hooks/useCellFocus.js'
import { useOnCopy } from '../hooks/useOnCopyToClipboard.js'
interface Props {
/** aria column index */
ariaColIndex: number
/** aria row index */
ariaRowIndex: number
/** column index in the original dataframe, used for callbacks like onDoubleClickCell */
columnIndex: number
/** index in the visible columns array (used for styling/widths) */
visibleColumnIndex: number
/** cell value, undefined if the value has not been fetched yet, or if the value is actually undefined. Use hasResolved to distinguish these cases. */
cellValue?: unknown
/** whether the cell value has been resolved */
hasResolved?: boolean
/** class name */
className?: string
/** the row index in the original data, undefined if the value has not been fetched yet */
rowNumber?: number
}
/**
* Render a table cell <td> with title and optional custom rendering
*/
export default function Cell({ cellValue, hasResolved, columnIndex, visibleColumnIndex, className, ariaColIndex, ariaRowIndex, rowNumber }: Props) {
const { onDoubleClickCell, onMouseDownCell, onKeyDownCell } = useContext(CellCallbacksContext)
const stringify = useContext(StringifyContext)
const renderCellContent = useContext(RenderCellContentContext)
const { tabIndex, navigateToCell, focusIfNeeded } = useCellFocus({ ariaColIndex, ariaRowIndex })
const cell = useMemo(() => {
if (!hasResolved) {
return undefined
}
return { value: cellValue }
}, [hasResolved, cellValue])
// Focus the cell if needed. We use an effect, as it acts on the DOM element after render.
const ref = useRef<HTMLTableCellElement | null>(null)
useEffect(() => {
focusIfNeeded?.(ref.current)
}, [focusIfNeeded])
// Get the column width from the context (use visibleColumnIndex for styling)
const columnStyle = useContext(ColumnWidthsContext).getStyle?.(visibleColumnIndex)
// render as truncated text
const str = useMemo(() => {
return stringify(cell?.value)
}, [stringify, cell])
const title = useMemo(() => {
if (str === undefined) {
return undefined
}
if (str.length > 400) {
return `${str.slice(0, 397)}\u2026` // ...
}
if (str.length > 100) {
return str
}
}, [str])
const content = useMemo(() => {
return renderCellContent?.({ cell, stringify, col: columnIndex, row: rowNumber }) ?? str
}, [cell, stringify, columnIndex, rowNumber, renderCellContent, str])
const handleMouseDown = useCallback((event: MouseEvent) => {
navigateToCell?.()
if (onMouseDownCell && rowNumber !== undefined) {
onMouseDownCell(event, columnIndex, rowNumber)
}
}, [navigateToCell, onMouseDownCell, rowNumber, columnIndex])
const handleDoubleClick = useCallback((event: MouseEvent) => {
navigateToCell?.()
if (onDoubleClickCell && rowNumber !== undefined) {
onDoubleClickCell(event, columnIndex, rowNumber)
}
}, [navigateToCell, onDoubleClickCell, rowNumber, columnIndex])
const handleKeyDown = useCallback((event: KeyboardEvent) => {
// No need to navigate to the cell when using the keyboard, it is already focused
if (onKeyDownCell && rowNumber !== undefined) {
onKeyDownCell(event, columnIndex, rowNumber)
}
}, [onKeyDownCell, rowNumber, columnIndex])
const handleCopy = useOnCopy(str)
return (
<td
ref={ref}
role="cell"
aria-busy={cell === undefined}
aria-rowindex={ariaRowIndex}
aria-colindex={ariaColIndex}
data-rownumber={rowNumber}
tabIndex={tabIndex}
onCopy={handleCopy}
onDoubleClick={handleDoubleClick}
onMouseDown={handleMouseDown}
onKeyDown={handleKeyDown}
style={columnStyle}
className={className}
title={title}
>
{content}
</td>
)
}