-
Notifications
You must be signed in to change notification settings - Fork 70
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
1,076 additions
and
58 deletions.
There are no files selected for viewing
147 changes: 147 additions & 0 deletions
147
weave-js/src/components/PagePanelComponents/Home/Browse3/datasets/CellRenderers.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
import {Box, Popover} from '@mui/material'; | ||
import { | ||
GridEditInputCell, | ||
GridRenderCellParams, | ||
GridRenderEditCellParams, | ||
} from '@mui/x-data-grid-pro'; | ||
import {Icon} from '@wandb/weave/components/Icon'; | ||
import React, {useState} from 'react'; | ||
import styled from 'styled-components'; | ||
|
||
const commonCellStyles = { | ||
height: '100%', | ||
width: '100%', | ||
fontFamily: '"Source Sans Pro", sans-serif', | ||
fontSize: '14px', | ||
lineHeight: '1.5', | ||
padding: '8px 12px', | ||
display: 'flex', | ||
alignItems: 'center', | ||
transition: 'background-color 0.2s ease', | ||
}; | ||
|
||
interface CellViewingRendererProps extends GridRenderCellParams { | ||
isEdited?: boolean; | ||
isDeleted?: boolean; | ||
isNew?: boolean; | ||
} | ||
|
||
const StyledEditCell = styled(GridEditInputCell)` | ||
textarea { | ||
height: 100% !important; | ||
padding: 8px 12px; | ||
font-family: 'Source Sans Pro', sans-serif; | ||
font-size: 14px; | ||
line-height: 1.5; | ||
} | ||
.MuiInputBase-root { | ||
height: 100%; | ||
padding: 0; | ||
} | ||
.MuiInputBase-input { | ||
height: 100% !important; | ||
} | ||
`; | ||
|
||
export const CellViewingRenderer: React.FC<CellViewingRendererProps> = ({ | ||
value, | ||
isEdited = false, | ||
isDeleted = false, | ||
isNew = false, | ||
api, | ||
id, | ||
field, | ||
}) => { | ||
const [isHovered, setIsHovered] = useState(false); | ||
|
||
const handleEditClick = (event: React.MouseEvent) => { | ||
event.stopPropagation(); | ||
api.startCellEditMode({id, field}); | ||
}; | ||
|
||
const getBackgroundColor = () => { | ||
if (isDeleted) { | ||
return 'rgba(255, 0, 0, 0.1)'; | ||
} | ||
if (isEdited) { | ||
return 'rgba(0, 128, 128, 0.1)'; | ||
} | ||
if (isNew) { | ||
return 'rgba(0, 255, 0, 0.1)'; | ||
} | ||
return 'transparent'; | ||
}; | ||
|
||
return ( | ||
<Box | ||
onClick={handleEditClick} | ||
onMouseEnter={() => setIsHovered(true)} | ||
onMouseLeave={() => setIsHovered(false)} | ||
sx={{ | ||
...commonCellStyles, | ||
position: 'relative', | ||
cursor: 'pointer', | ||
backgroundColor: getBackgroundColor(), | ||
opacity: isDeleted ? 0.5 : 1, | ||
textDecoration: isDeleted ? 'line-through' : 'none', | ||
'&:hover': { | ||
backgroundColor: 'rgba(0, 0, 0, 0.04)', | ||
}, | ||
}}> | ||
<span style={{flex: 1}}>{value}</span> | ||
{isHovered && ( | ||
<Box | ||
sx={{ | ||
display: 'flex', | ||
alignItems: 'center', | ||
opacity: 0, | ||
transition: 'opacity 0.2s ease', | ||
cursor: 'pointer', | ||
animation: 'fadeIn 0.2s ease forwards', | ||
'@keyframes fadeIn': { | ||
from: {opacity: 0}, | ||
to: {opacity: 0.5}, | ||
}, | ||
'&:hover': { | ||
opacity: 0.8, | ||
}, | ||
}}> | ||
<Icon name="pencil-edit" height={14} width={14} /> | ||
</Box> | ||
)} | ||
</Box> | ||
); | ||
}; | ||
|
||
export const CellEditingRenderer: React.FC< | ||
GridRenderEditCellParams | ||
> = params => { | ||
return ( | ||
<> | ||
<CellViewingRenderer {...params} /> | ||
<Popover | ||
open={true} | ||
anchorEl={params.api.getCellElement(params.id, params.field)} | ||
onClose={() => | ||
params.api.stopCellEditMode({id: params.id, field: params.field}) | ||
} | ||
anchorOrigin={{ | ||
vertical: 'bottom', | ||
horizontal: 'left', | ||
}} | ||
transformOrigin={{ | ||
vertical: 'top', | ||
horizontal: 'left', | ||
}} | ||
sx={{ | ||
'& .MuiPopover-paper': { | ||
minWidth: '200px', | ||
minHeight: '100px', | ||
padding: '8px', | ||
}, | ||
}}> | ||
<StyledEditCell {...params} multiline={true} /> | ||
</Popover> | ||
</> | ||
); | ||
}; |
132 changes: 132 additions & 0 deletions
132
weave-js/src/components/PagePanelComponents/Home/Browse3/datasets/DatasetEditorContext.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
import React, {createContext, useCallback, useContext, useState} from 'react'; | ||
|
||
interface DatasetRow { | ||
// id: string | number; | ||
[key: string]: any; | ||
___weave: { | ||
id: string; | ||
// The index must be set for rows of an existing dataset. | ||
index?: number; | ||
isNew?: boolean; | ||
}; | ||
} | ||
|
||
interface EditedCell { | ||
[fieldName: string]: unknown; | ||
} | ||
|
||
interface DatasetEditContextType { | ||
/** Map of edited cells, keyed by row absolute index */ | ||
editedCellsMap: Map<number, EditedCell>; | ||
setEditedCellsMap: React.Dispatch< | ||
React.SetStateAction<Map<number, EditedCell>> | ||
>; | ||
/** Map of complete edited rows, keyed by row absolute index */ | ||
editedRows: Map<number, DatasetRow>; | ||
setEditedRows: React.Dispatch<React.SetStateAction<Map<number, DatasetRow>>>; | ||
/** Callback to process row updates from the data grid */ | ||
processRowUpdate: (newRow: DatasetRow, oldRow: DatasetRow) => DatasetRow; | ||
/** Array of row indices that have been marked for deletion */ | ||
deletedRows: number[]; | ||
setDeletedRows: React.Dispatch<React.SetStateAction<number[]>>; | ||
/** Map of newly added rows, keyed by temporary row ID */ | ||
addedRows: Map<string, DatasetRow>; | ||
setAddedRows: React.Dispatch<React.SetStateAction<Map<string, DatasetRow>>>; | ||
/** Reset the context to its initial state */ | ||
reset: () => void; | ||
} | ||
|
||
export const DatasetEditContext = createContext< | ||
DatasetEditContextType | undefined | ||
>(undefined); | ||
|
||
export const useDatasetEditContext = () => { | ||
const context = useContext(DatasetEditContext); | ||
if (!context) { | ||
throw new Error( | ||
'useDatasetEditContext must be used within a DatasetEditProvider' | ||
); | ||
} | ||
return context; | ||
}; | ||
|
||
interface DatasetEditProviderProps { | ||
children: React.ReactNode; | ||
} | ||
|
||
export const DatasetEditProvider: React.FC<DatasetEditProviderProps> = ({ | ||
children, | ||
}) => { | ||
const [editedCellsMap, setEditedCellsMap] = useState<Map<number, EditedCell>>( | ||
new Map() | ||
); | ||
const [editedRows, setEditedRows] = useState<Map<number, DatasetRow>>( | ||
new Map() | ||
); | ||
const [deletedRows, setDeletedRows] = useState<number[]>([]); | ||
const [addedRows, setAddedRows] = useState<Map<string, DatasetRow>>( | ||
new Map() | ||
); | ||
|
||
const processRowUpdate = useCallback( | ||
(newRow: DatasetRow, oldRow: DatasetRow): DatasetRow => { | ||
const changedField = Object.keys(newRow).find( | ||
key => newRow[key] !== oldRow[key] && key !== 'id' | ||
); | ||
|
||
if (changedField) { | ||
const rowKey = String(oldRow.___weave.id); | ||
const rowIndex = oldRow.___weave.index; | ||
if (oldRow.___weave.isNew) { | ||
setAddedRows(prev => { | ||
const updatedMap = new Map(prev); | ||
updatedMap.set(rowKey, newRow); | ||
return updatedMap; | ||
}); | ||
} else { | ||
setEditedCellsMap(prev => { | ||
const existingEdits = prev.get(rowIndex!) || {}; | ||
const updatedMap = new Map(prev); | ||
updatedMap.set(rowIndex!, { | ||
...existingEdits, | ||
[changedField]: newRow[changedField], | ||
}); | ||
return updatedMap; | ||
}); | ||
setEditedRows(prev => { | ||
const updatedMap = new Map(prev); | ||
updatedMap.set(rowIndex!, newRow); | ||
return updatedMap; | ||
}); | ||
} | ||
} | ||
return newRow; | ||
}, | ||
[] | ||
); | ||
|
||
const reset = useCallback(() => { | ||
setEditedCellsMap(new Map()); | ||
setEditedRows(new Map()); | ||
setDeletedRows([]); | ||
setAddedRows(new Map()); | ||
}, []); | ||
|
||
return ( | ||
<DatasetEditContext.Provider | ||
value={{ | ||
editedCellsMap, | ||
setEditedCellsMap, | ||
editedRows, | ||
setEditedRows, | ||
processRowUpdate, | ||
deletedRows, | ||
setDeletedRows, | ||
addedRows, | ||
setAddedRows, | ||
reset, | ||
}}> | ||
{children} | ||
</DatasetEditContext.Provider> | ||
); | ||
}; |
Oops, something went wrong.