| 128 | } |
| 129 | |
| 130 | update(invalidationContext: InvalidationContext<O>): void { |
| 131 | let { |
| 132 | minItemSize = DEFAULT_OPTIONS.minItemSize, |
| 133 | maxItemSize = DEFAULT_OPTIONS.maxItemSize, |
| 134 | preserveAspectRatio = DEFAULT_OPTIONS.preserveAspectRatio, |
| 135 | minSpace = DEFAULT_OPTIONS.minSpace, |
| 136 | maxHorizontalSpace = DEFAULT_OPTIONS.maxSpace, |
| 137 | maxColumns = DEFAULT_OPTIONS.maxColumns, |
| 138 | dropIndicatorThickness = DEFAULT_OPTIONS.dropIndicatorThickness, |
| 139 | loaderHeight = DEFAULT_OPTIONS.loaderHeight, |
| 140 | direction = 'ltr' as const |
| 141 | } = invalidationContext.layoutOptions || {}; |
| 142 | this.dropIndicatorThickness = dropIndicatorThickness; |
| 143 | this.direction = direction; |
| 144 | |
| 145 | let virtualizerWidth = this.virtualizer!.size.width; |
| 146 | |
| 147 | // The max item width is always the entire viewport. |
| 148 | // If the max item height is infinity, scale in proportion to the max width. |
| 149 | let maxItemWidth = Math.min(maxItemSize.width, virtualizerWidth); |
| 150 | let maxItemHeight = Number.isFinite(maxItemSize.height) |
| 151 | ? maxItemSize.height |
| 152 | : Math.floor((minItemSize.height / minItemSize.width) * maxItemWidth); |
| 153 | |
| 154 | // Compute the number of rows and columns needed to display the content |
| 155 | let columns = Math.floor(virtualizerWidth / (minItemSize.width + minSpace.width)); |
| 156 | let numColumns = Math.max(1, Math.min(maxColumns, columns)); |
| 157 | this.numColumns = numColumns; |
| 158 | |
| 159 | // Compute the available width (minus the space between items) |
| 160 | let width = virtualizerWidth - minSpace.width * Math.max(0, numColumns); |
| 161 | |
| 162 | // Compute the item width based on the space available |
| 163 | let itemWidth = Math.floor(width / numColumns); |
| 164 | itemWidth = Math.max(minItemSize.width, Math.min(maxItemWidth, itemWidth)); |
| 165 | |
| 166 | // Compute the item height, which is proportional to the item width |
| 167 | let t = (itemWidth - minItemSize.width) / Math.max(1, maxItemWidth - minItemSize.width); |
| 168 | let itemHeight = minItemSize.height + Math.floor((maxItemHeight - minItemSize.height) * t); |
| 169 | itemHeight = Math.max(minItemSize.height, Math.min(maxItemHeight, itemHeight)); |
| 170 | |
| 171 | // Compute the horizontal spacing, content height and horizontal margin |
| 172 | let horizontalSpacing = Math.min( |
| 173 | Math.max(maxHorizontalSpace, minSpace.width), |
| 174 | Math.floor((virtualizerWidth - numColumns * itemWidth) / (numColumns + 1)) |
| 175 | ); |
| 176 | this.gap = new Size(horizontalSpacing, minSpace.height); |
| 177 | this.margin = Math.floor( |
| 178 | (virtualizerWidth - numColumns * itemWidth - horizontalSpacing * (numColumns + 1)) / 2 |
| 179 | ); |
| 180 | |
| 181 | // If there is a skeleton loader within the last 2 items in the collection, increment the collection size |
| 182 | // so that an additional row is added for the skeletons. |
| 183 | let collection = this.virtualizer!.collection; |
| 184 | let collectionSize = collection.size; |
| 185 | let lastKey = collection.getLastKey(); |
| 186 | for (let i = 0; i < 2 && lastKey != null; i++) { |
| 187 | let item = collection.getItem(lastKey); |