Reset scrolling when datasource changes and fix wasted pagination calls
This commit is contained in:
parent
d7666272e0
commit
e76c541627
|
@ -15,6 +15,8 @@
|
|||
import { createUserStores } from "./stores/users"
|
||||
import { createResizeStores } from "./stores/resize"
|
||||
import { createMenuStores } from "./stores/menu"
|
||||
import { createMaxScrollStores } from "./stores/max-scroll"
|
||||
import { createPaginationStores } from "./stores/pagination"
|
||||
import DeleteButton from "./DeleteButton.svelte"
|
||||
import SheetBody from "./SheetBody.svelte"
|
||||
import ResizeOverlay from "./ResizeOverlay.svelte"
|
||||
|
@ -57,16 +59,18 @@
|
|||
config,
|
||||
}
|
||||
context = { ...context, ...createEventManagers() }
|
||||
context = { ...context, ...createRowsStore(context) }
|
||||
context = { ...context, ...createColumnsStores(context) }
|
||||
context = { ...context, ...createResizeStores(context) }
|
||||
context = { ...context, ...createBoundsStores(context) }
|
||||
context = { ...context, ...createScrollStores(context) }
|
||||
context = { ...context, ...createRowsStore(context) }
|
||||
context = { ...context, ...createColumnsStores(context) }
|
||||
context = { ...context, ...createMaxScrollStores(context) }
|
||||
context = { ...context, ...createResizeStores(context) }
|
||||
context = { ...context, ...createViewportStores(context) }
|
||||
context = { ...context, ...createReorderStores(context) }
|
||||
context = { ...context, ...createUIStores(context) }
|
||||
context = { ...context, ...createUserStores(context) }
|
||||
context = { ...context, ...createMenuStores(context) }
|
||||
context = { ...context, ...createPaginationStores(context) }
|
||||
|
||||
// Reference some stores for local use
|
||||
const { isResizing, isReordering, ui, loaded } = context
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
import { derived, get } from "svelte/store"
|
||||
|
||||
export const createMaxScrollStores = context => {
|
||||
const { rows, visibleColumns, stickyColumn, bounds, cellHeight, scroll } =
|
||||
context
|
||||
const padding = 180
|
||||
|
||||
// Memoize store primitives
|
||||
const scrollTop = derived(scroll, $scroll => $scroll.top, 0)
|
||||
const scrollLeft = derived(scroll, $scroll => $scroll.left, 0)
|
||||
|
||||
// Derive vertical limits
|
||||
const height = derived(bounds, $bounds => $bounds.height, 0)
|
||||
const width = derived(bounds, $bounds => $bounds.width, 0)
|
||||
const contentHeight = derived(
|
||||
rows,
|
||||
$rows => $rows.length * cellHeight + padding,
|
||||
0
|
||||
)
|
||||
const maxScrollTop = derived(
|
||||
[height, contentHeight],
|
||||
([$height, $contentHeight]) => Math.max($contentHeight - $height, 0),
|
||||
0
|
||||
)
|
||||
|
||||
// Derive horizontal limits
|
||||
const contentWidth = derived(
|
||||
[visibleColumns, stickyColumn],
|
||||
([$visibleColumns, $stickyColumn]) => {
|
||||
let width = 40 + padding + ($stickyColumn?.width || 0)
|
||||
$visibleColumns.forEach(col => {
|
||||
width += col.width
|
||||
})
|
||||
return width
|
||||
},
|
||||
0
|
||||
)
|
||||
const screenWidth = derived(
|
||||
[width, stickyColumn],
|
||||
([$width, $stickyColumn]) => $width + 40 + ($stickyColumn?.width || 0),
|
||||
0
|
||||
)
|
||||
const maxScrollLeft = derived(
|
||||
[contentWidth, screenWidth],
|
||||
([$contentWidth, $screenWidth]) => {
|
||||
return Math.max($contentWidth - $screenWidth, 0)
|
||||
},
|
||||
0
|
||||
)
|
||||
|
||||
// Ensure scroll state never goes invalid, which can happen when changing
|
||||
// rows or tables
|
||||
const overscrollTop = derived(
|
||||
[scrollTop, maxScrollTop],
|
||||
([$scrollTop, $maxScrollTop]) => $scrollTop > $maxScrollTop,
|
||||
false
|
||||
)
|
||||
const overscrollLeft = derived(
|
||||
[scrollLeft, maxScrollLeft],
|
||||
([$scrollLeft, $maxScrollLeft]) => $scrollLeft > $maxScrollLeft,
|
||||
false
|
||||
)
|
||||
overscrollTop.subscribe(overscroll => {
|
||||
if (overscroll) {
|
||||
scroll.update(state => ({
|
||||
...state,
|
||||
top: get(maxScrollTop),
|
||||
}))
|
||||
}
|
||||
})
|
||||
overscrollLeft.subscribe(overscroll => {
|
||||
if (overscroll) {
|
||||
scroll.update(state => ({
|
||||
...state,
|
||||
left: get(maxScrollLeft),
|
||||
}))
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
contentHeight,
|
||||
contentWidth,
|
||||
maxScrollTop,
|
||||
maxScrollLeft,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
import { derived } from "svelte/store"
|
||||
|
||||
export const createPaginationStores = context => {
|
||||
const { scrolledRowCount, rows, visualRowCapacity } = context
|
||||
|
||||
// Derive how many rows we have in total
|
||||
const rowCount = derived(rows, $rows => $rows.length, 0)
|
||||
|
||||
// Derive how many rows we have available to scroll
|
||||
const remainingRows = derived(
|
||||
[scrolledRowCount, rowCount, visualRowCapacity],
|
||||
([$scrolledRowCount, $rowCount, $visualRowCapacity]) => {
|
||||
return Math.max(0, $rowCount - $scrolledRowCount - $visualRowCapacity)
|
||||
},
|
||||
100
|
||||
)
|
||||
|
||||
// Fetch next page when fewer than 25 remaining rows to scroll
|
||||
remainingRows.subscribe(remaining => {
|
||||
if (remaining < 25) {
|
||||
rows.actions.loadNextPage()
|
||||
}
|
||||
})
|
||||
|
||||
return null
|
||||
}
|
|
@ -3,13 +3,14 @@ import { fetchData } from "../../../fetch/fetchData"
|
|||
import { notifications } from "@budibase/bbui"
|
||||
|
||||
export const createRowsStore = context => {
|
||||
const { config, API } = context
|
||||
const { config, API, scroll } = context
|
||||
const tableId = derived(config, $config => $config.tableId)
|
||||
const rows = writable([])
|
||||
const schema = writable({})
|
||||
const table = writable(null)
|
||||
const filter = writable([])
|
||||
const loaded = writable(false)
|
||||
const instanceLoaded = writable(false)
|
||||
const fetch = writable(null)
|
||||
const initialSortState = {
|
||||
column: null,
|
||||
|
@ -51,6 +52,7 @@ export const createRowsStore = context => {
|
|||
// Unsub from previous fetch if one exists
|
||||
unsubscribe?.()
|
||||
fetch.set(null)
|
||||
instanceLoaded.set(false)
|
||||
|
||||
// Reset state
|
||||
sort.set(initialSortState)
|
||||
|
@ -75,10 +77,16 @@ export const createRowsStore = context => {
|
|||
// Subscribe to changes of this fetch model
|
||||
unsubscribe = newFetch.subscribe($fetch => {
|
||||
if ($fetch.loaded && !$fetch.loading) {
|
||||
if ($fetch.pageNumber === 0) {
|
||||
// Hydrate initial data
|
||||
rowCacheMap = {}
|
||||
rows.set([])
|
||||
const resetRows = $fetch.pageNumber === 0
|
||||
|
||||
// Reset scroll state when data changes
|
||||
if (!get(instanceLoaded)) {
|
||||
// Reset both top and left for a new table ID
|
||||
instanceLoaded.set(true)
|
||||
scroll.set({ top: 0, left: 0 })
|
||||
} else if (resetRows) {
|
||||
// Only reset top scroll position when resetting rows
|
||||
scroll.update(state => ({ ...state, top: 0 }))
|
||||
}
|
||||
|
||||
// Update schema and enrich primary display into schema
|
||||
|
@ -91,7 +99,7 @@ export const createRowsStore = context => {
|
|||
table.set($fetch.definition)
|
||||
|
||||
// Process new rows
|
||||
handleNewRows($fetch.rows)
|
||||
handleNewRows($fetch.rows, resetRows)
|
||||
|
||||
// Notify that we're loaded
|
||||
loaded.set(true)
|
||||
|
@ -222,7 +230,10 @@ export const createRowsStore = context => {
|
|||
|
||||
// Local handler to process new rows inside the fetch, and append any new
|
||||
// rows to state that we haven't encountered before
|
||||
const handleNewRows = newRows => {
|
||||
const handleNewRows = (newRows, resetRows) => {
|
||||
if (resetRows) {
|
||||
rowCacheMap = {}
|
||||
}
|
||||
let rowsToAppend = []
|
||||
let newRow
|
||||
for (let i = 0; i < newRows.length; i++) {
|
||||
|
@ -232,7 +243,9 @@ export const createRowsStore = context => {
|
|||
rowsToAppend.push(newRow)
|
||||
}
|
||||
}
|
||||
if (rowsToAppend.length) {
|
||||
if (resetRows) {
|
||||
rows.set(rowsToAppend)
|
||||
} else if (rowsToAppend.length) {
|
||||
rows.update(state => [...state, ...rowsToAppend])
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,107 +1,11 @@
|
|||
import { derived, get, writable } from "svelte/store"
|
||||
import { writable } from "svelte/store"
|
||||
|
||||
export const createScrollStores = context => {
|
||||
const { rows, visibleColumns, stickyColumn, bounds, cellHeight } = context
|
||||
const padding = 180
|
||||
export const createScrollStores = () => {
|
||||
const scroll = writable({
|
||||
left: 0,
|
||||
top: 0,
|
||||
})
|
||||
|
||||
// Memoize store primitives
|
||||
const scrollTop = derived(scroll, $scroll => $scroll.top, 0)
|
||||
const scrollLeft = derived(scroll, $scroll => $scroll.left, 0)
|
||||
|
||||
// Derive vertical limits
|
||||
const height = derived(bounds, $bounds => $bounds.height, 0)
|
||||
const width = derived(bounds, $bounds => $bounds.width, 0)
|
||||
const contentHeight = derived(
|
||||
rows,
|
||||
$rows => $rows.length * cellHeight + padding,
|
||||
0
|
||||
)
|
||||
const maxScrollTop = derived(
|
||||
[height, contentHeight],
|
||||
([$height, $contentHeight]) => Math.max($contentHeight - $height, 0),
|
||||
0
|
||||
)
|
||||
|
||||
// Derive horizontal limits
|
||||
const contentWidth = derived(
|
||||
[visibleColumns, stickyColumn],
|
||||
([$visibleColumns, $stickyColumn]) => {
|
||||
let width = 40 + padding + ($stickyColumn?.width || 0)
|
||||
$visibleColumns.forEach(col => {
|
||||
width += col.width
|
||||
})
|
||||
return width
|
||||
},
|
||||
0
|
||||
)
|
||||
const screenWidth = derived(
|
||||
[width, stickyColumn],
|
||||
([$width, $stickyColumn]) => $width + 40 + ($stickyColumn?.width || 0),
|
||||
0
|
||||
)
|
||||
const maxScrollLeft = derived(
|
||||
[contentWidth, screenWidth],
|
||||
([$contentWidth, $screenWidth]) => {
|
||||
return Math.max($contentWidth - $screenWidth, 0)
|
||||
},
|
||||
0
|
||||
)
|
||||
|
||||
// Ensure scroll state never goes invalid, which can happen when changing
|
||||
// rows or tables
|
||||
const overscrollTop = derived(
|
||||
[scrollTop, maxScrollTop],
|
||||
([$scrollTop, $maxScrollTop]) => $scrollTop > $maxScrollTop,
|
||||
false
|
||||
)
|
||||
const overscrollLeft = derived(
|
||||
[scrollLeft, maxScrollLeft],
|
||||
([$scrollLeft, $maxScrollLeft]) => $scrollLeft > $maxScrollLeft,
|
||||
false
|
||||
)
|
||||
overscrollTop.subscribe(overscroll => {
|
||||
if (overscroll) {
|
||||
scroll.update(state => ({
|
||||
...state,
|
||||
top: get(maxScrollTop),
|
||||
}))
|
||||
}
|
||||
})
|
||||
overscrollLeft.subscribe(overscroll => {
|
||||
if (overscroll) {
|
||||
scroll.update(state => ({
|
||||
...state,
|
||||
left: get(maxScrollLeft),
|
||||
}))
|
||||
}
|
||||
})
|
||||
|
||||
// Fetch next page when fewer than 50 scrollable rows remaining
|
||||
const scrollableRows = derived(
|
||||
[scrollTop, maxScrollTop],
|
||||
([$scrollTop, $maxScrollTop]) => {
|
||||
if (!$maxScrollTop) {
|
||||
return 100
|
||||
}
|
||||
return ($maxScrollTop - $scrollTop) / cellHeight
|
||||
},
|
||||
100
|
||||
)
|
||||
scrollableRows.subscribe(count => {
|
||||
if (count < 25) {
|
||||
rows.actions.loadNextPage()
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
scroll,
|
||||
contentHeight,
|
||||
contentWidth,
|
||||
maxScrollTop,
|
||||
maxScrollLeft,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,14 +12,14 @@ export const createViewportStores = context => {
|
|||
// Derive visible rows
|
||||
// Split into multiple stores containing primitives to optimise invalidation
|
||||
// as mich as possible
|
||||
const firstRowIdx = derived(
|
||||
const scrolledRowCount = derived(
|
||||
scrollTop,
|
||||
$scrollTop => {
|
||||
return Math.floor($scrollTop / cellHeight)
|
||||
},
|
||||
0
|
||||
)
|
||||
const renderedRowCount = derived(
|
||||
const visualRowCapacity = derived(
|
||||
height,
|
||||
$height => {
|
||||
return Math.ceil($height / cellHeight)
|
||||
|
@ -27,9 +27,12 @@ export const createViewportStores = context => {
|
|||
0
|
||||
)
|
||||
const renderedRows = derived(
|
||||
[rows, firstRowIdx, renderedRowCount],
|
||||
([$rows, $firstRowIdx, $visibleRowCount]) => {
|
||||
return $rows.slice($firstRowIdx, $firstRowIdx + $visibleRowCount)
|
||||
[rows, scrolledRowCount, visualRowCapacity],
|
||||
([$rows, $scrolledRowCount, $visualRowCapacity]) => {
|
||||
return $rows.slice(
|
||||
$scrolledRowCount,
|
||||
$scrolledRowCount + $visualRowCapacity
|
||||
)
|
||||
},
|
||||
[]
|
||||
)
|
||||
|
@ -74,5 +77,5 @@ export const createViewportStores = context => {
|
|||
[]
|
||||
)
|
||||
|
||||
return { renderedRows, renderedColumns }
|
||||
return { scrolledRowCount, visualRowCapacity, renderedRows, renderedColumns }
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue