2018-01-25 16:23:14 +00:00
|
|
|
import { mark, stop } from '../../_utils/marks'
|
2018-01-31 05:55:23 +00:00
|
|
|
import { RealmStore } from '../../_utils/RealmStore'
|
2018-03-04 21:37:55 +00:00
|
|
|
import { reselect } from '../../_utils/reselect'
|
2018-12-02 22:25:35 +00:00
|
|
|
import { observe } from 'svelte-extras'
|
2018-01-15 20:23:28 +00:00
|
|
|
|
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
|
|
|
}
|
2018-01-15 20:23:28 +00:00
|
|
|
}
|
|
|
|
|
2018-12-02 22:25:35 +00:00
|
|
|
VirtualListStore.prototype.observe = observe
|
|
|
|
|
2018-01-31 16:37:59 +00:00
|
|
|
const virtualListStore = new VirtualListStore()
|
2018-01-15 20:23:28 +00:00
|
|
|
|
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)
|
2018-03-23 00:33:42 +00:00
|
|
|
virtualListStore.computeForRealm('listOffset', 0)
|
2018-01-31 16:37:59 +00:00
|
|
|
virtualListStore.computeForRealm('itemHeights', {})
|
2018-01-27 16:13:28 +00:00
|
|
|
|
2018-03-04 21:37:55 +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
|
|
|
|
}
|
|
|
|
let effectiveScrollTop = scrollTop - listOffset
|
|
|
|
let renderBuffer = RENDER_BUFFER_FACTOR * offsetHeight
|
|
|
|
let visibleItems = []
|
|
|
|
let totalOffset = showHeader ? headerHeight : 0
|
|
|
|
let len = items.length
|
|
|
|
let i = -1
|
|
|
|
while (++i < len) {
|
|
|
|
let key = items[i]
|
|
|
|
let height = itemHeights[key] || 0
|
|
|
|
let currentOffset = totalOffset
|
|
|
|
totalOffset += height
|
|
|
|
let isAboveViewport = (currentOffset < effectiveScrollTop)
|
|
|
|
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,
|
|
|
|
key: key,
|
|
|
|
index: i
|
|
|
|
})
|
|
|
|
}
|
|
|
|
stop('compute visibleItems')
|
|
|
|
return visibleItems
|
|
|
|
})
|
2018-01-15 20:23:28 +00:00
|
|
|
|
2018-03-04 21:37:55 +00:00
|
|
|
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
|
|
|
|
let len = items.length
|
|
|
|
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
|
|
|
|
}
|
|
|
|
for (let visibleItem of visibleItems) {
|
|
|
|
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
|
|
|
|
2018-01-15 20:23:28 +00:00
|
|
|
if (process.browser && process.env.NODE_ENV !== 'production') {
|
|
|
|
window.virtualListStore = virtualListStore
|
2018-03-04 21:37:55 +00:00
|
|
|
|
2018-08-30 04:42:57 +00:00
|
|
|
virtualListStore.on('state', ({ changed }) => {
|
2018-05-01 00:20:20 +00:00
|
|
|
if (changed.visibleItems) {
|
|
|
|
window.visibleItemsChangedCount = (window.visibleItemsChangedCount || 0) + 1
|
|
|
|
}
|
|
|
|
if (changed.rawVisibleItems) {
|
|
|
|
window.rawVisibleItemsChangedCount = (window.rawVisibleItemsChangedCount || 0) + 1
|
|
|
|
}
|
2018-03-04 21:37:55 +00:00
|
|
|
})
|
2018-01-15 20:23:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export {
|
|
|
|
virtualListStore
|
2018-02-09 06:29:29 +00:00
|
|
|
}
|