forked from cybrespace/pinafore
		
	
		
			
				
	
	
		
			109 lines
		
	
	
		
			No EOL
		
	
	
		
			3.6 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			109 lines
		
	
	
		
			No EOL
		
	
	
		
			3.6 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
<VirtualListContainer :realm :containerQuery >
 | 
						|
  <div class="virtual-list {{shown ? '' : 'hidden'}}" style="height: {{$height}}px;">
 | 
						|
    <VirtualListHeader component="{{headerComponent}}" virtualProps="{{headerProps}}" shown="{{$showHeader}}"/>
 | 
						|
    {{#if $visibleItems}}
 | 
						|
      {{#each $visibleItems as visibleItem @key}}
 | 
						|
        <VirtualListLazyItem :component
 | 
						|
                             offset="{{visibleItem.offset}}"
 | 
						|
                             makeProps="{{makeProps}}"
 | 
						|
                             key="{{visibleItem.key}}"
 | 
						|
                             index="{{visibleItem.index}}"
 | 
						|
        />
 | 
						|
      {{/each}}
 | 
						|
    {{/if}}
 | 
						|
    {{#if $showFooter}}
 | 
						|
      <VirtualListFooter component="{{footerComponent}}" />
 | 
						|
    {{/if}}
 | 
						|
  </div>
 | 
						|
</VirtualListContainer>
 | 
						|
<style>
 | 
						|
  .virtual-list {
 | 
						|
    position: relative;
 | 
						|
    transition: opacity 0.25s linear;
 | 
						|
  }
 | 
						|
</style>
 | 
						|
<script>
 | 
						|
  import VirtualListContainer from './VirtualListContainer.html'
 | 
						|
  import VirtualListLazyItem from './VirtualListLazyItem'
 | 
						|
  import VirtualListFooter from './VirtualListFooter.html'
 | 
						|
  import VirtualListHeader from './VirtualListHeader.html'
 | 
						|
  import { virtualListStore } from './virtualListStore'
 | 
						|
  import throttle from 'lodash/throttle'
 | 
						|
  import { mark, stop } from '../../_utils/marks'
 | 
						|
 | 
						|
  const DISTANCE_FROM_BOTTOM_TO_FIRE = 400
 | 
						|
  const SCROLL_EVENT_THROTTLE = 1000
 | 
						|
 | 
						|
  export default {
 | 
						|
    oncreate () {
 | 
						|
      this.fireScrollToBottom = throttle(() => {
 | 
						|
        this.fire('scrollToBottom')
 | 
						|
      }, SCROLL_EVENT_THROTTLE)
 | 
						|
      this.fireScrollToTop = throttle(() => {
 | 
						|
        this.fire('scrollToTop')
 | 
						|
      }, SCROLL_EVENT_THROTTLE)
 | 
						|
      this.observe('showFooter', showFooter => {
 | 
						|
        mark('set showFooter')
 | 
						|
        this.store.setForRealm({showFooter: showFooter})
 | 
						|
        mark('set showFooter')
 | 
						|
      })
 | 
						|
      this.observe('showHeader', showHeader => {
 | 
						|
        mark('set showHeader')
 | 
						|
        this.store.setForRealm({showHeader: showHeader})
 | 
						|
        stop('set showHeader')
 | 
						|
      })
 | 
						|
      this.observe('itemsStringified', (itemsStringified) => {
 | 
						|
        let items = typeof itemsStringified === 'undefined' ? undefined :
 | 
						|
          JSON.parse(itemsStringified)
 | 
						|
        mark('set items')
 | 
						|
        this.store.setForRealm({items: items})
 | 
						|
        stop('set items')
 | 
						|
      })
 | 
						|
      this.observe('allVisibleItemsHaveHeight', allVisibleItemsHaveHeight => {
 | 
						|
        if (allVisibleItemsHaveHeight) {
 | 
						|
          this.fire('initializedVisibleItems')
 | 
						|
        }
 | 
						|
      })
 | 
						|
 | 
						|
      let observedOnce = false
 | 
						|
 | 
						|
      this.observe('distanceFromBottom', (distanceFromBottom) => {
 | 
						|
        if (!observedOnce) {
 | 
						|
          observedOnce = true // TODO: the first time is always 0... need better way to handle this
 | 
						|
          return
 | 
						|
        }
 | 
						|
        if (distanceFromBottom >= 0 &&
 | 
						|
            distanceFromBottom <= DISTANCE_FROM_BOTTOM_TO_FIRE) {
 | 
						|
          this.fireScrollToBottom()
 | 
						|
        }
 | 
						|
      })
 | 
						|
 | 
						|
      this.observe('scrollTop', (scrollTop) => {
 | 
						|
        this.fire('scrollTopChanged', scrollTop)
 | 
						|
        if (scrollTop === 0) {
 | 
						|
          this.fireScrollToTop()
 | 
						|
        }
 | 
						|
      })
 | 
						|
    },
 | 
						|
    data: () => ({
 | 
						|
      component: null
 | 
						|
    }),
 | 
						|
    store: () => virtualListStore,
 | 
						|
    components: {
 | 
						|
      VirtualListContainer,
 | 
						|
      VirtualListLazyItem,
 | 
						|
      VirtualListFooter,
 | 
						|
      VirtualListHeader
 | 
						|
    },
 | 
						|
    computed: {
 | 
						|
      distanceFromBottom: ($scrollHeight, $scrollTop, $offsetHeight) => {
 | 
						|
        return $scrollHeight - $scrollTop - $offsetHeight
 | 
						|
      },
 | 
						|
      scrollTop: ($scrollTop) => $scrollTop,
 | 
						|
      // TODO: bug in svelte store, shouldn't need to do this
 | 
						|
      allVisibleItemsHaveHeight: ($allVisibleItemsHaveHeight) => $allVisibleItemsHaveHeight,
 | 
						|
      // TODO: hack to avoid getting called too often
 | 
						|
      itemsStringified: (items) => JSON.stringify(items)
 | 
						|
    }
 | 
						|
  }
 | 
						|
</script> |