/ shared / components / src / components / Shelf / ShelfItem.svelte
ShelfItem.svelte
 1  <script lang="ts">
 2      import { getRafQueue } from '@amp/web-app-components/src/utils/rafQueue';
 3      import { onDestroy } from 'svelte';
 4      import { get, type Readable } from 'svelte/store';
 5      import type { VisibleIndexData } from '@amp/web-app-components/src/components/Shelf/store/visibleStore';
 6  
 7      export let index: number;
 8      export let visibleStore: Readable<VisibleIndexData>;
 9  
10      const rafQueue = getRafQueue();
11      const isBetween = (start: number, end: number, value: number) => {
12          return value >= start && value <= end;
13      };
14      // get value but dont subscribe to it.
15      let { startIndex, endIndex } = get(visibleStore);
16      $: isRendered = isBetween(startIndex, endIndex, index);
17      $: isSubscribed = true;
18  
19      // Elements should only be subscribed
20      // to the store if they are not rendered.
21      const unsubscribe = visibleStore.subscribe((store) => {
22          const { startIndex, endIndex } = store;
23          const currentIsRendered = isBetween(startIndex, endIndex, index);
24          // Manually handling subscription to
25          // update DOM using RAF in browser for smoother scrolling
26          if (currentIsRendered && !isRendered) {
27              rafQueue.add(() => {
28                  isRendered = currentIsRendered;
29              });
30          }
31      });
32  
33      /**
34       * Unsubscribe to the store only if `isSubscribed` is true
35       *
36       * This helps ensure that we do not accidentally call `unsubscribe` twice,
37       * which can cause errors in Svelte. One way that can happen is by unsubscribing
38       * both using `onDestory` and with the callback added to the `rafQueue`
39       *
40       * See https://github.com/sveltejs/svelte/issues/4765#issuecomment-1379243063
41       */
42      function unsubscribeIfNeeded() {
43          if (isSubscribed) {
44              unsubscribe();
45              isSubscribed = false;
46          }
47      }
48  
49      $: if (isSubscribed && isRendered) {
50          rafQueue.add(() => {
51              unsubscribeIfNeeded();
52          });
53      }
54  
55      onDestroy(() => {
56          unsubscribeIfNeeded();
57      });
58  </script>
59  
60  <slot {isRendered} />