@ -10,16 +10,16 @@ import DataEditor, {
} from '@glideapps/glide-data-grid' ;
import React , { useEffect , useReducer } from 'react' ;
import { Field , PanelProps , FieldType } from '@grafana/data' ;
import { Field , PanelProps , FieldType , DataFrame } from '@grafana/data' ;
import { PanelDataErrorView } from '@grafana/runtime' ;
import { useTheme2 } from '@grafana/ui' ;
import { usePanelContext , use Theme2 } from '@grafana/ui' ;
import '@glideapps/glide-data-grid/dist/index.css' ;
import { AddColumn } from './components/AddColumn' ;
import { DatagridContextMenu } from './components/DatagridContextMenu' ;
import { RenameColumnCell } from './components/RenameColumnCell' ;
import { isDatagridEditE nabled } from './featureFlagUtils' ;
import { isDatagridEnabled } from './featureFlagUtils' ;
import { PanelOptions } from './panelcfg.gen' ;
import { DatagridActionType , datagridReducer , initialState } from './state' ;
import {
@ -28,19 +28,21 @@ import {
EMPTY_CELL ,
getGridCellKind ,
getGridTheme ,
publishSnapshot ,
RIGHT_ELEMENT_PROPS ,
TRAILING_ROW_OPTIONS ,
getStyles ,
ROW_MARKER_BOTH ,
ROW_MARKER_NUMBER ,
hasGridSelection ,
updateSnapshot ,
} from './utils' ;
export interface DataGridProps extends PanelProps < PanelOptions > { }
export function DataGridPanel ( { options , data , id , fieldConfig , width , height } : DataGridProps ) {
const [ state , dispatch ] = useReducer ( datagridReducer , initialState ) ;
const { onUpdateData } = usePanelContext ( ) ;
const {
columns ,
contextMenuData ,
@ -74,9 +76,23 @@ export function DataGridPanel({ options, data, id, fieldConfig, width, height }:
return getGridCellKind ( field , row , hasGridSelection ( gridSelection ) ) ;
} ;
const onCellEdited = ( cell : Item , newValue : EditableGridCell ) = > {
const onCellEdited = async ( cell : Item , newValue : EditableGridCell ) = > {
// if there are rows selected, return early, we don't want to edit any cell
if ( hasGridSelection ( gridSelection ) ) {
return ;
}
const [ col , row ] = cell ;
const field : Field = frame . fields [ col ] ;
const frameCopy = {
. . . frame ,
fields : frame.fields.map ( ( f ) = > {
return {
. . . f ,
values : [ . . . f . values ] ,
} ;
} ) ,
} ;
const field : Field = frameCopy . fields [ col ] ;
if ( ! field ) {
return ;
@ -85,14 +101,14 @@ export function DataGridPanel({ options, data, id, fieldConfig, width, height }:
const values = field . values . toArray ( ) ;
values [ row ] = newValue . data ;
field . values = values ;
field . values = [ . . . values ] ;
publishSnapshot ( frame , id ) ;
updateSnapshot ( frameCopy , onUpdateData ) ;
} ;
const onColumnInputBlur = ( columnName : string ) = > {
const len = frame . length ? ? 0 ;
publish Snapshot(
update Snapshot(
{
. . . frame ,
fields : [
@ -105,7 +121,7 @@ export function DataGridPanel({ options, data, id, fieldConfig, width, height }:
} ,
] ,
} ,
id
onUpdateData
) ;
} ;
@ -115,7 +131,8 @@ export function DataGridPanel({ options, data, id, fieldConfig, width, height }:
values . push ( null ) ;
return { . . . f , values } ;
} ) ;
publishSnapshot ( { . . . frame , fields , length : frame.length + 1 } , id ) ;
updateSnapshot ( { . . . frame , fields , length : frame.length + 1 } , onUpdateData ) ;
} ;
const onColumnResize = ( column : GridColumn , width : number , columnIndex : number , newSizeWithGrow : number ) = > {
@ -133,12 +150,12 @@ export function DataGridPanel({ options, data, id, fieldConfig, width, height }:
const onDeletePressed = ( selection : GridSelection ) = > {
if ( selection . current && selection . current . range ) {
publish Snapshot( clearCellsFromRangeSelection ( frame , selection . current . range ) , id ) ;
update Snapshot( clearCellsFromRangeSelection ( frame , selection . current . range ) , onUpdateData ) ;
return true ;
}
if ( selection . rows ) {
publish Snapshot( deleteRows ( frame , selection . rows . toArray ( ) ) , id ) ;
update Snapshot( deleteRows ( frame , selection . rows . toArray ( ) ) , onUpdateData ) ;
return true ;
}
@ -162,14 +179,17 @@ export function DataGridPanel({ options, data, id, fieldConfig, width, height }:
} ) ;
} ;
const onColumnMove = ( from : number , to : number ) = > {
const onColumnMove = async ( from : number , to : number ) = > {
const fields = frame . fields . map ( ( f ) = > f ) ;
const field = fields [ from ] ;
fields . splice ( from , 1 ) ;
fields . splice ( to , 0 , field ) ;
const hasUpdated = await updateSnapshot ( { . . . frame , fields } , onUpdateData ) ;
if ( hasUpdated ) {
dispatch ( { type : DatagridActionType . columnMove , payload : { from , to } } ) ;
publishSnapshot ( { . . . frame , fields } , id ) ;
}
} ;
const onRowMove = ( from : number , to : number ) = > {
@ -181,7 +201,7 @@ export function DataGridPanel({ options, data, id, fieldConfig, width, height }:
field . values . splice ( to , 0 , value ) ;
}
publish Snapshot( { . . . frame , fields } , id ) ;
update Snapshot( { . . . frame , fields } , onUpdateData ) ;
} ;
const onColumnRename = ( ) = > {
@ -193,7 +213,8 @@ export function DataGridPanel({ options, data, id, fieldConfig, width, height }:
fields [ columnIdx ] . name = columnName ;
dispatch ( { type : DatagridActionType . hideColumnRenameInput } ) ;
publishSnapshot ( { . . . frame , fields } , id ) ;
updateSnapshot ( { . . . frame , fields } , onUpdateData ) ;
} ;
const onSearchClose = ( ) = > {
@ -204,10 +225,18 @@ export function DataGridPanel({ options, data, id, fieldConfig, width, height }:
dispatch ( { type : DatagridActionType . multipleCellsSelected , payload : { selection } } ) ;
} ;
const onContextMenuSave = ( data : DataFrame ) = > {
updateSnapshot ( data , onUpdateData ) ;
} ;
if ( ! frame ) {
return < PanelDataErrorView panelId = { id } fieldConfig = { fieldConfig } data = { data } / > ;
}
if ( ! isDatagridEnabled ( ) ) {
return < PanelDataErrorView panelId = { id } message = "Datagrid is not enabled" fieldConfig = { fieldConfig } data = { data } / > ;
}
if ( ! document . getElementById ( 'portal' ) ) {
const portal = document . createElement ( 'div' ) ;
portal . id = 'portal' ;
@ -230,37 +259,37 @@ export function DataGridPanel({ options, data, id, fieldConfig, width, height }:
smoothScrollX
smoothScrollY
overscrollY = { 50 }
onCellEdited = { isDatagridEditE nabled ( ) ? onCellEdited : undefined }
getCellsForSelection = { isDatagridEditE nabled ( ) ? true : undefined }
showSearch = { isDatagridEditE nabled ( ) ? toggleSearch : false }
onCellEdited = { isDatagridEnabled ( ) ? onCellEdited : undefined }
getCellsForSelection = { isDatagridEnabled ( ) ? true : undefined }
showSearch = { isDatagridEnabled ( ) ? toggleSearch : false }
onSearchClose = { onSearchClose }
onPaste = { isDatagridEditE nabled ( ) ? true : undefined }
onPaste = { isDatagridEnabled ( ) ? true : undefined }
gridSelection = { gridSelection }
onGridSelectionChange = { isDatagridEditE nabled ( ) ? onGridSelectionChange : undefined }
onRowAppended = { isDatagridEditE nabled ( ) ? addNewRow : undefined }
onDelete = { isDatagridEditE nabled ( ) ? onDeletePressed : undefined }
rowMarkers = { isDatagridEditE nabled ( ) ? ROW_MARKER_BOTH : ROW_MARKER_NUMBER }
onGridSelectionChange = { isDatagridEnabled ( ) ? onGridSelectionChange : undefined }
onRowAppended = { isDatagridEnabled ( ) ? addNewRow : undefined }
onDelete = { isDatagridEnabled ( ) ? onDeletePressed : undefined }
rowMarkers = { isDatagridEnabled ( ) ? ROW_MARKER_BOTH : ROW_MARKER_NUMBER }
onColumnResize = { onColumnResize }
onColumnResizeEnd = { onColumnResizeEnd }
onCellContextMenu = { isDatagridEditE nabled ( ) ? onCellContextMenu : undefined }
onHeaderContextMenu = { isDatagridEditE nabled ( ) ? onHeaderContextMenu : undefined }
onHeaderMenuClick = { isDatagridEditE nabled ( ) ? onHeaderMenuClick : undefined }
onCellContextMenu = { isDatagridEnabled ( ) ? onCellContextMenu : undefined }
onHeaderContextMenu = { isDatagridEnabled ( ) ? onHeaderContextMenu : undefined }
onHeaderMenuClick = { isDatagridEnabled ( ) ? onHeaderMenuClick : undefined }
trailingRowOptions = { TRAILING_ROW_OPTIONS }
rightElement = {
isDatagridEditE nabled ( ) ? (
isDatagridEnabled ( ) ? (
< AddColumn onColumnInputBlur = { onColumnInputBlur } divStyle = { styles . addColumnDiv } / >
) : null
}
rightElementProps = { RIGHT_ELEMENT_PROPS }
freezeColumns = { columnFreezeIndex }
onRowMoved = { isDatagridEditE nabled ( ) ? onRowMove : undefined }
onColumnMoved = { isDatagridEditE nabled ( ) ? onColumnMove : undefined }
onRowMoved = { isDatagridEnabled ( ) ? onRowMove : undefined }
onColumnMoved = { isDatagridEnabled ( ) ? onColumnMove : undefined }
/ >
{ contextMenuData . isContextMenuOpen && (
< DatagridContextMenu
menuData = { contextMenuData }
data = { frame }
saveData = { ( data ) = > publishSnapshot ( data , id ) }
saveData = { onContextMenuSave }
closeContextMenu = { closeContextMenu }
dispatch = { dispatch }
gridSelection = { gridSelection }