mirror of
https://github.com/dbgate/dbgate
synced 2024-11-07 20:26:23 +00:00
data grid refactor - working read only
This commit is contained in:
parent
7704e9b305
commit
b439c7bb70
17
packages/web/src/datagrid/ChangeSetDataGrid.js
Normal file
17
packages/web/src/datagrid/ChangeSetDataGrid.js
Normal file
@ -0,0 +1,17 @@
|
||||
import { getChangeSetInsertedRows } from '@dbgate/datalib';
|
||||
import React from 'react';
|
||||
import DataGridCore from './DataGridCore';
|
||||
|
||||
export default function ChangeSetDataGrid(props) {
|
||||
const { changeSet, display, dispatchChangeSet } = props;
|
||||
function undo() {
|
||||
dispatchChangeSet({ type: 'undo' });
|
||||
}
|
||||
function redo() {
|
||||
dispatchChangeSet({ type: 'redo' });
|
||||
}
|
||||
|
||||
const insertedRows = getChangeSetInsertedRows(changeSet, display.baseTable);
|
||||
|
||||
return <DataGridCore {...props} insertedRowCount={insertedRows.length} undo={undo} redo={redo} />;
|
||||
}
|
@ -42,7 +42,7 @@ const DataGridContainer = styled.div`
|
||||
flex-grow: 1;
|
||||
`;
|
||||
|
||||
/** @param props {import('./types').DataGridProps} */
|
||||
/** @param props {import('./types').LoadingDataGridProps} */
|
||||
export default function DataGrid(props) {
|
||||
const Container1 = props.showReferences ? ManagerOuterContainer1 : ManagerOuterContainerFull;
|
||||
const [managerSize, setManagerSize] = React.useState(0);
|
||||
|
@ -126,98 +126,35 @@ const LoadingInfoBox = styled.div`
|
||||
border: 1px solid gray;
|
||||
`;
|
||||
|
||||
/** @param props {import('./types').DataGridProps} */
|
||||
async function loadDataPage(props, offset, limit) {
|
||||
const { display, conid, database, jslid } = props;
|
||||
|
||||
if (jslid) {
|
||||
const response = await axios.request({
|
||||
url: 'jsldata/get-rows',
|
||||
method: 'get',
|
||||
params: {
|
||||
jslid,
|
||||
offset,
|
||||
limit,
|
||||
},
|
||||
});
|
||||
return response.data;
|
||||
}
|
||||
|
||||
const sql = display.getPageQuery(offset, limit);
|
||||
|
||||
const response = await axios.request({
|
||||
url: 'database-connections/query-data',
|
||||
method: 'post',
|
||||
params: {
|
||||
conid,
|
||||
database,
|
||||
},
|
||||
data: { sql },
|
||||
});
|
||||
|
||||
if (response.data.errorMessage) return response.data;
|
||||
return response.data.rows;
|
||||
}
|
||||
|
||||
function dataPageAvailable(props) {
|
||||
const { display, jslid } = props;
|
||||
if (jslid) return true;
|
||||
const sql = display.getPageQuery(0, 1);
|
||||
return !!sql;
|
||||
}
|
||||
|
||||
/** @param props {import('./types').DataGridProps} */
|
||||
async function loadRowCount(props) {
|
||||
const { display, conid, database, jslid } = props;
|
||||
|
||||
if (jslid) {
|
||||
const response = await axios.request({
|
||||
url: 'jsldata/get-stats',
|
||||
method: 'get',
|
||||
params: {
|
||||
jslid,
|
||||
},
|
||||
});
|
||||
return response.data.rowCount;
|
||||
}
|
||||
|
||||
const sql = display.getCountQuery();
|
||||
|
||||
const response = await axios.request({
|
||||
url: 'database-connections/query-data',
|
||||
method: 'post',
|
||||
params: {
|
||||
conid,
|
||||
database,
|
||||
},
|
||||
data: { sql },
|
||||
});
|
||||
|
||||
return parseInt(response.data.rows[0].count);
|
||||
}
|
||||
|
||||
/** @param props {import('./types').DataGridProps} */
|
||||
/** @param props {import('./types').DataGridCoreProps} */
|
||||
export default function DataGridCore(props) {
|
||||
const { conid, database, display, changeSetState, dispatchChangeSet, tabVisible, jslid } = props;
|
||||
const {
|
||||
display,
|
||||
conid,
|
||||
database,
|
||||
changeSetState,
|
||||
dispatchChangeSet,
|
||||
tabVisible,
|
||||
rows,
|
||||
loadNextData,
|
||||
errorMessage,
|
||||
isLoadedAll,
|
||||
loadedTime,
|
||||
exportGrid,
|
||||
allRowCount,
|
||||
openQuery,
|
||||
insertedRowCount,
|
||||
isLoading,
|
||||
undo,
|
||||
redo
|
||||
} = props;
|
||||
// console.log('RENDER GRID', display.baseTable.pureName);
|
||||
const columns = React.useMemo(() => display.allColumns, [display]);
|
||||
|
||||
// usePropsCompare(props);
|
||||
|
||||
// console.log(`GRID, conid=${conid}, database=${database}, sql=${sql}`);
|
||||
const [loadProps, setLoadProps] = React.useState({
|
||||
isLoading: false,
|
||||
loadedRows: [],
|
||||
isLoadedAll: false,
|
||||
loadedTime: new Date().getTime(),
|
||||
allRowCount: null,
|
||||
errorMessage: null,
|
||||
jslStatsCounter: 0,
|
||||
jslChangeIndex: 0,
|
||||
});
|
||||
const { isLoading, loadedRows, isLoadedAll, loadedTime, allRowCount, errorMessage } = loadProps;
|
||||
|
||||
const loadedTimeRef = React.useRef(0);
|
||||
const focusFieldRef = React.useRef(null);
|
||||
|
||||
const [vScrollValueToSet, setvScrollValueToSet] = React.useState();
|
||||
@ -239,14 +176,6 @@ export default function DataGridCore(props) {
|
||||
// const [inplaceEditorShouldSave, setInplaceEditorShouldSave] = React.useState(false);
|
||||
// const [inplaceEditorChangedOnCreate, setInplaceEditorChangedOnCreate] = React.useState(false);
|
||||
|
||||
const changeSet = changeSetState && changeSetState.value;
|
||||
const setChangeSet = React.useCallback((value) => dispatchChangeSet({ type: 'set', value }), [dispatchChangeSet]);
|
||||
const setOpenedTabs = useSetOpenedTabs();
|
||||
|
||||
const changeSetRef = React.useRef(changeSet);
|
||||
|
||||
changeSetRef.current = changeSet;
|
||||
|
||||
const autofillMarkerCell = React.useMemo(
|
||||
() =>
|
||||
selectedCells && selectedCells.length > 0 && _.uniq(selectedCells.map((x) => x[0])).length == 1
|
||||
@ -257,54 +186,6 @@ export default function DataGridCore(props) {
|
||||
|
||||
const showModal = useShowModal();
|
||||
|
||||
const handleLoadRowCount = async () => {
|
||||
const rowCount = await loadRowCount(props);
|
||||
setLoadProps((oldLoadProps) => ({
|
||||
...oldLoadProps,
|
||||
allRowCount: rowCount,
|
||||
}));
|
||||
};
|
||||
|
||||
const loadNextData = async () => {
|
||||
if (isLoading) return;
|
||||
setLoadProps((oldLoadProps) => ({
|
||||
...oldLoadProps,
|
||||
isLoading: true,
|
||||
}));
|
||||
const loadStart = new Date().getTime();
|
||||
loadedTimeRef.current = loadStart;
|
||||
|
||||
const nextRows = await loadDataPage(props, loadedRows.length, 100);
|
||||
if (loadedTimeRef.current !== loadStart) {
|
||||
// new load was dispatched
|
||||
return;
|
||||
}
|
||||
// if (!_.isArray(nextRows)) {
|
||||
// console.log('Error loading data from server', nextRows);
|
||||
// nextRows = [];
|
||||
// }
|
||||
// console.log('nextRows', nextRows);
|
||||
if (nextRows.errorMessage) {
|
||||
setLoadProps((oldLoadProps) => ({
|
||||
...oldLoadProps,
|
||||
isLoading: false,
|
||||
errorMessage: nextRows.errorMessage,
|
||||
}));
|
||||
} else {
|
||||
if (allRowCount == null) handleLoadRowCount();
|
||||
const loadedInfo = {
|
||||
loadedRows: [...loadedRows, ...nextRows],
|
||||
loadedTime,
|
||||
};
|
||||
setLoadProps((oldLoadProps) => ({
|
||||
...oldLoadProps,
|
||||
isLoading: false,
|
||||
isLoadedAll: oldLoadProps.jslStatsCounter == loadProps.jslStatsCounter && nextRows.length === 0,
|
||||
...loadedInfo,
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
// const data = useFetch({
|
||||
// url: 'database-connections/query-data',
|
||||
// method: 'post',
|
||||
@ -317,7 +198,6 @@ export default function DataGridCore(props) {
|
||||
// const { rows, columns } = data || {};
|
||||
const [firstVisibleRowScrollIndex, setFirstVisibleRowScrollIndex] = React.useState(0);
|
||||
const [firstVisibleColumnScrollIndex, setFirstVisibleColumnScrollIndex] = React.useState(0);
|
||||
const socket = useSocket();
|
||||
|
||||
const [headerRowRef, { height: rowHeight }] = useDimensions();
|
||||
const [tableBodyRef] = useDimensions();
|
||||
@ -326,6 +206,9 @@ export default function DataGridCore(props) {
|
||||
const confirmSqlModalState = useModalState();
|
||||
const [confirmSql, setConfirmSql] = React.useState('');
|
||||
|
||||
const changeSet = changeSetState && changeSetState.value;
|
||||
const setChangeSet = React.useCallback((value) => dispatchChangeSet({ type: 'set', value }), [dispatchChangeSet]);
|
||||
|
||||
const [inplaceEditorState, dispatchInsplaceEditor] = React.useReducer((state, action) => {
|
||||
switch (action.type) {
|
||||
case 'show':
|
||||
@ -355,8 +238,8 @@ export default function DataGridCore(props) {
|
||||
|
||||
// usePropsCompare({ loadedRows, columns, containerWidth, display });
|
||||
|
||||
const columnSizes = React.useMemo(() => countColumnSizes(loadedRows, columns, containerWidth, display), [
|
||||
loadedRows,
|
||||
const columnSizes = React.useMemo(() => countColumnSizes(rows, columns, containerWidth, display), [
|
||||
rows,
|
||||
columns,
|
||||
containerWidth,
|
||||
display,
|
||||
@ -376,43 +259,6 @@ export default function DataGridCore(props) {
|
||||
// console.log('visibleRowCountUpperBound', visibleRowCountUpperBound);
|
||||
// console.log('rowHeight', rowHeight);
|
||||
|
||||
const reload = () => {
|
||||
setLoadProps({
|
||||
allRowCount: null,
|
||||
isLoading: false,
|
||||
loadedRows: [],
|
||||
isLoadedAll: false,
|
||||
loadedTime: new Date().getTime(),
|
||||
errorMessage: null,
|
||||
jslStatsCounter: 0,
|
||||
jslChangeIndex: 0,
|
||||
});
|
||||
};
|
||||
|
||||
const insertedRows = getChangeSetInsertedRows(changeSet, display.baseTable);
|
||||
|
||||
const rowCountNewIncluded = loadedRows.length + insertedRows.length;
|
||||
|
||||
React.useEffect(() => {
|
||||
if (
|
||||
!isLoadedAll &&
|
||||
!errorMessage &&
|
||||
firstVisibleRowScrollIndex + visibleRowCountUpperBound >= loadedRows.length &&
|
||||
insertedRows.length == 0
|
||||
) {
|
||||
if (dataPageAvailable(props)) {
|
||||
// If not, callbacks to load missing metadata are dispatched
|
||||
loadNextData();
|
||||
}
|
||||
}
|
||||
if (props.masterLoadedTime && props.masterLoadedTime > loadedTime) {
|
||||
display.reload();
|
||||
}
|
||||
if (display.cache.refreshTime > loadedTime) {
|
||||
reload();
|
||||
}
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
if (tabVisible) {
|
||||
if (focusFieldRef.current) focusFieldRef.current.focus();
|
||||
@ -424,31 +270,11 @@ export default function DataGridCore(props) {
|
||||
return newColumn;
|
||||
}, [columnSizes, gridScrollAreaWidth]);
|
||||
|
||||
const handleJslDataStats = React.useCallback((stats) => {
|
||||
if (stats.changeIndex < loadProps.jslChangeIndex) return;
|
||||
setLoadProps((oldProps) => ({
|
||||
...oldProps,
|
||||
allRowCount: stats.rowCount,
|
||||
isLoadedAll: false,
|
||||
jslStatsCounter: oldProps.jslStatsCounter + 1,
|
||||
jslChangeIndex: stats.changeIndex,
|
||||
}));
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (jslid && socket) {
|
||||
socket.on(`jsldata-stats-${jslid}`, handleJslDataStats);
|
||||
return () => {
|
||||
socket.off(`jsldata-stats-${jslid}`, handleJslDataStats);
|
||||
};
|
||||
}
|
||||
}, [jslid]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (props.onReferenceSourceChanged && ((loadedRows && loadedRows.length > 0) || isLoadedAll)) {
|
||||
if (props.onReferenceSourceChanged && ((rows && rows.length > 0) || isLoadedAll)) {
|
||||
props.onReferenceSourceChanged(getSelectedRowData(), loadedTime);
|
||||
}
|
||||
}, [selectedCells, props.refReloadToken, loadedRows && loadedRows[0]]);
|
||||
}, [selectedCells, props.refReloadToken, rows && rows[0]]);
|
||||
|
||||
// const handleCloseInplaceEditor = React.useCallback(
|
||||
// mode => {
|
||||
@ -487,6 +313,12 @@ export default function DataGridCore(props) {
|
||||
}
|
||||
}, [display && display.focusedColumn]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (rows && loadNextData && firstVisibleRowScrollIndex + visibleRowCountUpperBound >= rows.length) {
|
||||
loadNextData();
|
||||
}
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
if (display.groupColumns) {
|
||||
props.onReferenceClick({
|
||||
@ -504,7 +336,7 @@ export default function DataGridCore(props) {
|
||||
const rowCountInfo = React.useMemo(() => {
|
||||
if (selectedCells.length > 1 && selectedCells.every((x) => _.isNumber(x[0]) && _.isNumber(x[1]))) {
|
||||
let sum = _.sumBy(selectedCells, (cell) => {
|
||||
const row = loadedRows[cell[0]];
|
||||
const row = rows[cell[0]];
|
||||
if (row) {
|
||||
const colName = realColumnUniqueNames[cell[1]];
|
||||
if (colName) {
|
||||
@ -526,9 +358,9 @@ export default function DataGridCore(props) {
|
||||
// if (this.isLoadingFirstPage) return "Loading first page...";
|
||||
// if (this.isFirstPageError) return "Error loading first page";
|
||||
// return `Rows: ${this.rowCount.toLocaleString()}`;
|
||||
}, [selectedCells, allRowCount, loadedRows, visibleRealColumns]);
|
||||
}, [selectedCells, allRowCount, rows, visibleRealColumns]);
|
||||
|
||||
if (!loadedRows || !columns || columns.length == 0)
|
||||
if (!rows || !columns || columns.length == 0)
|
||||
return (
|
||||
<LoadingInfoWrapper>
|
||||
<LoadingInfoBox>
|
||||
@ -563,7 +395,7 @@ export default function DataGridCore(props) {
|
||||
setNull={setNull}
|
||||
exportGrid={exportGrid}
|
||||
filterSelectedValue={filterSelectedValue}
|
||||
openQuery={display.baseTable ? openQuery : null}
|
||||
openQuery={openQuery}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -617,108 +449,83 @@ export default function DataGridCore(props) {
|
||||
copyToClipboard();
|
||||
}
|
||||
|
||||
function exportGrid() {
|
||||
const initialValues = {};
|
||||
if (jslid) {
|
||||
const archiveMatch = jslid.match(/^archive:\/\/([^/]+)\/(.*)$/);
|
||||
if (archiveMatch) {
|
||||
initialValues.sourceStorageType = 'archive';
|
||||
initialValues.sourceArchiveFolder = archiveMatch[1];
|
||||
initialValues.sourceList = [archiveMatch[2]];
|
||||
} else {
|
||||
initialValues.sourceStorageType = 'jsldata';
|
||||
initialValues.sourceJslId = jslid;
|
||||
initialValues.sourceList = ['query-data'];
|
||||
}
|
||||
} else {
|
||||
initialValues.sourceStorageType = 'query';
|
||||
initialValues.sourceConnectionId = conid;
|
||||
initialValues.sourceDatabaseName = database;
|
||||
initialValues.sourceSql = display.getExportQuery();
|
||||
initialValues.sourceList = display.baseTable ? [display.baseTable.pureName] : [];
|
||||
}
|
||||
showModal((modalState) => <ImportExportModal modalState={modalState} initialValues={initialValues} />);
|
||||
}
|
||||
|
||||
function setCellValue(chs, cell, value) {
|
||||
return setChangeSetValue(
|
||||
chs,
|
||||
display.getChangeSetField(
|
||||
loadedAndInsertedRows[cell[0]],
|
||||
realColumnUniqueNames[cell[1]],
|
||||
cell[0] >= loadedRows.length ? cell[0] - loadedRows.length : null
|
||||
),
|
||||
value
|
||||
);
|
||||
// return setChangeSetValue(
|
||||
// chs,
|
||||
// display.getChangeSetField(
|
||||
// rows[cell[0]],
|
||||
// realColumnUniqueNames[cell[1]],
|
||||
// cell[0] >= rows.length ? cell[0] - rows.length : null
|
||||
// ),
|
||||
// value
|
||||
// );
|
||||
}
|
||||
|
||||
function handlePaste(event) {
|
||||
var pastedText = undefined;
|
||||
// @ts-ignore
|
||||
if (window.clipboardData && window.clipboardData.getData) {
|
||||
// IE
|
||||
// @ts-ignore
|
||||
pastedText = window.clipboardData.getData('Text');
|
||||
} else if (event.clipboardData && event.clipboardData.getData) {
|
||||
pastedText = event.clipboardData.getData('text/plain');
|
||||
}
|
||||
event.preventDefault();
|
||||
const pasteRows = pastedText
|
||||
.replace(/\r/g, '')
|
||||
.split('\n')
|
||||
.map((row) => row.split('\t'));
|
||||
let chs = changeSet;
|
||||
let allRows = loadedAndInsertedRows;
|
||||
|
||||
if (selectedCells.length <= 1) {
|
||||
const startRow = isRegularCell(currentCell) ? currentCell[0] : loadedAndInsertedRows.length;
|
||||
const startCol = isRegularCell(currentCell) ? currentCell[1] : 0;
|
||||
let rowIndex = startRow;
|
||||
for (const rowData of pasteRows) {
|
||||
if (rowIndex >= allRows.length) {
|
||||
chs = changeSetInsertNewRow(chs, display.baseTable);
|
||||
allRows = [...loadedRows, ...getChangeSetInsertedRows(chs, display.baseTable)];
|
||||
}
|
||||
let colIndex = startCol;
|
||||
const row = allRows[rowIndex];
|
||||
for (const cell of rowData) {
|
||||
chs = setChangeSetValue(
|
||||
chs,
|
||||
display.getChangeSetField(
|
||||
row,
|
||||
realColumnUniqueNames[colIndex],
|
||||
rowIndex >= loadedRows.length ? rowIndex - loadedRows.length : null
|
||||
),
|
||||
cell == '(NULL)' ? null : cell
|
||||
);
|
||||
colIndex += 1;
|
||||
}
|
||||
rowIndex += 1;
|
||||
}
|
||||
}
|
||||
if (selectedCells.length > 1) {
|
||||
const regularSelected = selectedCells.filter(isRegularCell);
|
||||
const startRow = _.min(regularSelected.map((x) => x[0]));
|
||||
const startCol = _.min(regularSelected.map((x) => x[1]));
|
||||
for (const cell of regularSelected) {
|
||||
const [rowIndex, colIndex] = cell;
|
||||
const selectionRow = rowIndex - startRow;
|
||||
const selectionCol = colIndex - startCol;
|
||||
const pasteRow = pasteRows[selectionRow % pasteRows.length];
|
||||
const pasteCell = pasteRow[selectionCol % pasteRow.length];
|
||||
chs = setCellValue(chs, cell, pasteCell);
|
||||
}
|
||||
}
|
||||
|
||||
setChangeSet(chs);
|
||||
// var pastedText = undefined;
|
||||
// // @ts-ignore
|
||||
// if (window.clipboardData && window.clipboardData.getData) {
|
||||
// // IE
|
||||
// // @ts-ignore
|
||||
// pastedText = window.clipboardData.getData('Text');
|
||||
// } else if (event.clipboardData && event.clipboardData.getData) {
|
||||
// pastedText = event.clipboardData.getData('text/plain');
|
||||
// }
|
||||
// event.preventDefault();
|
||||
// const pasteRows = pastedText
|
||||
// .replace(/\r/g, '')
|
||||
// .split('\n')
|
||||
// .map((row) => row.split('\t'));
|
||||
// let chs = changeSet;
|
||||
// let allRows = loadedAndInsertedRows;
|
||||
// if (selectedCells.length <= 1) {
|
||||
// const startRow = isRegularCell(currentCell) ? currentCell[0] : loadedAndInsertedRows.length;
|
||||
// const startCol = isRegularCell(currentCell) ? currentCell[1] : 0;
|
||||
// let rowIndex = startRow;
|
||||
// for (const rowData of pasteRows) {
|
||||
// if (rowIndex >= allRows.length) {
|
||||
// chs = changeSetInsertNewRow(chs, display.baseTable);
|
||||
// allRows = [...loadedRows, ...getChangeSetInsertedRows(chs, display.baseTable)];
|
||||
// }
|
||||
// let colIndex = startCol;
|
||||
// const row = allRows[rowIndex];
|
||||
// for (const cell of rowData) {
|
||||
// chs = setChangeSetValue(
|
||||
// chs,
|
||||
// display.getChangeSetField(
|
||||
// row,
|
||||
// realColumnUniqueNames[colIndex],
|
||||
// rowIndex >= loadedRows.length ? rowIndex - loadedRows.length : null
|
||||
// ),
|
||||
// cell == '(NULL)' ? null : cell
|
||||
// );
|
||||
// colIndex += 1;
|
||||
// }
|
||||
// rowIndex += 1;
|
||||
// }
|
||||
// }
|
||||
// if (selectedCells.length > 1) {
|
||||
// const regularSelected = selectedCells.filter(isRegularCell);
|
||||
// const startRow = _.min(regularSelected.map((x) => x[0]));
|
||||
// const startCol = _.min(regularSelected.map((x) => x[1]));
|
||||
// for (const cell of regularSelected) {
|
||||
// const [rowIndex, colIndex] = cell;
|
||||
// const selectionRow = rowIndex - startRow;
|
||||
// const selectionCol = colIndex - startCol;
|
||||
// const pasteRow = pasteRows[selectionRow % pasteRows.length];
|
||||
// const pasteCell = pasteRow[selectionCol % pasteRow.length];
|
||||
// chs = setCellValue(chs, cell, pasteCell);
|
||||
// }
|
||||
// }
|
||||
// setChangeSet(chs);
|
||||
}
|
||||
|
||||
function setNull() {
|
||||
let chs = changeSet;
|
||||
selectedCells.filter(isRegularCell).forEach((cell) => {
|
||||
chs = setCellValue(chs, cell, null);
|
||||
});
|
||||
setChangeSet(chs);
|
||||
// let chs = changeSet;
|
||||
// selectedCells.filter(isRegularCell).forEach((cell) => {
|
||||
// chs = setCellValue(chs, cell, null);
|
||||
// });
|
||||
// setChangeSet(chs);
|
||||
}
|
||||
|
||||
function cellsToRegularCells(cells) {
|
||||
@ -746,7 +553,7 @@ export default function DataGridCore(props) {
|
||||
const rowIndexes = _.sortBy(_.uniq(cells.map((x) => x[0])));
|
||||
const lines = rowIndexes.map((rowIndex) => {
|
||||
let colIndexes = _.sortBy(cells.filter((x) => x[0] == rowIndex).map((x) => x[1]));
|
||||
const rowData = loadedAndInsertedRows[rowIndex];
|
||||
const rowData = rows[rowIndex];
|
||||
if (!rowData) return '';
|
||||
const line = colIndexes
|
||||
.map((col) => realColumnUniqueNames[col])
|
||||
@ -786,7 +593,7 @@ export default function DataGridCore(props) {
|
||||
const rowIndexes = _.uniq((autofillSelectedCells || []).map((x) => x[0])).filter((x) => x != currentRowNumber);
|
||||
// @ts-ignore
|
||||
const colNames = selectedCells.map((cell) => realColumnUniqueNames[cell[1]]);
|
||||
const changeObject = _.pick(loadedAndInsertedRows[currentRowNumber], colNames);
|
||||
const changeObject = _.pick(rows[currentRowNumber], colNames);
|
||||
setChangeSet(
|
||||
batchUpdateChangeSet(
|
||||
changeSet,
|
||||
@ -805,13 +612,13 @@ export default function DataGridCore(props) {
|
||||
|
||||
function getRowDefinitions(rowIndexes) {
|
||||
const res = [];
|
||||
if (!loadedAndInsertedRows) return res;
|
||||
for (const index of rowIndexes) {
|
||||
if (loadedAndInsertedRows[index] && _.isNumber(index)) {
|
||||
const insertedRowIndex = index >= loadedRows.length ? index - loadedRows.length : null;
|
||||
res.push(display.getChangeSetRow(loadedAndInsertedRows[index], insertedRowIndex));
|
||||
}
|
||||
}
|
||||
// if (!loadedAndInsertedRows) return res;
|
||||
// for (const index of rowIndexes) {
|
||||
// if (loadedAndInsertedRows[index] && _.isNumber(index)) {
|
||||
// const insertedRowIndex = index >= loadedRows.length ? index - loadedRows.length : null;
|
||||
// res.push(display.getChangeSetRow(loadedAndInsertedRows[index], insertedRowIndex));
|
||||
// }
|
||||
// }
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -824,7 +631,7 @@ export default function DataGridCore(props) {
|
||||
}
|
||||
|
||||
function getSelectedRowData() {
|
||||
return _.compact(getSelectedRowIndexes().map((index) => loadedRows && loadedRows[index]));
|
||||
return _.compact(getSelectedRowIndexes().map((index) => rows && rows[index]));
|
||||
}
|
||||
|
||||
function revertRowChanges() {
|
||||
@ -841,7 +648,7 @@ export default function DataGridCore(props) {
|
||||
if (!isRegularCell(cell)) continue;
|
||||
const modelIndex = columnSizes.realToModel(cell[1]);
|
||||
const columnName = columns[modelIndex].uniqueName;
|
||||
let value = loadedRows[cell[0]][columnName];
|
||||
let value = rows[cell[0]][columnName];
|
||||
let svalue = getFilterValueExpression(value, columns[modelIndex].dataType);
|
||||
if (_.has(flts, columnName)) flts[columnName] += ',' + svalue;
|
||||
else flts[columnName] = svalue;
|
||||
@ -850,21 +657,6 @@ export default function DataGridCore(props) {
|
||||
display.setFilters(flts);
|
||||
}
|
||||
|
||||
function openQuery() {
|
||||
openNewTab(setOpenedTabs, {
|
||||
title: 'Query',
|
||||
icon: 'sql.svg',
|
||||
tabComponent: 'QueryTab',
|
||||
props: {
|
||||
initialScript: display.getExportQuery(),
|
||||
schemaName: display.baseTable.schemaName,
|
||||
pureName: display.baseTable.pureName,
|
||||
conid,
|
||||
database,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function revertAllChanges() {
|
||||
setChangeSet(createChangeSet());
|
||||
}
|
||||
@ -882,7 +674,7 @@ export default function DataGridCore(props) {
|
||||
if (event.deltaY < 0) {
|
||||
newFirstVisibleRowScrollIndex -= wheelRowCount;
|
||||
}
|
||||
let rowCount = rowCountNewIncluded;
|
||||
let rowCount = rows.length;
|
||||
if (newFirstVisibleRowScrollIndex + visibleRowCountLowerBound > rowCount) {
|
||||
newFirstVisibleRowScrollIndex = rowCount - visibleRowCountLowerBound + 1;
|
||||
}
|
||||
@ -895,58 +687,44 @@ export default function DataGridCore(props) {
|
||||
setvScrollValueToSetDate(new Date());
|
||||
}
|
||||
|
||||
// async function blurEditorAndSave() {
|
||||
// setInplaceEditorCell(null);
|
||||
// setInplaceEditorInitText(null);
|
||||
// await sleep(1);
|
||||
// }
|
||||
|
||||
function undo() {
|
||||
dispatchChangeSet({ type: 'undo' });
|
||||
}
|
||||
function redo() {
|
||||
dispatchChangeSet({ type: 'redo' });
|
||||
}
|
||||
|
||||
function handleSave() {
|
||||
if (inplaceEditorState.cell) {
|
||||
// @ts-ignore
|
||||
dispatchInsplaceEditor({ type: 'shouldSave' });
|
||||
return;
|
||||
}
|
||||
const script = changeSetToSql(changeSetRef.current, display.dbinfo);
|
||||
const sql = scriptToSql(display.driver, script);
|
||||
setConfirmSql(sql);
|
||||
confirmSqlModalState.open();
|
||||
// const script = changeSetToSql(changeSetRef.current, display.dbinfo);
|
||||
// const sql = scriptToSql(display.driver, script);
|
||||
// setConfirmSql(sql);
|
||||
// confirmSqlModalState.open();
|
||||
}
|
||||
|
||||
async function handleConfirmSql() {
|
||||
const resp = await axios.request({
|
||||
url: 'database-connections/query-data',
|
||||
method: 'post',
|
||||
params: {
|
||||
conid,
|
||||
database,
|
||||
},
|
||||
data: { sql: confirmSql },
|
||||
});
|
||||
|
||||
const { errorMessage } = resp.data || {};
|
||||
if (errorMessage) {
|
||||
showModal((modalState) => (
|
||||
<ErrorMessageModal modalState={modalState} message={errorMessage} title="Error when saving" />
|
||||
));
|
||||
} else {
|
||||
dispatchChangeSet({ type: 'reset', value: createChangeSet() });
|
||||
setConfirmSql(null);
|
||||
display.reload();
|
||||
}
|
||||
// const resp = await axios.request({
|
||||
// url: 'database-connections/query-data',
|
||||
// method: 'post',
|
||||
// params: {
|
||||
// conid,
|
||||
// database,
|
||||
// },
|
||||
// data: { sql: confirmSql },
|
||||
// });
|
||||
// const { errorMessage } = resp.data || {};
|
||||
// if (errorMessage) {
|
||||
// showModal((modalState) => (
|
||||
// <ErrorMessageModal modalState={modalState} message={errorMessage} title="Error when saving" />
|
||||
// ));
|
||||
// } else {
|
||||
// dispatchChangeSet({ type: 'reset', value: createChangeSet() });
|
||||
// setConfirmSql(null);
|
||||
// display.reload();
|
||||
// }
|
||||
}
|
||||
|
||||
const insertNewRow = () => {
|
||||
if (display.baseTable) {
|
||||
setChangeSet(changeSetInsertNewRow(changeSet, display.baseTable));
|
||||
const cell = [rowCountNewIncluded, (currentCell && currentCell[1]) || 0];
|
||||
const cell = [rows.length, (currentCell && currentCell[1]) || 0];
|
||||
// @ts-ignore
|
||||
setCurrentCell(cell);
|
||||
// @ts-ignore
|
||||
@ -1060,7 +838,7 @@ export default function DataGridCore(props) {
|
||||
|
||||
function handleCursorMove(event) {
|
||||
if (!isRegularCell(currentCell)) return null;
|
||||
let rowCount = rowCountNewIncluded;
|
||||
let rowCount = rows.length;
|
||||
if (event.ctrlKey) {
|
||||
switch (event.keyCode) {
|
||||
case keycodes.upArrow:
|
||||
@ -1118,7 +896,7 @@ export default function DataGridCore(props) {
|
||||
}
|
||||
|
||||
function moveCurrentCell(row, col, event = null) {
|
||||
const rowCount = rowCountNewIncluded;
|
||||
const rowCount = rows.length;
|
||||
|
||||
if (row < 0) row = 0;
|
||||
if (row >= rowCount) row = rowCount - 1;
|
||||
@ -1140,7 +918,7 @@ export default function DataGridCore(props) {
|
||||
|
||||
if (row != null) {
|
||||
let newRow = null;
|
||||
const rowCount = rowCountNewIncluded;
|
||||
const rowCount = rows.length;
|
||||
if (rowCount == 0) return;
|
||||
|
||||
if (row < firstVisibleRowScrollIndex) newRow = row;
|
||||
@ -1202,7 +980,7 @@ export default function DataGridCore(props) {
|
||||
// columnSizes.getVisibleScrollSizeSum()
|
||||
// );
|
||||
|
||||
const loadedAndInsertedRows = [...loadedRows, ...insertedRows];
|
||||
// const loadedAndInsertedRows = [...loadedRows, ...insertedRows];
|
||||
|
||||
// console.log('focusFieldRef.current', focusFieldRef.current);
|
||||
|
||||
@ -1282,7 +1060,7 @@ export default function DataGridCore(props) {
|
||||
)}
|
||||
</TableHead>
|
||||
<TableBody ref={tableBodyRef}>
|
||||
{loadedAndInsertedRows
|
||||
{rows
|
||||
.slice(firstVisibleRowScrollIndex, firstVisibleRowScrollIndex + visibleRowCountUpperBound)
|
||||
.map((row, index) => (
|
||||
<DataGridRow
|
||||
@ -1295,8 +1073,8 @@ export default function DataGridCore(props) {
|
||||
autofillSelectedCells={autofillSelectedCells}
|
||||
selectedCells={filterCellsForRow(selectedCells, firstVisibleRowScrollIndex + index)}
|
||||
insertedRowIndex={
|
||||
firstVisibleRowScrollIndex + index >= loadedRows.length
|
||||
? firstVisibleRowScrollIndex + index - loadedRows.length
|
||||
firstVisibleRowScrollIndex + index >= rows.length - (insertedRowCount || 0)
|
||||
? firstVisibleRowScrollIndex + index - rows.length - (insertedRowCount || 0)
|
||||
: null
|
||||
}
|
||||
autofillMarkerCell={filterCellForRow(autofillMarkerCell, firstVisibleRowScrollIndex + index)}
|
||||
@ -1321,9 +1099,9 @@ export default function DataGridCore(props) {
|
||||
valueToSet={vScrollValueToSet}
|
||||
valueToSetDate={vScrollValueToSetDate}
|
||||
minimum={0}
|
||||
maximum={rowCountNewIncluded - visibleRowCountUpperBound + 2}
|
||||
maximum={rows.length - visibleRowCountUpperBound + 2}
|
||||
onScroll={handleRowScroll}
|
||||
viewportRatio={visibleRowCountUpperBound / rowCountNewIncluded}
|
||||
viewportRatio={visibleRowCountUpperBound / rows.length}
|
||||
/>
|
||||
<ConfirmSqlModal
|
||||
modalState={confirmSqlModalState}
|
||||
|
@ -1,6 +1,263 @@
|
||||
import React from 'react';
|
||||
import axios from '../utility/axios';
|
||||
import { useSetOpenedTabs } from '../utility/globalState';
|
||||
import DataGridCore from './DataGridCore';
|
||||
import useSocket from '../utility/SocketProvider';
|
||||
import useShowModal from '../modals/showModal';
|
||||
import ImportExportModal from '../modals/ImportExportModal';
|
||||
import { getChangeSetInsertedRows } from '@dbgate/datalib';
|
||||
import ChangeSetDataGrid from './ChangeSetDataGrid';
|
||||
import { openNewTab } from '../utility/common';
|
||||
|
||||
/** @param props {import('./types').LoadingDataGridProps} */
|
||||
async function loadDataPage(props, offset, limit) {
|
||||
const { display, conid, database, jslid } = props;
|
||||
|
||||
if (jslid) {
|
||||
const response = await axios.request({
|
||||
url: 'jsldata/get-rows',
|
||||
method: 'get',
|
||||
params: {
|
||||
jslid,
|
||||
offset,
|
||||
limit,
|
||||
},
|
||||
});
|
||||
return response.data;
|
||||
}
|
||||
|
||||
const sql = display.getPageQuery(offset, limit);
|
||||
|
||||
const response = await axios.request({
|
||||
url: 'database-connections/query-data',
|
||||
method: 'post',
|
||||
params: {
|
||||
conid,
|
||||
database,
|
||||
},
|
||||
data: { sql },
|
||||
});
|
||||
|
||||
if (response.data.errorMessage) return response.data;
|
||||
return response.data.rows;
|
||||
}
|
||||
|
||||
function dataPageAvailable(props) {
|
||||
const { display, jslid } = props;
|
||||
if (jslid) return true;
|
||||
const sql = display.getPageQuery(0, 1);
|
||||
return !!sql;
|
||||
}
|
||||
|
||||
/** @param props {import('./types').LoadingDataGridProps} */
|
||||
async function loadRowCount(props) {
|
||||
const { display, conid, database, jslid } = props;
|
||||
|
||||
if (jslid) {
|
||||
const response = await axios.request({
|
||||
url: 'jsldata/get-stats',
|
||||
method: 'get',
|
||||
params: {
|
||||
jslid,
|
||||
},
|
||||
});
|
||||
return response.data.rowCount;
|
||||
}
|
||||
|
||||
const sql = display.getCountQuery();
|
||||
|
||||
const response = await axios.request({
|
||||
url: 'database-connections/query-data',
|
||||
method: 'post',
|
||||
params: {
|
||||
conid,
|
||||
database,
|
||||
},
|
||||
data: { sql },
|
||||
});
|
||||
|
||||
return parseInt(response.data.rows[0].count);
|
||||
}
|
||||
|
||||
export default function LoadingDataGrid(props) {
|
||||
return <DataGridCore {...props} />;
|
||||
const { conid, database, display, changeSetState, dispatchChangeSet, tabVisible, jslid } = props;
|
||||
|
||||
const [loadProps, setLoadProps] = React.useState({
|
||||
isLoading: false,
|
||||
loadedRows: [],
|
||||
isLoadedAll: false,
|
||||
loadedTime: new Date().getTime(),
|
||||
allRowCount: null,
|
||||
errorMessage: null,
|
||||
jslStatsCounter: 0,
|
||||
jslChangeIndex: 0,
|
||||
});
|
||||
const { isLoading, loadedRows, isLoadedAll, loadedTime, allRowCount, errorMessage } = loadProps;
|
||||
const showModal = useShowModal();
|
||||
|
||||
const loadedTimeRef = React.useRef(0);
|
||||
|
||||
const changeSet = changeSetState && changeSetState.value;
|
||||
const setChangeSet = React.useCallback((value) => dispatchChangeSet({ type: 'set', value }), [dispatchChangeSet]);
|
||||
const setOpenedTabs = useSetOpenedTabs();
|
||||
const socket = useSocket();
|
||||
|
||||
const changeSetRef = React.useRef(changeSet);
|
||||
|
||||
changeSetRef.current = changeSet;
|
||||
|
||||
const handleLoadRowCount = async () => {
|
||||
const rowCount = await loadRowCount(props);
|
||||
setLoadProps((oldLoadProps) => ({
|
||||
...oldLoadProps,
|
||||
allRowCount: rowCount,
|
||||
}));
|
||||
};
|
||||
|
||||
const reload = () => {
|
||||
setLoadProps({
|
||||
allRowCount: null,
|
||||
isLoading: false,
|
||||
loadedRows: [],
|
||||
isLoadedAll: false,
|
||||
loadedTime: new Date().getTime(),
|
||||
errorMessage: null,
|
||||
jslStatsCounter: 0,
|
||||
jslChangeIndex: 0,
|
||||
});
|
||||
};
|
||||
|
||||
function exportGrid() {
|
||||
const initialValues = {};
|
||||
if (jslid) {
|
||||
const archiveMatch = jslid.match(/^archive:\/\/([^/]+)\/(.*)$/);
|
||||
if (archiveMatch) {
|
||||
initialValues.sourceStorageType = 'archive';
|
||||
initialValues.sourceArchiveFolder = archiveMatch[1];
|
||||
initialValues.sourceList = [archiveMatch[2]];
|
||||
} else {
|
||||
initialValues.sourceStorageType = 'jsldata';
|
||||
initialValues.sourceJslId = jslid;
|
||||
initialValues.sourceList = ['query-data'];
|
||||
}
|
||||
} else {
|
||||
initialValues.sourceStorageType = 'query';
|
||||
initialValues.sourceConnectionId = conid;
|
||||
initialValues.sourceDatabaseName = database;
|
||||
initialValues.sourceSql = display.getExportQuery();
|
||||
initialValues.sourceList = display.baseTable ? [display.baseTable.pureName] : [];
|
||||
}
|
||||
showModal((modalState) => <ImportExportModal modalState={modalState} initialValues={initialValues} />);
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
if (props.masterLoadedTime && props.masterLoadedTime > loadedTime) {
|
||||
display.reload();
|
||||
}
|
||||
if (display.cache.refreshTime > loadedTime) {
|
||||
reload();
|
||||
}
|
||||
});
|
||||
|
||||
const loadNextData = async () => {
|
||||
if (isLoading) return;
|
||||
setLoadProps((oldLoadProps) => ({
|
||||
...oldLoadProps,
|
||||
isLoading: true,
|
||||
}));
|
||||
const loadStart = new Date().getTime();
|
||||
loadedTimeRef.current = loadStart;
|
||||
|
||||
const nextRows = await loadDataPage(props, loadedRows.length, 100);
|
||||
if (loadedTimeRef.current !== loadStart) {
|
||||
// new load was dispatched
|
||||
return;
|
||||
}
|
||||
// if (!_.isArray(nextRows)) {
|
||||
// console.log('Error loading data from server', nextRows);
|
||||
// nextRows = [];
|
||||
// }
|
||||
// console.log('nextRows', nextRows);
|
||||
if (nextRows.errorMessage) {
|
||||
setLoadProps((oldLoadProps) => ({
|
||||
...oldLoadProps,
|
||||
isLoading: false,
|
||||
errorMessage: nextRows.errorMessage,
|
||||
}));
|
||||
} else {
|
||||
if (allRowCount == null) handleLoadRowCount();
|
||||
const loadedInfo = {
|
||||
loadedRows: [...loadedRows, ...nextRows],
|
||||
loadedTime,
|
||||
};
|
||||
setLoadProps((oldLoadProps) => ({
|
||||
...oldLoadProps,
|
||||
isLoading: false,
|
||||
isLoadedAll: oldLoadProps.jslStatsCounter == loadProps.jslStatsCounter && nextRows.length === 0,
|
||||
...loadedInfo,
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
const handleJslDataStats = React.useCallback((stats) => {
|
||||
if (stats.changeIndex < loadProps.jslChangeIndex) return;
|
||||
setLoadProps((oldProps) => ({
|
||||
...oldProps,
|
||||
allRowCount: stats.rowCount,
|
||||
isLoadedAll: false,
|
||||
jslStatsCounter: oldProps.jslStatsCounter + 1,
|
||||
jslChangeIndex: stats.changeIndex,
|
||||
}));
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (jslid && socket) {
|
||||
socket.on(`jsldata-stats-${jslid}`, handleJslDataStats);
|
||||
return () => {
|
||||
socket.off(`jsldata-stats-${jslid}`, handleJslDataStats);
|
||||
};
|
||||
}
|
||||
}, [jslid]);
|
||||
|
||||
const insertedRows = getChangeSetInsertedRows(changeSet, display.baseTable);
|
||||
const rowCountNewIncluded = loadedRows.length + insertedRows.length;
|
||||
|
||||
const handleLoadNextData = () => {
|
||||
if (!isLoadedAll && !errorMessage && insertedRows.length == 0) {
|
||||
if (dataPageAvailable(props)) {
|
||||
// If not, callbacks to load missing metadata are dispatched
|
||||
loadNextData();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function openQuery() {
|
||||
openNewTab(setOpenedTabs, {
|
||||
title: 'Query',
|
||||
icon: 'sql.svg',
|
||||
tabComponent: 'QueryTab',
|
||||
props: {
|
||||
initialScript: display.getExportQuery(),
|
||||
schemaName: display.baseTable.schemaName,
|
||||
pureName: display.baseTable.pureName,
|
||||
conid,
|
||||
database,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<ChangeSetDataGrid
|
||||
{...props}
|
||||
loadNextData={handleLoadNextData}
|
||||
errorMessage={errorMessage}
|
||||
isLoadedAll={isLoadedAll}
|
||||
loadedTime={loadedTime}
|
||||
exportGrid={exportGrid}
|
||||
allRowCount={allRowCount}
|
||||
openQuery={display.baseTable ? openQuery : null}
|
||||
isLoading={isLoading}
|
||||
rows={loadedRows}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -1,14 +1,11 @@
|
||||
import { GridDisplay, ChangeSet, GridReferenceDefinition } from '@dbgate/datalib';
|
||||
|
||||
export interface DataGridProps {
|
||||
conid?: string;
|
||||
database?: string;
|
||||
display: GridDisplay;
|
||||
tabVisible?: boolean;
|
||||
changeSetState?: { value: ChangeSet };
|
||||
dispatchChangeSet?: Function;
|
||||
toolbarPortalRef?: any;
|
||||
jslid?: string;
|
||||
showReferences?: boolean;
|
||||
onReferenceClick?: (def: GridReferenceDefinition) => void;
|
||||
onReferenceSourceChanged?: Function;
|
||||
@ -16,3 +13,27 @@ export interface DataGridProps {
|
||||
masterLoadedTime?: number;
|
||||
managerSize?: number;
|
||||
}
|
||||
|
||||
export interface DataGridCoreProps extends DataGridProps {
|
||||
rows: any[];
|
||||
loadNextData?: Function;
|
||||
exportGrid?: Function;
|
||||
openQuery?: Function;
|
||||
undo?: Function;
|
||||
redo?: Function;
|
||||
|
||||
errorMessage?: string;
|
||||
isLoadedAll?: boolean;
|
||||
loadedTime?: any;
|
||||
allRowCount?: number;
|
||||
conid?: string;
|
||||
database?: string;
|
||||
insertedRowCount?: number;
|
||||
isLoading?: boolean;
|
||||
}
|
||||
|
||||
export interface LoadingDataGridProps extends DataGridProps {
|
||||
conid?: string;
|
||||
database?: string;
|
||||
jslid?: string;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user