incremental loading

This commit is contained in:
Jan Prochazka
2022-07-21 17:05:07 +02:00
parent d71294621b
commit 2080a23b69
6 changed files with 111 additions and 23 deletions

View File

@@ -3,6 +3,9 @@ import _max from 'lodash/max';
import _range from 'lodash/max'; import _range from 'lodash/max';
import _fill from 'lodash/fill'; import _fill from 'lodash/fill';
import _findIndex from 'lodash/findIndex'; import _findIndex from 'lodash/findIndex';
import debug from 'debug';
const dbg = debug('dbgate:PerspectiveDisplay');
export class PerspectiveDisplayColumn { export class PerspectiveDisplayColumn {
title: string; title: string;
@@ -53,6 +56,7 @@ interface CollectedPerspectiveDisplayRow {
rowData: any[]; rowData: any[];
// rowSpans: number[] = null; // rowSpans: number[] = null;
subRowCollections: PerspectiveSubRowCollection[]; subRowCollections: PerspectiveSubRowCollection[];
incompleteRowsIndicator?: string[];
} }
export class PerspectiveDisplayRow { export class PerspectiveDisplayRow {
@@ -73,6 +77,7 @@ export class PerspectiveDisplayRow {
rowData: any[] = []; rowData: any[] = [];
rowSpans: number[] = null; rowSpans: number[] = null;
incompleteRowsIndicator: string[] = null;
} }
export class PerspectiveDisplay { export class PerspectiveDisplay {
@@ -81,12 +86,15 @@ export class PerspectiveDisplay {
readonly columnLevelCount: number; readonly columnLevelCount: number;
constructor(public root: PerspectiveTreeNode, rows: any[]) { constructor(public root: PerspectiveTreeNode, rows: any[]) {
// dbg('source rows', rows);
this.fillColumns(root.childNodes, []); this.fillColumns(root.childNodes, []);
this.columnLevelCount = _max(this.columns.map(x => x.parentNodes.length)) + 1; this.columnLevelCount = _max(this.columns.map(x => x.parentNodes.length)) + 1;
const collectedRows = this.collectRows(rows, root.childNodes); const collectedRows = this.collectRows(rows, root.childNodes);
// dbg('collected rows', collectedRows);
// console.log('COLLECTED', collectedRows); // console.log('COLLECTED', collectedRows);
// this.mergeRows(collectedRows); // this.mergeRows(collectedRows);
this.mergeRows(collectedRows); this.mergeRows(collectedRows);
// dbg('merged rows', this.rows);
// console.log('MERGED', this.rows); // console.log('MERGED', this.rows);
} }
@@ -163,6 +171,7 @@ export class PerspectiveDisplay {
rowData, rowData,
columnIndexes, columnIndexes,
subRowCollections, subRowCollections,
incompleteRowsIndicator: sourceRow.incompleteRowsIndicator,
}); });
} }
@@ -212,6 +221,7 @@ export class PerspectiveDisplay {
for (let i = 0; i < collectedRow.columnIndexes.length; i++) { for (let i = 0; i < collectedRow.columnIndexes.length; i++) {
resultRow.rowData[collectedRow.columnIndexes[i]] = collectedRow.rowData[i]; resultRow.rowData[collectedRow.columnIndexes[i]] = collectedRow.rowData[i];
} }
resultRow.incompleteRowsIndicator = collectedRow.incompleteRowsIndicator;
for (const subrows of collectedRow.subRowCollections) { for (const subrows of collectedRow.subRowCollections) {
let rowIndex = 0; let rowIndex = 0;

View File

@@ -0,0 +1,33 @@
<script lang="ts">
import { onMount } from 'svelte';
export let rootNode;
export let onLoadNext;
let domObserved;
function handleObserver(entries) {
// // console.log('HANDLE OBSERVER', loadedRows.length);
// if (isLoading || loadedRows.length == 0) return;
// loadNextData();
const [entry] = entries;
if (entry.isIntersecting) {
onLoadNext();
}
}
onMount(() => {
const observer = new IntersectionObserver(handleObserver, {
root: rootNode,
rootMargin: '100px',
threshold: 0.1,
});
observer.observe(domObserved);
return () => {
observer.disconnect();
};
});
</script>
<div bind:this={domObserved}>... data to be loaded</div>

View File

@@ -11,23 +11,37 @@
import { prop_dev, tick } from 'svelte/internal'; import { prop_dev, tick } from 'svelte/internal';
import { sleep } from '../utility/common'; import { sleep } from '../utility/common';
import resizeObserver from '../utility/resizeObserver'; import resizeObserver from '../utility/resizeObserver';
import PerspectiveIntersectionObserver from './PerspectiveIntersectionObserver.svelte';
import debug from 'debug';
const dbg = debug('dbgate:PerspectivaTable');
export let root: PerspectiveTreeNode; export let root: PerspectiveTreeNode;
export let loadedCounts;
let dataRows; let dataRows;
let domWrapper; let domWrapper;
let domTableHead; let domTableHead;
let domHeaderWrap; let domHeaderWrap;
let theadClone; let theadClone;
async function loadLevelData(node: PerspectiveTreeNode, parentRows: any[]) { async function loadLevelData(node: PerspectiveTreeNode, parentRows: any[], counts) {
dbg('load level data', counts);
// const loadProps: PerspectiveDataLoadPropsWithNode[] = []; // const loadProps: PerspectiveDataLoadPropsWithNode[] = [];
const loadChildNodes = []; const loadChildNodes = [];
const loadChildRows = []; const loadChildRows = [];
const loadProps = node.getNodeLoadProps(parentRows); const loadProps = node.getNodeLoadProps(parentRows);
const { rows, incomplete } = await node.dataProvider.loadData({ let { rows, incomplete } = await node.dataProvider.loadData({
...loadProps, ...loadProps,
topCount: 100, topCount: counts[node.uniqueName] || 100,
}); });
if (incomplete) {
rows = [
...rows,
{
incompleteRowsIndicator: [node.uniqueName],
},
];
}
// console.log('ROWS', rows, node.isRoot); // console.log('ROWS', rows, node.isRoot);
if (node.isRoot) { if (node.isRoot) {
@@ -42,7 +56,7 @@
for (const child of node.childNodes) { for (const child of node.childNodes) {
if (child.isExpandable && child.isChecked) { if (child.isExpandable && child.isChecked) {
await loadLevelData(child, rows); await loadLevelData(child, rows, counts);
// loadProps.push(child.getNodeLoadProps()); // loadProps.push(child.getNodeLoadProps());
} }
} }
@@ -63,11 +77,11 @@
// } // }
} }
async function loadData(node: PerspectiveTreeNode) { async function loadData(node: PerspectiveTreeNode, counts) {
// console.log('LOADING', node); // console.log('LOADING', node);
if (!node) return; if (!node) return;
const rows = []; const rows = [];
await loadLevelData(node, rows); await loadLevelData(node, rows, counts);
dataRows = rows; dataRows = rows;
// console.log('DISPLAY ROWS', rows); // console.log('DISPLAY ROWS', rows);
@@ -102,7 +116,7 @@
onMount(() => {}); onMount(() => {});
$: loadData(root); $: loadData(root, $loadedCounts);
$: display = root && dataRows ? new PerspectiveDisplay(root, dataRows) : null; $: display = root && dataRows ? new PerspectiveDisplay(root, dataRows) : null;
$: { $: {
@@ -137,12 +151,30 @@
<tbody> <tbody>
{#each display.rows as row} {#each display.rows as row}
<tr> <tr>
{#if row.incompleteRowsIndicator}
<td colspan={display.columns.length}
><PerspectiveIntersectionObserver
rootNode={domWrapper}
onLoadNext={() => {
dbg('load next', row.incompleteRowsIndicator);
loadedCounts.update(counts => {
const res = { ...counts };
for (const id of row.incompleteRowsIndicator) {
res[id] = (res[id] || 100) + 100;
}
return res;
});
}}
/></td
>
{:else}
{#each display.columns as column} {#each display.columns as column}
<!-- <td>{row.rowSpans[column.columnIndex]} {row.rowData[column.columnIndex]}</td> --> <!-- <td>{row.rowSpans[column.columnIndex]} {row.rowData[column.columnIndex]}</td> -->
{#if row.rowData[column.columnIndex] !== undefined} {#if row.rowData[column.columnIndex] !== undefined}
<td rowspan={row.rowSpans[column.columnIndex]}>{row.rowData[column.columnIndex]}</td> <td rowspan={row.rowSpans[column.columnIndex]}>{row.rowData[column.columnIndex]}</td>
{/if} {/if}
{/each} {/each}
{/if}
</tr> </tr>
{/each} {/each}
</tbody> </tbody>

View File

@@ -35,6 +35,7 @@
export let config; export let config;
export let setConfig; export let setConfig;
export let loadedCounts;
export let cache; export let cache;
@@ -58,7 +59,15 @@
$: dataProvider = new PerspectiveDataProvider(cache, loader); $: dataProvider = new PerspectiveDataProvider(cache, loader);
$: loader = new PerspectiveDataLoader(apiCall); $: loader = new PerspectiveDataLoader(apiCall);
$: root = $tableInfo $: root = $tableInfo
? new PerspectiveTableNode($tableInfo, $dbInfo, config, setConfig, dataProvider, { conid, database }, null) ? new PerspectiveTableNode(
$tableInfo,
$dbInfo,
config,
setConfig,
dataProvider,
{ conid, database },
null
)
: null; : null;
</script> </script>
@@ -76,7 +85,7 @@
</div> </div>
<svelte:fragment slot="2"> <svelte:fragment slot="2">
<PerspectiveTable {root} /> <PerspectiveTable {root} {loadedCounts} />
</svelte:fragment> </svelte:fragment>
</HorizontalSplitter> </HorizontalSplitter>

View File

@@ -4,6 +4,7 @@
import PerspectiveView from '../perspectives/PerspectiveView.svelte'; import PerspectiveView from '../perspectives/PerspectiveView.svelte';
import usePerspectiveConfig from '../utility/usePerspectiveConfig'; import usePerspectiveConfig from '../utility/usePerspectiveConfig';
import stableStringify from 'json-stable-stringify'; import stableStringify from 'json-stable-stringify';
import { writable } from 'svelte/store';
export let tabid; export let tabid;
export let conid; export let conid;
@@ -13,6 +14,16 @@
const config = usePerspectiveConfig(tabid); const config = usePerspectiveConfig(tabid);
const cache = new PerspectiveCache(stableStringify); const cache = new PerspectiveCache(stableStringify);
const loadedCounts = writable({});
</script> </script>
<PerspectiveView {conid} {database} {schemaName} {pureName} config={$config} setConfig={config.update} {cache} /> <PerspectiveView
{conid}
{database}
{schemaName}
{pureName}
config={$config}
setConfig={config.update}
{cache}
{loadedCounts}
/>

View File

@@ -25,10 +25,3 @@ export default function usePerspectiveConfig(tabid) {
onDestroy(unsubscribe); onDestroy(unsubscribe);
return config; return config;
} }
// export function usePerspectiveCache() {
// const cache = writable({
// tables: {},
// });
// return cache;
// }