@thangdevalone/meet-layout-grid-core 1.1.1 → 1.2.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.cjs CHANGED
@@ -21,7 +21,7 @@ function parseAspectRatio(ratio) {
21
21
  function calculateContentDimensions(cellDimensions, itemRatio, defaultRatio) {
22
22
  const { width: cellW, height: cellH } = cellDimensions;
23
23
  const effectiveRatio = itemRatio ?? (defaultRatio ? defaultRatio : void 0);
24
- if (!effectiveRatio || effectiveRatio === "fill" || effectiveRatio === "auto") {
24
+ if (!effectiveRatio || effectiveRatio === "auto") {
25
25
  return {
26
26
  width: cellW,
27
27
  height: cellH,
@@ -52,184 +52,7 @@ function createGetItemContentDimensions(getItemDimensions, itemAspectRatios, def
52
52
  return calculateContentDimensions(cellDimensions, effectiveRatio, defaultRatio);
53
53
  };
54
54
  }
55
- function calculateFlexLayout(options) {
56
- const { dimensions, count, aspectRatio, gap, itemAspectRatios, preferHorizontal } = options;
57
- if (count === 0 || dimensions.width === 0 || dimensions.height === 0) {
58
- return [];
59
- }
60
- const containerWidth = dimensions.width - gap * 2;
61
- const containerHeight = dimensions.height - gap * 2;
62
- const getItemAspectValue = (index) => {
63
- const itemRatio = itemAspectRatios?.[index] ?? aspectRatio;
64
- if (!itemRatio || itemRatio === "fill" || itemRatio === "auto") {
65
- return 16 / 9;
66
- }
67
- const parsed = parseAspectRatio(itemRatio);
68
- return parsed ? parsed.widthRatio / parsed.heightRatio : 16 / 9;
69
- };
70
- const aspectValues = [];
71
- for (let i = 0; i < count; i++) {
72
- aspectValues.push(getItemAspectValue(i));
73
- }
74
- const avgAspect = aspectValues.reduce((a, b) => a + b, 0) / count;
75
- const containerAspect = containerWidth / containerHeight;
76
- let numRows;
77
- if (preferHorizontal) {
78
- const sumAspects = aspectValues.reduce((a, b) => a + b, 0);
79
- const singleRowHeight = containerWidth / sumAspects;
80
- if (singleRowHeight <= containerHeight) {
81
- numRows = 1;
82
- } else {
83
- numRows = Math.ceil(Math.sqrt(count * containerHeight / containerWidth / avgAspect));
84
- numRows = Math.max(1, Math.min(count, numRows));
85
- }
86
- } else {
87
- numRows = Math.round(Math.sqrt(count / (containerAspect / avgAspect)));
88
- numRows = Math.max(1, Math.min(count, numRows));
89
- }
90
- const itemsPerRow = Math.ceil(count / numRows);
91
- const rows = [];
92
- for (let i = 0; i < count; i += itemsPerRow) {
93
- const row = [];
94
- for (let j = i; j < Math.min(i + itemsPerRow, count); j++) {
95
- row.push(j);
96
- }
97
- rows.push(row);
98
- }
99
- const rowHeights = [];
100
- for (const row of rows) {
101
- const sumAspects = row.reduce((sum, idx) => sum + aspectValues[idx], 0);
102
- const totalGapWidth = (row.length - 1) * gap;
103
- const height = (containerWidth - totalGapWidth) / sumAspects;
104
- rowHeights.push(height);
105
- }
106
- const totalGapHeight = (rows.length - 1) * gap;
107
- const totalNaturalHeight = rowHeights.reduce((a, b) => a + b, 0);
108
- const scale = Math.min(1, (containerHeight - totalGapHeight) / totalNaturalHeight);
109
- for (let i = 0; i < rowHeights.length; i++) {
110
- rowHeights[i] *= scale;
111
- }
112
- const totalScaledHeight = rowHeights.reduce((a, b) => a + b, 0) + totalGapHeight;
113
- const verticalOffset = (containerHeight - totalScaledHeight) / 2;
114
- const items = [];
115
- let currentTop = gap + verticalOffset;
116
- for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {
117
- const row = rows[rowIndex];
118
- const rowHeight = rowHeights[rowIndex];
119
- let rowWidth = 0;
120
- for (const idx of row) {
121
- rowWidth += rowHeight * aspectValues[idx];
122
- }
123
- rowWidth += (row.length - 1) * gap;
124
- const horizontalOffset = (containerWidth - rowWidth) / 2;
125
- let currentLeft = gap + horizontalOffset;
126
- for (const itemIndex of row) {
127
- const aspect = aspectValues[itemIndex];
128
- const itemWidth = rowHeight * aspect;
129
- items[itemIndex] = {
130
- index: itemIndex,
131
- width: itemWidth,
132
- height: rowHeight,
133
- left: currentLeft,
134
- top: currentTop,
135
- row: rowIndex,
136
- widthFactor: aspect
137
- };
138
- currentLeft += itemWidth + gap;
139
- }
140
- currentTop += rowHeight + gap;
141
- }
142
- return items;
143
- }
144
- function calculateFlexStrip(options) {
145
- const { dimensions, count, aspectRatio, gap, itemAspectRatios, direction, offset = { left: 0, top: 0 } } = options;
146
- if (count === 0 || dimensions.width === 0 || dimensions.height === 0) {
147
- return [];
148
- }
149
- const stripWidth = dimensions.width;
150
- const stripHeight = dimensions.height;
151
- const getItemAspectValue = (index) => {
152
- const itemRatio = itemAspectRatios?.[index] ?? aspectRatio;
153
- if (!itemRatio || itemRatio === "fill" || itemRatio === "auto") {
154
- return 16 / 9;
155
- }
156
- const parsed = parseAspectRatio(itemRatio);
157
- return parsed ? parsed.widthRatio / parsed.heightRatio : 16 / 9;
158
- };
159
- const aspectValues = [];
160
- for (let i = 0; i < count; i++) {
161
- aspectValues.push(getItemAspectValue(i));
162
- }
163
- const items = [];
164
- if (direction === "horizontal") {
165
- const availableHeight = stripHeight;
166
- const totalGaps = (count - 1) * gap;
167
- let totalNaturalWidth = 0;
168
- for (let i = 0; i < count; i++) {
169
- totalNaturalWidth += availableHeight * aspectValues[i];
170
- }
171
- totalNaturalWidth += totalGaps;
172
- let scale = (stripWidth - totalGaps) / (totalNaturalWidth - totalGaps);
173
- scale = Math.min(scale, 2);
174
- const itemHeight = availableHeight * scale;
175
- let scaledTotalWidth = totalGaps;
176
- for (let i = 0; i < count; i++) {
177
- scaledTotalWidth += itemHeight * aspectValues[i];
178
- }
179
- let currentLeft = offset.left + (stripWidth - scaledTotalWidth) / 2;
180
- const itemTop = offset.top + (stripHeight - itemHeight) / 2;
181
- for (let i = 0; i < count; i++) {
182
- const itemWidth = itemHeight * aspectValues[i];
183
- items[i] = {
184
- index: i,
185
- width: itemWidth,
186
- height: itemHeight,
187
- left: currentLeft,
188
- top: itemTop,
189
- row: 0,
190
- widthFactor: aspectValues[i]
191
- };
192
- currentLeft += itemWidth + gap;
193
- }
194
- } else {
195
- const availableWidth = stripWidth;
196
- const totalGaps = (count - 1) * gap;
197
- let totalNaturalHeight = 0;
198
- for (let i = 0; i < count; i++) {
199
- totalNaturalHeight += availableWidth / aspectValues[i];
200
- }
201
- totalNaturalHeight += totalGaps;
202
- let scale = (stripHeight - totalGaps) / (totalNaturalHeight - totalGaps);
203
- scale = Math.min(scale, 2);
204
- const itemWidth = availableWidth * scale;
205
- let scaledTotalHeight = totalGaps;
206
- for (let i = 0; i < count; i++) {
207
- scaledTotalHeight += itemWidth / aspectValues[i];
208
- }
209
- let currentTop = offset.top + (stripHeight - scaledTotalHeight) / 2;
210
- const itemLeft = offset.left + (stripWidth - itemWidth) / 2;
211
- for (let i = 0; i < count; i++) {
212
- const itemHeight = itemWidth / aspectValues[i];
213
- items[i] = {
214
- index: i,
215
- width: itemWidth,
216
- height: itemHeight,
217
- left: itemLeft,
218
- top: currentTop,
219
- row: i,
220
- widthFactor: aspectValues[i]
221
- };
222
- currentTop += itemHeight + gap;
223
- }
224
- }
225
- return items;
226
- }
227
- function getGridItemDimensions({
228
- count,
229
- dimensions,
230
- aspectRatio,
231
- gap
232
- }) {
55
+ function getGridItemDimensions({ count, dimensions, aspectRatio, gap }) {
233
56
  let { width: W, height: H } = dimensions;
234
57
  if (W === 0 || H === 0 || count === 0) {
235
58
  return { width: 0, height: 0, rows: 1, cols: 1 };
@@ -335,10 +158,19 @@ function createGrid({ aspectRatio, count, dimensions, gap }) {
335
158
  getPosition
336
159
  };
337
160
  }
338
- function createSidebarGrid(options) {
339
- const { dimensions, gap, aspectRatio, count, sidebarPosition = "right", sidebarRatio = 0.25, pinnedIndex = 0, maxVisible = 0, currentVisiblePage = 0, flexLayout = false, itemAspectRatios } = options;
161
+ function createFlexiblePinGrid(options) {
162
+ const {
163
+ dimensions,
164
+ gap,
165
+ aspectRatio,
166
+ count,
167
+ othersPosition = "right",
168
+ pinnedIndex = 0,
169
+ maxVisible = 0,
170
+ currentVisiblePage = 0
171
+ } = options;
340
172
  if (count === 0) {
341
- return createEmptyMeetGridResult("sidebar");
173
+ return createEmptyMeetGridResult("gallery");
342
174
  }
343
175
  if (count === 1) {
344
176
  const { width: W2, height: H2 } = dimensions;
@@ -351,7 +183,7 @@ function createSidebarGrid(options) {
351
183
  height: mainHeight2,
352
184
  rows: 1,
353
185
  cols: 1,
354
- layoutMode: "sidebar",
186
+ layoutMode: "gallery",
355
187
  getPosition: () => ({ top: gap, left: gap }),
356
188
  getItemDimensions: getItemDimensions2,
357
189
  isMainItem: () => true,
@@ -359,165 +191,113 @@ function createSidebarGrid(options) {
359
191
  isItemVisible: () => true,
360
192
  hiddenCount: 0,
361
193
  getLastVisibleOthersIndex: () => -1,
362
- getItemContentDimensions: createGetItemContentDimensions(getItemDimensions2, options.itemAspectRatios, aspectRatio)
194
+ getItemContentDimensions: createGetItemContentDimensions(
195
+ getItemDimensions2,
196
+ options.itemAspectRatios,
197
+ aspectRatio
198
+ )
363
199
  };
364
200
  }
365
201
  const { width: W, height: H } = dimensions;
202
+ const isPortrait = H > W;
203
+ const effectivePosition = isPortrait ? "bottom" : othersPosition;
204
+ const isVertical = effectivePosition === "bottom" || effectivePosition === "top";
366
205
  const ratio = getAspectRatio(aspectRatio);
367
- const isVertical = sidebarPosition === "bottom" || sidebarPosition === "top";
206
+ const totalOthers = count - 1;
207
+ const visibleOthers = maxVisible > 0 ? Math.min(maxVisible, totalOthers) : totalOthers;
208
+ const othersTotalPages = maxVisible > 0 ? Math.ceil(totalOthers / maxVisible) : 1;
209
+ const safeCurrentVisiblePage = Math.min(currentVisiblePage, Math.max(0, othersTotalPages - 1));
210
+ const startOthersIndex = safeCurrentVisiblePage * (maxVisible > 0 ? maxVisible : totalOthers);
211
+ const endOthersIndex = Math.min(startOthersIndex + visibleOthers, totalOthers);
212
+ const itemsOnPage = endOthersIndex - startOthersIndex;
213
+ const isPaginationMode = othersTotalPages > 1;
214
+ const isActivelyPaginating = isPaginationMode && currentVisiblePage > 0;
215
+ const hiddenCount = isActivelyPaginating ? 0 : totalOthers > itemsOnPage ? totalOthers - itemsOnPage + 1 : 0;
368
216
  let mainWidth;
369
217
  let mainHeight;
370
- let sidebarWidth;
371
- let sidebarHeight;
218
+ let othersAreaWidth;
219
+ let othersAreaHeight;
372
220
  if (isVertical) {
373
- const totalOthersCalc = count - 1;
374
- const visibleOthersCalc = maxVisible > 0 ? Math.min(maxVisible, totalOthersCalc) : totalOthersCalc;
375
- const baseSidebarW = W - gap * 2;
376
- const targetRatio = 1 / ratio;
377
- const isPortraitMode = H > W * 1.2;
378
- const maxSidebarRatio = isPortraitMode ? 0.45 : 0.38;
379
- const minSidebarRatio = isPortraitMode ? 0.25 : 0.18;
380
- const minThumbH = isPortraitMode ? 70 : 60;
381
- let bestSidebarH = 0;
221
+ const areaW = W - gap * 2;
222
+ const othersRatio = isPortrait ? 1 : ratio;
223
+ let bestOthersH = 0;
382
224
  let bestThumbArea = 0;
383
- const maxRows = Math.min(3, visibleOthersCalc || 1);
225
+ const maxRows = Math.min(3, visibleOthers || 1);
384
226
  for (let rows = 1; rows <= maxRows; rows++) {
385
- const cols = Math.ceil((visibleOthersCalc || 1) / rows);
386
- const thumbW = (baseSidebarW - (cols - 1) * gap) / cols;
387
- const thumbH = thumbW / targetRatio;
388
- const requiredSidebarH = rows * thumbH + (rows - 1) * gap + gap * 2;
389
- const sidebarRatioCalc = requiredSidebarH / H;
390
- if (sidebarRatioCalc > maxSidebarRatio + 0.05 || thumbH < minThumbH)
227
+ const cols = Math.ceil((visibleOthers || 1) / rows);
228
+ const thumbW = (areaW - (cols - 1) * gap) / cols;
229
+ const thumbH = thumbW * othersRatio;
230
+ const requiredH = rows * thumbH + (rows - 1) * gap + gap * 2;
231
+ const areaRatio = requiredH / H;
232
+ if (areaRatio > 0.5)
233
+ continue;
234
+ if (thumbH < 40)
391
235
  continue;
392
236
  const thumbArea = thumbW * thumbH;
393
237
  if (thumbArea > bestThumbArea) {
394
238
  bestThumbArea = thumbArea;
395
- bestSidebarH = requiredSidebarH;
239
+ bestOthersH = requiredH;
396
240
  }
397
241
  }
398
- if (bestSidebarH === 0) {
399
- bestSidebarH = H * (isPortraitMode ? 0.3 : 0.25);
242
+ if (bestOthersH === 0) {
243
+ bestOthersH = H * (isPortrait ? 0.25 : 0.2);
400
244
  }
401
- if (bestSidebarH / H < minSidebarRatio)
402
- bestSidebarH = H * minSidebarRatio;
403
- else if (bestSidebarH / H > maxSidebarRatio)
404
- bestSidebarH = H * maxSidebarRatio;
405
- sidebarHeight = bestSidebarH;
406
- sidebarWidth = baseSidebarW;
407
- mainHeight = H - sidebarHeight - gap * 3;
408
- mainWidth = baseSidebarW;
245
+ const minRatio = 0.12;
246
+ const maxRatio = 0.45;
247
+ if (bestOthersH / H < minRatio)
248
+ bestOthersH = H * minRatio;
249
+ else if (bestOthersH / H > maxRatio)
250
+ bestOthersH = H * maxRatio;
251
+ othersAreaHeight = bestOthersH;
252
+ othersAreaWidth = areaW;
253
+ mainHeight = H - othersAreaHeight - gap * 3;
254
+ mainWidth = areaW;
409
255
  } else {
410
- const totalOthersCalc = count - 1;
411
- const visibleOthersCalc = maxVisible > 0 ? Math.min(maxVisible, totalOthersCalc) : totalOthersCalc;
412
- const baseSidebarH = H - gap * 2;
413
- let bestSidebarW = W * sidebarRatio - gap;
256
+ const areaH = H - gap * 2;
257
+ let bestOthersW = W * 0.2;
414
258
  let bestScore = 0;
415
- const maxCols = Math.min(3, visibleOthersCalc || 1);
259
+ const maxCols = Math.min(3, visibleOthers || 1);
416
260
  for (let cols = 1; cols <= maxCols; cols++) {
417
- const rows = Math.ceil((visibleOthersCalc || 1) / cols);
418
- const thumbH = (baseSidebarH - (rows - 1) * gap) / rows;
261
+ const rows = Math.ceil((visibleOthers || 1) / cols);
262
+ const thumbH = (areaH - (rows - 1) * gap) / rows;
419
263
  const thumbW = thumbH / ratio;
420
- const requiredSidebarW = cols * thumbW + (cols - 1) * gap + gap * 2;
421
- const sidebarRatioTest = requiredSidebarW / W;
264
+ const requiredW = cols * thumbW + (cols - 1) * gap + gap * 2;
265
+ const areaRatio = requiredW / W;
422
266
  const thumbArea = thumbW * thumbH;
423
- const mainAreaBonus = (1 - sidebarRatioTest) * 0.5;
267
+ const mainAreaBonus = (1 - areaRatio) * 0.5;
424
268
  const score = thumbArea * (1 + mainAreaBonus);
425
- if (sidebarRatioTest >= 0.12 && sidebarRatioTest <= 0.4 && score > bestScore) {
426
- bestSidebarW = requiredSidebarW;
269
+ if (areaRatio >= 0.1 && areaRatio <= 0.4 && score > bestScore) {
270
+ bestOthersW = requiredW;
427
271
  bestScore = score;
428
272
  }
429
273
  }
430
- if (bestSidebarW / W < 0.12)
431
- bestSidebarW = W * 0.15;
432
- else if (bestSidebarW / W > 0.4)
433
- bestSidebarW = W * 0.35;
434
- sidebarWidth = bestSidebarW;
435
- sidebarHeight = baseSidebarH;
436
- mainWidth = W - sidebarWidth - gap * 2;
437
- mainHeight = baseSidebarH;
274
+ if (bestOthersW / W < 0.1)
275
+ bestOthersW = W * 0.12;
276
+ else if (bestOthersW / W > 0.4)
277
+ bestOthersW = W * 0.35;
278
+ othersAreaWidth = bestOthersW;
279
+ othersAreaHeight = areaH;
280
+ mainWidth = W - othersAreaWidth - gap * 2;
281
+ mainHeight = areaH;
438
282
  }
439
283
  const mainItemWidth = mainWidth;
440
284
  const mainItemHeight = mainHeight;
441
- const totalOthers = count - 1;
442
- const visibleOthers = maxVisible > 0 ? Math.min(maxVisible, totalOthers) : totalOthers;
443
- const othersTotalPages = maxVisible > 0 ? Math.ceil(totalOthers / maxVisible) : 1;
444
- const safeCurrentVisiblePage = Math.min(currentVisiblePage, Math.max(0, othersTotalPages - 1));
445
- const startOthersIndex = safeCurrentVisiblePage * (maxVisible > 0 ? maxVisible : totalOthers);
446
- const endOthersIndex = Math.min(startOthersIndex + visibleOthers, totalOthers);
447
- const itemsOnPage = endOthersIndex - startOthersIndex;
448
- const isPaginationMode = othersTotalPages > 1;
449
- const isActivelyPaginating = isPaginationMode && currentVisiblePage > 0;
450
- const hiddenCount = isActivelyPaginating ? 0 : totalOthers > itemsOnPage ? totalOthers - itemsOnPage + 1 : 0;
451
285
  const positions = [];
452
286
  let mainLeft;
453
287
  let mainTop;
454
288
  if (isVertical) {
455
289
  mainLeft = gap + (mainWidth - mainItemWidth) / 2;
456
- mainTop = sidebarPosition === "top" ? sidebarHeight + gap * 2 + (mainHeight - mainItemHeight) / 2 : gap + (mainHeight - mainItemHeight) / 2;
290
+ mainTop = effectivePosition === "top" ? othersAreaHeight + gap * 2 + (mainHeight - mainItemHeight) / 2 : gap + (mainHeight - mainItemHeight) / 2;
457
291
  } else {
458
- mainLeft = sidebarPosition === "left" ? sidebarWidth + gap * 2 + (mainWidth - mainItemWidth) / 2 : gap + (mainWidth - mainItemWidth) / 2;
292
+ mainLeft = effectivePosition === "left" ? othersAreaWidth + gap * 2 + (mainWidth - mainItemWidth) / 2 : gap + (mainWidth - mainItemWidth) / 2;
459
293
  mainTop = gap + (mainHeight - mainItemHeight) / 2;
460
294
  }
461
295
  positions[pinnedIndex] = {
462
296
  position: { top: mainTop, left: mainLeft },
463
297
  dimensions: { width: mainItemWidth, height: mainItemHeight }
464
298
  };
465
- let sidebarOffsetLeft;
466
- let sidebarOffsetTop;
467
- if (isVertical) {
468
- sidebarOffsetLeft = gap;
469
- sidebarOffsetTop = sidebarPosition === "top" ? gap : mainHeight + gap * 2;
470
- } else {
471
- sidebarOffsetLeft = sidebarPosition === "left" ? gap : mainWidth + gap * 2;
472
- sidebarOffsetTop = gap;
473
- }
474
- if (flexLayout && itemAspectRatios && itemsOnPage > 0) {
475
- const othersAspectRatios = [];
476
- const originalIndices = [];
477
- let othersIdx = 0;
478
- for (let i = 0; i < count; i++) {
479
- if (i === pinnedIndex)
480
- continue;
481
- if (othersIdx >= startOthersIndex && othersIdx < endOthersIndex) {
482
- othersAspectRatios.push(itemAspectRatios[i]);
483
- originalIndices.push(i);
484
- }
485
- othersIdx++;
486
- }
487
- const flexItems = calculateFlexLayout({
488
- dimensions: { width: sidebarWidth + gap * 2, height: sidebarHeight + gap * 2 },
489
- count: itemsOnPage,
490
- aspectRatio,
491
- gap,
492
- itemAspectRatios: othersAspectRatios,
493
- preferHorizontal: isVertical
494
- // bottom/top sidebars should use horizontal layout
495
- });
496
- for (let i = 0; i < flexItems.length; i++) {
497
- const originalIndex = originalIndices[i];
498
- const flexItem = flexItems[i];
499
- positions[originalIndex] = {
500
- position: {
501
- top: flexItem.top + sidebarOffsetTop - gap,
502
- left: flexItem.left + sidebarOffsetLeft - gap
503
- },
504
- dimensions: { width: flexItem.width, height: flexItem.height }
505
- };
506
- }
507
- let sidebarIndex = 0;
508
- for (let i = 0; i < count; i++) {
509
- if (i === pinnedIndex)
510
- continue;
511
- const isInVisibleRange = sidebarIndex >= startOthersIndex && sidebarIndex < endOthersIndex;
512
- if (!isInVisibleRange) {
513
- positions[i] = {
514
- position: { top: -9999, left: -9999 },
515
- dimensions: { width: 0, height: 0 }
516
- };
517
- }
518
- sidebarIndex++;
519
- }
520
- } else {
299
+ {
300
+ const othersRatio = isPortrait ? 1 : ratio;
521
301
  let thumbCols = 1;
522
302
  let thumbRows = 1;
523
303
  let thumbWidth = 0;
@@ -527,20 +307,17 @@ function createSidebarGrid(options) {
527
307
  let bestScore = -1;
528
308
  for (let cols = 1; cols <= visibleOthers; cols++) {
529
309
  const rows = Math.ceil(visibleOthers / cols);
530
- const maxTileW = (sidebarWidth - (cols - 1) * gap) / cols;
531
- const maxTileH = (sidebarHeight - (rows - 1) * gap) / rows;
310
+ const maxTileW = (othersAreaWidth - (cols - 1) * gap) / cols;
311
+ const maxTileH = (othersAreaHeight - (rows - 1) * gap) / rows;
532
312
  let tileW = maxTileW;
533
- let tileH = tileW * ratio;
313
+ let tileH = tileW * othersRatio;
534
314
  if (tileH > maxTileH) {
535
315
  tileH = maxTileH;
536
- tileW = tileH / ratio;
316
+ tileW = tileH / othersRatio;
537
317
  }
538
318
  const area = tileW * tileH;
539
- const itemRatio = tileW / tileH;
540
- const idealRatio = 16 / 9;
541
- const ratioScore = 1 / (1 + Math.abs(Math.log(itemRatio / idealRatio)));
542
319
  const colsMultiplier = cols >= rows ? 1.5 : 0.5;
543
- const score = area * ratioScore * colsMultiplier;
320
+ const score = area * colsMultiplier;
544
321
  if (score > bestScore) {
545
322
  bestScore = score;
546
323
  thumbCols = cols;
@@ -554,9 +331,9 @@ function createSidebarGrid(options) {
554
331
  const targetRatio = 1 / ratio;
555
332
  for (let rows = 1; rows <= visibleOthers; rows++) {
556
333
  const cols = Math.ceil(visibleOthers / rows);
557
- const maxTileH = (sidebarHeight - (rows - 1) * gap) / rows;
334
+ const maxTileH = (othersAreaHeight - (rows - 1) * gap) / rows;
558
335
  const idealTileW = maxTileH * targetRatio;
559
- const maxTileW = (sidebarWidth - (cols - 1) * gap) / cols;
336
+ const maxTileW = (othersAreaWidth - (cols - 1) * gap) / cols;
560
337
  let tileW, tileH;
561
338
  if (idealTileW <= maxTileW) {
562
339
  tileW = idealTileW;
@@ -566,9 +343,8 @@ function createSidebarGrid(options) {
566
343
  tileH = tileW / targetRatio;
567
344
  }
568
345
  const area = tileW * tileH * visibleOthers;
569
- const score = area;
570
- if (score > bestScore) {
571
- bestScore = score;
346
+ if (area > bestScore) {
347
+ bestScore = area;
572
348
  thumbCols = cols;
573
349
  thumbRows = rows;
574
350
  thumbWidth = tileW;
@@ -582,19 +358,19 @@ function createSidebarGrid(options) {
582
358
  let gridStartLeft;
583
359
  let gridStartTop;
584
360
  if (isVertical) {
585
- gridStartLeft = gap + (sidebarWidth - totalGridWidth) / 2;
586
- gridStartTop = sidebarPosition === "top" ? gap + (sidebarHeight - totalGridHeight) / 2 : mainHeight + gap * 2 + (sidebarHeight - totalGridHeight) / 2;
361
+ gridStartLeft = gap + (othersAreaWidth - totalGridWidth) / 2;
362
+ gridStartTop = effectivePosition === "top" ? gap + (othersAreaHeight - totalGridHeight) / 2 : mainHeight + gap * 2 + (othersAreaHeight - totalGridHeight) / 2;
587
363
  } else {
588
- gridStartLeft = sidebarPosition === "left" ? gap + (sidebarWidth - totalGridWidth) / 2 : mainWidth + gap * 2 + (sidebarWidth - totalGridWidth) / 2;
589
- gridStartTop = gap + (sidebarHeight - totalGridHeight) / 2;
364
+ gridStartLeft = effectivePosition === "left" ? gap + (othersAreaWidth - totalGridWidth) / 2 : mainWidth + gap * 2 + (othersAreaWidth - totalGridWidth) / 2;
365
+ gridStartTop = gap + (othersAreaHeight - totalGridHeight) / 2;
590
366
  }
591
- let sidebarIndex = 0;
367
+ let othersIndex = 0;
592
368
  for (let i = 0; i < count; i++) {
593
369
  if (i === pinnedIndex)
594
370
  continue;
595
- const isInVisibleRange = sidebarIndex >= startOthersIndex && sidebarIndex < endOthersIndex;
371
+ const isInVisibleRange = othersIndex >= startOthersIndex && othersIndex < endOthersIndex;
596
372
  if (isInVisibleRange) {
597
- const pageRelativeIndex = sidebarIndex - startOthersIndex;
373
+ const pageRelativeIndex = othersIndex - startOthersIndex;
598
374
  const row = Math.floor(pageRelativeIndex / thumbCols);
599
375
  const col = pageRelativeIndex % thumbCols;
600
376
  const itemsInLastRow = itemsOnPage % thumbCols || thumbCols;
@@ -603,9 +379,9 @@ function createSidebarGrid(options) {
603
379
  if (row === lastRowIndex && itemsInLastRow < thumbCols) {
604
380
  const rowWidth = itemsInLastRow * thumbWidth + (itemsInLastRow - 1) * gap;
605
381
  if (isVertical) {
606
- rowLeft = gap + (sidebarWidth - rowWidth) / 2;
382
+ rowLeft = gap + (othersAreaWidth - rowWidth) / 2;
607
383
  } else {
608
- rowLeft = (sidebarPosition === "left" ? gap : mainWidth + gap * 2) + (sidebarWidth - rowWidth) / 2;
384
+ rowLeft = (effectivePosition === "left" ? gap : mainWidth + gap * 2) + (othersAreaWidth - rowWidth) / 2;
609
385
  }
610
386
  }
611
387
  positions[i] = {
@@ -621,7 +397,7 @@ function createSidebarGrid(options) {
621
397
  dimensions: { width: 0, height: 0 }
622
398
  };
623
399
  }
624
- sidebarIndex++;
400
+ othersIndex++;
625
401
  }
626
402
  }
627
403
  const pagination = {
@@ -653,7 +429,7 @@ function createSidebarGrid(options) {
653
429
  height: mainItemHeight,
654
430
  rows: isVertical ? 2 : 1,
655
431
  cols: isVertical ? 1 : 2,
656
- layoutMode: "sidebar",
432
+ layoutMode: "gallery",
657
433
  getPosition: (index) => positions[index]?.position ?? { top: 0, left: 0 },
658
434
  getItemDimensions,
659
435
  isMainItem: (index) => index === pinnedIndex,
@@ -670,14 +446,18 @@ function createSidebarGrid(options) {
670
446
  },
671
447
  hiddenCount,
672
448
  getLastVisibleOthersIndex,
673
- getItemContentDimensions: createGetItemContentDimensions(getItemDimensions, options.itemAspectRatios, aspectRatio)
449
+ getItemContentDimensions: createGetItemContentDimensions(
450
+ getItemDimensions,
451
+ options.itemAspectRatios,
452
+ aspectRatio
453
+ )
674
454
  };
675
455
  }
676
456
  function createSpotlightGrid(options) {
677
- const { dimensions, gap, aspectRatio, pinnedIndex = 0, flexLayout = false, itemAspectRatios } = options;
457
+ const { dimensions, gap, aspectRatio, pinnedIndex = 0, itemAspectRatios } = options;
678
458
  const { width: W, height: H } = dimensions;
679
- const itemRatio = flexLayout && itemAspectRatios?.[pinnedIndex];
680
- const shouldFill = itemRatio === "fill" || itemRatio === "auto";
459
+ const itemRatio = itemAspectRatios?.[pinnedIndex];
460
+ const shouldFill = itemRatio === "auto";
681
461
  let spotWidth;
682
462
  let spotHeight;
683
463
  if (shouldFill) {
@@ -711,7 +491,11 @@ function createSpotlightGrid(options) {
711
491
  isItemVisible: (index) => index === pinnedIndex,
712
492
  hiddenCount: 0,
713
493
  getLastVisibleOthersIndex: () => -1,
714
- getItemContentDimensions: createGetItemContentDimensions(getItemDimensions, options.itemAspectRatios, aspectRatio)
494
+ getItemContentDimensions: createGetItemContentDimensions(
495
+ getItemDimensions,
496
+ options.itemAspectRatios,
497
+ aspectRatio
498
+ )
715
499
  };
716
500
  }
717
501
  function createDefaultPagination(count) {
@@ -759,23 +543,143 @@ function createEmptyMeetGridResult(layoutMode) {
759
543
  getItemContentDimensions: () => ({ width: 0, height: 0, offsetTop: 0, offsetLeft: 0 })
760
544
  };
761
545
  }
546
+ function hasMixedRatios(itemAspectRatios, defaultRatio) {
547
+ if (!itemAspectRatios || itemAspectRatios.length === 0)
548
+ return false;
549
+ const ratios = /* @__PURE__ */ new Set();
550
+ for (const r of itemAspectRatios) {
551
+ ratios.add(r ?? defaultRatio);
552
+ }
553
+ return ratios.size > 1;
554
+ }
555
+ function createFlexibleGalleryGrid(options) {
556
+ const { dimensions, gap, aspectRatio, count, itemAspectRatios = [] } = options;
557
+ if (count === 0) {
558
+ return createEmptyMeetGridResult("gallery");
559
+ }
560
+ const { width: W, height: H } = dimensions;
561
+ const availW = W - gap * 2;
562
+ const availH = H - gap * 2;
563
+ const itemWHRatios = [];
564
+ for (let i = 0; i < count; i++) {
565
+ const ratioStr = itemAspectRatios[i] ?? aspectRatio;
566
+ const hw = getAspectRatio(ratioStr);
567
+ itemWHRatios.push(1 / hw);
568
+ }
569
+ function packRows(rowHeight) {
570
+ const result = [];
571
+ let currentRow = [];
572
+ let rowW = 0;
573
+ for (let i = 0; i < count; i++) {
574
+ const itemW = itemWHRatios[i] * rowHeight;
575
+ const gapW = currentRow.length > 0 ? gap : 0;
576
+ if (currentRow.length > 0 && rowW + gapW + itemW > availW) {
577
+ result.push(currentRow);
578
+ currentRow = [i];
579
+ rowW = itemW;
580
+ } else {
581
+ currentRow.push(i);
582
+ rowW += gapW + itemW;
583
+ }
584
+ }
585
+ if (currentRow.length > 0) {
586
+ result.push(currentRow);
587
+ }
588
+ return result;
589
+ }
590
+ let lo = 20;
591
+ let hi = availH;
592
+ let bestRows = [];
593
+ for (let iter = 0; iter < 50; iter++) {
594
+ const mid = (lo + hi) / 2;
595
+ const rows = packRows(mid);
596
+ const totalH = rows.length * mid + (rows.length - 1) * gap;
597
+ if (totalH > availH) {
598
+ hi = mid;
599
+ } else {
600
+ lo = mid;
601
+ }
602
+ }
603
+ const idealRowH = (lo + hi) / 2;
604
+ bestRows = packRows(idealRowH);
605
+ if (bestRows.length > 1 && bestRows[bestRows.length - 1].length === 1) {
606
+ const lastItem = bestRows[bestRows.length - 1][0];
607
+ const prevRow = bestRows[bestRows.length - 2];
608
+ if (prevRow.length <= 4) {
609
+ prevRow.push(lastItem);
610
+ bestRows.pop();
611
+ }
612
+ }
613
+ const posMap = /* @__PURE__ */ new Map();
614
+ const rowCount = bestRows.length;
615
+ const rowHeights = [];
616
+ for (const row of bestRows) {
617
+ const totalUnitW = row.reduce((s, idx) => s + itemWHRatios[idx], 0);
618
+ const netW = availW - (row.length - 1) * gap;
619
+ rowHeights.push(netW / totalUnitW);
620
+ }
621
+ const totalRowH = rowHeights.reduce((s, h) => s + h, 0) + (rowCount - 1) * gap;
622
+ const globalScale = availH / totalRowH;
623
+ let currentTop = gap;
624
+ for (let ri = 0; ri < rowCount; ri++) {
625
+ const row = bestRows[ri];
626
+ const finalRowH = rowHeights[ri] * globalScale;
627
+ const totalUnitW = row.reduce((s, idx) => s + itemWHRatios[idx], 0);
628
+ const netW = availW - (row.length - 1) * gap;
629
+ const itemWidths = row.map((idx) => itemWHRatios[idx] / totalUnitW * netW);
630
+ let currentLeft = gap;
631
+ for (let ci = 0; ci < row.length; ci++) {
632
+ posMap.set(row[ci], {
633
+ position: { top: currentTop, left: currentLeft },
634
+ dimensions: { width: itemWidths[ci], height: finalRowH }
635
+ });
636
+ currentLeft += itemWidths[ci] + gap;
637
+ }
638
+ currentTop += finalRowH + gap;
639
+ }
640
+ const getPosition = (index) => {
641
+ return posMap.get(index)?.position ?? { top: -9999, left: -9999 };
642
+ };
643
+ const getItemDimensions = (index) => {
644
+ return posMap.get(index)?.dimensions ?? { width: 0, height: 0 };
645
+ };
646
+ const pagination = createDefaultPagination(count);
647
+ return {
648
+ width: availW,
649
+ height: availH,
650
+ rows: rowCount,
651
+ cols: rowCount > 0 ? Math.max(...bestRows.map((r) => r.length)) : 0,
652
+ layoutMode: "gallery",
653
+ getPosition,
654
+ getItemDimensions,
655
+ isMainItem: () => false,
656
+ pagination,
657
+ isItemVisible: () => true,
658
+ hiddenCount: 0,
659
+ getLastVisibleOthersIndex: () => -1,
660
+ getItemContentDimensions: createGetItemContentDimensions(
661
+ getItemDimensions,
662
+ itemAspectRatios,
663
+ aspectRatio
664
+ )
665
+ };
666
+ }
762
667
  function createMeetGrid(options) {
763
- const { layoutMode = "gallery", count, flexLayout = false } = options;
668
+ const { layoutMode = "gallery", count } = options;
764
669
  if (count === 0) {
765
670
  return createEmptyMeetGridResult(layoutMode);
766
671
  }
767
672
  switch (layoutMode) {
768
673
  case "spotlight":
769
674
  return createSpotlightGrid(options);
770
- case "sidebar":
771
- return createSidebarGrid(options);
772
675
  case "gallery":
773
676
  default: {
774
- const { maxItemsPerPage, currentPage, pinnedIndex, sidebarPosition = "right", dimensions, maxVisible = 0 } = options;
677
+ const { maxItemsPerPage, currentPage, pinnedIndex, maxVisible = 0 } = options;
775
678
  if (pinnedIndex !== void 0 && pinnedIndex >= 0 && pinnedIndex < count) {
776
- const isPortrait = dimensions.width < dimensions.height;
777
- const effectiveSidebarPosition = isPortrait ? "bottom" : sidebarPosition;
778
- return createSidebarGrid({ ...options, sidebarPosition: effectiveSidebarPosition });
679
+ return createFlexiblePinGrid(options);
680
+ }
681
+ if (hasMixedRatios(options.itemAspectRatios, options.aspectRatio)) {
682
+ return createFlexibleGalleryGrid(options);
779
683
  }
780
684
  let visibleCount = count;
781
685
  let hiddenCount = 0;
@@ -801,57 +705,6 @@ function createMeetGrid(options) {
801
705
  endIndex
802
706
  };
803
707
  const effectiveCount = visibleCount;
804
- if (flexLayout && options.itemAspectRatios) {
805
- const pageItemRatios = options.itemAspectRatios.slice(startIndex, startIndex + effectiveCount);
806
- const flexItems = calculateFlexLayout({
807
- dimensions: options.dimensions,
808
- count: effectiveCount,
809
- aspectRatio: options.aspectRatio,
810
- gap: options.gap,
811
- itemAspectRatios: pageItemRatios
812
- });
813
- const getPosition2 = (index) => {
814
- const relativeIndex = index - startIndex;
815
- if (relativeIndex < 0 || relativeIndex >= effectiveCount) {
816
- return { top: -9999, left: -9999 };
817
- }
818
- const item = flexItems[relativeIndex];
819
- return item ? { top: item.top, left: item.left } : { top: -9999, left: -9999 };
820
- };
821
- const getItemDimensions2 = (index) => {
822
- const relativeIndex = index - startIndex;
823
- if (relativeIndex < 0 || relativeIndex >= effectiveCount) {
824
- return { width: 0, height: 0 };
825
- }
826
- const item = flexItems[relativeIndex];
827
- return item ? { width: item.width, height: item.height } : { width: 0, height: 0 };
828
- };
829
- const getItemContentDimensions = (index) => {
830
- const dims = getItemDimensions2(index);
831
- return {
832
- width: dims.width,
833
- height: dims.height,
834
- offsetTop: 0,
835
- offsetLeft: 0
836
- };
837
- };
838
- const lastVisibleIndex2 = endIndex - 1;
839
- return {
840
- width: flexItems[0]?.width ?? 0,
841
- height: flexItems[0]?.height ?? 0,
842
- rows: Math.max(...flexItems.map((i) => i.row)) + 1,
843
- cols: flexItems.filter((i) => i.row === 0).length,
844
- layoutMode: "gallery",
845
- getPosition: getPosition2,
846
- getItemDimensions: getItemDimensions2,
847
- isMainItem: () => false,
848
- pagination,
849
- isItemVisible: (index) => index >= startIndex && index < endIndex,
850
- hiddenCount,
851
- getLastVisibleOthersIndex: () => hiddenCount > 0 ? lastVisibleIndex2 : -1,
852
- getItemContentDimensions
853
- };
854
- }
855
708
  const grid = createGrid({ ...options, count: effectiveCount });
856
709
  const getPosition = (index) => {
857
710
  const relativeIndex = index - startIndex;
@@ -872,7 +725,11 @@ function createMeetGrid(options) {
872
725
  isItemVisible: (index) => index >= startIndex && index < endIndex,
873
726
  hiddenCount,
874
727
  getLastVisibleOthersIndex: () => hiddenCount > 0 ? lastVisibleIndex : -1,
875
- getItemContentDimensions: createGetItemContentDimensions(getItemDimensions, options.itemAspectRatios, options.aspectRatio)
728
+ getItemContentDimensions: createGetItemContentDimensions(
729
+ getItemDimensions,
730
+ options.itemAspectRatios,
731
+ options.aspectRatio
732
+ )
876
733
  };
877
734
  }
878
735
  }
@@ -895,8 +752,6 @@ function getSpringConfig(preset = "smooth") {
895
752
  }
896
753
 
897
754
  exports.calculateContentDimensions = calculateContentDimensions;
898
- exports.calculateFlexLayout = calculateFlexLayout;
899
- exports.calculateFlexStrip = calculateFlexStrip;
900
755
  exports.createGrid = createGrid;
901
756
  exports.createGridItemPositioner = createGridItemPositioner;
902
757
  exports.createMeetGrid = createMeetGrid;