@thangdevalone/meet-layout-grid-core 1.0.8 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -16,6 +16,212 @@ function parseAspectRatio(ratio) {
16
16
  }
17
17
  return { widthRatio: width, heightRatio: height };
18
18
  }
19
+ function calculateContentDimensions(cellDimensions, itemRatio, defaultRatio) {
20
+ const { width: cellW, height: cellH } = cellDimensions;
21
+ const effectiveRatio = itemRatio ?? (defaultRatio ? defaultRatio : void 0);
22
+ if (!effectiveRatio || effectiveRatio === "fill" || effectiveRatio === "auto") {
23
+ return {
24
+ width: cellW,
25
+ height: cellH,
26
+ offsetTop: 0,
27
+ offsetLeft: 0
28
+ };
29
+ }
30
+ const ratio = getAspectRatio(effectiveRatio);
31
+ let contentW = cellW;
32
+ let contentH = contentW * ratio;
33
+ if (contentH > cellH) {
34
+ contentH = cellH;
35
+ contentW = contentH / ratio;
36
+ }
37
+ const offsetTop = (cellH - contentH) / 2;
38
+ const offsetLeft = (cellW - contentW) / 2;
39
+ return {
40
+ width: contentW,
41
+ height: contentH,
42
+ offsetTop,
43
+ offsetLeft
44
+ };
45
+ }
46
+ function createGetItemContentDimensions(getItemDimensions, itemAspectRatios, defaultRatio) {
47
+ return (index, itemRatio) => {
48
+ const cellDimensions = getItemDimensions(index);
49
+ const effectiveRatio = itemRatio ?? itemAspectRatios?.[index] ?? defaultRatio;
50
+ return calculateContentDimensions(cellDimensions, effectiveRatio, defaultRatio);
51
+ };
52
+ }
53
+ function calculateFlexLayout(options) {
54
+ const { dimensions, count, aspectRatio, gap, itemAspectRatios, preferHorizontal } = options;
55
+ if (count === 0 || dimensions.width === 0 || dimensions.height === 0) {
56
+ return [];
57
+ }
58
+ const containerWidth = dimensions.width - gap * 2;
59
+ const containerHeight = dimensions.height - gap * 2;
60
+ const getItemAspectValue = (index) => {
61
+ const itemRatio = itemAspectRatios?.[index] ?? aspectRatio;
62
+ if (!itemRatio || itemRatio === "fill" || itemRatio === "auto") {
63
+ return 16 / 9;
64
+ }
65
+ const parsed = parseAspectRatio(itemRatio);
66
+ return parsed ? parsed.widthRatio / parsed.heightRatio : 16 / 9;
67
+ };
68
+ const aspectValues = [];
69
+ for (let i = 0; i < count; i++) {
70
+ aspectValues.push(getItemAspectValue(i));
71
+ }
72
+ const avgAspect = aspectValues.reduce((a, b) => a + b, 0) / count;
73
+ const containerAspect = containerWidth / containerHeight;
74
+ let numRows;
75
+ if (preferHorizontal) {
76
+ const sumAspects = aspectValues.reduce((a, b) => a + b, 0);
77
+ const singleRowHeight = containerWidth / sumAspects;
78
+ if (singleRowHeight <= containerHeight) {
79
+ numRows = 1;
80
+ } else {
81
+ numRows = Math.ceil(Math.sqrt(count * containerHeight / containerWidth / avgAspect));
82
+ numRows = Math.max(1, Math.min(count, numRows));
83
+ }
84
+ } else {
85
+ numRows = Math.round(Math.sqrt(count / (containerAspect / avgAspect)));
86
+ numRows = Math.max(1, Math.min(count, numRows));
87
+ }
88
+ const itemsPerRow = Math.ceil(count / numRows);
89
+ const rows = [];
90
+ for (let i = 0; i < count; i += itemsPerRow) {
91
+ const row = [];
92
+ for (let j = i; j < Math.min(i + itemsPerRow, count); j++) {
93
+ row.push(j);
94
+ }
95
+ rows.push(row);
96
+ }
97
+ const rowHeights = [];
98
+ for (const row of rows) {
99
+ const sumAspects = row.reduce((sum, idx) => sum + aspectValues[idx], 0);
100
+ const totalGapWidth = (row.length - 1) * gap;
101
+ const height = (containerWidth - totalGapWidth) / sumAspects;
102
+ rowHeights.push(height);
103
+ }
104
+ const totalGapHeight = (rows.length - 1) * gap;
105
+ const totalNaturalHeight = rowHeights.reduce((a, b) => a + b, 0);
106
+ const scale = Math.min(1, (containerHeight - totalGapHeight) / totalNaturalHeight);
107
+ for (let i = 0; i < rowHeights.length; i++) {
108
+ rowHeights[i] *= scale;
109
+ }
110
+ const totalScaledHeight = rowHeights.reduce((a, b) => a + b, 0) + totalGapHeight;
111
+ const verticalOffset = (containerHeight - totalScaledHeight) / 2;
112
+ const items = [];
113
+ let currentTop = gap + verticalOffset;
114
+ for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {
115
+ const row = rows[rowIndex];
116
+ const rowHeight = rowHeights[rowIndex];
117
+ let rowWidth = 0;
118
+ for (const idx of row) {
119
+ rowWidth += rowHeight * aspectValues[idx];
120
+ }
121
+ rowWidth += (row.length - 1) * gap;
122
+ const horizontalOffset = (containerWidth - rowWidth) / 2;
123
+ let currentLeft = gap + horizontalOffset;
124
+ for (const itemIndex of row) {
125
+ const aspect = aspectValues[itemIndex];
126
+ const itemWidth = rowHeight * aspect;
127
+ items[itemIndex] = {
128
+ index: itemIndex,
129
+ width: itemWidth,
130
+ height: rowHeight,
131
+ left: currentLeft,
132
+ top: currentTop,
133
+ row: rowIndex,
134
+ widthFactor: aspect
135
+ };
136
+ currentLeft += itemWidth + gap;
137
+ }
138
+ currentTop += rowHeight + gap;
139
+ }
140
+ return items;
141
+ }
142
+ function calculateFlexStrip(options) {
143
+ const { dimensions, count, aspectRatio, gap, itemAspectRatios, direction, offset = { left: 0, top: 0 } } = options;
144
+ if (count === 0 || dimensions.width === 0 || dimensions.height === 0) {
145
+ return [];
146
+ }
147
+ const stripWidth = dimensions.width;
148
+ const stripHeight = dimensions.height;
149
+ const getItemAspectValue = (index) => {
150
+ const itemRatio = itemAspectRatios?.[index] ?? aspectRatio;
151
+ if (!itemRatio || itemRatio === "fill" || itemRatio === "auto") {
152
+ return 16 / 9;
153
+ }
154
+ const parsed = parseAspectRatio(itemRatio);
155
+ return parsed ? parsed.widthRatio / parsed.heightRatio : 16 / 9;
156
+ };
157
+ const aspectValues = [];
158
+ for (let i = 0; i < count; i++) {
159
+ aspectValues.push(getItemAspectValue(i));
160
+ }
161
+ const items = [];
162
+ if (direction === "horizontal") {
163
+ const availableHeight = stripHeight;
164
+ const totalGaps = (count - 1) * gap;
165
+ let totalNaturalWidth = 0;
166
+ for (let i = 0; i < count; i++) {
167
+ totalNaturalWidth += availableHeight * aspectValues[i];
168
+ }
169
+ totalNaturalWidth += totalGaps;
170
+ let scale = (stripWidth - totalGaps) / (totalNaturalWidth - totalGaps);
171
+ scale = Math.min(scale, 2);
172
+ const itemHeight = availableHeight * scale;
173
+ let scaledTotalWidth = totalGaps;
174
+ for (let i = 0; i < count; i++) {
175
+ scaledTotalWidth += itemHeight * aspectValues[i];
176
+ }
177
+ let currentLeft = offset.left + (stripWidth - scaledTotalWidth) / 2;
178
+ const itemTop = offset.top + (stripHeight - itemHeight) / 2;
179
+ for (let i = 0; i < count; i++) {
180
+ const itemWidth = itemHeight * aspectValues[i];
181
+ items[i] = {
182
+ index: i,
183
+ width: itemWidth,
184
+ height: itemHeight,
185
+ left: currentLeft,
186
+ top: itemTop,
187
+ row: 0,
188
+ widthFactor: aspectValues[i]
189
+ };
190
+ currentLeft += itemWidth + gap;
191
+ }
192
+ } else {
193
+ const availableWidth = stripWidth;
194
+ const totalGaps = (count - 1) * gap;
195
+ let totalNaturalHeight = 0;
196
+ for (let i = 0; i < count; i++) {
197
+ totalNaturalHeight += availableWidth / aspectValues[i];
198
+ }
199
+ totalNaturalHeight += totalGaps;
200
+ let scale = (stripHeight - totalGaps) / (totalNaturalHeight - totalGaps);
201
+ scale = Math.min(scale, 2);
202
+ const itemWidth = availableWidth * scale;
203
+ let scaledTotalHeight = totalGaps;
204
+ for (let i = 0; i < count; i++) {
205
+ scaledTotalHeight += itemWidth / aspectValues[i];
206
+ }
207
+ let currentTop = offset.top + (stripHeight - scaledTotalHeight) / 2;
208
+ const itemLeft = offset.left + (stripWidth - itemWidth) / 2;
209
+ for (let i = 0; i < count; i++) {
210
+ const itemHeight = itemWidth / aspectValues[i];
211
+ items[i] = {
212
+ index: i,
213
+ width: itemWidth,
214
+ height: itemHeight,
215
+ left: itemLeft,
216
+ top: currentTop,
217
+ row: i,
218
+ widthFactor: aspectValues[i]
219
+ };
220
+ currentTop += itemHeight + gap;
221
+ }
222
+ }
223
+ return items;
224
+ }
19
225
  function getGridItemDimensions({
20
226
  count,
21
227
  dimensions,
@@ -35,8 +241,7 @@ function getGridItemDimensions({
35
241
  let h = 0;
36
242
  let a = 1;
37
243
  let b = 1;
38
- const isPortrait = H > W;
39
- const minCols = N >= 2 && isPortrait ? 2 : 1;
244
+ const minCols = 1;
40
245
  const widths = [];
41
246
  for (let n = 1; n <= N; n++) {
42
247
  widths.push((W - s * (n - 1)) / n, (H - s * (n - 1)) / (n * r));
@@ -129,20 +334,30 @@ function createGrid({ aspectRatio, count, dimensions, gap }) {
129
334
  };
130
335
  }
131
336
  function createSidebarGrid(options) {
132
- const { dimensions, gap, aspectRatio, count, sidebarPosition = "right", sidebarRatio = 0.25, pinnedIndex = 0, maxVisibleOthers = 0, currentOthersPage = 0 } = options;
337
+ const { dimensions, gap, aspectRatio, count, sidebarPosition = "right", sidebarRatio = 0.25, pinnedIndex = 0, maxVisible = 0, currentVisiblePage = 0, flexLayout = false, itemAspectRatios } = options;
133
338
  if (count === 0) {
134
339
  return createEmptyMeetGridResult("sidebar");
135
340
  }
136
341
  if (count === 1) {
137
- const grid = createGrid({ ...options, count: 1 });
342
+ const { width: W2, height: H2 } = dimensions;
343
+ const mainWidth2 = W2 - gap * 2;
344
+ const mainHeight2 = H2 - gap * 2;
345
+ const getItemDimensions2 = () => ({ width: mainWidth2, height: mainHeight2 });
138
346
  const pagination2 = createDefaultPagination(1);
139
347
  return {
140
- ...grid,
348
+ width: mainWidth2,
349
+ height: mainHeight2,
350
+ rows: 1,
351
+ cols: 1,
141
352
  layoutMode: "sidebar",
142
- getItemDimensions: () => ({ width: grid.width, height: grid.height }),
353
+ getPosition: () => ({ top: gap, left: gap }),
354
+ getItemDimensions: getItemDimensions2,
143
355
  isMainItem: () => true,
144
356
  pagination: pagination2,
145
- isItemVisible: () => true
357
+ isItemVisible: () => true,
358
+ hiddenCount: 0,
359
+ getLastVisibleOthersIndex: () => -1,
360
+ getItemContentDimensions: createGetItemContentDimensions(getItemDimensions2, options.itemAspectRatios, aspectRatio)
146
361
  };
147
362
  }
148
363
  const { width: W, height: H } = dimensions;
@@ -153,76 +368,84 @@ function createSidebarGrid(options) {
153
368
  let sidebarWidth;
154
369
  let sidebarHeight;
155
370
  if (isVertical) {
156
- mainHeight = H * (1 - sidebarRatio) - gap;
157
- mainWidth = W - gap * 2;
158
- sidebarHeight = H * sidebarRatio - gap;
159
- sidebarWidth = W - gap * 2;
371
+ const totalOthersCalc = count - 1;
372
+ const visibleOthersCalc = maxVisible > 0 ? Math.min(maxVisible, totalOthersCalc) : totalOthersCalc;
373
+ const baseSidebarW = W - gap * 2;
374
+ const targetRatio = 1 / ratio;
375
+ const isPortraitMode = H > W * 1.2;
376
+ const maxSidebarRatio = isPortraitMode ? 0.45 : 0.38;
377
+ const minSidebarRatio = isPortraitMode ? 0.25 : 0.18;
378
+ const minThumbH = isPortraitMode ? 70 : 60;
379
+ let bestSidebarH = 0;
380
+ let bestThumbArea = 0;
381
+ const maxRows = Math.min(3, visibleOthersCalc || 1);
382
+ for (let rows = 1; rows <= maxRows; rows++) {
383
+ const cols = Math.ceil((visibleOthersCalc || 1) / rows);
384
+ const thumbW = (baseSidebarW - (cols - 1) * gap) / cols;
385
+ const thumbH = thumbW / targetRatio;
386
+ const requiredSidebarH = rows * thumbH + (rows - 1) * gap + gap * 2;
387
+ const sidebarRatioCalc = requiredSidebarH / H;
388
+ if (sidebarRatioCalc > maxSidebarRatio + 0.05 || thumbH < minThumbH)
389
+ continue;
390
+ const thumbArea = thumbW * thumbH;
391
+ if (thumbArea > bestThumbArea) {
392
+ bestThumbArea = thumbArea;
393
+ bestSidebarH = requiredSidebarH;
394
+ }
395
+ }
396
+ if (bestSidebarH === 0) {
397
+ bestSidebarH = H * (isPortraitMode ? 0.3 : 0.25);
398
+ }
399
+ if (bestSidebarH / H < minSidebarRatio)
400
+ bestSidebarH = H * minSidebarRatio;
401
+ else if (bestSidebarH / H > maxSidebarRatio)
402
+ bestSidebarH = H * maxSidebarRatio;
403
+ sidebarHeight = bestSidebarH;
404
+ sidebarWidth = baseSidebarW;
405
+ mainHeight = H - sidebarHeight - gap * 3;
406
+ mainWidth = baseSidebarW;
160
407
  } else {
161
- mainWidth = W * (1 - sidebarRatio) - gap * 2;
162
- mainHeight = H - gap * 2;
163
- sidebarWidth = W * sidebarRatio - gap;
164
- sidebarHeight = H - gap * 2;
165
- }
166
- let mainItemWidth = mainWidth;
167
- let mainItemHeight = mainItemWidth * ratio;
168
- if (mainItemHeight > mainHeight) {
169
- mainItemHeight = mainHeight;
170
- mainItemWidth = mainItemHeight / ratio;
408
+ const totalOthersCalc = count - 1;
409
+ const visibleOthersCalc = maxVisible > 0 ? Math.min(maxVisible, totalOthersCalc) : totalOthersCalc;
410
+ const baseSidebarH = H - gap * 2;
411
+ let bestSidebarW = W * sidebarRatio - gap;
412
+ let bestScore = 0;
413
+ const maxCols = Math.min(3, visibleOthersCalc || 1);
414
+ for (let cols = 1; cols <= maxCols; cols++) {
415
+ const rows = Math.ceil((visibleOthersCalc || 1) / cols);
416
+ const thumbH = (baseSidebarH - (rows - 1) * gap) / rows;
417
+ const thumbW = thumbH / ratio;
418
+ const requiredSidebarW = cols * thumbW + (cols - 1) * gap + gap * 2;
419
+ const sidebarRatioTest = requiredSidebarW / W;
420
+ const thumbArea = thumbW * thumbH;
421
+ const mainAreaBonus = (1 - sidebarRatioTest) * 0.5;
422
+ const score = thumbArea * (1 + mainAreaBonus);
423
+ if (sidebarRatioTest >= 0.12 && sidebarRatioTest <= 0.4 && score > bestScore) {
424
+ bestSidebarW = requiredSidebarW;
425
+ bestScore = score;
426
+ }
427
+ }
428
+ if (bestSidebarW / W < 0.12)
429
+ bestSidebarW = W * 0.15;
430
+ else if (bestSidebarW / W > 0.4)
431
+ bestSidebarW = W * 0.35;
432
+ sidebarWidth = bestSidebarW;
433
+ sidebarHeight = baseSidebarH;
434
+ mainWidth = W - sidebarWidth - gap * 2;
435
+ mainHeight = baseSidebarH;
171
436
  }
437
+ const mainItemWidth = mainWidth;
438
+ const mainItemHeight = mainHeight;
172
439
  const totalOthers = count - 1;
173
- const visibleOthers = maxVisibleOthers > 0 ? Math.min(maxVisibleOthers, totalOthers) : totalOthers;
174
- const othersTotalPages = maxVisibleOthers > 0 ? Math.ceil(totalOthers / maxVisibleOthers) : 1;
175
- const safeCurrentOthersPage = Math.min(currentOthersPage, Math.max(0, othersTotalPages - 1));
176
- const startOthersIndex = safeCurrentOthersPage * maxVisibleOthers;
440
+ const visibleOthers = maxVisible > 0 ? Math.min(maxVisible, totalOthers) : totalOthers;
441
+ const othersTotalPages = maxVisible > 0 ? Math.ceil(totalOthers / maxVisible) : 1;
442
+ const safeCurrentVisiblePage = Math.min(currentVisiblePage, Math.max(0, othersTotalPages - 1));
443
+ const startOthersIndex = safeCurrentVisiblePage * (maxVisible > 0 ? maxVisible : totalOthers);
177
444
  const endOthersIndex = Math.min(startOthersIndex + visibleOthers, totalOthers);
178
445
  const itemsOnPage = endOthersIndex - startOthersIndex;
179
- const hiddenCount = totalOthers - itemsOnPage;
180
- let thumbCols = 1;
181
- let thumbRows = 1;
182
- let thumbWidth = 0;
183
- let thumbHeight = 0;
184
- if (visibleOthers > 0) {
185
- let bestArea = 0;
186
- if (isVertical) {
187
- for (let cols = 1; cols <= visibleOthers; cols++) {
188
- const rows = Math.ceil(visibleOthers / cols);
189
- const maxTileW = (sidebarWidth - (cols - 1) * gap) / cols;
190
- const maxTileH = (sidebarHeight - (rows - 1) * gap) / rows;
191
- let tileW = maxTileW;
192
- let tileH = tileW * ratio;
193
- if (tileH > maxTileH) {
194
- tileH = maxTileH;
195
- tileW = tileH / ratio;
196
- }
197
- if (tileW * tileH > bestArea) {
198
- bestArea = tileW * tileH;
199
- thumbCols = cols;
200
- thumbRows = rows;
201
- thumbWidth = tileW;
202
- thumbHeight = tileH;
203
- }
204
- }
205
- } else {
206
- for (let rows = 1; rows <= visibleOthers; rows++) {
207
- const cols = Math.ceil(visibleOthers / rows);
208
- const maxTileH = (sidebarHeight - (rows - 1) * gap) / rows;
209
- const maxTileW = (sidebarWidth - (cols - 1) * gap) / cols;
210
- let tileH = maxTileH;
211
- let tileW = tileH / ratio;
212
- if (tileW > maxTileW) {
213
- tileW = maxTileW;
214
- tileH = tileW * ratio;
215
- }
216
- if (tileW * tileH > bestArea) {
217
- bestArea = tileW * tileH;
218
- thumbCols = cols;
219
- thumbRows = rows;
220
- thumbWidth = tileW;
221
- thumbHeight = tileH;
222
- }
223
- }
224
- }
225
- }
446
+ const isPaginationMode = othersTotalPages > 1;
447
+ const isActivelyPaginating = isPaginationMode && currentVisiblePage > 0;
448
+ const hiddenCount = isActivelyPaginating ? 0 : totalOthers > itemsOnPage ? totalOthers - itemsOnPage + 1 : 0;
226
449
  const positions = [];
227
450
  let mainLeft;
228
451
  let mainTop;
@@ -237,68 +460,200 @@ function createSidebarGrid(options) {
237
460
  position: { top: mainTop, left: mainLeft },
238
461
  dimensions: { width: mainItemWidth, height: mainItemHeight }
239
462
  };
240
- const totalGridWidth = thumbCols * thumbWidth + (thumbCols - 1) * gap;
241
- const totalGridHeight = thumbRows * thumbHeight + (thumbRows - 1) * gap;
242
- let gridStartLeft;
243
- let gridStartTop;
463
+ let sidebarOffsetLeft;
464
+ let sidebarOffsetTop;
244
465
  if (isVertical) {
245
- gridStartLeft = gap + (sidebarWidth - totalGridWidth) / 2;
246
- gridStartTop = sidebarPosition === "top" ? gap + (sidebarHeight - totalGridHeight) / 2 : mainHeight + gap * 2 + (sidebarHeight - totalGridHeight) / 2;
466
+ sidebarOffsetLeft = gap;
467
+ sidebarOffsetTop = sidebarPosition === "top" ? gap : mainHeight + gap * 2;
247
468
  } else {
248
- gridStartLeft = sidebarPosition === "left" ? gap + (sidebarWidth - totalGridWidth) / 2 : mainWidth + gap * 2 + (sidebarWidth - totalGridWidth) / 2;
249
- gridStartTop = gap + (sidebarHeight - totalGridHeight) / 2;
469
+ sidebarOffsetLeft = sidebarPosition === "left" ? gap : mainWidth + gap * 2;
470
+ sidebarOffsetTop = gap;
250
471
  }
251
- let sidebarIndex = 0;
252
- for (let i = 0; i < count; i++) {
253
- if (i === pinnedIndex)
254
- continue;
255
- const isInVisibleRange = sidebarIndex >= startOthersIndex && sidebarIndex < endOthersIndex;
256
- if (isInVisibleRange) {
257
- const pageRelativeIndex = sidebarIndex - startOthersIndex;
258
- const row = Math.floor(pageRelativeIndex / thumbCols);
259
- const col = pageRelativeIndex % thumbCols;
260
- const itemsInLastRow = itemsOnPage % thumbCols || thumbCols;
261
- let rowLeft = gridStartLeft;
262
- const lastRowIndex = Math.ceil(itemsOnPage / thumbCols) - 1;
263
- if (row === lastRowIndex && itemsInLastRow < thumbCols) {
264
- const rowWidth = itemsInLastRow * thumbWidth + (itemsInLastRow - 1) * gap;
265
- if (isVertical) {
266
- rowLeft = gap + (sidebarWidth - rowWidth) / 2;
267
- } else {
268
- rowLeft = (sidebarPosition === "left" ? gap : mainWidth + gap * 2) + (sidebarWidth - rowWidth) / 2;
269
- }
472
+ if (flexLayout && itemAspectRatios && itemsOnPage > 0) {
473
+ const othersAspectRatios = [];
474
+ const originalIndices = [];
475
+ let othersIdx = 0;
476
+ for (let i = 0; i < count; i++) {
477
+ if (i === pinnedIndex)
478
+ continue;
479
+ if (othersIdx >= startOthersIndex && othersIdx < endOthersIndex) {
480
+ othersAspectRatios.push(itemAspectRatios[i]);
481
+ originalIndices.push(i);
270
482
  }
271
- positions[i] = {
483
+ othersIdx++;
484
+ }
485
+ const flexItems = calculateFlexLayout({
486
+ dimensions: { width: sidebarWidth + gap * 2, height: sidebarHeight + gap * 2 },
487
+ count: itemsOnPage,
488
+ aspectRatio,
489
+ gap,
490
+ itemAspectRatios: othersAspectRatios,
491
+ preferHorizontal: isVertical
492
+ // bottom/top sidebars should use horizontal layout
493
+ });
494
+ for (let i = 0; i < flexItems.length; i++) {
495
+ const originalIndex = originalIndices[i];
496
+ const flexItem = flexItems[i];
497
+ positions[originalIndex] = {
272
498
  position: {
273
- top: gridStartTop + row * (thumbHeight + gap),
274
- left: rowLeft + col * (thumbWidth + gap)
499
+ top: flexItem.top + sidebarOffsetTop - gap,
500
+ left: flexItem.left + sidebarOffsetLeft - gap
275
501
  },
276
- dimensions: { width: thumbWidth, height: thumbHeight }
502
+ dimensions: { width: flexItem.width, height: flexItem.height }
277
503
  };
504
+ }
505
+ let sidebarIndex = 0;
506
+ for (let i = 0; i < count; i++) {
507
+ if (i === pinnedIndex)
508
+ continue;
509
+ const isInVisibleRange = sidebarIndex >= startOthersIndex && sidebarIndex < endOthersIndex;
510
+ if (!isInVisibleRange) {
511
+ positions[i] = {
512
+ position: { top: -9999, left: -9999 },
513
+ dimensions: { width: 0, height: 0 }
514
+ };
515
+ }
516
+ sidebarIndex++;
517
+ }
518
+ } else {
519
+ let thumbCols = 1;
520
+ let thumbRows = 1;
521
+ let thumbWidth = 0;
522
+ let thumbHeight = 0;
523
+ if (visibleOthers > 0) {
524
+ if (isVertical) {
525
+ let bestScore = -1;
526
+ for (let cols = 1; cols <= visibleOthers; cols++) {
527
+ const rows = Math.ceil(visibleOthers / cols);
528
+ const maxTileW = (sidebarWidth - (cols - 1) * gap) / cols;
529
+ const maxTileH = (sidebarHeight - (rows - 1) * gap) / rows;
530
+ let tileW = maxTileW;
531
+ let tileH = tileW * ratio;
532
+ if (tileH > maxTileH) {
533
+ tileH = maxTileH;
534
+ tileW = tileH / ratio;
535
+ }
536
+ const area = tileW * tileH;
537
+ const itemRatio = tileW / tileH;
538
+ const idealRatio = 16 / 9;
539
+ const ratioScore = 1 / (1 + Math.abs(Math.log(itemRatio / idealRatio)));
540
+ const colsMultiplier = cols >= rows ? 1.5 : 0.5;
541
+ const score = area * ratioScore * colsMultiplier;
542
+ if (score > bestScore) {
543
+ bestScore = score;
544
+ thumbCols = cols;
545
+ thumbRows = rows;
546
+ thumbWidth = tileW;
547
+ thumbHeight = tileH;
548
+ }
549
+ }
550
+ } else {
551
+ let bestScore = -1;
552
+ const targetRatio = 1 / ratio;
553
+ for (let rows = 1; rows <= visibleOthers; rows++) {
554
+ const cols = Math.ceil(visibleOthers / rows);
555
+ const maxTileH = (sidebarHeight - (rows - 1) * gap) / rows;
556
+ const idealTileW = maxTileH * targetRatio;
557
+ const maxTileW = (sidebarWidth - (cols - 1) * gap) / cols;
558
+ let tileW, tileH;
559
+ if (idealTileW <= maxTileW) {
560
+ tileW = idealTileW;
561
+ tileH = maxTileH;
562
+ } else {
563
+ tileW = maxTileW;
564
+ tileH = tileW / targetRatio;
565
+ }
566
+ const area = tileW * tileH * visibleOthers;
567
+ const score = area;
568
+ if (score > bestScore) {
569
+ bestScore = score;
570
+ thumbCols = cols;
571
+ thumbRows = rows;
572
+ thumbWidth = tileW;
573
+ thumbHeight = tileH;
574
+ }
575
+ }
576
+ }
577
+ }
578
+ const totalGridWidth = thumbCols * thumbWidth + (thumbCols - 1) * gap;
579
+ const totalGridHeight = thumbRows * thumbHeight + (thumbRows - 1) * gap;
580
+ let gridStartLeft;
581
+ let gridStartTop;
582
+ if (isVertical) {
583
+ gridStartLeft = gap + (sidebarWidth - totalGridWidth) / 2;
584
+ gridStartTop = sidebarPosition === "top" ? gap + (sidebarHeight - totalGridHeight) / 2 : mainHeight + gap * 2 + (sidebarHeight - totalGridHeight) / 2;
278
585
  } else {
279
- positions[i] = {
280
- position: { top: -9999, left: -9999 },
281
- dimensions: { width: 0, height: 0 }
282
- };
586
+ gridStartLeft = sidebarPosition === "left" ? gap + (sidebarWidth - totalGridWidth) / 2 : mainWidth + gap * 2 + (sidebarWidth - totalGridWidth) / 2;
587
+ gridStartTop = gap + (sidebarHeight - totalGridHeight) / 2;
588
+ }
589
+ let sidebarIndex = 0;
590
+ for (let i = 0; i < count; i++) {
591
+ if (i === pinnedIndex)
592
+ continue;
593
+ const isInVisibleRange = sidebarIndex >= startOthersIndex && sidebarIndex < endOthersIndex;
594
+ if (isInVisibleRange) {
595
+ const pageRelativeIndex = sidebarIndex - startOthersIndex;
596
+ const row = Math.floor(pageRelativeIndex / thumbCols);
597
+ const col = pageRelativeIndex % thumbCols;
598
+ const itemsInLastRow = itemsOnPage % thumbCols || thumbCols;
599
+ let rowLeft = gridStartLeft;
600
+ const lastRowIndex = Math.ceil(itemsOnPage / thumbCols) - 1;
601
+ if (row === lastRowIndex && itemsInLastRow < thumbCols) {
602
+ const rowWidth = itemsInLastRow * thumbWidth + (itemsInLastRow - 1) * gap;
603
+ if (isVertical) {
604
+ rowLeft = gap + (sidebarWidth - rowWidth) / 2;
605
+ } else {
606
+ rowLeft = (sidebarPosition === "left" ? gap : mainWidth + gap * 2) + (sidebarWidth - rowWidth) / 2;
607
+ }
608
+ }
609
+ positions[i] = {
610
+ position: {
611
+ top: gridStartTop + row * (thumbHeight + gap),
612
+ left: rowLeft + col * (thumbWidth + gap)
613
+ },
614
+ dimensions: { width: thumbWidth, height: thumbHeight }
615
+ };
616
+ } else {
617
+ positions[i] = {
618
+ position: { top: -9999, left: -9999 },
619
+ dimensions: { width: 0, height: 0 }
620
+ };
621
+ }
622
+ sidebarIndex++;
283
623
  }
284
- sidebarIndex++;
285
624
  }
286
625
  const pagination = {
287
- enabled: maxVisibleOthers > 0 && totalOthers > maxVisibleOthers,
288
- currentPage: safeCurrentOthersPage,
626
+ enabled: maxVisible > 0 && totalOthers > maxVisible,
627
+ currentPage: safeCurrentVisiblePage,
289
628
  totalPages: othersTotalPages,
290
629
  itemsOnPage,
291
630
  startIndex: startOthersIndex,
292
631
  endIndex: endOthersIndex
293
632
  };
633
+ const getItemDimensions = (index) => positions[index]?.dimensions ?? { width: 0, height: 0 };
634
+ const getLastVisibleOthersIndex = () => {
635
+ if (itemsOnPage === 0)
636
+ return -1;
637
+ let othersIdx = 0;
638
+ let lastVisibleOriginalIdx = -1;
639
+ for (let i = 0; i < count; i++) {
640
+ if (i === pinnedIndex)
641
+ continue;
642
+ if (othersIdx >= startOthersIndex && othersIdx < endOthersIndex) {
643
+ lastVisibleOriginalIdx = i;
644
+ }
645
+ othersIdx++;
646
+ }
647
+ return lastVisibleOriginalIdx;
648
+ };
294
649
  return {
295
650
  width: mainItemWidth,
296
651
  height: mainItemHeight,
297
- rows: isVertical ? 1 + thumbRows : thumbRows,
298
- cols: isVertical ? thumbCols : 1 + thumbCols,
652
+ rows: isVertical ? 2 : 1,
653
+ cols: isVertical ? 1 : 2,
299
654
  layoutMode: "sidebar",
300
655
  getPosition: (index) => positions[index]?.position ?? { top: 0, left: 0 },
301
- getItemDimensions: (index) => positions[index]?.dimensions ?? { width: 0, height: 0 },
656
+ getItemDimensions,
302
657
  isMainItem: (index) => index === pinnedIndex,
303
658
  pagination,
304
659
  isItemVisible: (index) => {
@@ -311,160 +666,36 @@ function createSidebarGrid(options) {
311
666
  }
312
667
  return sIdx >= startOthersIndex && sIdx < endOthersIndex;
313
668
  },
314
- // Extra info
315
- hiddenCount
316
- };
317
- }
318
- function createSpeakerGrid(options) {
319
- const { dimensions, gap, aspectRatio, count, speakerIndex = 0, maxVisibleOthers = 0, currentOthersPage = 0 } = options;
320
- if (count === 0) {
321
- return createEmptyMeetGridResult("speaker");
322
- }
323
- if (count === 1) {
324
- const grid = createGrid({ ...options, count: 1 });
325
- const pagination2 = createDefaultPagination(1);
326
- return {
327
- ...grid,
328
- layoutMode: "speaker",
329
- getItemDimensions: () => ({ width: grid.width, height: grid.height }),
330
- isMainItem: () => true,
331
- pagination: pagination2,
332
- isItemVisible: () => true
333
- };
334
- }
335
- const { width: W, height: H } = dimensions;
336
- const ratio = getAspectRatio(aspectRatio);
337
- const totalOthers = count - 1;
338
- const visibleOthers = maxVisibleOthers > 0 ? Math.min(maxVisibleOthers, totalOthers) : totalOthers;
339
- const othersTotalPages = maxVisibleOthers > 0 ? Math.ceil(totalOthers / maxVisibleOthers) : 1;
340
- const safeCurrentOthersPage = Math.min(currentOthersPage, Math.max(0, othersTotalPages - 1));
341
- const startOthersIndex = safeCurrentOthersPage * maxVisibleOthers;
342
- const endOthersIndex = Math.min(startOthersIndex + visibleOthers, totalOthers);
343
- const itemsOnPage = endOthersIndex - startOthersIndex;
344
- const hiddenCount = totalOthers - itemsOnPage;
345
- const speakerAreaHeight = (H - gap * 3) * 0.65;
346
- const othersAreaHeight = (H - gap * 3) * 0.35;
347
- const othersAreaWidth = W - gap * 2;
348
- let speakerW = W - gap * 2;
349
- let speakerH = speakerW * ratio;
350
- if (speakerH > speakerAreaHeight) {
351
- speakerH = speakerAreaHeight;
352
- speakerW = speakerH / ratio;
353
- }
354
- let bestCols = 1;
355
- let bestTileW = 0;
356
- let bestTileH = 0;
357
- if (visibleOthers > 0) {
358
- for (let cols = 1; cols <= visibleOthers; cols++) {
359
- const rows = Math.ceil(visibleOthers / cols);
360
- const maxTileW = (othersAreaWidth - (cols - 1) * gap) / cols;
361
- const maxTileH = (othersAreaHeight - (rows - 1) * gap) / rows;
362
- let tileW = maxTileW;
363
- let tileH = tileW * ratio;
364
- if (tileH > maxTileH) {
365
- tileH = maxTileH;
366
- tileW = tileH / ratio;
367
- }
368
- if (tileW * tileH > bestTileW * bestTileH) {
369
- bestCols = cols;
370
- bestTileW = tileW;
371
- bestTileH = tileH;
372
- }
373
- }
374
- }
375
- const otherCols = bestCols;
376
- const otherRows = Math.ceil(visibleOthers / otherCols) || 1;
377
- const otherW = bestTileW;
378
- const otherH = bestTileH;
379
- const positions = [];
380
- positions[speakerIndex] = {
381
- position: {
382
- top: gap + (speakerAreaHeight - speakerH) / 2,
383
- left: gap + (W - gap * 2 - speakerW) / 2
384
- },
385
- dimensions: { width: speakerW, height: speakerH }
386
- };
387
- const totalGridWidth = otherCols * otherW + (otherCols - 1) * gap;
388
- const totalGridHeight = otherRows * otherH + (otherRows - 1) * gap;
389
- const gridStartLeft = gap + (othersAreaWidth - totalGridWidth) / 2;
390
- const gridStartTop = speakerAreaHeight + gap * 2 + (othersAreaHeight - totalGridHeight) / 2;
391
- let otherIndex = 0;
392
- for (let i = 0; i < count; i++) {
393
- if (i === speakerIndex)
394
- continue;
395
- const isInVisibleRange = otherIndex >= startOthersIndex && otherIndex < endOthersIndex;
396
- if (isInVisibleRange) {
397
- const pageRelativeIndex = otherIndex - startOthersIndex;
398
- const row = Math.floor(pageRelativeIndex / otherCols);
399
- const col = pageRelativeIndex % otherCols;
400
- const lastRowIndex = Math.ceil(itemsOnPage / otherCols) - 1;
401
- const itemsInLastRow = itemsOnPage % otherCols || otherCols;
402
- let rowStartLeft = gridStartLeft;
403
- if (row === lastRowIndex && itemsInLastRow < otherCols) {
404
- const rowWidth = itemsInLastRow * otherW + (itemsInLastRow - 1) * gap;
405
- rowStartLeft = gap + (othersAreaWidth - rowWidth) / 2;
406
- }
407
- positions[i] = {
408
- position: {
409
- top: gridStartTop + row * (otherH + gap),
410
- left: rowStartLeft + col * (otherW + gap)
411
- },
412
- dimensions: { width: otherW, height: otherH }
413
- };
414
- } else {
415
- positions[i] = {
416
- position: { top: -9999, left: -9999 },
417
- dimensions: { width: 0, height: 0 }
418
- };
419
- }
420
- otherIndex++;
421
- }
422
- const pagination = {
423
- enabled: maxVisibleOthers > 0 && totalOthers > maxVisibleOthers,
424
- currentPage: safeCurrentOthersPage,
425
- totalPages: othersTotalPages,
426
- itemsOnPage,
427
- startIndex: startOthersIndex,
428
- endIndex: endOthersIndex
429
- };
430
- return {
431
- width: speakerW,
432
- height: speakerH,
433
- rows: 1 + otherRows,
434
- cols: otherCols,
435
- layoutMode: "speaker",
436
- getPosition: (index) => positions[index]?.position ?? { top: 0, left: 0 },
437
- getItemDimensions: (index) => positions[index]?.dimensions ?? { width: 0, height: 0 },
438
- isMainItem: (index) => index === speakerIndex,
439
- pagination,
440
- isItemVisible: (index) => {
441
- if (index === speakerIndex)
442
- return true;
443
- let sIdx = 0;
444
- for (let i = 0; i < index; i++) {
445
- if (i !== speakerIndex)
446
- sIdx++;
447
- }
448
- return sIdx >= startOthersIndex && sIdx < endOthersIndex;
449
- },
450
- hiddenCount
669
+ hiddenCount,
670
+ getLastVisibleOthersIndex,
671
+ getItemContentDimensions: createGetItemContentDimensions(getItemDimensions, options.itemAspectRatios, aspectRatio)
451
672
  };
452
673
  }
453
674
  function createSpotlightGrid(options) {
454
- const { dimensions, gap, aspectRatio, pinnedIndex = 0 } = options;
675
+ const { dimensions, gap, aspectRatio, pinnedIndex = 0, flexLayout = false, itemAspectRatios } = options;
455
676
  const { width: W, height: H } = dimensions;
456
- const ratio = getAspectRatio(aspectRatio);
457
- let spotWidth = W - gap * 2;
458
- let spotHeight = spotWidth * ratio;
459
- if (spotHeight > H - gap * 2) {
677
+ const itemRatio = flexLayout && itemAspectRatios?.[pinnedIndex];
678
+ const shouldFill = itemRatio === "fill" || itemRatio === "auto";
679
+ let spotWidth;
680
+ let spotHeight;
681
+ if (shouldFill) {
682
+ spotWidth = W - gap * 2;
460
683
  spotHeight = H - gap * 2;
461
- spotWidth = spotHeight / ratio;
684
+ } else {
685
+ const ratio = itemRatio ? getAspectRatio(itemRatio) : getAspectRatio(aspectRatio);
686
+ spotWidth = W - gap * 2;
687
+ spotHeight = spotWidth * ratio;
688
+ if (spotHeight > H - gap * 2) {
689
+ spotHeight = H - gap * 2;
690
+ spotWidth = spotHeight / ratio;
691
+ }
462
692
  }
463
693
  const position = {
464
694
  top: gap + (H - gap * 2 - spotHeight) / 2,
465
695
  left: gap + (W - gap * 2 - spotWidth) / 2
466
696
  };
467
697
  const pagination = createDefaultPagination(1);
698
+ const getItemDimensions = (index) => index === pinnedIndex ? { width: spotWidth, height: spotHeight } : { width: 0, height: 0 };
468
699
  return {
469
700
  width: spotWidth,
470
701
  height: spotHeight,
@@ -472,10 +703,13 @@ function createSpotlightGrid(options) {
472
703
  cols: 1,
473
704
  layoutMode: "spotlight",
474
705
  getPosition: (index) => index === pinnedIndex ? position : { top: -9999, left: -9999 },
475
- getItemDimensions: (index) => index === pinnedIndex ? { width: spotWidth, height: spotHeight } : { width: 0, height: 0 },
706
+ getItemDimensions,
476
707
  isMainItem: (index) => index === pinnedIndex,
477
708
  pagination,
478
- isItemVisible: (index) => index === pinnedIndex
709
+ isItemVisible: (index) => index === pinnedIndex,
710
+ hiddenCount: 0,
711
+ getLastVisibleOthersIndex: () => -1,
712
+ getItemContentDimensions: createGetItemContentDimensions(getItemDimensions, options.itemAspectRatios, aspectRatio)
479
713
  };
480
714
  }
481
715
  function createDefaultPagination(count) {
@@ -506,6 +740,7 @@ function createPaginationInfo(count, maxItemsPerPage, currentPage) {
506
740
  };
507
741
  }
508
742
  function createEmptyMeetGridResult(layoutMode) {
743
+ const getItemDimensions = () => ({ width: 0, height: 0 });
509
744
  return {
510
745
  width: 0,
511
746
  height: 0,
@@ -513,51 +748,129 @@ function createEmptyMeetGridResult(layoutMode) {
513
748
  cols: 0,
514
749
  layoutMode,
515
750
  getPosition: () => ({ top: 0, left: 0 }),
516
- getItemDimensions: () => ({ width: 0, height: 0 }),
751
+ getItemDimensions,
517
752
  isMainItem: () => false,
518
753
  pagination: createDefaultPagination(0),
519
- isItemVisible: () => false
754
+ isItemVisible: () => false,
755
+ hiddenCount: 0,
756
+ getLastVisibleOthersIndex: () => -1,
757
+ getItemContentDimensions: () => ({ width: 0, height: 0, offsetTop: 0, offsetLeft: 0 })
520
758
  };
521
759
  }
522
760
  function createMeetGrid(options) {
523
- const { layoutMode = "gallery", count } = options;
761
+ const { layoutMode = "gallery", count, flexLayout = false } = options;
524
762
  if (count === 0) {
525
763
  return createEmptyMeetGridResult(layoutMode);
526
764
  }
527
765
  switch (layoutMode) {
528
766
  case "spotlight":
529
767
  return createSpotlightGrid(options);
530
- case "speaker":
531
- return createSpeakerGrid(options);
532
768
  case "sidebar":
533
769
  return createSidebarGrid(options);
534
770
  case "gallery":
535
771
  default: {
536
- const { maxItemsPerPage, currentPage, pinnedIndex, sidebarPosition = "right" } = options;
772
+ const { maxItemsPerPage, currentPage, pinnedIndex, sidebarPosition = "right", dimensions, maxVisible = 0 } = options;
537
773
  if (pinnedIndex !== void 0 && pinnedIndex >= 0 && pinnedIndex < count) {
538
- return createSidebarGrid({ ...options, sidebarPosition });
774
+ const isPortrait = dimensions.width < dimensions.height;
775
+ const effectiveSidebarPosition = isPortrait ? "bottom" : sidebarPosition;
776
+ return createSidebarGrid({ ...options, sidebarPosition: effectiveSidebarPosition });
777
+ }
778
+ let visibleCount = count;
779
+ let hiddenCount = 0;
780
+ let startIndex = 0;
781
+ let endIndex = count;
782
+ if (maxItemsPerPage && maxItemsPerPage > 0) {
783
+ const pagination2 = createPaginationInfo(count, maxItemsPerPage, currentPage);
784
+ visibleCount = pagination2.itemsOnPage;
785
+ startIndex = pagination2.startIndex;
786
+ endIndex = pagination2.endIndex;
787
+ } else if (maxVisible > 0 && count > maxVisible) {
788
+ visibleCount = maxVisible;
789
+ hiddenCount = count - maxVisible + 1;
790
+ startIndex = 0;
791
+ endIndex = maxVisible;
792
+ }
793
+ const pagination = maxItemsPerPage && maxItemsPerPage > 0 ? createPaginationInfo(count, maxItemsPerPage, currentPage) : {
794
+ enabled: false,
795
+ currentPage: 0,
796
+ totalPages: 1,
797
+ itemsOnPage: visibleCount,
798
+ startIndex,
799
+ endIndex
800
+ };
801
+ const effectiveCount = visibleCount;
802
+ if (flexLayout && options.itemAspectRatios) {
803
+ const pageItemRatios = options.itemAspectRatios.slice(startIndex, startIndex + effectiveCount);
804
+ const flexItems = calculateFlexLayout({
805
+ dimensions: options.dimensions,
806
+ count: effectiveCount,
807
+ aspectRatio: options.aspectRatio,
808
+ gap: options.gap,
809
+ itemAspectRatios: pageItemRatios
810
+ });
811
+ const getPosition2 = (index) => {
812
+ const relativeIndex = index - startIndex;
813
+ if (relativeIndex < 0 || relativeIndex >= effectiveCount) {
814
+ return { top: -9999, left: -9999 };
815
+ }
816
+ const item = flexItems[relativeIndex];
817
+ return item ? { top: item.top, left: item.left } : { top: -9999, left: -9999 };
818
+ };
819
+ const getItemDimensions2 = (index) => {
820
+ const relativeIndex = index - startIndex;
821
+ if (relativeIndex < 0 || relativeIndex >= effectiveCount) {
822
+ return { width: 0, height: 0 };
823
+ }
824
+ const item = flexItems[relativeIndex];
825
+ return item ? { width: item.width, height: item.height } : { width: 0, height: 0 };
826
+ };
827
+ const getItemContentDimensions = (index) => {
828
+ const dims = getItemDimensions2(index);
829
+ return {
830
+ width: dims.width,
831
+ height: dims.height,
832
+ offsetTop: 0,
833
+ offsetLeft: 0
834
+ };
835
+ };
836
+ const lastVisibleIndex2 = endIndex - 1;
837
+ return {
838
+ width: flexItems[0]?.width ?? 0,
839
+ height: flexItems[0]?.height ?? 0,
840
+ rows: Math.max(...flexItems.map((i) => i.row)) + 1,
841
+ cols: flexItems.filter((i) => i.row === 0).length,
842
+ layoutMode: "gallery",
843
+ getPosition: getPosition2,
844
+ getItemDimensions: getItemDimensions2,
845
+ isMainItem: () => false,
846
+ pagination,
847
+ isItemVisible: (index) => index >= startIndex && index < endIndex,
848
+ hiddenCount,
849
+ getLastVisibleOthersIndex: () => hiddenCount > 0 ? lastVisibleIndex2 : -1,
850
+ getItemContentDimensions
851
+ };
539
852
  }
540
- const pagination = createPaginationInfo(count, maxItemsPerPage, currentPage);
541
- const effectiveCount = pagination.enabled ? pagination.itemsOnPage : count;
542
853
  const grid = createGrid({ ...options, count: effectiveCount });
543
854
  const getPosition = (index) => {
544
- if (!pagination.enabled) {
545
- return grid.getPosition(index);
546
- }
547
- const pageRelativeIndex = index - pagination.startIndex;
548
- if (pageRelativeIndex < 0 || pageRelativeIndex >= pagination.itemsOnPage) {
855
+ const relativeIndex = index - startIndex;
856
+ if (relativeIndex < 0 || relativeIndex >= effectiveCount) {
549
857
  return { top: -9999, left: -9999 };
550
858
  }
551
- return grid.getPosition(pageRelativeIndex);
859
+ return grid.getPosition(relativeIndex);
552
860
  };
861
+ const getItemDimensions = () => ({ width: grid.width, height: grid.height });
862
+ const lastVisibleIndex = endIndex - 1;
553
863
  return {
554
864
  ...grid,
555
865
  layoutMode: "gallery",
556
866
  getPosition,
557
- getItemDimensions: () => ({ width: grid.width, height: grid.height }),
867
+ getItemDimensions,
558
868
  isMainItem: () => false,
559
869
  pagination,
560
- isItemVisible: (index) => index >= pagination.startIndex && index < pagination.endIndex
870
+ isItemVisible: (index) => index >= startIndex && index < endIndex,
871
+ hiddenCount,
872
+ getLastVisibleOthersIndex: () => hiddenCount > 0 ? lastVisibleIndex : -1,
873
+ getItemContentDimensions: createGetItemContentDimensions(getItemDimensions, options.itemAspectRatios, options.aspectRatio)
561
874
  };
562
875
  }
563
876
  }
@@ -579,4 +892,4 @@ function getSpringConfig(preset = "smooth") {
579
892
  };
580
893
  }
581
894
 
582
- export { createGrid, createGridItemPositioner, createMeetGrid, getAspectRatio, getGridItemDimensions, getSpringConfig, parseAspectRatio, springPresets };
895
+ export { calculateContentDimensions, calculateFlexLayout, calculateFlexStrip, createGrid, createGridItemPositioner, createMeetGrid, getAspectRatio, getGridItemDimensions, getSpringConfig, parseAspectRatio, springPresets };