mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-05-01 23:43:57 +00:00
Merge branch 'grid-refactor'
This commit is contained in:
@@ -19,7 +19,7 @@ class TableWriter {
|
|||||||
this.jslid = uuidv1();
|
this.jslid = uuidv1();
|
||||||
this.currentFile = path.join(jsldir(), `${this.jslid}.jsonl`);
|
this.currentFile = path.join(jsldir(), `${this.jslid}.jsonl`);
|
||||||
this.currentRowCount = 0;
|
this.currentRowCount = 0;
|
||||||
this.currentChangeIndex = 0;
|
this.currentChangeIndex = 1;
|
||||||
fs.writeFileSync(this.currentFile, JSON.stringify({ columns }) + '\n');
|
fs.writeFileSync(this.currentFile, JSON.stringify({ columns }) + '\n');
|
||||||
this.currentStream = fs.createWriteStream(this.currentFile, { flags: 'a' });
|
this.currentStream = fs.createWriteStream(this.currentFile, { flags: 'a' });
|
||||||
this.writeCurrentStats(false, false);
|
this.writeCurrentStats(false, false);
|
||||||
|
|||||||
157
packages/web/src/datagrid/ChangeSetGrider.ts
Normal file
157
packages/web/src/datagrid/ChangeSetGrider.ts
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
import {
|
||||||
|
ChangeSet,
|
||||||
|
changeSetContainsChanges,
|
||||||
|
changeSetInsertNewRow,
|
||||||
|
createChangeSet,
|
||||||
|
deleteChangeSetRows,
|
||||||
|
findExistingChangeSetItem,
|
||||||
|
getChangeSetInsertedRows,
|
||||||
|
GridDisplay,
|
||||||
|
revertChangeSetRowChanges,
|
||||||
|
setChangeSetValue,
|
||||||
|
} from '@dbgate/datalib';
|
||||||
|
import Grider, { GriderRowStatus } from './Grider';
|
||||||
|
|
||||||
|
export default class ChangeSetGrider extends Grider {
|
||||||
|
public insertedRows: any[];
|
||||||
|
public changeSet: ChangeSet;
|
||||||
|
public setChangeSet: Function;
|
||||||
|
private rowCacheIndexes: Set<number>;
|
||||||
|
private rowDataCache;
|
||||||
|
private rowStatusCache;
|
||||||
|
private rowDefinitionsCache;
|
||||||
|
private batchChangeSet: ChangeSet;
|
||||||
|
|
||||||
|
constructor(public sourceRows: any[], public changeSetState, public dispatchChangeSet, public display: GridDisplay) {
|
||||||
|
super();
|
||||||
|
this.changeSet = changeSetState && changeSetState.value;
|
||||||
|
this.insertedRows = getChangeSetInsertedRows(this.changeSet, display.baseTable);
|
||||||
|
this.setChangeSet = (value) => dispatchChangeSet({ type: 'set', value });
|
||||||
|
this.rowCacheIndexes = new Set();
|
||||||
|
this.rowDataCache = {};
|
||||||
|
this.rowStatusCache = {};
|
||||||
|
this.rowDefinitionsCache = {};
|
||||||
|
this.batchChangeSet = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getRowSource(index: number) {
|
||||||
|
if (index < this.sourceRows.length) return this.sourceRows[index];
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getInsertedRowIndex(index) {
|
||||||
|
return index >= this.sourceRows.length ? index - this.sourceRows.length : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
requireRowCache(index: number) {
|
||||||
|
if (this.rowCacheIndexes.has(index)) return;
|
||||||
|
const row = this.getRowSource(index);
|
||||||
|
const insertedRowIndex = this.getInsertedRowIndex(index);
|
||||||
|
const rowDefinition = this.display.getChangeSetRow(row, insertedRowIndex);
|
||||||
|
const [matchedField, matchedChangeSetItem] = findExistingChangeSetItem(this.changeSet, rowDefinition);
|
||||||
|
const rowUpdated = matchedChangeSetItem ? { ...row, ...matchedChangeSetItem.fields } : row;
|
||||||
|
let status = 'regular';
|
||||||
|
if (matchedChangeSetItem && matchedField == 'updates') status = 'updated';
|
||||||
|
if (matchedField == 'deletes') status = 'deleted';
|
||||||
|
if (insertedRowIndex != null) status = 'inserted';
|
||||||
|
const rowStatus = {
|
||||||
|
status,
|
||||||
|
modifiedFields: new Set(
|
||||||
|
matchedChangeSetItem && matchedChangeSetItem.fields ? Object.keys(matchedChangeSetItem.fields) : []
|
||||||
|
),
|
||||||
|
};
|
||||||
|
this.rowDataCache[index] = rowUpdated;
|
||||||
|
this.rowStatusCache[index] = rowStatus;
|
||||||
|
this.rowDefinitionsCache[index] = rowDefinition;
|
||||||
|
this.rowCacheIndexes.add(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
getRowData(index: number) {
|
||||||
|
this.requireRowCache(index);
|
||||||
|
return this.rowDataCache[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
getRowStatus(index): GriderRowStatus {
|
||||||
|
this.requireRowCache(index);
|
||||||
|
return this.rowStatusCache[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
get rowCount() {
|
||||||
|
return this.sourceRows.length + this.insertedRows.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
applyModification(changeSetReducer) {
|
||||||
|
if (this.batchChangeSet) {
|
||||||
|
this.batchChangeSet = changeSetReducer(this.batchChangeSet);
|
||||||
|
} else {
|
||||||
|
this.setChangeSet(changeSetReducer(this.changeSet));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setCellValue(index: number, uniqueName: string, value: any) {
|
||||||
|
const row = this.getRowSource(index);
|
||||||
|
const definition = this.display.getChangeSetField(row, uniqueName, this.getInsertedRowIndex(index));
|
||||||
|
this.applyModification((chs) => setChangeSetValue(chs, definition, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteRow(index: number) {
|
||||||
|
this.requireRowCache(index);
|
||||||
|
this.applyModification((chs) => deleteChangeSetRows(chs, this.rowDefinitionsCache[index]));
|
||||||
|
}
|
||||||
|
|
||||||
|
get rowCountInUpdate() {
|
||||||
|
if (this.batchChangeSet) {
|
||||||
|
const newRows = getChangeSetInsertedRows(this.batchChangeSet, this.display.baseTable);
|
||||||
|
return this.sourceRows.length + newRows.length;
|
||||||
|
} else {
|
||||||
|
return this.rowCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
insertRow(): number {
|
||||||
|
const res = this.rowCountInUpdate;
|
||||||
|
this.applyModification((chs) => changeSetInsertNewRow(chs, this.display.baseTable));
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
beginUpdate() {
|
||||||
|
this.batchChangeSet = this.changeSet;
|
||||||
|
}
|
||||||
|
endUpdate() {
|
||||||
|
this.setChangeSet(this.batchChangeSet);
|
||||||
|
this.batchChangeSet = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
revertRowChanges(index: number) {
|
||||||
|
this.requireRowCache(index);
|
||||||
|
this.applyModification((chs) => revertChangeSetRowChanges(chs, this.rowDefinitionsCache[index]));
|
||||||
|
}
|
||||||
|
revertAllChanges() {
|
||||||
|
this.applyModification((chs) => createChangeSet());
|
||||||
|
}
|
||||||
|
undo() {
|
||||||
|
this.dispatchChangeSet({ type: 'undo' });
|
||||||
|
}
|
||||||
|
redo() {
|
||||||
|
this.dispatchChangeSet({ type: 'redo' });
|
||||||
|
}
|
||||||
|
get canUndo() {
|
||||||
|
return this.changeSetState.canUndo;
|
||||||
|
}
|
||||||
|
get canRedo() {
|
||||||
|
return this.changeSetState.canRedo;
|
||||||
|
}
|
||||||
|
get containsChanges() {
|
||||||
|
return changeSetContainsChanges(this.changeSet);
|
||||||
|
}
|
||||||
|
get disableLoadNextPage() {
|
||||||
|
return this.insertedRows.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static factory({ sourceRows, changeSetState, dispatchChangeSet, display }): ChangeSetGrider {
|
||||||
|
return new ChangeSetGrider(sourceRows, changeSetState, dispatchChangeSet, display);
|
||||||
|
}
|
||||||
|
static factoryDeps({ sourceRows, changeSetState, dispatchChangeSet, display }) {
|
||||||
|
return [sourceRows, changeSetState ? changeSetState.value : null, dispatchChangeSet, display];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -41,8 +41,8 @@ const DataGridContainer = styled.div`
|
|||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
/** @param props {import('./types').DataGridProps} */
|
|
||||||
export default function DataGrid(props) {
|
export default function DataGrid(props) {
|
||||||
|
const { GridCore } = 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);
|
||||||
return (
|
return (
|
||||||
@@ -50,18 +50,18 @@ export default function DataGrid(props) {
|
|||||||
<LeftContainer>
|
<LeftContainer>
|
||||||
<ManagerMainContainer>
|
<ManagerMainContainer>
|
||||||
<Container1>
|
<Container1>
|
||||||
<ColumnManager {...props} managerSize={managerSize}/>
|
<ColumnManager {...props} managerSize={managerSize} />
|
||||||
</Container1>
|
</Container1>
|
||||||
{props.showReferences && (
|
{props.showReferences && (
|
||||||
<ManagerOuterContainer2>
|
<ManagerOuterContainer2>
|
||||||
<ReferenceManager {...props} managerSize={managerSize}/>
|
<ReferenceManager {...props} managerSize={managerSize} />
|
||||||
</ManagerOuterContainer2>
|
</ManagerOuterContainer2>
|
||||||
)}
|
)}
|
||||||
</ManagerMainContainer>
|
</ManagerMainContainer>
|
||||||
</LeftContainer>
|
</LeftContainer>
|
||||||
|
|
||||||
<DataGridContainer>
|
<DataGridContainer>
|
||||||
<DataGridCore {...props} />
|
<GridCore {...props} />
|
||||||
</DataGridContainer>
|
</DataGridContainer>
|
||||||
</HorizontalSplitter>
|
</HorizontalSplitter>
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import ReactDOM from 'react-dom';
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { HorizontalScrollBar, VerticalScrollBar } from './ScrollBars';
|
import { HorizontalScrollBar, VerticalScrollBar } from './ScrollBars';
|
||||||
import useDimensions from '../utility/useDimensions';
|
import useDimensions from '../utility/useDimensions';
|
||||||
import axios from '../utility/axios';
|
|
||||||
import DataFilterControl from './DataFilterControl';
|
import DataFilterControl from './DataFilterControl';
|
||||||
import stableStringify from 'json-stable-stringify';
|
import stableStringify from 'json-stable-stringify';
|
||||||
import { getFilterType, getFilterValueExpression } from '@dbgate/filterparser';
|
import { getFilterType, getFilterValueExpression } from '@dbgate/filterparser';
|
||||||
@@ -18,19 +17,6 @@ import {
|
|||||||
filterCellsForRow,
|
filterCellsForRow,
|
||||||
cellIsSelected,
|
cellIsSelected,
|
||||||
} from './gridutil';
|
} from './gridutil';
|
||||||
import useModalState from '../modals/useModalState';
|
|
||||||
import ConfirmSqlModal from '../modals/ConfirmSqlModal';
|
|
||||||
import {
|
|
||||||
changeSetToSql,
|
|
||||||
createChangeSet,
|
|
||||||
revertChangeSetRowChanges,
|
|
||||||
getChangeSetInsertedRows,
|
|
||||||
changeSetInsertNewRow,
|
|
||||||
deleteChangeSetRows,
|
|
||||||
batchUpdateChangeSet,
|
|
||||||
setChangeSetValue,
|
|
||||||
} from '@dbgate/datalib';
|
|
||||||
import { scriptToSql } from '@dbgate/sqltree';
|
|
||||||
import { copyTextToClipboard } from '../utility/clipboard';
|
import { copyTextToClipboard } from '../utility/clipboard';
|
||||||
import DataGridToolbar from './DataGridToolbar';
|
import DataGridToolbar from './DataGridToolbar';
|
||||||
// import usePropsCompare from '../utility/usePropsCompare';
|
// import usePropsCompare from '../utility/usePropsCompare';
|
||||||
@@ -38,14 +24,8 @@ import ColumnHeaderControl from './ColumnHeaderControl';
|
|||||||
import InlineButton from '../widgets/InlineButton';
|
import InlineButton from '../widgets/InlineButton';
|
||||||
import { showMenu } from '../modals/DropDownMenu';
|
import { showMenu } from '../modals/DropDownMenu';
|
||||||
import DataGridContextMenu from './DataGridContextMenu';
|
import DataGridContextMenu from './DataGridContextMenu';
|
||||||
import useSocket from '../utility/SocketProvider';
|
|
||||||
import LoadingInfo from '../widgets/LoadingInfo';
|
import LoadingInfo from '../widgets/LoadingInfo';
|
||||||
import ErrorInfo from '../widgets/ErrorInfo';
|
import ErrorInfo from '../widgets/ErrorInfo';
|
||||||
import useShowModal from '../modals/showModal';
|
|
||||||
import ErrorMessageModal from '../modals/ErrorMessageModal';
|
|
||||||
import ImportExportModal from '../modals/ImportExportModal';
|
|
||||||
import { openNewTab } from '../utility/common';
|
|
||||||
import { useSetOpenedTabs } from '../utility/globalState';
|
|
||||||
|
|
||||||
const GridContainer = styled.div`
|
const GridContainer = styled.div`
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -126,98 +106,31 @@ const LoadingInfoBox = styled.div`
|
|||||||
border: 1px solid gray;
|
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').DataGridProps} */
|
||||||
export default function DataGridCore(props) {
|
export default function DataGridCore(props) {
|
||||||
const { conid, database, display, changeSetState, dispatchChangeSet, tabVisible, jslid } = props;
|
const {
|
||||||
|
display,
|
||||||
|
conid,
|
||||||
|
database,
|
||||||
|
tabVisible,
|
||||||
|
loadNextData,
|
||||||
|
errorMessage,
|
||||||
|
isLoadedAll,
|
||||||
|
loadedTime,
|
||||||
|
exportGrid,
|
||||||
|
allRowCount,
|
||||||
|
openQuery,
|
||||||
|
onSave,
|
||||||
|
isLoading,
|
||||||
|
grider,
|
||||||
|
} = 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();
|
||||||
@@ -234,19 +147,6 @@ export default function DataGridCore(props) {
|
|||||||
const [autofillSelectedCells, setAutofillSelectedCells] = React.useState(emptyCellArray);
|
const [autofillSelectedCells, setAutofillSelectedCells] = React.useState(emptyCellArray);
|
||||||
const [focusFilterInputs, setFocusFilterInputs] = React.useState({});
|
const [focusFilterInputs, setFocusFilterInputs] = React.useState({});
|
||||||
|
|
||||||
// const [inplaceEditorCell, setInplaceEditorCell] = React.useState(nullCell);
|
|
||||||
// const [inplaceEditorInitText, setInplaceEditorInitText] = React.useState('');
|
|
||||||
// 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(
|
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
|
||||||
@@ -255,76 +155,12 @@ export default function DataGridCore(props) {
|
|||||||
[selectedCells]
|
[selectedCells]
|
||||||
);
|
);
|
||||||
|
|
||||||
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',
|
|
||||||
// params: {
|
|
||||||
// conid,
|
|
||||||
// database,
|
|
||||||
// },
|
|
||||||
// data: { sql },
|
|
||||||
// });
|
|
||||||
// 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();
|
||||||
const [containerRef, { height: containerHeight, width: containerWidth }] = useDimensions();
|
const [containerRef, { height: containerHeight, width: containerWidth }] = useDimensions();
|
||||||
// const [tableRef, { height: tableHeight, width: tableWidth }] = useDimensions();
|
|
||||||
const confirmSqlModalState = useModalState();
|
|
||||||
const [confirmSql, setConfirmSql] = React.useState('');
|
|
||||||
|
|
||||||
const [inplaceEditorState, dispatchInsplaceEditor] = React.useReducer((state, action) => {
|
const [inplaceEditorState, dispatchInsplaceEditor] = React.useReducer((state, action) => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
@@ -355,8 +191,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(grider, columns, containerWidth, display), [
|
||||||
loadedRows,
|
grider,
|
||||||
columns,
|
columns,
|
||||||
containerWidth,
|
containerWidth,
|
||||||
display,
|
display,
|
||||||
@@ -376,43 +212,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,45 +223,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 && (grider.rowCount > 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, grider.getRowData(0)]);
|
||||||
|
|
||||||
// const handleCloseInplaceEditor = React.useCallback(
|
|
||||||
// mode => {
|
|
||||||
// const [row, col] = currentCell || [];
|
|
||||||
// setInplaceEditorCell(null);
|
|
||||||
// setInplaceEditorInitText(null);
|
|
||||||
// setInplaceEditorShouldSave(false);
|
|
||||||
// if (tableElement) tableElement.focus();
|
|
||||||
// // @ts-ignore
|
|
||||||
// if (mode == 'enter' && row) moveCurrentCell(row + 1, col);
|
|
||||||
// if (mode == 'save') setTimeout(handleSave, 1);
|
|
||||||
// },
|
|
||||||
// [tableElement, currentCell]
|
|
||||||
// );
|
|
||||||
|
|
||||||
// usePropsCompare({ columnSizes, firstVisibleColumnScrollIndex, gridScrollAreaWidth, columns });
|
// usePropsCompare({ columnSizes, firstVisibleColumnScrollIndex, gridScrollAreaWidth, columns });
|
||||||
|
|
||||||
@@ -487,6 +252,12 @@ export default function DataGridCore(props) {
|
|||||||
}
|
}
|
||||||
}, [display && display.focusedColumn]);
|
}, [display && display.focusedColumn]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (loadNextData && firstVisibleRowScrollIndex + visibleRowCountUpperBound >= grider.rowCount) {
|
||||||
|
loadNextData();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (display.groupColumns) {
|
if (display.groupColumns) {
|
||||||
props.onReferenceClick({
|
props.onReferenceClick({
|
||||||
@@ -504,7 +275,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 = grider.getRowData(cell[0]);
|
||||||
if (row) {
|
if (row) {
|
||||||
const colName = realColumnUniqueNames[cell[1]];
|
const colName = realColumnUniqueNames[cell[1]];
|
||||||
if (colName) {
|
if (colName) {
|
||||||
@@ -523,12 +294,9 @@ export default function DataGridCore(props) {
|
|||||||
}
|
}
|
||||||
if (allRowCount == null) return 'Loading row count...';
|
if (allRowCount == null) return 'Loading row count...';
|
||||||
return `Rows: ${allRowCount.toLocaleString()}`;
|
return `Rows: ${allRowCount.toLocaleString()}`;
|
||||||
// if (this.isLoadingFirstPage) return "Loading first page...";
|
}, [selectedCells, allRowCount, grider, visibleRealColumns]);
|
||||||
// if (this.isFirstPageError) return "Error loading first page";
|
|
||||||
// return `Rows: ${this.rowCount.toLocaleString()}`;
|
|
||||||
}, [selectedCells, allRowCount, loadedRows, visibleRealColumns]);
|
|
||||||
|
|
||||||
if (!loadedRows || !columns || columns.length == 0)
|
if (!columns || columns.length == 0)
|
||||||
return (
|
return (
|
||||||
<LoadingInfoWrapper>
|
<LoadingInfoWrapper>
|
||||||
<LoadingInfoBox>
|
<LoadingInfoBox>
|
||||||
@@ -563,7 +331,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,39 +385,8 @@ export default function DataGridCore(props) {
|
|||||||
copyToClipboard();
|
copyToClipboard();
|
||||||
}
|
}
|
||||||
|
|
||||||
function exportGrid() {
|
function setCellValue(cell, value) {
|
||||||
const initialValues = {};
|
grider.setCellValue(cell[0], realColumnUniqueNames[cell[1]], value);
|
||||||
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
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handlePaste(event) {
|
function handlePaste(event) {
|
||||||
@@ -663,34 +400,22 @@ export default function DataGridCore(props) {
|
|||||||
pastedText = event.clipboardData.getData('text/plain');
|
pastedText = event.clipboardData.getData('text/plain');
|
||||||
}
|
}
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
grider.beginUpdate();
|
||||||
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 allRows = loadedAndInsertedRows;
|
|
||||||
|
|
||||||
if (selectedCells.length <= 1) {
|
if (selectedCells.length <= 1) {
|
||||||
const startRow = isRegularCell(currentCell) ? currentCell[0] : loadedAndInsertedRows.length;
|
const startRow = isRegularCell(currentCell) ? currentCell[0] : grider.rowCount;
|
||||||
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 >= grider.rowCountInUpdate) {
|
||||||
chs = changeSetInsertNewRow(chs, display.baseTable);
|
grider.insertRow();
|
||||||
allRows = [...loadedRows, ...getChangeSetInsertedRows(chs, display.baseTable)];
|
|
||||||
}
|
}
|
||||||
let colIndex = startCol;
|
let colIndex = startCol;
|
||||||
const row = allRows[rowIndex];
|
|
||||||
for (const cell of rowData) {
|
for (const cell of rowData) {
|
||||||
chs = setChangeSetValue(
|
setCellValue([rowIndex, colIndex], cell == '(NULL)' ? null : cell);
|
||||||
chs,
|
|
||||||
display.getChangeSetField(
|
|
||||||
row,
|
|
||||||
realColumnUniqueNames[colIndex],
|
|
||||||
rowIndex >= loadedRows.length ? rowIndex - loadedRows.length : null
|
|
||||||
),
|
|
||||||
cell == '(NULL)' ? null : cell
|
|
||||||
);
|
|
||||||
colIndex += 1;
|
colIndex += 1;
|
||||||
}
|
}
|
||||||
rowIndex += 1;
|
rowIndex += 1;
|
||||||
@@ -706,19 +431,18 @@ export default function DataGridCore(props) {
|
|||||||
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);
|
setCellValue(cell, pasteCell);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
grider.endUpdate();
|
||||||
setChangeSet(chs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function setNull() {
|
function setNull() {
|
||||||
let chs = changeSet;
|
grider.beginUpdate();
|
||||||
selectedCells.filter(isRegularCell).forEach((cell) => {
|
selectedCells.filter(isRegularCell).forEach((cell) => {
|
||||||
chs = setCellValue(chs, cell, null);
|
setCellValue(cell, null);
|
||||||
});
|
});
|
||||||
setChangeSet(chs);
|
grider.endUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
function cellsToRegularCells(cells) {
|
function cellsToRegularCells(cells) {
|
||||||
@@ -746,7 +470,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 = grider.getRowData(rowIndex);
|
||||||
if (!rowData) return '';
|
if (!rowData) return '';
|
||||||
const line = colIndexes
|
const line = colIndexes
|
||||||
.map((col) => realColumnUniqueNames[col])
|
.map((col) => realColumnUniqueNames[col])
|
||||||
@@ -784,17 +508,11 @@ export default function DataGridCore(props) {
|
|||||||
const currentRowNumber = currentCell[0];
|
const currentRowNumber = currentCell[0];
|
||||||
if (_.isNumber(currentRowNumber)) {
|
if (_.isNumber(currentRowNumber)) {
|
||||||
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
|
|
||||||
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(grider.getRowData(currentRowNumber), colNames);
|
||||||
setChangeSet(
|
grider.beginUpdate();
|
||||||
batchUpdateChangeSet(
|
for (const index of rowIndexes) grider.updateRow(index, changeObject);
|
||||||
changeSet,
|
grider.endUpdate();
|
||||||
getRowDefinitions(rowIndexes),
|
|
||||||
// @ts-ignore
|
|
||||||
rowIndexes.map(() => changeObject)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setAutofillDragStartCell(null);
|
setAutofillDragStartCell(null);
|
||||||
@@ -803,36 +521,20 @@ 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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSelectedRowIndexes() {
|
function getSelectedRowIndexes() {
|
||||||
return _.uniq((selectedCells || []).map((x) => x[0]));
|
return _.uniq((selectedCells || []).map((x) => x[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSelectedRowDefinitions() {
|
|
||||||
return getRowDefinitions(getSelectedRowIndexes());
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSelectedRowData() {
|
function getSelectedRowData() {
|
||||||
return _.compact(getSelectedRowIndexes().map((index) => loadedRows && loadedRows[index]));
|
return _.compact(getSelectedRowIndexes().map((index) => grider.getRowData(index)));
|
||||||
}
|
}
|
||||||
|
|
||||||
function revertRowChanges() {
|
function revertRowChanges() {
|
||||||
const updatedChangeSet = getSelectedRowDefinitions().reduce(
|
grider.beginUpdate();
|
||||||
(chs, row) => revertChangeSetRowChanges(chs, row),
|
for (const index of getSelectedRowIndexes()) {
|
||||||
changeSet
|
if (_.isNumber(index)) grider.revertRowChanges(index);
|
||||||
);
|
}
|
||||||
setChangeSet(updatedChangeSet);
|
grider.endUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
function filterSelectedValue() {
|
function filterSelectedValue() {
|
||||||
@@ -841,7 +543,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 = grider.getRowData(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,28 +552,12 @@ 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() {
|
|
||||||
setChangeSet(createChangeSet());
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteSelectedRows() {
|
function deleteSelectedRows() {
|
||||||
const updatedChangeSet = getSelectedRowDefinitions().reduce((chs, row) => deleteChangeSetRows(chs, row), changeSet);
|
grider.beginUpdate();
|
||||||
setChangeSet(updatedChangeSet);
|
for (const index of getSelectedRowIndexes()) {
|
||||||
|
if (_.isNumber(index)) grider.deleteRow(index);
|
||||||
|
}
|
||||||
|
grider.endUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleGridWheel(event) {
|
function handleGridWheel(event) {
|
||||||
@@ -882,7 +568,7 @@ export default function DataGridCore(props) {
|
|||||||
if (event.deltaY < 0) {
|
if (event.deltaY < 0) {
|
||||||
newFirstVisibleRowScrollIndex -= wheelRowCount;
|
newFirstVisibleRowScrollIndex -= wheelRowCount;
|
||||||
}
|
}
|
||||||
let rowCount = rowCountNewIncluded;
|
let rowCount = grider.rowCount;
|
||||||
if (newFirstVisibleRowScrollIndex + visibleRowCountLowerBound > rowCount) {
|
if (newFirstVisibleRowScrollIndex + visibleRowCountLowerBound > rowCount) {
|
||||||
newFirstVisibleRowScrollIndex = rowCount - visibleRowCountLowerBound + 1;
|
newFirstVisibleRowScrollIndex = rowCount - visibleRowCountLowerBound + 1;
|
||||||
}
|
}
|
||||||
@@ -895,17 +581,11 @@ export default function DataGridCore(props) {
|
|||||||
setvScrollValueToSetDate(new Date());
|
setvScrollValueToSetDate(new Date());
|
||||||
}
|
}
|
||||||
|
|
||||||
// async function blurEditorAndSave() {
|
|
||||||
// setInplaceEditorCell(null);
|
|
||||||
// setInplaceEditorInitText(null);
|
|
||||||
// await sleep(1);
|
|
||||||
// }
|
|
||||||
|
|
||||||
function undo() {
|
function undo() {
|
||||||
dispatchChangeSet({ type: 'undo' });
|
grider.undo();
|
||||||
}
|
}
|
||||||
function redo() {
|
function redo() {
|
||||||
dispatchChangeSet({ type: 'redo' });
|
grider.redo();
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSave() {
|
function handleSave() {
|
||||||
@@ -914,39 +594,13 @@ export default function DataGridCore(props) {
|
|||||||
dispatchInsplaceEditor({ type: 'shouldSave' });
|
dispatchInsplaceEditor({ type: 'shouldSave' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const script = changeSetToSql(changeSetRef.current, display.dbinfo);
|
if (onSave) onSave();
|
||||||
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 insertNewRow = () => {
|
const insertNewRow = () => {
|
||||||
if (display.baseTable) {
|
if (display.baseTable) {
|
||||||
setChangeSet(changeSetInsertNewRow(changeSet, display.baseTable));
|
const rowIndex = grider.insertRow();
|
||||||
const cell = [rowCountNewIncluded, (currentCell && currentCell[1]) || 0];
|
const cell = [rowIndex, (currentCell && currentCell[1]) || 0];
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
setCurrentCell(cell);
|
setCurrentCell(cell);
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@@ -1060,7 +714,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 = grider.rowCount;
|
||||||
if (event.ctrlKey) {
|
if (event.ctrlKey) {
|
||||||
switch (event.keyCode) {
|
switch (event.keyCode) {
|
||||||
case keycodes.upArrow:
|
case keycodes.upArrow:
|
||||||
@@ -1118,7 +772,7 @@ export default function DataGridCore(props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function moveCurrentCell(row, col, event = null) {
|
function moveCurrentCell(row, col, event = null) {
|
||||||
const rowCount = rowCountNewIncluded;
|
const rowCount = grider.rowCount;
|
||||||
|
|
||||||
if (row < 0) row = 0;
|
if (row < 0) row = 0;
|
||||||
if (row >= rowCount) row = rowCount - 1;
|
if (row >= rowCount) row = rowCount - 1;
|
||||||
@@ -1140,7 +794,7 @@ export default function DataGridCore(props) {
|
|||||||
|
|
||||||
if (row != null) {
|
if (row != null) {
|
||||||
let newRow = null;
|
let newRow = null;
|
||||||
const rowCount = rowCountNewIncluded;
|
const rowCount = grider.rowCount;
|
||||||
if (rowCount == 0) return;
|
if (rowCount == 0) return;
|
||||||
|
|
||||||
if (row < firstVisibleRowScrollIndex) newRow = row;
|
if (row < firstVisibleRowScrollIndex) newRow = row;
|
||||||
@@ -1202,7 +856,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,28 +936,20 @@ export default function DataGridCore(props) {
|
|||||||
)}
|
)}
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableBody ref={tableBodyRef}>
|
<TableBody ref={tableBodyRef}>
|
||||||
{loadedAndInsertedRows
|
{_.range(firstVisibleRowScrollIndex, firstVisibleRowScrollIndex + visibleRowCountUpperBound)
|
||||||
.slice(firstVisibleRowScrollIndex, firstVisibleRowScrollIndex + visibleRowCountUpperBound)
|
.map((rowIndex) => (
|
||||||
.map((row, index) => (
|
|
||||||
<DataGridRow
|
<DataGridRow
|
||||||
key={firstVisibleRowScrollIndex + index}
|
key={rowIndex}
|
||||||
rowIndex={firstVisibleRowScrollIndex + index}
|
grider={grider}
|
||||||
|
rowIndex={rowIndex}
|
||||||
rowHeight={rowHeight}
|
rowHeight={rowHeight}
|
||||||
visibleRealColumns={visibleRealColumns}
|
visibleRealColumns={visibleRealColumns}
|
||||||
inplaceEditorState={inplaceEditorState}
|
inplaceEditorState={inplaceEditorState}
|
||||||
dispatchInsplaceEditor={dispatchInsplaceEditor}
|
dispatchInsplaceEditor={dispatchInsplaceEditor}
|
||||||
autofillSelectedCells={autofillSelectedCells}
|
autofillSelectedCells={autofillSelectedCells}
|
||||||
selectedCells={filterCellsForRow(selectedCells, firstVisibleRowScrollIndex + index)}
|
selectedCells={filterCellsForRow(selectedCells, rowIndex)}
|
||||||
insertedRowIndex={
|
autofillMarkerCell={filterCellForRow(autofillMarkerCell, rowIndex)}
|
||||||
firstVisibleRowScrollIndex + index >= loadedRows.length
|
|
||||||
? firstVisibleRowScrollIndex + index - loadedRows.length
|
|
||||||
: null
|
|
||||||
}
|
|
||||||
autofillMarkerCell={filterCellForRow(autofillMarkerCell, firstVisibleRowScrollIndex + index)}
|
|
||||||
changeSet={changeSet}
|
|
||||||
setChangeSet={setChangeSet}
|
|
||||||
display={display}
|
display={display}
|
||||||
row={row}
|
|
||||||
focusedColumn={display.focusedColumn}
|
focusedColumn={display.focusedColumn}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
@@ -1321,15 +967,9 @@ export default function DataGridCore(props) {
|
|||||||
valueToSet={vScrollValueToSet}
|
valueToSet={vScrollValueToSet}
|
||||||
valueToSetDate={vScrollValueToSetDate}
|
valueToSetDate={vScrollValueToSetDate}
|
||||||
minimum={0}
|
minimum={0}
|
||||||
maximum={rowCountNewIncluded - visibleRowCountUpperBound + 2}
|
maximum={grider.rowCount - visibleRowCountUpperBound + 2}
|
||||||
onScroll={handleRowScroll}
|
onScroll={handleRowScroll}
|
||||||
viewportRatio={visibleRowCountUpperBound / rowCountNewIncluded}
|
viewportRatio={visibleRowCountUpperBound / grider.rowCount}
|
||||||
/>
|
|
||||||
<ConfirmSqlModal
|
|
||||||
modalState={confirmSqlModalState}
|
|
||||||
sql={confirmSql}
|
|
||||||
engine={display.engine}
|
|
||||||
onConfirm={handleConfirmSql}
|
|
||||||
/>
|
/>
|
||||||
{allRowCount && <RowCountLabel>{rowCountInfo}</RowCountLabel>}
|
{allRowCount && <RowCountLabel>{rowCountInfo}</RowCountLabel>}
|
||||||
{props.toolbarPortalRef &&
|
{props.toolbarPortalRef &&
|
||||||
@@ -1338,9 +978,7 @@ export default function DataGridCore(props) {
|
|||||||
<DataGridToolbar
|
<DataGridToolbar
|
||||||
reload={() => display.reload()}
|
reload={() => display.reload()}
|
||||||
save={handleSave}
|
save={handleSave}
|
||||||
changeSetState={changeSetState}
|
grider={grider}
|
||||||
dispatchChangeSet={dispatchChangeSet}
|
|
||||||
revert={revertAllChanges}
|
|
||||||
/>,
|
/>,
|
||||||
props.toolbarPortalRef.current
|
props.toolbarPortalRef.current
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -34,13 +34,13 @@ const TableBodyCell = styled.td`
|
|||||||
color: white;`}
|
color: white;`}
|
||||||
|
|
||||||
${(props) =>
|
${(props) =>
|
||||||
props.isModifiedRow &&
|
props.isModifiedRow &&
|
||||||
!props.isInsertedRow &&
|
!props.isInsertedRow &&
|
||||||
!props.isSelected &&
|
!props.isSelected &&
|
||||||
!props.isAutofillSelected &&
|
!props.isAutofillSelected &&
|
||||||
!props.isModifiedCell &&
|
!props.isModifiedCell &&
|
||||||
!props.isFocusedColumn &&
|
!props.isFocusedColumn &&
|
||||||
`
|
`
|
||||||
background-color: #FFFFDB;`}
|
background-color: #FFFFDB;`}
|
||||||
${(props) =>
|
${(props) =>
|
||||||
!props.isSelected &&
|
!props.isSelected &&
|
||||||
@@ -60,11 +60,11 @@ const TableBodyCell = styled.td`
|
|||||||
background-color: #DBFFDB;`}
|
background-color: #DBFFDB;`}
|
||||||
|
|
||||||
${(props) =>
|
${(props) =>
|
||||||
!props.isSelected &&
|
!props.isSelected &&
|
||||||
!props.isAutofillSelected &&
|
!props.isAutofillSelected &&
|
||||||
!props.isFocusedColumn &&
|
!props.isFocusedColumn &&
|
||||||
props.isDeletedRow &&
|
props.isDeletedRow &&
|
||||||
`
|
`
|
||||||
background-color: #FFDBFF;
|
background-color: #FFDBFF;
|
||||||
`}
|
`}
|
||||||
|
|
||||||
@@ -75,14 +75,13 @@ const TableBodyCell = styled.td`
|
|||||||
`}
|
`}
|
||||||
|
|
||||||
${(props) =>
|
${(props) =>
|
||||||
props.isDeletedRow &&
|
props.isDeletedRow &&
|
||||||
`
|
`
|
||||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAEElEQVQImWNgIAX8x4KJBAD+agT8INXz9wAAAABJRU5ErkJggg==');
|
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAEElEQVQImWNgIAX8x4KJBAD+agT8INXz9wAAAABJRU5ErkJggg==');
|
||||||
// from http://www.patternify.com/
|
// from http://www.patternify.com/
|
||||||
background-repeat: repeat-x;
|
background-repeat: repeat-x;
|
||||||
background-position: 50% 50%;`}
|
background-position: 50% 50%;`}
|
||||||
|
`;
|
||||||
`;
|
|
||||||
|
|
||||||
const HintSpan = styled.span`
|
const HintSpan = styled.span`
|
||||||
color: gray;
|
color: gray;
|
||||||
@@ -163,22 +162,20 @@ function CellFormattedValue({ value, dataType }) {
|
|||||||
return value.toString();
|
return value.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
function DataGridRow({
|
/** @param props {import('./types').DataGridProps} */
|
||||||
rowHeight,
|
function DataGridRow(props) {
|
||||||
rowIndex,
|
const {
|
||||||
visibleRealColumns,
|
rowHeight,
|
||||||
inplaceEditorState,
|
rowIndex,
|
||||||
dispatchInsplaceEditor,
|
visibleRealColumns,
|
||||||
row,
|
inplaceEditorState,
|
||||||
display,
|
dispatchInsplaceEditor,
|
||||||
changeSet,
|
autofillMarkerCell,
|
||||||
setChangeSet,
|
selectedCells,
|
||||||
insertedRowIndex,
|
autofillSelectedCells,
|
||||||
autofillMarkerCell,
|
focusedColumn,
|
||||||
selectedCells,
|
grider,
|
||||||
autofillSelectedCells,
|
} = props;
|
||||||
focusedColumn,
|
|
||||||
}) {
|
|
||||||
// usePropsCompare({
|
// usePropsCompare({
|
||||||
// rowHeight,
|
// rowHeight,
|
||||||
// rowIndex,
|
// rowIndex,
|
||||||
@@ -197,18 +194,19 @@ function DataGridRow({
|
|||||||
|
|
||||||
// console.log('RENDER ROW', rowIndex);
|
// console.log('RENDER ROW', rowIndex);
|
||||||
|
|
||||||
const rowDefinition = display.getChangeSetRow(row, insertedRowIndex);
|
const rowData = grider.getRowData(rowIndex);
|
||||||
const [matchedField, matchedChangeSetItem] = findExistingChangeSetItem(changeSet, rowDefinition);
|
const rowStatus = grider.getRowStatus(rowIndex);
|
||||||
const rowUpdated = matchedChangeSetItem ? { ...row, ...matchedChangeSetItem.fields } : row;
|
|
||||||
const hintFieldsAllowed = visibleRealColumns
|
const hintFieldsAllowed = visibleRealColumns
|
||||||
.filter((col) => {
|
.filter((col) => {
|
||||||
if (!col.hintColumnName) return false;
|
if (!col.hintColumnName) return false;
|
||||||
if (matchedChangeSetItem && matchedField == 'updates' && col.uniqueName in matchedChangeSetItem.fields)
|
if (rowStatus.status == 'updated' && rowStatus.modifiedFields.has(col.uniqueName)) return false;
|
||||||
return false;
|
|
||||||
return true;
|
return true;
|
||||||
})
|
})
|
||||||
.map((col) => col.uniqueName);
|
.map((col) => col.uniqueName);
|
||||||
|
|
||||||
|
if (!rowData) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableBodyRow style={{ height: `${rowHeight}px` }}>
|
<TableBodyRow style={{ height: `${rowHeight}px` }}>
|
||||||
<TableHeaderCell data-row={rowIndex} data-col="header">
|
<TableHeaderCell data-row={rowIndex} data-col="header">
|
||||||
@@ -226,13 +224,11 @@ function DataGridRow({
|
|||||||
data-col={col.colIndex}
|
data-col={col.colIndex}
|
||||||
isSelected={cellIsSelected(rowIndex, col.colIndex, selectedCells)}
|
isSelected={cellIsSelected(rowIndex, col.colIndex, selectedCells)}
|
||||||
isAutofillSelected={cellIsSelected(rowIndex, col.colIndex, autofillSelectedCells)}
|
isAutofillSelected={cellIsSelected(rowIndex, col.colIndex, autofillSelectedCells)}
|
||||||
isModifiedRow={!!matchedChangeSetItem}
|
isModifiedRow={rowStatus.status == 'updated'}
|
||||||
isFocusedColumn={col.uniqueName == focusedColumn}
|
isFocusedColumn={col.uniqueName == focusedColumn}
|
||||||
isModifiedCell={
|
isModifiedCell={rowStatus.status == 'updated' && rowStatus.modifiedFields.has(col.uniqueName)}
|
||||||
matchedChangeSetItem && matchedField == 'updates' && col.uniqueName in matchedChangeSetItem.fields
|
isInsertedRow={rowStatus.status == 'inserted'}
|
||||||
}
|
isDeletedRow={rowStatus.status == 'deleted'}
|
||||||
isInsertedRow={insertedRowIndex != null}
|
|
||||||
isDeletedRow={matchedField == 'deletes'}
|
|
||||||
>
|
>
|
||||||
{inplaceEditorState.cell &&
|
{inplaceEditorState.cell &&
|
||||||
rowIndex == inplaceEditorState.cell[0] &&
|
rowIndex == inplaceEditorState.cell[0] &&
|
||||||
@@ -241,16 +237,15 @@ function DataGridRow({
|
|||||||
widthPx={col.widthPx}
|
widthPx={col.widthPx}
|
||||||
inplaceEditorState={inplaceEditorState}
|
inplaceEditorState={inplaceEditorState}
|
||||||
dispatchInsplaceEditor={dispatchInsplaceEditor}
|
dispatchInsplaceEditor={dispatchInsplaceEditor}
|
||||||
cellValue={rowUpdated[col.uniqueName]}
|
cellValue={rowData[col.uniqueName]}
|
||||||
changeSet={changeSet}
|
grider={grider}
|
||||||
setChangeSet={setChangeSet}
|
rowIndex={rowIndex}
|
||||||
insertedRowIndex={insertedRowIndex}
|
uniqueName={col.uniqueName}
|
||||||
definition={display.getChangeSetField(row, col.uniqueName, insertedRowIndex)}
|
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<CellFormattedValue value={rowUpdated[col.uniqueName]} dataType={col.dataType} />
|
<CellFormattedValue value={rowData[col.uniqueName]} dataType={col.dataType} />
|
||||||
{hintFieldsAllowed.includes(col.uniqueName) && <HintSpan>{row[col.hintColumnName]}</HintSpan>}
|
{hintFieldsAllowed.includes(col.uniqueName) && <HintSpan>{rowData[col.hintColumnName]}</HintSpan>}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{autofillMarkerCell && autofillMarkerCell[1] == col.colIndex && autofillMarkerCell[0] == rowIndex && (
|
{autofillMarkerCell && autofillMarkerCell[1] == col.colIndex && autofillMarkerCell[0] == rowIndex && (
|
||||||
|
|||||||
@@ -1,23 +1,22 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ToolbarButton from '../widgets/ToolbarButton';
|
import ToolbarButton from '../widgets/ToolbarButton';
|
||||||
import { changeSetContainsChanges } from '@dbgate/datalib';
|
|
||||||
|
|
||||||
export default function DataGridToolbar({ reload, changeSetState, dispatchChangeSet, save, revert }) {
|
export default function DataGridToolbar({ reload, grider, save }) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ToolbarButton onClick={reload} icon="fas fa-sync">
|
<ToolbarButton onClick={reload} icon="fas fa-sync">
|
||||||
Refresh
|
Refresh
|
||||||
</ToolbarButton>
|
</ToolbarButton>
|
||||||
<ToolbarButton disabled={!changeSetState.canUndo} onClick={() => dispatchChangeSet({ type: 'undo' })} icon="fas fa-undo">
|
<ToolbarButton disabled={!grider.canUndo} onClick={() => grider.undo()} icon="fas fa-undo">
|
||||||
Undo
|
Undo
|
||||||
</ToolbarButton>
|
</ToolbarButton>
|
||||||
<ToolbarButton disabled={!changeSetState.canRedo} onClick={() => dispatchChangeSet({ type: 'redo' })} icon="fas fa-redo">
|
<ToolbarButton disabled={!grider.canRedo} onClick={() => grider.redo()} icon="fas fa-redo">
|
||||||
Redo
|
Redo
|
||||||
</ToolbarButton>
|
</ToolbarButton>
|
||||||
<ToolbarButton disabled={!changeSetContainsChanges(changeSetState.value)} onClick={save} icon="fas fa-save">
|
<ToolbarButton disabled={!grider.containsChanges} onClick={save} icon="fas fa-save">
|
||||||
Save
|
Save
|
||||||
</ToolbarButton>
|
</ToolbarButton>
|
||||||
<ToolbarButton disabled={!changeSetContainsChanges(changeSetState.value)} onClick={revert} icon="fas fa-times">
|
<ToolbarButton disabled={!grider.containsChanges} onClick={() => grider.revertAllChanges()} icon="fas fa-times">
|
||||||
Revert
|
Revert
|
||||||
</ToolbarButton>
|
</ToolbarButton>
|
||||||
</>
|
</>
|
||||||
|
|||||||
52
packages/web/src/datagrid/Grider.ts
Normal file
52
packages/web/src/datagrid/Grider.ts
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
export interface GriderRowStatus {
|
||||||
|
status: 'regular' | 'updated' | 'deleted' | 'inserted';
|
||||||
|
modifiedFields: Set<string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default abstract class Grider {
|
||||||
|
abstract getRowData(index): any;
|
||||||
|
abstract get rowCount(): number;
|
||||||
|
|
||||||
|
getRowsSample() {
|
||||||
|
return [this.getRowData(0)];
|
||||||
|
}
|
||||||
|
|
||||||
|
getRowStatus(index): GriderRowStatus {
|
||||||
|
const res: GriderRowStatus = {
|
||||||
|
status: 'regular',
|
||||||
|
modifiedFields: new Set(),
|
||||||
|
};
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
beginUpdate() {}
|
||||||
|
endUpdate() {}
|
||||||
|
setCellValue(index: number, uniqueName: string, value: any) {}
|
||||||
|
deleteRow(index: number) {}
|
||||||
|
insertRow(): number {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
revertRowChanges(index: number) {}
|
||||||
|
revertAllChanges() {}
|
||||||
|
undo() {}
|
||||||
|
redo() {}
|
||||||
|
get rowCountInUpdate() {
|
||||||
|
return this.rowCount;
|
||||||
|
}
|
||||||
|
get canUndo() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
get canRedo() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
get containsChanges() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
get disableLoadNextPage() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
updateRow(index, changeObject) {
|
||||||
|
for (const key of Object.keys(changeObject)) {
|
||||||
|
this.setCellValue(index, key, changeObject[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,6 @@ import React from 'react';
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import theme from '../theme';
|
import theme from '../theme';
|
||||||
import keycodes from '../utility/keycodes';
|
import keycodes from '../utility/keycodes';
|
||||||
import { setChangeSetValue } from '@dbgate/datalib';
|
|
||||||
|
|
||||||
const StyledInput = styled.input`
|
const StyledInput = styled.input`
|
||||||
border: 0px solid;
|
border: 0px solid;
|
||||||
@@ -16,13 +15,12 @@ const StyledInput = styled.input`
|
|||||||
|
|
||||||
export default function InplaceEditor({
|
export default function InplaceEditor({
|
||||||
widthPx,
|
widthPx,
|
||||||
definition,
|
rowIndex,
|
||||||
changeSet,
|
uniqueName,
|
||||||
setChangeSet,
|
grider,
|
||||||
cellValue,
|
cellValue,
|
||||||
inplaceEditorState,
|
inplaceEditorState,
|
||||||
dispatchInsplaceEditor,
|
dispatchInsplaceEditor,
|
||||||
isInsertedRow,
|
|
||||||
}) {
|
}) {
|
||||||
const editorRef = React.useRef();
|
const editorRef = React.useRef();
|
||||||
const isChangedRef = React.useRef(!!inplaceEditorState.text);
|
const isChangedRef = React.useRef(!!inplaceEditorState.text);
|
||||||
@@ -37,7 +35,7 @@ export default function InplaceEditor({
|
|||||||
function handleBlur() {
|
function handleBlur() {
|
||||||
if (isChangedRef.current) {
|
if (isChangedRef.current) {
|
||||||
const editor = editorRef.current;
|
const editor = editorRef.current;
|
||||||
setChangeSet(setChangeSetValue(changeSet, definition, editor.value));
|
grider.setCellValue(rowIndex, uniqueName, editor.value);
|
||||||
isChangedRef.current = false;
|
isChangedRef.current = false;
|
||||||
}
|
}
|
||||||
dispatchInsplaceEditor({ type: 'close' });
|
dispatchInsplaceEditor({ type: 'close' });
|
||||||
@@ -45,7 +43,7 @@ export default function InplaceEditor({
|
|||||||
if (inplaceEditorState.shouldSave) {
|
if (inplaceEditorState.shouldSave) {
|
||||||
const editor = editorRef.current;
|
const editor = editorRef.current;
|
||||||
if (isChangedRef.current) {
|
if (isChangedRef.current) {
|
||||||
setChangeSet(setChangeSetValue(changeSet, definition, editor.value));
|
grider.setCellValue(rowIndex, uniqueName, editor.value);
|
||||||
isChangedRef.current = false;
|
isChangedRef.current = false;
|
||||||
}
|
}
|
||||||
editor.blur();
|
editor.blur();
|
||||||
@@ -60,7 +58,7 @@ export default function InplaceEditor({
|
|||||||
break;
|
break;
|
||||||
case keycodes.enter:
|
case keycodes.enter:
|
||||||
if (isChangedRef.current) {
|
if (isChangedRef.current) {
|
||||||
setChangeSet(setChangeSetValue(changeSet, definition, editor.value));
|
grider.setCellValue(rowIndex, uniqueName, editor.value);
|
||||||
isChangedRef.current = false;
|
isChangedRef.current = false;
|
||||||
}
|
}
|
||||||
editor.blur();
|
editor.blur();
|
||||||
@@ -69,7 +67,7 @@ export default function InplaceEditor({
|
|||||||
case keycodes.s:
|
case keycodes.s:
|
||||||
if (event.ctrlKey) {
|
if (event.ctrlKey) {
|
||||||
if (isChangedRef.current) {
|
if (isChangedRef.current) {
|
||||||
setChangeSet(setChangeSetValue(changeSet, definition, editor.value));
|
grider.setCellValue(rowIndex, uniqueName, editor.value);
|
||||||
isChangedRef.current = false;
|
isChangedRef.current = false;
|
||||||
}
|
}
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|||||||
93
packages/web/src/datagrid/JslDataGridCore.js
Normal file
93
packages/web/src/datagrid/JslDataGridCore.js
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import axios from '../utility/axios';
|
||||||
|
import { useSetOpenedTabs } from '../utility/globalState';
|
||||||
|
import useSocket from '../utility/SocketProvider';
|
||||||
|
import useShowModal from '../modals/showModal';
|
||||||
|
import ImportExportModal from '../modals/ImportExportModal';
|
||||||
|
import LoadingDataGridCore from './LoadingDataGridCore';
|
||||||
|
import RowsArrayGrider from './RowsArrayGrider';
|
||||||
|
|
||||||
|
async function loadDataPage(props, offset, limit) {
|
||||||
|
const { jslid } = props;
|
||||||
|
|
||||||
|
const response = await axios.request({
|
||||||
|
url: 'jsldata/get-rows',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
jslid,
|
||||||
|
offset,
|
||||||
|
limit,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
function dataPageAvailable(props) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadRowCount(props) {
|
||||||
|
const { jslid } = props;
|
||||||
|
|
||||||
|
const response = await axios.request({
|
||||||
|
url: 'jsldata/get-stats',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
jslid,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return response.data.rowCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function JslDataGridCore(props) {
|
||||||
|
const { jslid } = props;
|
||||||
|
const [changeIndex, setChangeIndex] = React.useState(0);
|
||||||
|
|
||||||
|
const showModal = useShowModal();
|
||||||
|
|
||||||
|
const setOpenedTabs = useSetOpenedTabs();
|
||||||
|
const socket = useSocket();
|
||||||
|
|
||||||
|
function exportGrid() {
|
||||||
|
const initialValues = {};
|
||||||
|
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'];
|
||||||
|
}
|
||||||
|
showModal((modalState) => <ImportExportModal modalState={modalState} initialValues={initialValues} />);
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleJslDataStats = React.useCallback((stats) => {
|
||||||
|
if (stats.changeIndex < changeIndex) return;
|
||||||
|
setChangeIndex(stats.changeIndex);
|
||||||
|
}, [changeIndex]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (jslid && socket) {
|
||||||
|
socket.on(`jsldata-stats-${jslid}`, handleJslDataStats);
|
||||||
|
return () => {
|
||||||
|
socket.off(`jsldata-stats-${jslid}`, handleJslDataStats);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, [jslid]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<LoadingDataGridCore
|
||||||
|
{...props}
|
||||||
|
exportGrid={exportGrid}
|
||||||
|
loadDataPage={loadDataPage}
|
||||||
|
dataPageAvailable={dataPageAvailable}
|
||||||
|
loadRowCount={loadRowCount}
|
||||||
|
loadNextDataToken={changeIndex}
|
||||||
|
onReload={() => setChangeIndex(0)}
|
||||||
|
griderFactory={RowsArrayGrider.factory}
|
||||||
|
griderFactoryDeps={RowsArrayGrider.factoryDeps}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
135
packages/web/src/datagrid/LoadingDataGridCore.js
Normal file
135
packages/web/src/datagrid/LoadingDataGridCore.js
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import DataGridCore from './DataGridCore';
|
||||||
|
|
||||||
|
export default function LoadingDataGridCore(props) {
|
||||||
|
const {
|
||||||
|
display,
|
||||||
|
loadDataPage,
|
||||||
|
dataPageAvailable,
|
||||||
|
loadRowCount,
|
||||||
|
loadNextDataToken,
|
||||||
|
onReload,
|
||||||
|
exportGrid,
|
||||||
|
openQuery,
|
||||||
|
griderFactory,
|
||||||
|
griderFactoryDeps,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const [loadProps, setLoadProps] = React.useState({
|
||||||
|
isLoading: false,
|
||||||
|
loadedRows: [],
|
||||||
|
isLoadedAll: false,
|
||||||
|
loadedTime: new Date().getTime(),
|
||||||
|
allRowCount: null,
|
||||||
|
errorMessage: null,
|
||||||
|
loadNextDataToken: 0,
|
||||||
|
});
|
||||||
|
const { isLoading, loadedRows, isLoadedAll, loadedTime, allRowCount, errorMessage } = loadProps;
|
||||||
|
|
||||||
|
const loadedTimeRef = React.useRef(0);
|
||||||
|
|
||||||
|
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,
|
||||||
|
loadNextDataToken: 0,
|
||||||
|
});
|
||||||
|
if (onReload) onReload();
|
||||||
|
};
|
||||||
|
|
||||||
|
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.loadNextDataToken == loadNextDataToken && nextRows.length === 0,
|
||||||
|
loadNextDataToken,
|
||||||
|
...loadedInfo,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
setLoadProps((oldProps) => ({
|
||||||
|
...oldProps,
|
||||||
|
isLoadedAll: false,
|
||||||
|
}));
|
||||||
|
}, [loadNextDataToken]);
|
||||||
|
|
||||||
|
const griderProps = { ...props, sourceRows: loadedRows };
|
||||||
|
const grider = React.useMemo(() => griderFactory(griderProps), griderFactoryDeps(griderProps));
|
||||||
|
|
||||||
|
const handleLoadNextData = () => {
|
||||||
|
if (!isLoadedAll && !errorMessage && !grider.disableLoadNextPage) {
|
||||||
|
if (dataPageAvailable(props)) {
|
||||||
|
// If not, callbacks to load missing metadata are dispatched
|
||||||
|
loadNextData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DataGridCore
|
||||||
|
{...props}
|
||||||
|
loadNextData={handleLoadNextData}
|
||||||
|
errorMessage={errorMessage}
|
||||||
|
isLoadedAll={isLoadedAll}
|
||||||
|
loadedTime={loadedTime}
|
||||||
|
exportGrid={exportGrid}
|
||||||
|
allRowCount={allRowCount}
|
||||||
|
openQuery={openQuery}
|
||||||
|
isLoading={isLoading}
|
||||||
|
grider={grider}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
23
packages/web/src/datagrid/RowsArrayGrider.ts
Normal file
23
packages/web/src/datagrid/RowsArrayGrider.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import Grider, { GriderRowStatus } from './Grider';
|
||||||
|
|
||||||
|
export default class RowsArrayGrider extends Grider {
|
||||||
|
constructor(private rows: any[]) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
getRowData(index: any) {
|
||||||
|
return this.rows[index];
|
||||||
|
}
|
||||||
|
get rowCount() {
|
||||||
|
return this.rows.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
static factory({ sourceRows }): RowsArrayGrider {
|
||||||
|
return new RowsArrayGrider(sourceRows);
|
||||||
|
}
|
||||||
|
static factoryDeps({ sourceRows }) {
|
||||||
|
return [sourceRows];
|
||||||
|
}
|
||||||
|
getRowsSample() {
|
||||||
|
return this.rows;
|
||||||
|
}
|
||||||
|
}
|
||||||
151
packages/web/src/datagrid/SqlDataGridCore.js
Normal file
151
packages/web/src/datagrid/SqlDataGridCore.js
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
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 { changeSetToSql, createChangeSet, getChangeSetInsertedRows } from '@dbgate/datalib';
|
||||||
|
import { openNewTab } from '../utility/common';
|
||||||
|
import LoadingDataGridCore from './LoadingDataGridCore';
|
||||||
|
import ChangeSetGrider from './ChangeSetGrider';
|
||||||
|
import { scriptToSql } from '@dbgate/sqltree';
|
||||||
|
import useModalState from '../modals/useModalState';
|
||||||
|
import ConfirmSqlModal from '../modals/ConfirmSqlModal';
|
||||||
|
import ErrorMessageModal from '../modals/ErrorMessageModal';
|
||||||
|
|
||||||
|
/** @param props {import('./types').DataGridProps} */
|
||||||
|
async function loadDataPage(props, offset, limit) {
|
||||||
|
const { display, conid, database } = props;
|
||||||
|
|
||||||
|
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 } = props;
|
||||||
|
const sql = display.getPageQuery(0, 1);
|
||||||
|
return !!sql;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadRowCount(props) {
|
||||||
|
const { display, conid, database } = props;
|
||||||
|
|
||||||
|
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 SqlDataGridCore(props) {
|
||||||
|
const { conid, database, display, changeSetState, dispatchChangeSet, tabVisible } = props;
|
||||||
|
const showModal = useShowModal();
|
||||||
|
const setOpenedTabs = useSetOpenedTabs();
|
||||||
|
|
||||||
|
const confirmSqlModalState = useModalState();
|
||||||
|
const [confirmSql, setConfirmSql] = React.useState('');
|
||||||
|
|
||||||
|
const changeSet = changeSetState && changeSetState.value;
|
||||||
|
const changeSetRef = React.useRef(changeSet);
|
||||||
|
changeSetRef.current = changeSet;
|
||||||
|
|
||||||
|
function exportGrid() {
|
||||||
|
const initialValues = {};
|
||||||
|
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 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 handleSave() {
|
||||||
|
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 grider = React.useMemo(()=>new ChangeSetGrider())
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<LoadingDataGridCore
|
||||||
|
{...props}
|
||||||
|
exportGrid={exportGrid}
|
||||||
|
openQuery={openQuery}
|
||||||
|
loadDataPage={loadDataPage}
|
||||||
|
dataPageAvailable={dataPageAvailable}
|
||||||
|
loadRowCount={loadRowCount}
|
||||||
|
griderFactory={ChangeSetGrider.factory}
|
||||||
|
griderFactoryDeps={ChangeSetGrider.factoryDeps}
|
||||||
|
// changeSet={changeSetState && changeSetState.value}
|
||||||
|
onSave={handleSave}
|
||||||
|
/>
|
||||||
|
<ConfirmSqlModal
|
||||||
|
modalState={confirmSqlModalState}
|
||||||
|
sql={confirmSql}
|
||||||
|
engine={display.engine}
|
||||||
|
onConfirm={handleConfirmSql}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ import useSocket from '../utility/SocketProvider';
|
|||||||
import { VerticalSplitter } from '../widgets/Splitter';
|
import { VerticalSplitter } from '../widgets/Splitter';
|
||||||
import stableStringify from 'json-stable-stringify';
|
import stableStringify from 'json-stable-stringify';
|
||||||
import ReferenceHeader from './ReferenceHeader';
|
import ReferenceHeader from './ReferenceHeader';
|
||||||
|
import SqlDataGridCore from './SqlDataGridCore';
|
||||||
|
|
||||||
const ReferenceContainer = styled.div`
|
const ReferenceContainer = styled.div`
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -162,6 +163,7 @@ export default function TableDataGrid({
|
|||||||
onReferenceSourceChanged={reference ? handleReferenceSourceChanged : null}
|
onReferenceSourceChanged={reference ? handleReferenceSourceChanged : null}
|
||||||
refReloadToken={refReloadToken.toString()}
|
refReloadToken={refReloadToken.toString()}
|
||||||
masterLoadedTime={masterLoadedTime}
|
masterLoadedTime={masterLoadedTime}
|
||||||
|
GridCore={SqlDataGridCore}
|
||||||
/>
|
/>
|
||||||
{reference && (
|
{reference && (
|
||||||
<ReferenceContainer>
|
<ReferenceContainer>
|
||||||
|
|||||||
@@ -2,10 +2,11 @@ import _ from 'lodash';
|
|||||||
import { SeriesSizes } from './SeriesSizes';
|
import { SeriesSizes } from './SeriesSizes';
|
||||||
import { CellAddress } from './selection';
|
import { CellAddress } from './selection';
|
||||||
import { GridDisplay } from '@dbgate/datalib';
|
import { GridDisplay } from '@dbgate/datalib';
|
||||||
|
import Grider from './Grider';
|
||||||
|
|
||||||
export function countColumnSizes(loadedRows, columns, containerWidth, display: GridDisplay) {
|
export function countColumnSizes(grider: Grider, columns, containerWidth, display: GridDisplay) {
|
||||||
const columnSizes = new SeriesSizes();
|
const columnSizes = new SeriesSizes();
|
||||||
if (!loadedRows || !columns) return columnSizes;
|
if (!grider || !columns) return columnSizes;
|
||||||
|
|
||||||
let canvas = document.createElement('canvas');
|
let canvas = document.createElement('canvas');
|
||||||
let context = canvas.getContext('2d');
|
let context = canvas.getContext('2d');
|
||||||
@@ -51,7 +52,8 @@ export function countColumnSizes(loadedRows, columns, containerWidth, display: G
|
|||||||
// if (headerWidth > this.rowHeaderWidth) this.rowHeaderWidth = headerWidth;
|
// if (headerWidth > this.rowHeaderWidth) this.rowHeaderWidth = headerWidth;
|
||||||
|
|
||||||
context.font = '14px Helvetica';
|
context.font = '14px Helvetica';
|
||||||
for (let row of loadedRows.slice(0, 20)) {
|
for (let rowIndex = 0; rowIndex < Math.min(grider.rowCount, 20); rowIndex += 1) {
|
||||||
|
const row = grider.getRowData(rowIndex);
|
||||||
for (let colIndex = 0; colIndex < columns.length; colIndex++) {
|
for (let colIndex = 0; colIndex < columns.length; colIndex++) {
|
||||||
const uqName = columns[colIndex].uniqueName;
|
const uqName = columns[colIndex].uniqueName;
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,46 @@
|
|||||||
import { GridDisplay, ChangeSet, GridReferenceDefinition } from '@dbgate/datalib';
|
import { GridDisplay, ChangeSet, GridReferenceDefinition } from '@dbgate/datalib';
|
||||||
|
import Grider from './Grider';
|
||||||
|
|
||||||
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;
|
||||||
refReloadToken?: string;
|
refReloadToken?: string;
|
||||||
masterLoadedTime?: number;
|
masterLoadedTime?: number;
|
||||||
managerSize?: number;
|
managerSize?: number;
|
||||||
|
grider?: Grider;
|
||||||
|
conid?: string;
|
||||||
|
database?: string;
|
||||||
|
jslid?: string;
|
||||||
|
|
||||||
|
[field: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
// }
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import React from 'react';
|
|||||||
import DataGrid from '../datagrid/DataGrid';
|
import DataGrid from '../datagrid/DataGrid';
|
||||||
import { JslGridDisplay, createGridConfig, createGridCache } from '@dbgate/datalib';
|
import { JslGridDisplay, createGridConfig, createGridCache } from '@dbgate/datalib';
|
||||||
import useFetch from '../utility/useFetch';
|
import useFetch from '../utility/useFetch';
|
||||||
|
import JslDataGridCore from '../datagrid/JslDataGridCore';
|
||||||
|
|
||||||
export default function JslDataGrid({ jslid }) {
|
export default function JslDataGrid({ jslid }) {
|
||||||
const info = useFetch({
|
const info = useFetch({
|
||||||
@@ -19,5 +20,5 @@ export default function JslDataGrid({ jslid }) {
|
|||||||
cache,
|
cache,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return <DataGrid display={display} jslid={jslid} />;
|
return <DataGrid display={display} jslid={jslid} GridCore={JslDataGridCore} />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import useUndoReducer from '../utility/useUndoReducer';
|
|||||||
import usePropsCompare from '../utility/usePropsCompare';
|
import usePropsCompare from '../utility/usePropsCompare';
|
||||||
import { useUpdateDatabaseForTab } from '../utility/globalState';
|
import { useUpdateDatabaseForTab } from '../utility/globalState';
|
||||||
import useGridConfig from '../utility/useGridConfig';
|
import useGridConfig from '../utility/useGridConfig';
|
||||||
|
import SqlDataGridCore from '../datagrid/SqlDataGridCore';
|
||||||
|
|
||||||
export default function ViewDataTab({ conid, database, schemaName, pureName, tabVisible, toolbarPortalRef, tabid }) {
|
export default function ViewDataTab({ conid, database, schemaName, pureName, tabVisible, toolbarPortalRef, tabid }) {
|
||||||
const viewInfo = useViewInfo({ conid, database, schemaName, pureName });
|
const viewInfo = useViewInfo({ conid, database, schemaName, pureName });
|
||||||
@@ -50,6 +51,7 @@ export default function ViewDataTab({ conid, database, schemaName, pureName, tab
|
|||||||
changeSetState={changeSetState}
|
changeSetState={changeSetState}
|
||||||
dispatchChangeSet={dispatchChangeSet}
|
dispatchChangeSet={dispatchChangeSet}
|
||||||
toolbarPortalRef={toolbarPortalRef}
|
toolbarPortalRef={toolbarPortalRef}
|
||||||
/>
|
GridCore={SqlDataGridCore}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user