138 lines
		
	
	
		
			No EOL
		
	
	
		
			4.1 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			138 lines
		
	
	
		
			No EOL
		
	
	
		
			4.1 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
| <div class="container"
 | ||
|      on:scroll="onScroll(event)"
 | ||
|      on:fullscreen="onFullscreenChange()"
 | ||
|      ref:node>
 | ||
|   <slot></slot>
 | ||
| </div>
 | ||
| <script>
 | ||
|   import { virtualListStore } from '../_utils/virtualListStore'
 | ||
| 
 | ||
|   import throttle from 'lodash/throttle'
 | ||
|   import { isFullscreen, attachFullscreenListener, detachFullscreenListener } from '../_utils/fullscreen'
 | ||
|   import { mark, stop } from '../_utils/marks'
 | ||
| 
 | ||
|   const SCROLL_EVENT_DELAY = 300
 | ||
| 
 | ||
|   const cachedVirtualStores = {}
 | ||
| 
 | ||
|   if (process.browser && process.env.NODE_ENV !== 'production') {
 | ||
|     window.cachedVirtualStores = cachedVirtualStores
 | ||
|   }
 | ||
| 
 | ||
|   export default {
 | ||
|     oncreate() {
 | ||
|       mark('onCreate VirtualListContainer')
 | ||
|       let node = this.refs.node
 | ||
|       let storeKey = this.get('storeKey')
 | ||
|       let cachedStore
 | ||
|       if (storeKey && (cachedStore = cachedVirtualStores[storeKey])) {
 | ||
|         this.store.set({
 | ||
|           scrollTop: cachedStore.state.scrollTop,
 | ||
|           scrollHeight: cachedStore.state.scrollHeight,
 | ||
|           offsetHeight: cachedStore.state.offsetHeight
 | ||
|         })
 | ||
|         this.store.set(cachedStore.state)
 | ||
| 
 | ||
|         // rehydrate scroll top
 | ||
|         let cachedScrollTop = cachedStore.state.scrollTop || 0
 | ||
|         if (cachedScrollTop === 0) {
 | ||
|           if (process.env.NODE_ENV !== 'production') {
 | ||
|             console.log('no need to force scroll top')
 | ||
|           }
 | ||
|           return
 | ||
|         }
 | ||
|         let initializedScrollTop = false
 | ||
|         let node = this.refs.node
 | ||
|         this.observe('allVisibleItemsHaveHeight', allVisibleItemsHaveHeight => {
 | ||
|           if (process.env.NODE_ENV !== 'production') {
 | ||
|             console.log('allVisibleItemsHaveHeight', allVisibleItemsHaveHeight)
 | ||
|           }
 | ||
|           if (!initializedScrollTop && allVisibleItemsHaveHeight && node) {
 | ||
|             initializedScrollTop = true
 | ||
|             requestAnimationFrame(() => {
 | ||
|               mark('set scrollTop')
 | ||
|               if (process.env.NODE_ENV !== 'production') {
 | ||
|                 console.log('forcing scroll top to ', cachedScrollTop)
 | ||
|               }
 | ||
|               node.scrollTop = cachedScrollTop
 | ||
|               stop('set scrollTop')
 | ||
|             })
 | ||
|           }
 | ||
|         })
 | ||
|       } else {
 | ||
|         this.store.set({
 | ||
|           scrollTop: 0,
 | ||
|           scrollHeight: node.scrollHeight,
 | ||
|           offsetHeight: node.offsetHeight
 | ||
|         })
 | ||
|       }
 | ||
|       stop('onCreate VirtualListContainer')
 | ||
|     },
 | ||
|     ondestroy() {
 | ||
|       let storeKey = this.get('storeKey')
 | ||
|       if (storeKey) {
 | ||
|         let clonedState = this.store.cloneState()
 | ||
|         if (process.env.NODE_ENV !== 'production') {
 | ||
|           console.log('caching scroll top', clonedState.scrollTop)
 | ||
|         }
 | ||
|         cachedVirtualStores[storeKey] = {
 | ||
|           state: clonedState
 | ||
|         }
 | ||
|       }
 | ||
|     },
 | ||
|     store: () => virtualListStore,
 | ||
|     events: {
 | ||
|       scroll(node, callback) {
 | ||
|         const onScroll = throttle(event => {
 | ||
|           mark('onScroll')
 | ||
|           if (this.get('fullscreen')) {
 | ||
|             return
 | ||
|           }
 | ||
|           callback(event)
 | ||
|           stop('onScroll')
 | ||
|         }, SCROLL_EVENT_DELAY, {
 | ||
|           leading: true,
 | ||
|           trailing: true
 | ||
|         })
 | ||
|         node.addEventListener('scroll', onScroll)
 | ||
| 
 | ||
|         return {
 | ||
|           teardown() {
 | ||
|             node.removeEventListener('scroll', onScroll)
 | ||
|           }
 | ||
|         }
 | ||
|       },
 | ||
|       fullscreen(node, callback) {
 | ||
|         const onFullscreen = (() => {
 | ||
|           callback()
 | ||
|         })
 | ||
|         attachFullscreenListener(onFullscreen)
 | ||
|         return {
 | ||
|           teardown() {
 | ||
|             detachFullscreenListener(onFullscreen)
 | ||
|           }
 | ||
|         }
 | ||
|       }
 | ||
|     },
 | ||
|     methods: {
 | ||
|       onScroll(event) {
 | ||
|         this.store.set({
 | ||
|           scrollTop: event.target.scrollTop,
 | ||
|           scrollHeight: event.target.scrollHeight
 | ||
|         })
 | ||
|       },
 | ||
|       onFullscreenChange() {
 | ||
|         mark('onFullscreenChange')
 | ||
|         if (process.env.NODE_ENV !== 'production') {
 | ||
|           console.log('is fullscreen? ', isFullscreen())
 | ||
|         }
 | ||
|         this.set({ fullscreen: isFullscreen() })
 | ||
|         stop('onFullscreenChange')
 | ||
|       }
 | ||
|     },
 | ||
|     computed: {
 | ||
|       // TODO: bug in svelte/store – the observer in oncreate() never get removed without this hack
 | ||
|       allVisibleItemsHaveHeight: ($allVisibleItemsHaveHeight) => $allVisibleItemsHaveHeight
 | ||
|     }
 | ||
|   };
 | ||
| </script> |