1- import React from 'react' ;
1+ import React , { useEffect } from 'react' ;
22import { areEqual } from 'react-window' ;
33import tw , { TwStyle } from 'twin.macro' ;
44import anchorme from 'anchorme' ;
55import { cellTypeMap } from '../store' ;
66import { DashIcon , DiffModifiedIcon , PlusIcon } from '@primer/octicons-react' ;
77import DOMPurify from 'dompurify' ;
8+ import { EditableCell } from './editable-cell' ;
89
910interface CellProps {
1011 type : string ;
@@ -19,9 +20,13 @@ interface CellProps {
1920 isFirstColumn ?: boolean ;
2021 hasStatusIndicator ?: boolean ;
2122 background ?: string ;
23+ isEditable : boolean ;
24+ onCellChange ?: ( value : any ) => void ;
25+ isFocused : boolean ;
26+ onFocusChange : ( value : [ number , number ] | null ) => void ;
2227 onMouseEnter ?: Function ;
2328}
24- export const Cell = React . memo ( function ( props : CellProps ) {
29+ export const Cell = React . memo ( function ( props : CellProps ) {
2530 const {
2631 type,
2732 value,
@@ -32,36 +37,45 @@ export const Cell = React.memo(function(props: CellProps) {
3237 isFirstColumn,
3338 isNearRightEdge,
3439 isNearBottomEdge,
40+ isEditable,
41+ onCellChange,
42+ isFocused,
43+ onFocusChange,
3544 background,
3645 style = { } ,
37- onMouseEnter = ( ) => { } ,
46+ onMouseEnter = ( ) => { } ,
3847 } = props ;
3948
4049 // @ts -ignore
4150 const cellInfo = cellTypeMap [ type ] ;
42- if ( ! cellInfo ) return null ;
4351
44- const { cell : CellComponent } = cellInfo ;
52+ const { cell : CellComponent } = cellInfo || { }
4553
4654 const displayValue = formattedValue || value ;
4755 const isLongValue = ( displayValue || '' ) . length > 23 ;
48- const stringWithLinks = displayValue
49- ? React . useMemo (
50- ( ) =>
51- DOMPurify . sanitize (
52- anchorme ( {
53- input : displayValue + '' ,
54- options : {
55- attributes : {
56- target : '_blank' ,
57- rel : 'noopener' ,
58- } ,
59- } ,
60- } )
61- ) ,
62- [ value ]
56+ const stringWithLinks = React . useMemo (
57+ ( ) => displayValue ? (
58+ DOMPurify . sanitize (
59+ anchorme ( {
60+ input : displayValue + '' ,
61+ options : {
62+ attributes : {
63+ target : '_blank' ,
64+ rel : 'noopener' ,
65+ } ,
66+ } ,
67+ } )
6368 )
64- : '' ;
69+ ) : "" ,
70+ [ value ]
71+ )
72+
73+ useEffect ( ( ) => {
74+ if ( ! isFocused ) return
75+ onMouseEnter ( )
76+ } , [ isFocused ] )
77+
78+ if ( ! cellInfo ) return null ;
6579
6680 const StatusIcon =
6781 isFirstColumn &&
@@ -86,54 +100,66 @@ export const Cell = React.memo(function(props: CellProps) {
86100 < div
87101 className = "cell group"
88102 css = { [
89- tw `flex flex-none items-center px-4 border-b border-r` ,
90-
91- typeof value === 'undefined' ||
92- ( Number . isNaN ( value ) && tw `text-gray-300` ) ,
103+ tw `flex border-b border-r` ,
93104 status === 'new' && tw `border-green-200` ,
94105 status === 'old' && tw `border-pink-200` ,
95106 status === 'modified' && tw `border-yellow-200` ,
96- ! status && tw `border-gray-200` ,
107+ ! status && tw `border-gray-200`
97108 ] }
98- onMouseEnter = { ( ) => onMouseEnter ( ) }
99109 style = { {
100110 ...style ,
101111 background : background || '#fff' ,
102- } }
103- >
104- { isFirstColumn && (
105- < div css = { [ tw `w-6 flex-none` , statusColor ] } >
106- { StatusIcon && < StatusIcon /> }
107- </ div >
108- ) }
109-
110- < CellComponent
111- value = { value }
112- formattedValue = { stringWithLinks }
113- rawValue = { rawValue }
114- categoryColor = { categoryColor }
115- />
116-
117- { isLongValue && (
112+ } } >
113+ < EditableCell
114+ type = { type }
115+ value = { rawValue }
116+ isEditable = { isEditable }
117+ onChange = { onCellChange }
118+ isFocused = { isFocused }
119+ onFocusChange = { onFocusChange } >
118120 < div
119- className = "cell__long-value"
120121 css = { [
121- tw ` absolute p-4 py-2 bg-white opacity-0 group-hover:opacity-100 z-30 border border-gray-200 shadow-md pointer-events-none ` ,
122- isNearBottomEdge ? tw `bottom-0` : tw `top-0` ,
123- isNearRightEdge ? tw `right-0` : tw `left-0` ,
122+ tw `w-full h-full flex flex-none items-center px-4 ` ,
123+ typeof value === 'undefined' ||
124+ ( Number . isNaN ( value ) && tw `text-gray-300` ) ,
124125 ] }
125- style = { {
126- width : 'max-content' ,
127- maxWidth : '27em' ,
128- } }
129- title = { rawValue }
126+ onMouseEnter = { ( ) => onMouseEnter ( ) }
130127 >
131- < div
132- tw = "line-clamp-9"
133- dangerouslySetInnerHTML = { { __html : stringWithLinks } }
128+ { isFirstColumn && (
129+ < div css = { [ tw `w-6 flex-none` , statusColor ] } >
130+ { StatusIcon && < StatusIcon /> }
131+ </ div >
132+ ) }
133+
134+ < CellComponent
135+ value = { value }
136+ formattedValue = { stringWithLinks }
137+ rawValue = { rawValue }
138+ categoryColor = { categoryColor }
134139 />
140+
141+ { isLongValue && (
142+ < div
143+ className = "cell__long-value"
144+ css = { [
145+ tw ` absolute p-4 py-2 bg-white opacity-0 group-hover:opacity-100 z-30 border border-gray-200 shadow-md pointer-events-none` ,
146+ isNearBottomEdge ? tw `bottom-0` : tw `top-0` ,
147+ isNearRightEdge ? tw `right-0` : tw `left-0` ,
148+ ] }
149+ style = { {
150+ width : 'max-content' ,
151+ maxWidth : '27em' ,
152+ } }
153+ title = { rawValue }
154+ >
155+ < div
156+ tw = "line-clamp-9"
157+ dangerouslySetInnerHTML = { { __html : stringWithLinks } }
158+ />
159+ </ div >
160+ ) }
135161 </ div >
136- ) }
162+ </ EditableCell >
137163 </ div >
138164 ) ;
139165} , areEqual ) ;
0 commit comments