diff --git a/packages/web/src/datagrid/ChangeSetDataGrid.js b/packages/web/src/datagrid/ChangeSetDataGrid.js new file mode 100644 index 00000000..3c20ec51 --- /dev/null +++ b/packages/web/src/datagrid/ChangeSetDataGrid.js @@ -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 ; +} diff --git a/packages/web/src/datagrid/DataGrid.js b/packages/web/src/datagrid/DataGrid.js index 36db2362..f7cfb19c 100644 --- a/packages/web/src/datagrid/DataGrid.js +++ b/packages/web/src/datagrid/DataGrid.js @@ -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); diff --git a/packages/web/src/datagrid/DataGridCore.js b/packages/web/src/datagrid/DataGridCore.js index e7e5791a..d67480e8 100644 --- a/packages/web/src/datagrid/DataGridCore.js +++ b/packages/web/src/datagrid/DataGridCore.js @@ -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 ( @@ -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) => ); - } - 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) => ( - - )); - } 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) => ( + // + // )); + // } 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) { )} - {loadedAndInsertedRows + {rows .slice(firstVisibleRowScrollIndex, firstVisibleRowScrollIndex + visibleRowCountUpperBound) .map((row, 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} /> ; + 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) => ); + } + + 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 ( + + ); } diff --git a/packages/web/src/datagrid/types.ts b/packages/web/src/datagrid/types.ts index 65a591ad..069fbeef 100644 --- a/packages/web/src/datagrid/types.ts +++ b/packages/web/src/datagrid/types.ts @@ -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; +}