-
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
1 parent
db93564
commit 108cbba
Showing
28 changed files
with
2,254 additions
and
18 deletions.
There are no files selected for viewing
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
94 changes: 94 additions & 0 deletions
94
weave-js/src/components/PagePanelComponents/Home/Browse3/compare/CodeDiff.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,94 @@ | ||
/** | ||
* Show code in the Monaco editor. If code has changed show a diff. | ||
*/ | ||
|
||
import {DiffEditor, Editor} from '@monaco-editor/react'; | ||
import {Box} from '@mui/material'; | ||
import React from 'react'; | ||
|
||
import {sanitizeString} from '../../../../../util/sanitizeSecrets'; | ||
import {Loading} from '../../../../Loading'; | ||
import {useWFHooks} from '../pages/wfReactInterface/context'; | ||
|
||
// Simple language detection based on file extension or content | ||
// TODO: Unify this utility method with Browse2OpDefCode.tsx | ||
const detectLanguage = (uri: string, code: string) => { | ||
if (uri.endsWith('.py')) { | ||
return 'python'; | ||
} | ||
if (uri.endsWith('.js') || uri.endsWith('.ts')) { | ||
return 'javascript'; | ||
} | ||
if (code.includes('def ') || code.includes('import ')) { | ||
return 'python'; | ||
} | ||
if (code.includes('function ') || code.includes('const ')) { | ||
return 'javascript'; | ||
} | ||
return 'plaintext'; | ||
}; | ||
|
||
type CodeDiffProps = { | ||
oldValueRef: string; | ||
newValueRef: string; | ||
}; | ||
|
||
export const CodeDiff = ({oldValueRef, newValueRef}: CodeDiffProps) => { | ||
const { | ||
derived: {useCodeForOpRef}, | ||
} = useWFHooks(); | ||
const opContentsQueryOld = useCodeForOpRef(oldValueRef); | ||
const opContentsQueryNew = useCodeForOpRef(newValueRef); | ||
const textOld = opContentsQueryOld.result ?? ''; | ||
const textNew = opContentsQueryNew.result ?? ''; | ||
const loading = opContentsQueryOld.loading || opContentsQueryNew.loading; | ||
|
||
if (loading) { | ||
return <Loading centered size={25} />; | ||
} | ||
|
||
const sanitizedOld = sanitizeString(textOld); | ||
const sanitizedNew = sanitizeString(textNew); | ||
const languageOld = detectLanguage(oldValueRef, sanitizedOld); | ||
const languageNew = detectLanguage(newValueRef, sanitizedNew); | ||
|
||
const inner = | ||
sanitizedOld !== sanitizedNew ? ( | ||
<DiffEditor | ||
height="100%" | ||
originalLanguage={languageOld} | ||
modifiedLanguage={languageNew} | ||
loading={loading} | ||
original={sanitizedOld} | ||
modified={sanitizedNew} | ||
options={{ | ||
readOnly: true, | ||
minimap: {enabled: false}, | ||
scrollBeyondLastLine: false, | ||
padding: {top: 10, bottom: 10}, | ||
renderSideBySide: false, | ||
}} | ||
/> | ||
) : ( | ||
<Editor | ||
height="100%" | ||
language={languageNew} | ||
loading={loading} | ||
value={sanitizedNew} | ||
options={{ | ||
readOnly: true, | ||
minimap: {enabled: false}, | ||
scrollBeyondLastLine: false, | ||
padding: {top: 10, bottom: 10}, | ||
}} | ||
/> | ||
); | ||
|
||
const maxRowsInView = 20; | ||
const totalLines = sanitizedNew.split('\n').length ?? 0; | ||
const showLines = Math.min(totalLines, maxRowsInView); | ||
const lineHeight = 18; | ||
const padding = 20; | ||
const height = showLines * lineHeight + padding + 'px'; | ||
return <Box sx={{height}}>{inner}</Box>; | ||
}; |
266 changes: 266 additions & 0 deletions
266
weave-js/src/components/PagePanelComponents/Home/Browse3/compare/CompareGrid.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,266 @@ | ||
/** | ||
* This is similar to the ObjectViewer but allows for multiple objects | ||
* to be displayed side-by-side. | ||
*/ | ||
|
||
import { | ||
DataGridProProps, | ||
GRID_TREE_DATA_GROUPING_FIELD, | ||
GridColDef, | ||
GridPinnedColumnFields, | ||
GridRowHeightParams, | ||
GridRowId, | ||
GridValidRowModel, | ||
useGridApiRef, | ||
} from '@mui/x-data-grid-pro'; | ||
import React, {useCallback, useEffect, useMemo} from 'react'; | ||
|
||
import {WeaveObjectRef} from '../../../../../react'; | ||
import {SmallRef} from '../../Browse2/SmallRef'; | ||
import {ObjectVersionSchema} from '../pages/wfReactInterface/wfDataModelHooksInterface'; | ||
import {StyledDataGrid} from '../StyledDataGrid'; | ||
import {RowDataWithDiff, UNCHANGED} from './compare'; | ||
import {CompareGridCell} from './CompareGridCell'; | ||
import {CompareGridGroupingCell} from './CompareGridGroupingCell'; | ||
import {ComparableObject, Mode} from './types'; | ||
|
||
type CompareGridProps = { | ||
objectType: 'object' | 'call'; | ||
objectIds: string[]; | ||
objects: ComparableObject[]; | ||
rows: RowDataWithDiff[]; | ||
mode: Mode; | ||
baselineEnabled: boolean; | ||
onlyChanged: boolean; | ||
isExpanded?: boolean; | ||
|
||
expandedIds: GridRowId[]; | ||
setExpandedIds: React.Dispatch<React.SetStateAction<GridRowId[]>>; | ||
addExpandedRefs: (path: string, refs: string[]) => void; | ||
}; | ||
|
||
const objectVersionSchemaToRef = ( | ||
objVersion: ObjectVersionSchema | ||
): WeaveObjectRef => { | ||
return { | ||
scheme: 'weave', | ||
entityName: objVersion.entity, | ||
projectName: objVersion.project, | ||
weaveKind: 'object', | ||
artifactName: objVersion.objectId, | ||
artifactVersion: objVersion.versionHash, | ||
}; | ||
}; | ||
|
||
export const CompareGrid = ({ | ||
objectType, | ||
objectIds, | ||
objects, | ||
rows, | ||
mode, | ||
baselineEnabled, | ||
onlyChanged, | ||
isExpanded, | ||
expandedIds, | ||
setExpandedIds, | ||
addExpandedRefs, | ||
}: CompareGridProps) => { | ||
const apiRef = useGridApiRef(); | ||
|
||
const filteredRows = onlyChanged | ||
? rows.filter(row => row.changeType !== UNCHANGED) | ||
: rows; | ||
|
||
const pinnedColumns: GridPinnedColumnFields = { | ||
left: [ | ||
GRID_TREE_DATA_GROUPING_FIELD, | ||
...(baselineEnabled ? [objectIds[0]] : []), | ||
], | ||
}; | ||
const columns: GridColDef[] = []; | ||
if (mode === 'unified' && objectIds.length === 2) { | ||
columns.push({ | ||
field: 'value', | ||
headerName: 'Value', | ||
flex: 1, | ||
display: 'flex', | ||
sortable: false, | ||
renderCell: cellParams => { | ||
const objId = objectIds[1]; | ||
const compareIdx = baselineEnabled | ||
? 0 | ||
: Math.max(0, objectIds.indexOf(objId) - 1); | ||
const compareId = objectIds[compareIdx]; | ||
const compareValue = cellParams.row.values[compareId]; | ||
const compareValueType = cellParams.row.types[compareId]; | ||
const value = cellParams.row.values[objId]; | ||
const valueType = cellParams.row.types[objId]; | ||
const rowChangeType = cellParams.row.changeType; | ||
return ( | ||
<div className="w-full p-8"> | ||
<CompareGridCell | ||
path={cellParams.row.path} | ||
displayType="both" | ||
value={value} | ||
valueType={valueType} | ||
compareValue={compareValue} | ||
compareValueType={compareValueType} | ||
rowChangeType={rowChangeType} | ||
/> | ||
</div> | ||
); | ||
}, | ||
}); | ||
} else { | ||
const versionCols: GridColDef[] = objectIds.map(objId => ({ | ||
field: objId, | ||
headerName: objId, | ||
flex: 1, | ||
display: 'flex', | ||
width: 500, | ||
sortable: false, | ||
valueGetter: (unused: any, row: any) => { | ||
return row.values[objId]; | ||
}, | ||
renderHeader: (params: any) => { | ||
if (objectType === 'call') { | ||
// TODO: Make this a peek drawer link | ||
return objId; | ||
} | ||
const idx = objectIds.indexOf(objId); | ||
const objVersion = objects[idx]; | ||
const objRef = objectVersionSchemaToRef( | ||
objVersion as ObjectVersionSchema | ||
); | ||
return <SmallRef objRef={objRef} />; | ||
}, | ||
renderCell: (cellParams: any) => { | ||
const compareIdx = baselineEnabled | ||
? 0 | ||
: Math.max(0, objectIds.indexOf(objId) - 1); | ||
const compareId = objectIds[compareIdx]; | ||
const compareValue = cellParams.row.values[compareId]; | ||
const compareValueType = cellParams.row.types[compareId]; | ||
const value = cellParams.row.values[objId]; | ||
const valueType = cellParams.row.types[objId]; | ||
const rowChangeType = cellParams.row.changeType; | ||
return ( | ||
<div className="w-full p-8"> | ||
<CompareGridCell | ||
path={cellParams.row.path} | ||
displayType="diff" | ||
value={value} | ||
valueType={valueType} | ||
compareValue={compareValue} | ||
compareValueType={compareValueType} | ||
rowChangeType={rowChangeType} | ||
/> | ||
</div> | ||
); | ||
}, | ||
})); | ||
columns.push(...versionCols); | ||
} | ||
|
||
const groupingColDef: DataGridProProps['groupingColDef'] = useMemo( | ||
() => ({ | ||
field: '__group__', | ||
hideDescendantCount: true, | ||
width: 300, | ||
renderHeader: () => { | ||
// Keep padding in sync with | ||
// INSET_SPACING (32) + left change indication border (3) - header padding (10) | ||
return <div className="pl-[25px]">Path</div>; | ||
}, | ||
renderCell: params => { | ||
return ( | ||
<CompareGridGroupingCell | ||
{...params} | ||
onClick={() => { | ||
setExpandedIds(eIds => { | ||
if (eIds.includes(params.row.id)) { | ||
return eIds.filter(id => id !== params.row.id); | ||
} | ||
return [...eIds, params.row.id]; | ||
}); | ||
addExpandedRefs(params.row.id, params.row.expandableRefs); | ||
}} | ||
/> | ||
); | ||
}, | ||
}), | ||
[addExpandedRefs, setExpandedIds] | ||
); | ||
|
||
const getRowId = (row: GridValidRowModel) => { | ||
return row.path.toString(); | ||
}; | ||
|
||
// Next we define a function that updates the row expansion state. This | ||
// function is responsible for setting the expansion state of rows that have | ||
// been expanded by the user. It is bound to the `rowsSet` event so that it is | ||
// called whenever the rows are updated. The MUI data grid will rerender and | ||
// close all expanded rows when the rows are updated. This function is | ||
// responsible for re-expanding the rows that were previously expanded. | ||
const updateRowExpand = useCallback(() => { | ||
expandedIds.forEach(id => { | ||
if (apiRef.current.getRow(id)) { | ||
const children = apiRef.current.getRowGroupChildren({groupId: id}); | ||
if (children.length !== 0) { | ||
apiRef.current.setRowChildrenExpansion(id, true); | ||
} | ||
} | ||
}); | ||
}, [apiRef, expandedIds]); | ||
useEffect(() => { | ||
updateRowExpand(); | ||
return apiRef.current.subscribeEvent('rowsSet', () => { | ||
updateRowExpand(); | ||
}); | ||
}, [apiRef, expandedIds, updateRowExpand]); | ||
|
||
const getGroupIds = useCallback(() => { | ||
const rowIds = apiRef.current.getAllRowIds(); | ||
return rowIds.filter(rowId => { | ||
const rowNode = apiRef.current.getRowNode(rowId); | ||
return rowNode && rowNode.type === 'group'; | ||
}); | ||
}, [apiRef]); | ||
|
||
// On first render expand groups | ||
useEffect(() => { | ||
setExpandedIds(getGroupIds()); | ||
}, [setExpandedIds, getGroupIds]); | ||
|
||
return ( | ||
<StyledDataGrid | ||
apiRef={apiRef} | ||
getRowId={getRowId} | ||
autoHeight | ||
treeData | ||
groupingColDef={groupingColDef} | ||
getTreeDataPath={row => row.path.toStringArray()} | ||
columns={columns} | ||
rows={filteredRows} | ||
isGroupExpandedByDefault={node => { | ||
return expandedIds.includes(node.id); | ||
}} | ||
columnHeaderHeight={38} | ||
disableColumnReorder={true} | ||
disableColumnMenu={true} | ||
getRowHeight={(params: GridRowHeightParams) => { | ||
return 'auto'; | ||
}} | ||
rowSelection={false} | ||
hideFooter | ||
pinnedColumns={pinnedColumns} | ||
keepBorders | ||
sx={{ | ||
'& .MuiDataGrid-cell': { | ||
alignItems: 'flex-start', | ||
padding: 0, | ||
}, | ||
}} | ||
/> | ||
); | ||
}; |
Oops, something went wrong.