@@ -12,20 +12,22 @@ import 'ag-grid-community/styles/ag-grid.css';
1212import 'ag-grid-community/styles/ag-theme-alpine.css' ;
1313import { Grid , useTheme } from '@mui/material' ;
1414import { useIntl } from 'react-intl' ;
15- import { CellEditingStoppedEvent , ColumnState , SortChangedEvent } from 'ag-grid-community' ;
16- import BottomRightButtons from './BottomRightButtons' ;
15+ import { ColDef , GridApi , GridOptions } from 'ag-grid-community' ;
16+ import BottomRightButtons , { BottomRightButtonsProps } from './BottomRightButtons' ;
1717import FieldConstants from '../../../../utils/constants/fieldConstants' ;
1818
19+ type AgGridFn < TFn extends keyof GridOptions , TData > = NonNullable < GridOptions < TData > [ TFn ] > ;
20+
1921export const ROW_DRAGGING_SELECTION_COLUMN_DEF = [
2022 {
2123 rowDrag : true ,
2224 headerCheckboxSelection : true ,
2325 checkboxSelection : true ,
2426 maxWidth : 50 ,
2527 } ,
26- ] ;
28+ ] as const satisfies Readonly < ColDef [ ] > ;
2729
28- const style = ( customProps : any ) => ( {
30+ const style = ( customProps : object = { } ) => ( {
2931 grid : ( theme : any ) => ( {
3032 width : 'auto' ,
3133 height : '100%' ,
@@ -83,21 +85,23 @@ const style = (customProps: any) => ({
8385 } ) ,
8486} ) ;
8587
86- export interface CustomAgGridTableProps {
88+ export interface CustomAgGridTableProps < TData , TValue > {
8789 name : string ;
88- columnDefs : any ;
89- makeDefaultRowData : any ;
90- csvProps : unknown ;
91- cssProps : unknown ;
92- defaultColDef : unknown ;
90+ columnDefs : ColDef < TData , TValue > [ ] ;
91+ makeDefaultRowData : ( ) => unknown ;
92+ csvProps : BottomRightButtonsProps [ 'csvProps' ] ;
93+ cssProps ?: object ;
94+ defaultColDef : GridOptions < TData > [ 'defaultColDef' ] ;
9395 pagination : boolean ;
9496 paginationPageSize : number ;
9597 suppressRowClickSelection : boolean ;
9698 alwaysShowVerticalScroll : boolean ;
9799 stopEditingWhenCellsLoseFocus : boolean ;
98100}
99101
100- function CustomAgGridTable ( {
102+ // TODO: rename ContingencyAgGridTable
103+ // TODO: used only once in gridexplore, move to gridexplore?
104+ function CustomAgGridTable < TData = unknown , TValue = unknown > ( {
101105 name,
102106 columnDefs,
103107 makeDefaultRowData,
@@ -109,11 +113,10 @@ function CustomAgGridTable({
109113 suppressRowClickSelection,
110114 alwaysShowVerticalScroll,
111115 stopEditingWhenCellsLoseFocus,
112- ...props
113- } : CustomAgGridTableProps ) {
116+ } : Readonly < CustomAgGridTableProps < TData , TValue > > ) {
114117 const theme : any = useTheme ( ) ;
115- const [ gridApi , setGridApi ] = useState < any > ( null ) ;
116- const [ selectedRows , setSelectedRows ] = useState ( [ ] ) ;
118+ const [ gridApi , setGridApi ] = useState < GridApi < TData > > ( ) ;
119+ const [ selectedRows , setSelectedRows ] = useState < TData [ ] > ( [ ] ) ;
117120 const [ newRowAdded , setNewRowAdded ] = useState ( false ) ;
118121 const [ isSortApplied , setIsSortApplied ] = useState ( false ) ;
119122
@@ -124,15 +127,15 @@ function CustomAgGridTable({
124127 } ) ;
125128 const { append, remove, update, swap, move } = useFieldArrayOutput ;
126129
127- const rowData = watch ( name ) ;
130+ const rowData = watch ( name ) ; // TODO: use correct types for useFormContext<...>()
128131
129132 const isFirstSelected = Boolean (
130- rowData ?. length && gridApi ?. api . getRowNode ( rowData [ 0 ] [ FieldConstants . AG_GRID_ROW_UUID ] ) ?. isSelected ( )
133+ rowData ?. length && gridApi ?. getRowNode ( rowData [ 0 ] [ FieldConstants . AG_GRID_ROW_UUID ] ) ?. isSelected ( )
131134 ) ;
132135
133136 const isLastSelected = Boolean (
134137 rowData ?. length &&
135- gridApi ?. api . getRowNode ( rowData [ rowData . length - 1 ] [ FieldConstants . AG_GRID_ROW_UUID ] ) ?. isSelected ( )
138+ gridApi ?. getRowNode ( rowData [ rowData . length - 1 ] [ FieldConstants . AG_GRID_ROW_UUID ] ) ?. isSelected ( )
136139 ) ;
137140
138141 const noRowSelected = selectedRows . length === 0 ;
@@ -146,26 +149,26 @@ function CustomAgGridTable({
146149 [ getValues , name ]
147150 ) ;
148151
149- const handleMoveRowUp = ( ) => {
152+ const handleMoveRowUp = useCallback ( ( ) => {
150153 selectedRows
151- . map ( ( row ) => getIndex ( row ) )
154+ . map ( getIndex )
152155 . sort ( )
153156 . forEach ( ( idx ) => {
154157 swap ( idx , idx - 1 ) ;
155158 } ) ;
156- } ;
159+ } , [ getIndex , selectedRows , swap ] ) ;
157160
158- const handleMoveRowDown = ( ) => {
161+ const handleMoveRowDown = useCallback ( ( ) => {
159162 selectedRows
160- . map ( ( row ) => getIndex ( row ) )
163+ . map ( getIndex )
161164 . sort ( )
162165 . reverse ( )
163166 . forEach ( ( idx ) => {
164167 swap ( idx , idx + 1 ) ;
165168 } ) ;
166- } ;
169+ } , [ getIndex , selectedRows , swap ] ) ;
167170
168- const handleDeleteRows = ( ) => {
171+ const handleDeleteRows = useCallback ( ( ) => {
169172 if ( selectedRows . length === rowData . length ) {
170173 remove ( ) ;
171174 } else {
@@ -174,52 +177,59 @@ function CustomAgGridTable({
174177 remove ( idx ) ;
175178 } ) ;
176179 }
177- } ;
178-
179- useEffect ( ( ) => {
180- if ( gridApi ) {
181- gridApi . api . refreshCells ( {
182- force : true ,
183- } ) ;
184- }
185- } , [ gridApi , rowData ] ) ;
180+ } , [ getIndex , remove , rowData . length , selectedRows ] ) ;
186181
187- const handleAddRow = ( ) => {
182+ const handleAddRow = useCallback ( ( ) => {
188183 append ( makeDefaultRowData ( ) ) ;
189184 setNewRowAdded ( true ) ;
190- } ;
185+ } , [ append , makeDefaultRowData ] ) ;
191186
192187 useEffect ( ( ) => {
193- if ( gridApi ) {
194- gridApi . api . sizeColumnsToFit ( ) ;
195- }
188+ gridApi ?. refreshCells ( {
189+ force : true ,
190+ } ) ;
191+ } , [ gridApi , rowData ] ) ;
192+
193+ useEffect ( ( ) => {
194+ gridApi ?. sizeColumnsToFit ( ) ;
196195 } , [ columnDefs , gridApi ] ) ;
197196
198197 const intl = useIntl ( ) ;
199- const getLocaleText = useCallback (
200- ( params : any ) => {
201- const key = `agGrid.${ params . key } ` ;
202- return intl . messages [ key ] || params . defaultValue ;
203- } ,
198+ const getLocaleText = useCallback < AgGridFn < 'getLocaleText' , TData > > (
199+ ( params ) => intl . formatMessage ( { id : `agGrid.${ params . key } ` , defaultMessage : params . defaultValue } ) ,
204200 [ intl ]
205201 ) ;
206202
207- const onGridReady = ( params : any ) => {
208- setGridApi ( params ) ;
209- } ;
203+ const onGridReady = useCallback < AgGridFn < 'onGridReady' , TData > > ( ( event ) => {
204+ setGridApi ( event . api ) ;
205+ } , [ ] ) ;
206+
207+ const onRowDragEnd = useCallback < AgGridFn < 'onRowDragEnd' , TData > > (
208+ ( e ) => move ( getIndex ( e . node . data ) , e . overIndex ) ,
209+ [ getIndex , move ]
210+ ) ;
211+
212+ const onSelectionChanged = useCallback < AgGridFn < 'onSelectionChanged' , TData > > (
213+ // @ts -expect-error TODO manage null api case (not possible at runtime?)
214+ ( ) => setSelectedRows ( gridApi . getSelectedRows ( ) ) ,
215+ [ gridApi ]
216+ ) ;
210217
211- const onRowDataUpdated = ( ) => {
212- setNewRowAdded ( false ) ;
213- if ( gridApi ?. api ) {
214- // update due to new appended row, let's scroll
215- const lastIndex = rowData . length - 1 ;
216- gridApi . api . paginationGoToLastPage ( ) ;
217- gridApi . api . ensureIndexVisible ( lastIndex , 'bottom' ) ;
218- }
219- } ;
218+ const onRowDataUpdated = useCallback < AgGridFn < 'onRowDataUpdated' , TData > > (
219+ ( /* event */ ) => {
220+ setNewRowAdded ( false ) ;
221+ if ( gridApi ) {
222+ // update due to new appended row, let's scroll
223+ const lastIndex = rowData . length - 1 ;
224+ gridApi . paginationGoToLastPage ( ) ;
225+ gridApi . ensureIndexVisible ( lastIndex , 'bottom' ) ;
226+ }
227+ } ,
228+ [ gridApi , rowData . length ]
229+ ) ;
220230
221- const onCellEditingStopped = useCallback (
222- ( event : CellEditingStoppedEvent ) => {
231+ const onCellEditingStopped = useCallback < AgGridFn < 'onCellEditingStopped' , TData > > (
232+ ( event ) => {
223233 const rowIndex = getIndex ( event . data ) ;
224234 if ( rowIndex === - 1 ) {
225235 return ;
@@ -229,15 +239,22 @@ function CustomAgGridTable({
229239 [ getIndex , update ]
230240 ) ;
231241
232- const onSortChanged = useCallback ( ( event : SortChangedEvent ) => {
233- const isAnycolumnhasSort = event . api . getColumnState ( ) . some ( ( col : ColumnState ) => col . sort ) ;
234- setIsSortApplied ( isAnycolumnhasSort ) ;
235- } , [ ] ) ;
242+ const onSortChanged = useCallback < AgGridFn < 'onSortChanged' , TData > > (
243+ ( event ) => setIsSortApplied ( event . api . getColumnState ( ) . some ( ( col ) => col . sort ) ) ,
244+ [ ]
245+ ) ;
246+
247+ const getRowId = useCallback < AgGridFn < 'getRowId' , TData > > (
248+ // @ts -expect-error: we don't know at compile time if TData has a "FieldConstants.AG_GRID_ROW_UUID" field
249+ // TODO maybe force TData type to have this field?
250+ ( row ) => row . data [ FieldConstants . AG_GRID_ROW_UUID ] ,
251+ [ ]
252+ ) ;
236253
237254 return (
238255 < Grid container spacing = { 2 } >
239256 < Grid item xs = { 12 } className = { theme . aggrid . theme } sx = { style ( cssProps ) . grid } >
240- < AgGridReact
257+ < AgGridReact < TData >
241258 rowData = { rowData }
242259 onGridReady = { onGridReady }
243260 getLocaleText = { getLocaleText }
@@ -246,23 +263,21 @@ function CustomAgGridTable({
246263 domLayout = "autoHeight"
247264 rowDragEntireRow
248265 rowDragManaged
249- onRowDragEnd = { ( e ) => move ( getIndex ( e . node . data ) , e . overIndex ) }
266+ onRowDragEnd = { onRowDragEnd }
250267 suppressBrowserResizeObserver
268+ defaultColDef = { defaultColDef }
251269 columnDefs = { columnDefs }
252270 detailRowAutoHeight
253- onSelectionChanged = { ( ) => {
254- setSelectedRows ( gridApi . api . getSelectedRows ( ) ) ;
255- } }
271+ onSelectionChanged = { onSelectionChanged }
256272 onRowDataUpdated = { newRowAdded ? onRowDataUpdated : undefined }
257273 onCellEditingStopped = { onCellEditingStopped }
258274 onSortChanged = { onSortChanged }
259- getRowId = { ( row ) => row . data [ FieldConstants . AG_GRID_ROW_UUID ] }
275+ getRowId = { getRowId }
260276 pagination = { pagination }
261277 paginationPageSize = { paginationPageSize }
262278 suppressRowClickSelection = { suppressRowClickSelection }
263279 alwaysShowVerticalScroll = { alwaysShowVerticalScroll }
264280 stopEditingWhenCellsLoseFocus = { stopEditingWhenCellsLoseFocus }
265- { ...props }
266281 />
267282 </ Grid >
268283 < BottomRightButtons
0 commit comments