<div class="virtual-list-item {{shown ? 'shown' : ''}}" aria-hidden="{{!shown}}" virtual-list-key="{{key}}" ref:node style="transform: translateY({{offset}}px);" > <:Component {component} virtualProps="{{props}}" virtualIndex="{{index}}" virtualLength="{{$length}}" on:recalculateHeight="doRecalculateHeight()"/> </div> <style> .virtual-list-item { position: absolute; top: 0; opacity: 0; pointer-events: none; transition: opacity 0.333s linear; } .virtual-list-item.shown { opacity: 1; pointer-events: auto; } </style> <script> import { virtualListStore } from './virtualListStore' import { AsyncLayout } from '../../_utils/AsyncLayout' export default { oncreate() { let asyncLayout = new AsyncLayout(node => node.getAttribute('virtual-list-key')) let key = this.get('key') asyncLayout.observe(key, this.refs.node, (rect) => { asyncLayout.disconnect() // update all item heights in one batch for better perf this.store.batchUpdateForRealm('itemHeights', key, rect.height) }) }, store: () => virtualListStore, computed: { 'shown': ($itemHeights, key) => $itemHeights[key] > 0 }, methods: { doRecalculateHeight() { // Recalculate immediately because this is done on-demand, e.g. // when clicking the "More" button on a spoiler. let rect = this.refs.node.getBoundingClientRect() let key = this.get('key') let itemHeights = this.store.get('itemHeights') itemHeights[key] = rect.height this.store.setForRealm({itemHeights}) } } } </script>