mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-19 21:16:00 +00:00
dynamic loading of next rows
This commit is contained in:
@@ -1,9 +1,14 @@
|
|||||||
import GridConfig from "./GridConfig";
|
import GridConfig from "./GridConfig";
|
||||||
|
|
||||||
|
export interface DisplayColumn {
|
||||||
|
columnName: string;
|
||||||
|
}
|
||||||
|
|
||||||
export default abstract class GridDisplay {
|
export default abstract class GridDisplay {
|
||||||
constructor(
|
constructor(
|
||||||
public config: GridConfig,
|
public config: GridConfig,
|
||||||
protected setConfig: (configh: GridConfig) => void
|
protected setConfig: (configh: GridConfig) => void
|
||||||
) {}
|
) {}
|
||||||
abstract getPageQuery(offset: number, count: number): string;
|
abstract getPageQuery(offset: number, count: number): string;
|
||||||
|
columns: DisplayColumn[];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,7 @@
|
|||||||
import GridDisplay from "./GridDisplay";
|
import GridDisplay from './GridDisplay';
|
||||||
import {
|
import { Select, treeToSql, dumpSqlSelect, createColumnResultField } from '@dbgate/sqltree';
|
||||||
Select,
|
import { TableInfo, EngineDriver } from '@dbgate/types';
|
||||||
treeToSql,
|
import GridConfig from './GridConfig';
|
||||||
dumpSqlSelect,
|
|
||||||
createColumnResultField
|
|
||||||
} from "@dbgate/sqltree";
|
|
||||||
import { TableInfo, EngineDriver } from "@dbgate/types";
|
|
||||||
import GridConfig from "./GridConfig";
|
|
||||||
|
|
||||||
export default class TableGridDisplay extends GridDisplay {
|
export default class TableGridDisplay extends GridDisplay {
|
||||||
constructor(
|
constructor(
|
||||||
@@ -16,17 +11,16 @@ export default class TableGridDisplay extends GridDisplay {
|
|||||||
setConfig: (configh: GridConfig) => void
|
setConfig: (configh: GridConfig) => void
|
||||||
) {
|
) {
|
||||||
super(config, setConfig);
|
super(config, setConfig);
|
||||||
|
this.columns = table.columns;
|
||||||
}
|
}
|
||||||
|
|
||||||
createSelect() {
|
createSelect() {
|
||||||
const select: Select = {
|
const select: Select = {
|
||||||
commandType: "select",
|
commandType: 'select',
|
||||||
from: {
|
from: {
|
||||||
source: { name: this.table }
|
source: { name: this.table },
|
||||||
},
|
},
|
||||||
columns: this.table.columns.map(col =>
|
columns: this.table.columns.map(col => createColumnResultField(col.columnName)),
|
||||||
createColumnResultField(col.columnName)
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
return select;
|
return select;
|
||||||
}
|
}
|
||||||
@@ -34,8 +28,7 @@ export default class TableGridDisplay extends GridDisplay {
|
|||||||
getPageQuery(offset: number, count: number) {
|
getPageQuery(offset: number, count: number) {
|
||||||
const select = this.createSelect();
|
const select = this.createSelect();
|
||||||
if (this.driver.dialect.limitSelect) select.topRecords = count;
|
if (this.driver.dialect.limitSelect) select.topRecords = count;
|
||||||
if (this.driver.dialect.rangeSelect)
|
if (this.driver.dialect.rangeSelect) select.range = { offset: offset, limit: count };
|
||||||
select.range = { offset: offset, limit: count };
|
|
||||||
const sql = treeToSql(this.driver, select, dumpSqlSelect);
|
const sql = treeToSql(this.driver, select, dumpSqlSelect);
|
||||||
return sql;
|
return sql;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
export { default as GridDisplay } from "./GridDisplay";
|
export { default as GridDisplay, DisplayColumn } from "./GridDisplay";
|
||||||
export { default as TableGridDisplay } from "./TableGridDisplay";
|
export { default as TableGridDisplay } from "./TableGridDisplay";
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import theme from '../theme';
|
|||||||
import { HorizontalScrollBar, VerticalScrollBar } from './ScrollBars';
|
import { HorizontalScrollBar, VerticalScrollBar } from './ScrollBars';
|
||||||
import useDimensions from '../utility/useDimensions';
|
import useDimensions from '../utility/useDimensions';
|
||||||
import { SeriesSizes } from './SeriesSizes';
|
import { SeriesSizes } from './SeriesSizes';
|
||||||
|
import axios from '../utility/axios';
|
||||||
|
|
||||||
const GridContainer = styled.div`
|
const GridContainer = styled.div`
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -71,11 +72,31 @@ const TableBodyCell = styled.td`
|
|||||||
*/
|
*/
|
||||||
export default function DataGrid(props) {
|
export default function DataGrid(props) {
|
||||||
const { conid, database, display } = props;
|
const { conid, database, display } = props;
|
||||||
const sql = display.getPageQuery(0, 100);
|
const columns = display.columns;
|
||||||
|
|
||||||
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(),
|
||||||
|
});
|
||||||
|
const { isLoading, loadedRows, isLoadedAll, loadedTime } = loadProps;
|
||||||
|
|
||||||
const data = useFetch({
|
const loadedTimeRef = React.useRef(0);
|
||||||
|
|
||||||
|
const loadNextData = async () => {
|
||||||
|
if (isLoading) return;
|
||||||
|
setLoadProps({
|
||||||
|
...loadProps,
|
||||||
|
isLoading: true,
|
||||||
|
});
|
||||||
|
const loadStart = new Date().getTime();
|
||||||
|
loadedTimeRef.current = loadStart;
|
||||||
|
|
||||||
|
const sql = display.getPageQuery(loadedRows.length, 100);
|
||||||
|
|
||||||
|
let response = await axios.request({
|
||||||
url: 'database-connections/query-data',
|
url: 'database-connections/query-data',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
params: {
|
params: {
|
||||||
@@ -84,7 +105,38 @@ export default function DataGrid(props) {
|
|||||||
},
|
},
|
||||||
data: { sql },
|
data: { sql },
|
||||||
});
|
});
|
||||||
const { rows, columns } = data || {};
|
if (loadedTimeRef.current !== loadStart) {
|
||||||
|
// new load was dispatched
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// if (!_.isArray(nextRows)) {
|
||||||
|
// console.log('Error loading data from server', nextRows);
|
||||||
|
// nextRows = [];
|
||||||
|
// }
|
||||||
|
const { rows: nextRows } = response.data;
|
||||||
|
console.log('nextRows',nextRows)
|
||||||
|
const loadedInfo = {
|
||||||
|
loadedRows: [...loadedRows, ...nextRows],
|
||||||
|
loadedTime,
|
||||||
|
isLoadedAll: nextRows.length === 0,
|
||||||
|
};
|
||||||
|
setLoadProps({
|
||||||
|
...loadProps,
|
||||||
|
isLoading: false,
|
||||||
|
...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);
|
||||||
|
|
||||||
@@ -92,7 +144,7 @@ export default function DataGrid(props) {
|
|||||||
const [tableBodyRef] = useDimensions();
|
const [tableBodyRef] = useDimensions();
|
||||||
const [containerRef, { height: containerHeight, width: containerWidth }] = useDimensions();
|
const [containerRef, { height: containerHeight, width: containerWidth }] = useDimensions();
|
||||||
|
|
||||||
const columnSizes = React.useMemo(() => countColumnSizes(), [data, containerWidth]);
|
const columnSizes = React.useMemo(() => countColumnSizes(), [loadedRows, containerWidth]);
|
||||||
|
|
||||||
console.log('containerWidth', containerWidth);
|
console.log('containerWidth', containerWidth);
|
||||||
|
|
||||||
@@ -104,8 +156,14 @@ export default function DataGrid(props) {
|
|||||||
// const visibleRowCountUpperBound = 20;
|
// const visibleRowCountUpperBound = 20;
|
||||||
// const visibleRowCountLowerBound = 20;
|
// const visibleRowCountLowerBound = 20;
|
||||||
|
|
||||||
if (!columns || !rows) return null;
|
React.useEffect(() => {
|
||||||
const rowCountNewIncluded = rows.length;
|
if (!isLoadedAll && firstVisibleRowScrollIndex + visibleRowCountUpperBound >= loadedRows.length) {
|
||||||
|
loadNextData();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!loadedRows || !columns) return null;
|
||||||
|
const rowCountNewIncluded = loadedRows.length;
|
||||||
|
|
||||||
const handleRowScroll = value => {
|
const handleRowScroll = value => {
|
||||||
setFirstVisibleRowScrollIndex(value);
|
setFirstVisibleRowScrollIndex(value);
|
||||||
@@ -121,9 +179,9 @@ export default function DataGrid(props) {
|
|||||||
|
|
||||||
//return this.context.measureText(txt).width;
|
//return this.context.measureText(txt).width;
|
||||||
const columnSizes = new SeriesSizes();
|
const columnSizes = new SeriesSizes();
|
||||||
if (!rows || !columns) return columnSizes;
|
if (!loadedRows || !columns) return columnSizes;
|
||||||
|
|
||||||
console.log('countColumnSizes', rows.length, containerWidth);
|
console.log('countColumnSizes', loadedRows.length, containerWidth);
|
||||||
|
|
||||||
columnSizes.maxSize = (containerWidth * 2) / 3;
|
columnSizes.maxSize = (containerWidth * 2) / 3;
|
||||||
columnSizes.count = columns.length;
|
columnSizes.count = columns.length;
|
||||||
@@ -139,7 +197,7 @@ export default function DataGrid(props) {
|
|||||||
// else context.font = "14px Helvetica";
|
// else context.font = "14px Helvetica";
|
||||||
context.font = 'bold 14px Helvetica';
|
context.font = 'bold 14px Helvetica';
|
||||||
|
|
||||||
let text = column.name;
|
let text = column.columnName;
|
||||||
let headerWidth = context.measureText(text).width + 32;
|
let headerWidth = context.measureText(text).width + 32;
|
||||||
|
|
||||||
// if (column.columnClientObject != null && column.columnClientObject.icon != null) headerWidth += 16;
|
// if (column.columnClientObject != null && column.columnClientObject.icon != null) headerWidth += 16;
|
||||||
@@ -155,9 +213,9 @@ export default function DataGrid(props) {
|
|||||||
// 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 data.rows) {
|
for (let row of loadedRows) {
|
||||||
for (let colIndex = 0; colIndex < columns.length; colIndex++) {
|
for (let colIndex = 0; colIndex < columns.length; colIndex++) {
|
||||||
let colName = columns[colIndex].name;
|
let colName = columns[colIndex].columnName;
|
||||||
let text = row[colName];
|
let text = row[colName];
|
||||||
let width = context.measureText(text).width + 8;
|
let width = context.measureText(text).width + 8;
|
||||||
// console.log('colName', colName, text, width);
|
// console.log('colName', colName, text, width);
|
||||||
@@ -188,6 +246,7 @@ export default function DataGrid(props) {
|
|||||||
|
|
||||||
const visibleRealColumnIndexes = [];
|
const visibleRealColumnIndexes = [];
|
||||||
const modelIndexes = {};
|
const modelIndexes = {};
|
||||||
|
/** @type {(import('@dbgate/datalib').DisplayColumn & {widthPx: string})[]} */
|
||||||
const realColumns = [];
|
const realColumns = [];
|
||||||
|
|
||||||
// frozen columns
|
// frozen columns
|
||||||
@@ -226,25 +285,25 @@ export default function DataGrid(props) {
|
|||||||
<TableHeaderRow ref={headerRowRef}>
|
<TableHeaderRow ref={headerRowRef}>
|
||||||
{realColumns.map(col => (
|
{realColumns.map(col => (
|
||||||
<TableHeaderCell
|
<TableHeaderCell
|
||||||
key={col.name}
|
key={col.columnName}
|
||||||
style={{ width: col.widthPx, minWidth: col.widthPx, maxWidth: col.widthPx }}
|
style={{ width: col.widthPx, minWidth: col.widthPx, maxWidth: col.widthPx }}
|
||||||
>
|
>
|
||||||
{col.name}
|
{col.columnName}
|
||||||
</TableHeaderCell>
|
</TableHeaderCell>
|
||||||
))}
|
))}
|
||||||
</TableHeaderRow>
|
</TableHeaderRow>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableBody ref={tableBodyRef}>
|
<TableBody ref={tableBodyRef}>
|
||||||
{rows
|
{loadedRows
|
||||||
.slice(firstVisibleRowScrollIndex, firstVisibleRowScrollIndex + visibleRowCountUpperBound)
|
.slice(firstVisibleRowScrollIndex, firstVisibleRowScrollIndex + visibleRowCountUpperBound)
|
||||||
.map((row, index) => (
|
.map((row, index) => (
|
||||||
<TableBodyRow key={firstVisibleRowScrollIndex + index}>
|
<TableBodyRow key={firstVisibleRowScrollIndex + index}>
|
||||||
{realColumns.map(col => (
|
{realColumns.map(col => (
|
||||||
<TableBodyCell
|
<TableBodyCell
|
||||||
key={col.name}
|
key={col.columnName}
|
||||||
style={{ width: col.widthPx, minWidth: col.widthPx, maxWidth: col.widthPx }}
|
style={{ width: col.widthPx, minWidth: col.widthPx, maxWidth: col.widthPx }}
|
||||||
>
|
>
|
||||||
{row[col.name]}
|
{row[col.columnName]}
|
||||||
</TableBodyCell>
|
</TableBodyCell>
|
||||||
))}
|
))}
|
||||||
</TableBodyRow>
|
</TableBodyRow>
|
||||||
|
|||||||
Reference in New Issue
Block a user