data grid refactor - working read only

This commit is contained in:
Jan Prochazka 2020-10-22 15:22:58 +02:00
parent 7704e9b305
commit b439c7bb70
5 changed files with 453 additions and 380 deletions

View 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} />;
}

View File

@ -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);

View File

@ -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}

View File

@ -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}
/>
);
}

View File

@ -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;
}