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; flex-grow: 1;
`; `;
/** @param props {import('./types').DataGridProps} */ /** @param props {import('./types').LoadingDataGridProps} */
export default function DataGrid(props) { export default function DataGrid(props) {
const Container1 = props.showReferences ? ManagerOuterContainer1 : ManagerOuterContainerFull; const Container1 = props.showReferences ? ManagerOuterContainer1 : ManagerOuterContainerFull;
const [managerSize, setManagerSize] = React.useState(0); const [managerSize, setManagerSize] = React.useState(0);

View File

@@ -126,98 +126,35 @@ const LoadingInfoBox = styled.div`
border: 1px solid gray; border: 1px solid gray;
`; `;
/** @param props {import('./types').DataGridProps} */ /** @param props {import('./types').DataGridCoreProps} */
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} */
export default function DataGridCore(props) { 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); // console.log('RENDER GRID', display.baseTable.pureName);
const columns = React.useMemo(() => display.allColumns, [display]); const columns = React.useMemo(() => display.allColumns, [display]);
// usePropsCompare(props); // usePropsCompare(props);
// console.log(`GRID, conid=${conid}, database=${database}, sql=${sql}`); // 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 focusFieldRef = React.useRef(null);
const [vScrollValueToSet, setvScrollValueToSet] = React.useState(); const [vScrollValueToSet, setvScrollValueToSet] = React.useState();
@@ -239,14 +176,6 @@ export default function DataGridCore(props) {
// const [inplaceEditorShouldSave, setInplaceEditorShouldSave] = React.useState(false); // const [inplaceEditorShouldSave, setInplaceEditorShouldSave] = React.useState(false);
// const [inplaceEditorChangedOnCreate, setInplaceEditorChangedOnCreate] = 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( const autofillMarkerCell = React.useMemo(
() => () =>
selectedCells && selectedCells.length > 0 && _.uniq(selectedCells.map((x) => x[0])).length == 1 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 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({ // const data = useFetch({
// url: 'database-connections/query-data', // url: 'database-connections/query-data',
// method: 'post', // method: 'post',
@@ -317,7 +198,6 @@ export default function DataGridCore(props) {
// const { rows, columns } = data || {}; // const { rows, columns } = data || {};
const [firstVisibleRowScrollIndex, setFirstVisibleRowScrollIndex] = React.useState(0); const [firstVisibleRowScrollIndex, setFirstVisibleRowScrollIndex] = React.useState(0);
const [firstVisibleColumnScrollIndex, setFirstVisibleColumnScrollIndex] = React.useState(0); const [firstVisibleColumnScrollIndex, setFirstVisibleColumnScrollIndex] = React.useState(0);
const socket = useSocket();
const [headerRowRef, { height: rowHeight }] = useDimensions(); const [headerRowRef, { height: rowHeight }] = useDimensions();
const [tableBodyRef] = useDimensions(); const [tableBodyRef] = useDimensions();
@@ -326,6 +206,9 @@ export default function DataGridCore(props) {
const confirmSqlModalState = useModalState(); const confirmSqlModalState = useModalState();
const [confirmSql, setConfirmSql] = React.useState(''); 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) => { const [inplaceEditorState, dispatchInsplaceEditor] = React.useReducer((state, action) => {
switch (action.type) { switch (action.type) {
case 'show': case 'show':
@@ -355,8 +238,8 @@ export default function DataGridCore(props) {
// usePropsCompare({ loadedRows, columns, containerWidth, display }); // usePropsCompare({ loadedRows, columns, containerWidth, display });
const columnSizes = React.useMemo(() => countColumnSizes(loadedRows, columns, containerWidth, display), [ const columnSizes = React.useMemo(() => countColumnSizes(rows, columns, containerWidth, display), [
loadedRows, rows,
columns, columns,
containerWidth, containerWidth,
display, display,
@@ -376,43 +259,6 @@ export default function DataGridCore(props) {
// console.log('visibleRowCountUpperBound', visibleRowCountUpperBound); // console.log('visibleRowCountUpperBound', visibleRowCountUpperBound);
// console.log('rowHeight', rowHeight); // 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(() => { React.useEffect(() => {
if (tabVisible) { if (tabVisible) {
if (focusFieldRef.current) focusFieldRef.current.focus(); if (focusFieldRef.current) focusFieldRef.current.focus();
@@ -424,31 +270,11 @@ export default function DataGridCore(props) {
return newColumn; return newColumn;
}, [columnSizes, gridScrollAreaWidth]); }, [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(() => { React.useEffect(() => {
if (jslid && socket) { if (props.onReferenceSourceChanged && ((rows && rows.length > 0) || isLoadedAll)) {
socket.on(`jsldata-stats-${jslid}`, handleJslDataStats);
return () => {
socket.off(`jsldata-stats-${jslid}`, handleJslDataStats);
};
}
}, [jslid]);
React.useEffect(() => {
if (props.onReferenceSourceChanged && ((loadedRows && loadedRows.length > 0) || isLoadedAll)) {
props.onReferenceSourceChanged(getSelectedRowData(), loadedTime); props.onReferenceSourceChanged(getSelectedRowData(), loadedTime);
} }
}, [selectedCells, props.refReloadToken, loadedRows && loadedRows[0]]); }, [selectedCells, props.refReloadToken, rows && rows[0]]);
// const handleCloseInplaceEditor = React.useCallback( // const handleCloseInplaceEditor = React.useCallback(
// mode => { // mode => {
@@ -487,6 +313,12 @@ export default function DataGridCore(props) {
} }
}, [display && display.focusedColumn]); }, [display && display.focusedColumn]);
React.useEffect(() => {
if (rows && loadNextData && firstVisibleRowScrollIndex + visibleRowCountUpperBound >= rows.length) {
loadNextData();
}
});
React.useEffect(() => { React.useEffect(() => {
if (display.groupColumns) { if (display.groupColumns) {
props.onReferenceClick({ props.onReferenceClick({
@@ -504,7 +336,7 @@ export default function DataGridCore(props) {
const rowCountInfo = React.useMemo(() => { const rowCountInfo = React.useMemo(() => {
if (selectedCells.length > 1 && selectedCells.every((x) => _.isNumber(x[0]) && _.isNumber(x[1]))) { if (selectedCells.length > 1 && selectedCells.every((x) => _.isNumber(x[0]) && _.isNumber(x[1]))) {
let sum = _.sumBy(selectedCells, (cell) => { let sum = _.sumBy(selectedCells, (cell) => {
const row = loadedRows[cell[0]]; const row = rows[cell[0]];
if (row) { if (row) {
const colName = realColumnUniqueNames[cell[1]]; const colName = realColumnUniqueNames[cell[1]];
if (colName) { if (colName) {
@@ -526,9 +358,9 @@ export default function DataGridCore(props) {
// if (this.isLoadingFirstPage) return "Loading first page..."; // if (this.isLoadingFirstPage) return "Loading first page...";
// if (this.isFirstPageError) return "Error loading first page"; // if (this.isFirstPageError) return "Error loading first page";
// return `Rows: ${this.rowCount.toLocaleString()}`; // 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 ( return (
<LoadingInfoWrapper> <LoadingInfoWrapper>
<LoadingInfoBox> <LoadingInfoBox>
@@ -563,7 +395,7 @@ export default function DataGridCore(props) {
setNull={setNull} setNull={setNull}
exportGrid={exportGrid} exportGrid={exportGrid}
filterSelectedValue={filterSelectedValue} filterSelectedValue={filterSelectedValue}
openQuery={display.baseTable ? openQuery : null} openQuery={openQuery}
/> />
); );
}; };
@@ -617,108 +449,83 @@ export default function DataGridCore(props) {
copyToClipboard(); 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) { function setCellValue(chs, cell, value) {
return setChangeSetValue( // return setChangeSetValue(
chs, // chs,
display.getChangeSetField( // display.getChangeSetField(
loadedAndInsertedRows[cell[0]], // rows[cell[0]],
realColumnUniqueNames[cell[1]], // realColumnUniqueNames[cell[1]],
cell[0] >= loadedRows.length ? cell[0] - loadedRows.length : null // cell[0] >= rows.length ? cell[0] - rows.length : null
), // ),
value // value
); // );
} }
function handlePaste(event) { function handlePaste(event) {
var pastedText = undefined; // var pastedText = undefined;
// @ts-ignore // // @ts-ignore
if (window.clipboardData && window.clipboardData.getData) { // if (window.clipboardData && window.clipboardData.getData) {
// IE // // IE
// @ts-ignore // // @ts-ignore
pastedText = window.clipboardData.getData('Text'); // pastedText = window.clipboardData.getData('Text');
} else if (event.clipboardData && event.clipboardData.getData) { // } else if (event.clipboardData && event.clipboardData.getData) {
pastedText = event.clipboardData.getData('text/plain'); // pastedText = event.clipboardData.getData('text/plain');
} // }
event.preventDefault(); // event.preventDefault();
const pasteRows = pastedText // const pasteRows = pastedText
.replace(/\r/g, '') // .replace(/\r/g, '')
.split('\n') // .split('\n')
.map((row) => row.split('\t')); // .map((row) => row.split('\t'));
let chs = changeSet; // let chs = changeSet;
let allRows = loadedAndInsertedRows; // let allRows = loadedAndInsertedRows;
// if (selectedCells.length <= 1) {
if (selectedCells.length <= 1) { // const startRow = isRegularCell(currentCell) ? currentCell[0] : loadedAndInsertedRows.length;
const startRow = isRegularCell(currentCell) ? currentCell[0] : loadedAndInsertedRows.length; // const startCol = isRegularCell(currentCell) ? currentCell[1] : 0;
const startCol = isRegularCell(currentCell) ? currentCell[1] : 0; // let rowIndex = startRow;
let rowIndex = startRow; // for (const rowData of pasteRows) {
for (const rowData of pasteRows) { // if (rowIndex >= allRows.length) {
if (rowIndex >= allRows.length) { // chs = changeSetInsertNewRow(chs, display.baseTable);
chs = changeSetInsertNewRow(chs, display.baseTable); // allRows = [...loadedRows, ...getChangeSetInsertedRows(chs, display.baseTable)];
allRows = [...loadedRows, ...getChangeSetInsertedRows(chs, display.baseTable)]; // }
} // let colIndex = startCol;
let colIndex = startCol; // const row = allRows[rowIndex];
const row = allRows[rowIndex]; // for (const cell of rowData) {
for (const cell of rowData) { // chs = setChangeSetValue(
chs = setChangeSetValue( // chs,
chs, // display.getChangeSetField(
display.getChangeSetField( // row,
row, // realColumnUniqueNames[colIndex],
realColumnUniqueNames[colIndex], // rowIndex >= loadedRows.length ? rowIndex - loadedRows.length : null
rowIndex >= loadedRows.length ? rowIndex - loadedRows.length : null // ),
), // cell == '(NULL)' ? null : cell
cell == '(NULL)' ? null : cell // );
); // colIndex += 1;
colIndex += 1; // }
} // rowIndex += 1;
rowIndex += 1; // }
} // }
} // if (selectedCells.length > 1) {
if (selectedCells.length > 1) { // const regularSelected = selectedCells.filter(isRegularCell);
const regularSelected = selectedCells.filter(isRegularCell); // const startRow = _.min(regularSelected.map((x) => x[0]));
const startRow = _.min(regularSelected.map((x) => x[0])); // const startCol = _.min(regularSelected.map((x) => x[1]));
const startCol = _.min(regularSelected.map((x) => x[1])); // for (const cell of regularSelected) {
for (const cell of regularSelected) { // const [rowIndex, colIndex] = cell;
const [rowIndex, colIndex] = cell; // const selectionRow = rowIndex - startRow;
const selectionRow = rowIndex - startRow; // const selectionCol = colIndex - startCol;
const selectionCol = colIndex - startCol; // const pasteRow = pasteRows[selectionRow % pasteRows.length];
const pasteRow = pasteRows[selectionRow % pasteRows.length]; // const pasteCell = pasteRow[selectionCol % pasteRow.length];
const pasteCell = pasteRow[selectionCol % pasteRow.length]; // chs = setCellValue(chs, cell, pasteCell);
chs = setCellValue(chs, cell, pasteCell); // }
} // }
} // setChangeSet(chs);
setChangeSet(chs);
} }
function setNull() { function setNull() {
let chs = changeSet; // let chs = changeSet;
selectedCells.filter(isRegularCell).forEach((cell) => { // selectedCells.filter(isRegularCell).forEach((cell) => {
chs = setCellValue(chs, cell, null); // chs = setCellValue(chs, cell, null);
}); // });
setChangeSet(chs); // setChangeSet(chs);
} }
function cellsToRegularCells(cells) { function cellsToRegularCells(cells) {
@@ -746,7 +553,7 @@ export default function DataGridCore(props) {
const rowIndexes = _.sortBy(_.uniq(cells.map((x) => x[0]))); const rowIndexes = _.sortBy(_.uniq(cells.map((x) => x[0])));
const lines = rowIndexes.map((rowIndex) => { const lines = rowIndexes.map((rowIndex) => {
let colIndexes = _.sortBy(cells.filter((x) => x[0] == rowIndex).map((x) => x[1])); let colIndexes = _.sortBy(cells.filter((x) => x[0] == rowIndex).map((x) => x[1]));
const rowData = loadedAndInsertedRows[rowIndex]; const rowData = rows[rowIndex];
if (!rowData) return ''; if (!rowData) return '';
const line = colIndexes const line = colIndexes
.map((col) => realColumnUniqueNames[col]) .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); const rowIndexes = _.uniq((autofillSelectedCells || []).map((x) => x[0])).filter((x) => x != currentRowNumber);
// @ts-ignore // @ts-ignore
const colNames = selectedCells.map((cell) => realColumnUniqueNames[cell[1]]); const colNames = selectedCells.map((cell) => realColumnUniqueNames[cell[1]]);
const changeObject = _.pick(loadedAndInsertedRows[currentRowNumber], colNames); const changeObject = _.pick(rows[currentRowNumber], colNames);
setChangeSet( setChangeSet(
batchUpdateChangeSet( batchUpdateChangeSet(
changeSet, changeSet,
@@ -805,13 +612,13 @@ export default function DataGridCore(props) {
function getRowDefinitions(rowIndexes) { function getRowDefinitions(rowIndexes) {
const res = []; const res = [];
if (!loadedAndInsertedRows) return res; // if (!loadedAndInsertedRows) return res;
for (const index of rowIndexes) { // for (const index of rowIndexes) {
if (loadedAndInsertedRows[index] && _.isNumber(index)) { // if (loadedAndInsertedRows[index] && _.isNumber(index)) {
const insertedRowIndex = index >= loadedRows.length ? index - loadedRows.length : null; // const insertedRowIndex = index >= loadedRows.length ? index - loadedRows.length : null;
res.push(display.getChangeSetRow(loadedAndInsertedRows[index], insertedRowIndex)); // res.push(display.getChangeSetRow(loadedAndInsertedRows[index], insertedRowIndex));
} // }
} // }
return res; return res;
} }
@@ -824,7 +631,7 @@ export default function DataGridCore(props) {
} }
function getSelectedRowData() { function getSelectedRowData() {
return _.compact(getSelectedRowIndexes().map((index) => loadedRows && loadedRows[index])); return _.compact(getSelectedRowIndexes().map((index) => rows && rows[index]));
} }
function revertRowChanges() { function revertRowChanges() {
@@ -841,7 +648,7 @@ export default function DataGridCore(props) {
if (!isRegularCell(cell)) continue; if (!isRegularCell(cell)) continue;
const modelIndex = columnSizes.realToModel(cell[1]); const modelIndex = columnSizes.realToModel(cell[1]);
const columnName = columns[modelIndex].uniqueName; const columnName = columns[modelIndex].uniqueName;
let value = loadedRows[cell[0]][columnName]; let value = rows[cell[0]][columnName];
let svalue = getFilterValueExpression(value, columns[modelIndex].dataType); let svalue = getFilterValueExpression(value, columns[modelIndex].dataType);
if (_.has(flts, columnName)) flts[columnName] += ',' + svalue; if (_.has(flts, columnName)) flts[columnName] += ',' + svalue;
else flts[columnName] = svalue; else flts[columnName] = svalue;
@@ -850,21 +657,6 @@ export default function DataGridCore(props) {
display.setFilters(flts); 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() { function revertAllChanges() {
setChangeSet(createChangeSet()); setChangeSet(createChangeSet());
} }
@@ -882,7 +674,7 @@ export default function DataGridCore(props) {
if (event.deltaY < 0) { if (event.deltaY < 0) {
newFirstVisibleRowScrollIndex -= wheelRowCount; newFirstVisibleRowScrollIndex -= wheelRowCount;
} }
let rowCount = rowCountNewIncluded; let rowCount = rows.length;
if (newFirstVisibleRowScrollIndex + visibleRowCountLowerBound > rowCount) { if (newFirstVisibleRowScrollIndex + visibleRowCountLowerBound > rowCount) {
newFirstVisibleRowScrollIndex = rowCount - visibleRowCountLowerBound + 1; newFirstVisibleRowScrollIndex = rowCount - visibleRowCountLowerBound + 1;
} }
@@ -895,58 +687,44 @@ export default function DataGridCore(props) {
setvScrollValueToSetDate(new Date()); 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() { function handleSave() {
if (inplaceEditorState.cell) { if (inplaceEditorState.cell) {
// @ts-ignore // @ts-ignore
dispatchInsplaceEditor({ type: 'shouldSave' }); dispatchInsplaceEditor({ type: 'shouldSave' });
return; return;
} }
const script = changeSetToSql(changeSetRef.current, display.dbinfo); // const script = changeSetToSql(changeSetRef.current, display.dbinfo);
const sql = scriptToSql(display.driver, script); // const sql = scriptToSql(display.driver, script);
setConfirmSql(sql); // setConfirmSql(sql);
confirmSqlModalState.open(); // confirmSqlModalState.open();
} }
async function handleConfirmSql() { async function handleConfirmSql() {
const resp = await axios.request({ // const resp = await axios.request({
url: 'database-connections/query-data', // url: 'database-connections/query-data',
method: 'post', // method: 'post',
params: { // params: {
conid, // conid,
database, // database,
}, // },
data: { sql: confirmSql }, // data: { sql: confirmSql },
}); // });
// const { errorMessage } = resp.data || {};
const { errorMessage } = resp.data || {}; // if (errorMessage) {
if (errorMessage) { // showModal((modalState) => (
showModal((modalState) => ( // <ErrorMessageModal modalState={modalState} message={errorMessage} title="Error when saving" />
<ErrorMessageModal modalState={modalState} message={errorMessage} title="Error when saving" /> // ));
)); // } else {
} else { // dispatchChangeSet({ type: 'reset', value: createChangeSet() });
dispatchChangeSet({ type: 'reset', value: createChangeSet() }); // setConfirmSql(null);
setConfirmSql(null); // display.reload();
display.reload(); // }
}
} }
const insertNewRow = () => { const insertNewRow = () => {
if (display.baseTable) { if (display.baseTable) {
setChangeSet(changeSetInsertNewRow(changeSet, display.baseTable)); setChangeSet(changeSetInsertNewRow(changeSet, display.baseTable));
const cell = [rowCountNewIncluded, (currentCell && currentCell[1]) || 0]; const cell = [rows.length, (currentCell && currentCell[1]) || 0];
// @ts-ignore // @ts-ignore
setCurrentCell(cell); setCurrentCell(cell);
// @ts-ignore // @ts-ignore
@@ -1060,7 +838,7 @@ export default function DataGridCore(props) {
function handleCursorMove(event) { function handleCursorMove(event) {
if (!isRegularCell(currentCell)) return null; if (!isRegularCell(currentCell)) return null;
let rowCount = rowCountNewIncluded; let rowCount = rows.length;
if (event.ctrlKey) { if (event.ctrlKey) {
switch (event.keyCode) { switch (event.keyCode) {
case keycodes.upArrow: case keycodes.upArrow:
@@ -1118,7 +896,7 @@ export default function DataGridCore(props) {
} }
function moveCurrentCell(row, col, event = null) { function moveCurrentCell(row, col, event = null) {
const rowCount = rowCountNewIncluded; const rowCount = rows.length;
if (row < 0) row = 0; if (row < 0) row = 0;
if (row >= rowCount) row = rowCount - 1; if (row >= rowCount) row = rowCount - 1;
@@ -1140,7 +918,7 @@ export default function DataGridCore(props) {
if (row != null) { if (row != null) {
let newRow = null; let newRow = null;
const rowCount = rowCountNewIncluded; const rowCount = rows.length;
if (rowCount == 0) return; if (rowCount == 0) return;
if (row < firstVisibleRowScrollIndex) newRow = row; if (row < firstVisibleRowScrollIndex) newRow = row;
@@ -1202,7 +980,7 @@ export default function DataGridCore(props) {
// columnSizes.getVisibleScrollSizeSum() // columnSizes.getVisibleScrollSizeSum()
// ); // );
const loadedAndInsertedRows = [...loadedRows, ...insertedRows]; // const loadedAndInsertedRows = [...loadedRows, ...insertedRows];
// console.log('focusFieldRef.current', focusFieldRef.current); // console.log('focusFieldRef.current', focusFieldRef.current);
@@ -1282,7 +1060,7 @@ export default function DataGridCore(props) {
)} )}
</TableHead> </TableHead>
<TableBody ref={tableBodyRef}> <TableBody ref={tableBodyRef}>
{loadedAndInsertedRows {rows
.slice(firstVisibleRowScrollIndex, firstVisibleRowScrollIndex + visibleRowCountUpperBound) .slice(firstVisibleRowScrollIndex, firstVisibleRowScrollIndex + visibleRowCountUpperBound)
.map((row, index) => ( .map((row, index) => (
<DataGridRow <DataGridRow
@@ -1295,8 +1073,8 @@ export default function DataGridCore(props) {
autofillSelectedCells={autofillSelectedCells} autofillSelectedCells={autofillSelectedCells}
selectedCells={filterCellsForRow(selectedCells, firstVisibleRowScrollIndex + index)} selectedCells={filterCellsForRow(selectedCells, firstVisibleRowScrollIndex + index)}
insertedRowIndex={ insertedRowIndex={
firstVisibleRowScrollIndex + index >= loadedRows.length firstVisibleRowScrollIndex + index >= rows.length - (insertedRowCount || 0)
? firstVisibleRowScrollIndex + index - loadedRows.length ? firstVisibleRowScrollIndex + index - rows.length - (insertedRowCount || 0)
: null : null
} }
autofillMarkerCell={filterCellForRow(autofillMarkerCell, firstVisibleRowScrollIndex + index)} autofillMarkerCell={filterCellForRow(autofillMarkerCell, firstVisibleRowScrollIndex + index)}
@@ -1321,9 +1099,9 @@ export default function DataGridCore(props) {
valueToSet={vScrollValueToSet} valueToSet={vScrollValueToSet}
valueToSetDate={vScrollValueToSetDate} valueToSetDate={vScrollValueToSetDate}
minimum={0} minimum={0}
maximum={rowCountNewIncluded - visibleRowCountUpperBound + 2} maximum={rows.length - visibleRowCountUpperBound + 2}
onScroll={handleRowScroll} onScroll={handleRowScroll}
viewportRatio={visibleRowCountUpperBound / rowCountNewIncluded} viewportRatio={visibleRowCountUpperBound / rows.length}
/> />
<ConfirmSqlModal <ConfirmSqlModal
modalState={confirmSqlModalState} modalState={confirmSqlModalState}

View File

@@ -1,6 +1,263 @@
import React from 'react'; import React from 'react';
import axios from '../utility/axios';
import { useSetOpenedTabs } from '../utility/globalState';
import DataGridCore from './DataGridCore'; 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) { 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'; import { GridDisplay, ChangeSet, GridReferenceDefinition } from '@dbgate/datalib';
export interface DataGridProps { export interface DataGridProps {
conid?: string;
database?: string;
display: GridDisplay; display: GridDisplay;
tabVisible?: boolean; tabVisible?: boolean;
changeSetState?: { value: ChangeSet }; changeSetState?: { value: ChangeSet };
dispatchChangeSet?: Function; dispatchChangeSet?: Function;
toolbarPortalRef?: any; toolbarPortalRef?: any;
jslid?: string;
showReferences?: boolean; showReferences?: boolean;
onReferenceClick?: (def: GridReferenceDefinition) => void; onReferenceClick?: (def: GridReferenceDefinition) => void;
onReferenceSourceChanged?: Function; onReferenceSourceChanged?: Function;
@@ -16,3 +13,27 @@ export interface DataGridProps {
masterLoadedTime?: number; masterLoadedTime?: number;
managerSize?: 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;
}