1<script> 2export default { 3 name: 'DynamicScrollerItem', 4 5 inject: [ 6 'vscrollData', 7 'vscrollParent', 8 'vscrollResizeObserver', 9 ], 10 11 props: { 12 // eslint-disable-next-line vue/require-prop-types 13 item: { 14 required: true, 15 }, 16 17 watchData: { 18 type: Boolean, 19 default: false, 20 }, 21 22 /** 23 * Indicates if the view is actively used to display an item. 24 */ 25 active: { 26 type: Boolean, 27 required: true, 28 }, 29 30 index: { 31 type: Number, 32 default: undefined, 33 }, 34 35 sizeDependencies: { 36 type: [Array, Object], 37 default: null, 38 }, 39 40 emitResize: { 41 type: Boolean, 42 default: false, 43 }, 44 45 tag: { 46 type: String, 47 default: 'div', 48 }, 49 }, 50 51 computed: { 52 id () { 53 return this.vscrollData.simpleArray ? this.index : this.item[this.vscrollData.keyField] 54 }, 55 56 size () { 57 return (this.vscrollData.validSizes[this.id] && this.vscrollData.sizes[this.id]) || 0 58 }, 59 60 finalActive () { 61 return this.active && this.vscrollData.active 62 }, 63 }, 64 65 watch: { 66 watchData: 'updateWatchData', 67 68 id () { 69 if (!this.size) { 70 this.onDataUpdate() 71 } 72 }, 73 74 finalActive (value) { 75 if (!this.size) { 76 if (value) { 77 if (!this.vscrollParent.$_undefinedMap[this.id]) { 78 this.vscrollParent.$_undefinedSizes++ 79 this.vscrollParent.$_undefinedMap[this.id] = true 80 } 81 } else { 82 if (this.vscrollParent.$_undefinedMap[this.id]) { 83 this.vscrollParent.$_undefinedSizes-- 84 this.vscrollParent.$_undefinedMap[this.id] = false 85 } 86 } 87 } 88 89 if (this.vscrollResizeObserver) { 90 if (value) { 91 this.observeSize() 92 } else { 93 this.unobserveSize() 94 } 95 } else if (value && this.$_pendingVScrollUpdate === this.id) { 96 this.updateSize() 97 } 98 }, 99 }, 100 101 created () { 102 if (this.$isServer) return 103 104 this.$_forceNextVScrollUpdate = null 105 this.updateWatchData() 106 107 if (!this.vscrollResizeObserver) { 108 for (const k in this.sizeDependencies) { 109 this.$watch(() => this.sizeDependencies[k], this.onDataUpdate) 110 } 111 112 this.vscrollParent.$on('vscroll:update', this.onVscrollUpdate) 113 this.vscrollParent.$on('vscroll:update-size', this.onVscrollUpdateSize) 114 } 115 }, 116 117 mounted () { 118 if (this.vscrollData.active) { 119 this.updateSize() 120 this.observeSize() 121 } 122 }, 123 124 beforeDestroy () { 125 this.vscrollParent.$off('vscroll:update', this.onVscrollUpdate) 126 this.vscrollParent.$off('vscroll:update-size', this.onVscrollUpdateSize) 127 this.unobserveSize() 128 }, 129 130 methods: { 131 updateSize () { 132 if (this.finalActive) { 133 if (this.$_pendingSizeUpdate !== this.id) { 134 this.$_pendingSizeUpdate = this.id 135 this.$_forceNextVScrollUpdate = null 136 this.$_pendingVScrollUpdate = null 137 this.computeSize(this.id) 138 } 139 } else { 140 this.$_forceNextVScrollUpdate = this.id 141 } 142 }, 143 144 updateWatchData () { 145 if (this.watchData) { 146 this.$_watchData = this.$watch('data', () => { 147 this.onDataUpdate() 148 }, { 149 deep: true, 150 }) 151 } else if (this.$_watchData) { 152 this.$_watchData() 153 this.$_watchData = null 154 } 155 }, 156 157 onVscrollUpdate ({ force }) { 158 // If not active, sechedule a size update when it becomes active 159 if (!this.finalActive && force) { 160 this.$_pendingVScrollUpdate = this.id 161 } 162 163 if (this.$_forceNextVScrollUpdate === this.id || force || !this.size) { 164 this.updateSize() 165 } 166 }, 167 168 onDataUpdate () { 169 this.updateSize() 170 }, 171 172 computeSize (id) { 173 this.$nextTick(() => { 174 if (this.id === id) { 175 const width = this.$el.offsetWidth 176 const height = this.$el.offsetHeight 177 this.applySize(width, height) 178 } 179 this.$_pendingSizeUpdate = null 180 }) 181 }, 182 183 applySize (width, height) { 184 const size = Math.round(this.vscrollParent.direction === 'vertical' ? height : width) 185 if (size && this.size !== size) { 186 if (this.vscrollParent.$_undefinedMap[this.id]) { 187 this.vscrollParent.$_undefinedSizes-- 188 this.vscrollParent.$_undefinedMap[this.id] = undefined 189 } 190 this.$set(this.vscrollData.sizes, this.id, size) 191 this.$set(this.vscrollData.validSizes, this.id, true) 192 if (this.emitResize) this.$emit('resize', this.id) 193 } 194 }, 195 196 observeSize () { 197 if (!this.vscrollResizeObserver) return 198 this.$_parentNode = this.$el.parentNode; 199 this.vscrollResizeObserver.observe(this.$_parentNode) 200 this.$_parentNode.addEventListener('resize', this.onResize) 201 }, 202 203 unobserveSize () { 204 if (!this.vscrollResizeObserver) return 205 this.vscrollResizeObserver.unobserve(this.$_parentNode) 206 this.$_parentNode.removeEventListener('resize', this.onResize) 207 }, 208 209 onResize (event) { 210 const { width, height } = event.detail.contentRect 211 this.applySize(width, height) 212 }, 213 }, 214 215 render (h) { 216 return h(this.tag, this.$slots.default) 217 }, 218} 219</script> 220