@@ -218,6 +218,10 @@ export interface DataEditorProps extends Props, Pick<DataGridSearchProps, "image
218218 * @group Editing
219219 */
220220 readonly onRowAppended ?: ( ) => Promise < "top" | "bottom" | number | undefined > | void ;
221+ /** Emitted whenever a column append operation is requested. Append location can be set in callback.
222+ * @group Editing
223+ */
224+ readonly onColumnAppended ?: ( ) => Promise < "left" | "right" | number | undefined > | void ;
221225 /** Emitted when a column header should show a context menu. Usually right click.
222226 * @group Events
223227 */
@@ -705,6 +709,12 @@ export interface DataEditorRef {
705709 * @returns A promise which waits for the append to complete.
706710 */
707711 appendRow : ( col : number , openOverlay ?: boolean , behavior ?: ScrollBehavior ) => Promise < void > ;
712+ /**
713+ * Programatically appends a column.
714+ * @param row The row index to focus in the new column.
715+ * @returns A promise which waits for the append to complete.
716+ */
717+ appendColumn : ( row : number , openOverlay ?: boolean ) => Promise < void > ;
708718 /**
709719 * Triggers cells to redraw.
710720 */
@@ -744,7 +754,7 @@ const loadingCell: GridCell = {
744754 allowOverlay : false ,
745755} ;
746756
747- const emptyGridSelection : GridSelection = {
757+ export const emptyGridSelection : GridSelection = {
748758 columns : CompactSelection . empty ( ) ,
749759 rows : CompactSelection . empty ( ) ,
750760 current : undefined ,
@@ -808,6 +818,7 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
808818 keybindings : keybindingsIn ,
809819 editOnType = true ,
810820 onRowAppended,
821+ onColumnAppended,
811822 onColumnMoved,
812823 validateCell : validateCellIn ,
813824 highlightRegions : highlightRegionsIn ,
@@ -929,7 +940,7 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
929940 const rowMarkerWidth = rowMarkerWidthRaw ?? ( rowsIn > 10_000 ? 48 : rowsIn > 1000 ? 44 : rowsIn > 100 ? 36 : 32 ) ;
930941 const hasRowMarkers = rowMarkers !== "none" ;
931942 const rowMarkerOffset = hasRowMarkers ? 1 : 0 ;
932- const showTrailingBlankRow = onRowAppended !== undefined ;
943+ const showTrailingBlankRow = trailingRowOptions !== undefined ;
933944 const lastRowSticky = trailingRowOptions ?. sticky === true ;
934945
935946 const [ showSearchInner , setShowSearchInner ] = React . useState ( false ) ;
@@ -1649,10 +1660,16 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
16491660
16501661 const focusCallback = React . useRef ( focusOnRowFromTrailingBlankRow ) ;
16511662 const getCellContentRef = React . useRef ( getCellContent ) ;
1652- const rowsRef = React . useRef ( rows ) ;
1663+
16531664 focusCallback . current = focusOnRowFromTrailingBlankRow ;
16541665 getCellContentRef . current = getCellContent ;
1666+
1667+ const rowsRef = React . useRef ( rows ) ;
16551668 rowsRef . current = rows ;
1669+
1670+ const colsRef = React . useRef ( mangledCols . length ) ;
1671+ colsRef . current = mangledCols . length ;
1672+
16561673 const appendRow = React . useCallback (
16571674 async ( col : number , openOverlay : boolean = true , behavior ?: ScrollBehavior ) : Promise < void > => {
16581675 const c = mangledCols [ col ] ;
@@ -1710,6 +1727,57 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
17101727 [ mangledCols , onRowAppended , rowMarkerOffset , rows , setCurrent ]
17111728 ) ;
17121729
1730+ const appendColumn = React . useCallback (
1731+ async ( row : number , openOverlay : boolean = true ) : Promise < void > => {
1732+ const appendResult = onColumnAppended ?.( ) ;
1733+
1734+ let r : "left" | "right" | number | undefined = undefined ;
1735+ let right = true ;
1736+ if ( appendResult !== undefined ) {
1737+ r = await appendResult ;
1738+ if ( r === "left" ) right = false ;
1739+ if ( typeof r === "number" ) right = false ;
1740+ }
1741+
1742+ let backoff = 0 ;
1743+ const doFocus = ( ) => {
1744+ if ( colsRef . current <= mangledCols . length ) {
1745+ if ( backoff < 500 ) {
1746+ window . setTimeout ( doFocus , backoff ) ;
1747+ }
1748+ backoff = 50 + backoff * 2 ;
1749+ return ;
1750+ }
1751+
1752+ const col = typeof r === "number" ? r : right ? mangledCols . length : 0 ;
1753+ scrollTo ( col - rowMarkerOffset , row ) ;
1754+ setCurrent (
1755+ {
1756+ cell : [ col , row ] ,
1757+ range : {
1758+ x : col ,
1759+ y : row ,
1760+ width : 1 ,
1761+ height : 1 ,
1762+ } ,
1763+ } ,
1764+ false ,
1765+ false ,
1766+ "edit"
1767+ ) ;
1768+
1769+ const cell = getCellContentRef . current ( [ col - rowMarkerOffset , row ] ) ;
1770+ if ( cell . allowOverlay && isReadWriteCell ( cell ) && cell . readonly !== true && openOverlay ) {
1771+ window . setTimeout ( ( ) => {
1772+ focusCallback . current ( col , row ) ;
1773+ } , 0 ) ;
1774+ }
1775+ } ;
1776+ doFocus ( ) ;
1777+ } ,
1778+ [ mangledCols , onColumnAppended , rowMarkerOffset , scrollTo , setCurrent ]
1779+ ) ;
1780+
17131781 const getCustomNewRowTargetColumn = React . useCallback (
17141782 ( col : number ) : number | undefined => {
17151783 const customTargetColumn =
@@ -2991,14 +3059,29 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
29913059
29923060 const [ movX , movY ] = movement ;
29933061 if ( gridSelection . current !== undefined && ( movX !== 0 || movY !== 0 ) ) {
2994- const isEditingTrailingRow =
2995- gridSelection . current . cell [ 1 ] === mangledRows - 1 && newValue !== undefined ;
2996- updateSelectedCell (
2997- clamp ( gridSelection . current . cell [ 0 ] + movX , 0 , mangledCols . length - 1 ) ,
2998- clamp ( gridSelection . current . cell [ 1 ] + movY , 0 , mangledRows - 1 ) ,
2999- isEditingTrailingRow ,
3000- false
3001- ) ;
3062+ const isEditingLastRow = gridSelection . current . cell [ 1 ] === mangledRows - 1 && newValue !== undefined ;
3063+ const isEditingLastCol =
3064+ gridSelection . current . cell [ 0 ] === mangledCols . length - 1 && newValue !== undefined ;
3065+ let updateSelected = true ;
3066+ if ( isEditingLastRow && movY === 1 && onRowAppended !== undefined ) {
3067+ updateSelected = false ;
3068+ const col = gridSelection . current . cell [ 0 ] + movX ;
3069+ const customTargetColumn = getCustomNewRowTargetColumn ( col ) ;
3070+ void appendRow ( customTargetColumn ?? col , false ) ;
3071+ }
3072+ if ( isEditingLastCol && movX === 1 && onColumnAppended !== undefined ) {
3073+ updateSelected = false ;
3074+ const row = gridSelection . current . cell [ 1 ] + movY ;
3075+ void appendColumn ( row , false ) ;
3076+ }
3077+ if ( updateSelected ) {
3078+ updateSelectedCell (
3079+ clamp ( gridSelection . current . cell [ 0 ] + movX , 0 , mangledCols . length - 1 ) ,
3080+ clamp ( gridSelection . current . cell [ 1 ] + movY , 0 , mangledRows - 1 ) ,
3081+ isEditingLastRow ,
3082+ false
3083+ ) ;
3084+ }
30023085 }
30033086 onFinishedEditing ?.( newValue , movement ) ;
30043087 } ,
@@ -3011,6 +3094,11 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
30113094 mangledRows ,
30123095 updateSelectedCell ,
30133096 mangledCols . length ,
3097+ appendRow ,
3098+ appendColumn ,
3099+ onRowAppended ,
3100+ onColumnAppended ,
3101+ getCustomNewRowTargetColumn ,
30143102 ]
30153103 ) ;
30163104
@@ -3862,6 +3950,7 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
38623950 forwardedRef ,
38633951 ( ) => ( {
38643952 appendRow : ( col : number , openOverlay ?: boolean ) => appendRow ( col + rowMarkerOffset , openOverlay ) ,
3953+ appendColumn : ( row : number , openOverlay ?: boolean ) => appendColumn ( row , openOverlay ) ,
38653954 updateCells : damageList => {
38663955 if ( rowMarkerOffset !== 0 ) {
38673956 damageList = damageList . map ( x => ( { cell : [ x . cell [ 0 ] + rowMarkerOffset , x . cell [ 1 ] ] } ) ) ;
@@ -3971,7 +4060,17 @@ const DataEditorImpl: React.ForwardRefRenderFunction<DataEditorRef, DataEditorPr
39714060 } ;
39724061 } ,
39734062 } ) ,
3974- [ appendRow , normalSizeColumn , scrollRef , onCopy , onKeyDown , onPasteInternal , rowMarkerOffset , scrollTo ]
4063+ [
4064+ appendRow ,
4065+ appendColumn ,
4066+ normalSizeColumn ,
4067+ scrollRef ,
4068+ onCopy ,
4069+ onKeyDown ,
4070+ onPasteInternal ,
4071+ rowMarkerOffset ,
4072+ scrollTo ,
4073+ ]
39754074 ) ;
39764075
39774076 const [ selCol , selRow ] = currentCell ?? [ ] ;
0 commit comments