pinafore/src/routes/_components/virtualList/virtualListStore.js

139 wiersze
4.1 KiB
JavaScript
Czysty Zwykły widok Historia

import { mark, stop } from '../../_utils/marks.js'
import { RealmStore } from '../../_utils/RealmStore.js'
import { reselect } from '../../_utils/reselect.js'
2018-03-23 05:31:29 +00:00
const RENDER_BUFFER_FACTOR = 2.5
2018-01-20 23:37:40 +00:00
2018-01-31 05:55:23 +00:00
class VirtualListStore extends RealmStore {
2018-02-09 06:29:29 +00:00
constructor (state) {
2018-01-31 05:55:23 +00:00
super(state, /* maxSize */ 10)
2018-01-18 03:41:37 +00:00
}
// TODO: this is hacky
clearRealmByPrefix (prefix) {
const { realms } = this.get()
if (!realms) {
return
}
for (const key of realms.getAllKeys()) {
if (key.startsWith(prefix)) {
console.log('deleted realm', key)
realms.delete(key)
}
}
this.set({ realms })
}
}
2018-01-31 16:37:59 +00:00
const virtualListStore = new VirtualListStore()
2018-02-09 02:54:48 +00:00
virtualListStore.computeForRealm('items', null)
2018-01-31 05:55:23 +00:00
virtualListStore.computeForRealm('showFooter', false)
2018-01-31 16:37:59 +00:00
virtualListStore.computeForRealm('footerHeight', 0)
2018-02-12 03:15:21 +00:00
virtualListStore.computeForRealm('showHeader', false)
virtualListStore.computeForRealm('headerHeight', 0)
2018-01-31 05:55:23 +00:00
virtualListStore.computeForRealm('scrollTop', 0)
virtualListStore.computeForRealm('scrollHeight', 0)
virtualListStore.computeForRealm('offsetHeight', 0)
virtualListStore.computeForRealm('listOffset', 0)
2018-01-31 16:37:59 +00:00
virtualListStore.computeForRealm('itemHeights', {})
2018-01-27 16:13:28 +00:00
virtualListStore.compute('rawVisibleItems',
2018-04-19 03:43:13 +00:00
['items', 'scrollTop', 'itemHeights', 'offsetHeight', 'showHeader', 'headerHeight', 'listOffset'],
(items, scrollTop, itemHeights, offsetHeight, showHeader, headerHeight, listOffset) => {
2018-05-12 21:01:25 +00:00
if (process.browser && process.env.NODE_ENV !== 'production') {
window.rawVisibleItemsComputed = (window.rawVisibleItemsComputed || 0) + 1
}
2018-04-19 03:43:13 +00:00
mark('compute visibleItems')
if (!items) {
return null
}
2019-08-03 20:49:37 +00:00
const effectiveScrollTop = scrollTop - listOffset
const renderBuffer = RENDER_BUFFER_FACTOR * offsetHeight
const visibleItems = []
2018-04-19 03:43:13 +00:00
let totalOffset = showHeader ? headerHeight : 0
2019-08-03 20:49:37 +00:00
const len = items.length
2018-04-19 03:43:13 +00:00
let i = -1
while (++i < len) {
2019-08-03 20:49:37 +00:00
const key = items[i]
const height = itemHeights[key] || 0
const currentOffset = totalOffset
2018-04-19 03:43:13 +00:00
totalOffset += height
2019-08-03 20:49:37 +00:00
const isAboveViewport = (currentOffset < effectiveScrollTop)
2018-04-19 03:43:13 +00:00
if (isAboveViewport) {
if ((effectiveScrollTop - height - renderBuffer) > currentOffset) {
continue // above the area we want to render
}
} else {
if (currentOffset > (effectiveScrollTop + offsetHeight + renderBuffer)) {
break // below the area we want to render
2018-02-09 06:29:29 +00:00
}
2018-01-16 01:25:32 +00:00
}
2018-04-19 03:43:13 +00:00
visibleItems.push({
offset: currentOffset,
2022-11-18 17:32:31 +00:00
key,
2018-04-19 03:43:13 +00:00
index: i
})
}
stop('compute visibleItems')
return visibleItems
})
reselect(virtualListStore, 'visibleItems', 'rawVisibleItems')
2018-01-22 00:07:11 +00:00
virtualListStore.compute('heightWithoutFooter',
2018-04-19 03:43:13 +00:00
['items', 'itemHeights', 'showHeader', 'headerHeight'],
(items, itemHeights, showHeader, headerHeight) => {
if (!items) {
return 0
}
let sum = showHeader ? headerHeight : 0
let i = -1
2019-08-03 20:49:37 +00:00
const len = items.length
2018-04-19 03:43:13 +00:00
while (++i < len) {
sum += itemHeights[items[i]] || 0
}
return sum
})
2018-01-22 00:07:11 +00:00
virtualListStore.compute('height',
2018-04-19 03:43:13 +00:00
['heightWithoutFooter', 'showFooter', 'footerHeight'],
(heightWithoutFooter, showFooter, footerHeight) => {
return showFooter ? (heightWithoutFooter + footerHeight) : heightWithoutFooter
})
2018-01-22 00:07:11 +00:00
2018-02-04 18:05:01 +00:00
virtualListStore.compute('length', ['items'], (items) => items ? items.length : 0)
2018-01-18 07:00:33 +00:00
2018-01-25 02:04:25 +00:00
virtualListStore.compute('allVisibleItemsHaveHeight',
2018-04-19 03:43:13 +00:00
['visibleItems', 'itemHeights'],
(visibleItems, itemHeights) => {
if (!visibleItems) {
return false
}
2019-08-03 20:49:37 +00:00
for (const visibleItem of visibleItems) {
2018-04-19 03:43:13 +00:00
if (!itemHeights[visibleItem.key]) {
2018-02-09 06:29:29 +00:00
return false
}
2018-04-19 03:43:13 +00:00
}
return true
})
2018-01-25 02:04:25 +00:00
if (process.browser) {
window.__virtualListStore = virtualListStore // for debugging
if (process.env.NODE_ENV !== 'production') { // for extra debugging
virtualListStore.on('state', ({ changed }) => {
if (changed.visibleItems) {
window.visibleItemsChangedCount = (window.visibleItemsChangedCount || 0) + 1
}
if (changed.rawVisibleItems) {
window.rawVisibleItemsChangedCount = (window.rawVisibleItemsChangedCount || 0) + 1
}
})
}
}
export {
virtualListStore
2018-02-09 06:29:29 +00:00
}