@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.mjs CHANGED
@@ -19,7 +19,7 @@ function parseAspectRatio(ratio) {
19
19
  function calculateContentDimensions(cellDimensions, itemRatio, defaultRatio) {
20
20
  const { width: cellW, height: cellH } = cellDimensions;
21
21
  const effectiveRatio = itemRatio ?? (defaultRatio ? defaultRatio : void 0);
22
- if (!effectiveRatio || effectiveRatio === "fill" || effectiveRatio === "auto") {
22
+ if (!effectiveRatio || effectiveRatio === "auto") {
23
23
  return {
24
24
  width: cellW,
25
25
  height: cellH,
@@ -50,184 +50,7 @@ function createGetItemContentDimensions(getItemDimensions, itemAspectRatios, def
50
50
  return calculateContentDimensions(cellDimensions, effectiveRatio, defaultRatio);
51
51
  };
52
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
- }
225
- function getGridItemDimensions({
226
- count,
227
- dimensions,
228
- aspectRatio,
229
- gap
230
- }) {
53
+ function getGridItemDimensions({ count, dimensions, aspectRatio, gap }) {
231
54
  let { width: W, height: H } = dimensions;
232
55
  if (W === 0 || H === 0 || count === 0) {
233
56
  return { width: 0, height: 0, rows: 1, cols: 1 };
@@ -333,10 +156,19 @@ function createGrid({ aspectRatio, count, dimensions, gap }) {
333
156
  getPosition
334
157
  };
335
158
  }
336
- function createSidebarGrid(options) {
337
- const { dimensions, gap, aspectRatio, count, sidebarPosition = "right", sidebarRatio = 0.25, pinnedIndex = 0, maxVisible = 0, currentVisiblePage = 0, flexLayout = false, itemAspectRatios } = options;
159
+ function createFlexiblePinGrid(options) {
160
+ const {
161
+ dimensions,
162
+ gap,
163
+ aspectRatio,
164
+ count,
165
+ othersPosition = "right",
166
+ pinnedIndex = 0,
167
+ maxVisible = 0,
168
+ currentVisiblePage = 0
169
+ } = options;
338
170
  if (count === 0) {
339
- return createEmptyMeetGridResult("sidebar");
171
+ return createEmptyMeetGridResult("gallery");
340
172
  }
341
173
  if (count === 1) {
342
174
  const { width: W2, height: H2 } = dimensions;
@@ -349,7 +181,7 @@ function createSidebarGrid(options) {
349
181
  height: mainHeight2,
350
182
  rows: 1,
351
183
  cols: 1,
352
- layoutMode: "sidebar",
184
+ layoutMode: "gallery",
353
185
  getPosition: () => ({ top: gap, left: gap }),
354
186
  getItemDimensions: getItemDimensions2,
355
187
  isMainItem: () => true,
@@ -357,165 +189,113 @@ function createSidebarGrid(options) {
357
189
  isItemVisible: () => true,
358
190
  hiddenCount: 0,
359
191
  getLastVisibleOthersIndex: () => -1,
360
- getItemContentDimensions: createGetItemContentDimensions(getItemDimensions2, options.itemAspectRatios, aspectRatio)
192
+ getItemContentDimensions: createGetItemContentDimensions(
193
+ getItemDimensions2,
194
+ options.itemAspectRatios,
195
+ aspectRatio
196
+ )
361
197
  };
362
198
  }
363
199
  const { width: W, height: H } = dimensions;
200
+ const isPortrait = H > W;
201
+ const effectivePosition = isPortrait ? "bottom" : othersPosition;
202
+ const isVertical = effectivePosition === "bottom" || effectivePosition === "top";
364
203
  const ratio = getAspectRatio(aspectRatio);
365
- const isVertical = sidebarPosition === "bottom" || sidebarPosition === "top";
204
+ const totalOthers = count - 1;
205
+ const visibleOthers = maxVisible > 0 ? Math.min(maxVisible, totalOthers) : totalOthers;
206
+ const othersTotalPages = maxVisible > 0 ? Math.ceil(totalOthers / maxVisible) : 1;
207
+ const safeCurrentVisiblePage = Math.min(currentVisiblePage, Math.max(0, othersTotalPages - 1));
208
+ const startOthersIndex = safeCurrentVisiblePage * (maxVisible > 0 ? maxVisible : totalOthers);
209
+ const endOthersIndex = Math.min(startOthersIndex + visibleOthers, totalOthers);
210
+ const itemsOnPage = endOthersIndex - startOthersIndex;
211
+ const isPaginationMode = othersTotalPages > 1;
212
+ const isActivelyPaginating = isPaginationMode && currentVisiblePage > 0;
213
+ const hiddenCount = isActivelyPaginating ? 0 : totalOthers > itemsOnPage ? totalOthers - itemsOnPage + 1 : 0;
366
214
  let mainWidth;
367
215
  let mainHeight;
368
- let sidebarWidth;
369
- let sidebarHeight;
216
+ let othersAreaWidth;
217
+ let othersAreaHeight;
370
218
  if (isVertical) {
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;
219
+ const areaW = W - gap * 2;
220
+ const othersRatio = isPortrait ? 1 : ratio;
221
+ let bestOthersH = 0;
380
222
  let bestThumbArea = 0;
381
- const maxRows = Math.min(3, visibleOthersCalc || 1);
223
+ const maxRows = Math.min(3, visibleOthers || 1);
382
224
  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)
225
+ const cols = Math.ceil((visibleOthers || 1) / rows);
226
+ const thumbW = (areaW - (cols - 1) * gap) / cols;
227
+ const thumbH = thumbW * othersRatio;
228
+ const requiredH = rows * thumbH + (rows - 1) * gap + gap * 2;
229
+ const areaRatio = requiredH / H;
230
+ if (areaRatio > 0.5)
231
+ continue;
232
+ if (thumbH < 40)
389
233
  continue;
390
234
  const thumbArea = thumbW * thumbH;
391
235
  if (thumbArea > bestThumbArea) {
392
236
  bestThumbArea = thumbArea;
393
- bestSidebarH = requiredSidebarH;
237
+ bestOthersH = requiredH;
394
238
  }
395
239
  }
396
- if (bestSidebarH === 0) {
397
- bestSidebarH = H * (isPortraitMode ? 0.3 : 0.25);
240
+ if (bestOthersH === 0) {
241
+ bestOthersH = H * (isPortrait ? 0.25 : 0.2);
398
242
  }
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;
243
+ const minRatio = 0.12;
244
+ const maxRatio = 0.45;
245
+ if (bestOthersH / H < minRatio)
246
+ bestOthersH = H * minRatio;
247
+ else if (bestOthersH / H > maxRatio)
248
+ bestOthersH = H * maxRatio;
249
+ othersAreaHeight = bestOthersH;
250
+ othersAreaWidth = areaW;
251
+ mainHeight = H - othersAreaHeight - gap * 3;
252
+ mainWidth = areaW;
407
253
  } else {
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;
254
+ const areaH = H - gap * 2;
255
+ let bestOthersW = W * 0.2;
412
256
  let bestScore = 0;
413
- const maxCols = Math.min(3, visibleOthersCalc || 1);
257
+ const maxCols = Math.min(3, visibleOthers || 1);
414
258
  for (let cols = 1; cols <= maxCols; cols++) {
415
- const rows = Math.ceil((visibleOthersCalc || 1) / cols);
416
- const thumbH = (baseSidebarH - (rows - 1) * gap) / rows;
259
+ const rows = Math.ceil((visibleOthers || 1) / cols);
260
+ const thumbH = (areaH - (rows - 1) * gap) / rows;
417
261
  const thumbW = thumbH / ratio;
418
- const requiredSidebarW = cols * thumbW + (cols - 1) * gap + gap * 2;
419
- const sidebarRatioTest = requiredSidebarW / W;
262
+ const requiredW = cols * thumbW + (cols - 1) * gap + gap * 2;
263
+ const areaRatio = requiredW / W;
420
264
  const thumbArea = thumbW * thumbH;
421
- const mainAreaBonus = (1 - sidebarRatioTest) * 0.5;
265
+ const mainAreaBonus = (1 - areaRatio) * 0.5;
422
266
  const score = thumbArea * (1 + mainAreaBonus);
423
- if (sidebarRatioTest >= 0.12 && sidebarRatioTest <= 0.4 && score > bestScore) {
424
- bestSidebarW = requiredSidebarW;
267
+ if (areaRatio >= 0.1 && areaRatio <= 0.4 && score > bestScore) {
268
+ bestOthersW = requiredW;
425
269
  bestScore = score;
426
270
  }
427
271
  }
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;
272
+ if (bestOthersW / W < 0.1)
273
+ bestOthersW = W * 0.12;
274
+ else if (bestOthersW / W > 0.4)
275
+ bestOthersW = W * 0.35;
276
+ othersAreaWidth = bestOthersW;
277
+ othersAreaHeight = areaH;
278
+ mainWidth = W - othersAreaWidth - gap * 2;
279
+ mainHeight = areaH;
436
280
  }
437
281
  const mainItemWidth = mainWidth;
438
282
  const mainItemHeight = mainHeight;
439
- const totalOthers = count - 1;
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);
444
- const endOthersIndex = Math.min(startOthersIndex + visibleOthers, totalOthers);
445
- const itemsOnPage = endOthersIndex - startOthersIndex;
446
- const isPaginationMode = othersTotalPages > 1;
447
- const isActivelyPaginating = isPaginationMode && currentVisiblePage > 0;
448
- const hiddenCount = isActivelyPaginating ? 0 : totalOthers > itemsOnPage ? totalOthers - itemsOnPage + 1 : 0;
449
283
  const positions = [];
450
284
  let mainLeft;
451
285
  let mainTop;
452
286
  if (isVertical) {
453
287
  mainLeft = gap + (mainWidth - mainItemWidth) / 2;
454
- mainTop = sidebarPosition === "top" ? sidebarHeight + gap * 2 + (mainHeight - mainItemHeight) / 2 : gap + (mainHeight - mainItemHeight) / 2;
288
+ mainTop = effectivePosition === "top" ? othersAreaHeight + gap * 2 + (mainHeight - mainItemHeight) / 2 : gap + (mainHeight - mainItemHeight) / 2;
455
289
  } else {
456
- mainLeft = sidebarPosition === "left" ? sidebarWidth + gap * 2 + (mainWidth - mainItemWidth) / 2 : gap + (mainWidth - mainItemWidth) / 2;
290
+ mainLeft = effectivePosition === "left" ? othersAreaWidth + gap * 2 + (mainWidth - mainItemWidth) / 2 : gap + (mainWidth - mainItemWidth) / 2;
457
291
  mainTop = gap + (mainHeight - mainItemHeight) / 2;
458
292
  }
459
293
  positions[pinnedIndex] = {
460
294
  position: { top: mainTop, left: mainLeft },
461
295
  dimensions: { width: mainItemWidth, height: mainItemHeight }
462
296
  };
463
- let sidebarOffsetLeft;
464
- let sidebarOffsetTop;
465
- if (isVertical) {
466
- sidebarOffsetLeft = gap;
467
- sidebarOffsetTop = sidebarPosition === "top" ? gap : mainHeight + gap * 2;
468
- } else {
469
- sidebarOffsetLeft = sidebarPosition === "left" ? gap : mainWidth + gap * 2;
470
- sidebarOffsetTop = gap;
471
- }
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);
482
- }
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] = {
498
- position: {
499
- top: flexItem.top + sidebarOffsetTop - gap,
500
- left: flexItem.left + sidebarOffsetLeft - gap
501
- },
502
- dimensions: { width: flexItem.width, height: flexItem.height }
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 {
297
+ {
298
+ const othersRatio = isPortrait ? 1 : ratio;
519
299
  let thumbCols = 1;
520
300
  let thumbRows = 1;
521
301
  let thumbWidth = 0;
@@ -525,20 +305,17 @@ function createSidebarGrid(options) {
525
305
  let bestScore = -1;
526
306
  for (let cols = 1; cols <= visibleOthers; cols++) {
527
307
  const rows = Math.ceil(visibleOthers / cols);
528
- const maxTileW = (sidebarWidth - (cols - 1) * gap) / cols;
529
- const maxTileH = (sidebarHeight - (rows - 1) * gap) / rows;
308
+ const maxTileW = (othersAreaWidth - (cols - 1) * gap) / cols;
309
+ const maxTileH = (othersAreaHeight - (rows - 1) * gap) / rows;
530
310
  let tileW = maxTileW;
531
- let tileH = tileW * ratio;
311
+ let tileH = tileW * othersRatio;
532
312
  if (tileH > maxTileH) {
533
313
  tileH = maxTileH;
534
- tileW = tileH / ratio;
314
+ tileW = tileH / othersRatio;
535
315
  }
536
316
  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
317
  const colsMultiplier = cols >= rows ? 1.5 : 0.5;
541
- const score = area * ratioScore * colsMultiplier;
318
+ const score = area * colsMultiplier;
542
319
  if (score > bestScore) {
543
320
  bestScore = score;
544
321
  thumbCols = cols;
@@ -552,9 +329,9 @@ function createSidebarGrid(options) {
552
329
  const targetRatio = 1 / ratio;
553
330
  for (let rows = 1; rows <= visibleOthers; rows++) {
554
331
  const cols = Math.ceil(visibleOthers / rows);
555
- const maxTileH = (sidebarHeight - (rows - 1) * gap) / rows;
332
+ const maxTileH = (othersAreaHeight - (rows - 1) * gap) / rows;
556
333
  const idealTileW = maxTileH * targetRatio;
557
- const maxTileW = (sidebarWidth - (cols - 1) * gap) / cols;
334
+ const maxTileW = (othersAreaWidth - (cols - 1) * gap) / cols;
558
335
  let tileW, tileH;
559
336
  if (idealTileW <= maxTileW) {
560
337
  tileW = idealTileW;
@@ -564,9 +341,8 @@ function createSidebarGrid(options) {
564
341
  tileH = tileW / targetRatio;
565
342
  }
566
343
  const area = tileW * tileH * visibleOthers;
567
- const score = area;
568
- if (score > bestScore) {
569
- bestScore = score;
344
+ if (area > bestScore) {
345
+ bestScore = area;
570
346
  thumbCols = cols;
571
347
  thumbRows = rows;
572
348
  thumbWidth = tileW;
@@ -580,19 +356,19 @@ function createSidebarGrid(options) {
580
356
  let gridStartLeft;
581
357
  let gridStartTop;
582
358
  if (isVertical) {
583
- gridStartLeft = gap + (sidebarWidth - totalGridWidth) / 2;
584
- gridStartTop = sidebarPosition === "top" ? gap + (sidebarHeight - totalGridHeight) / 2 : mainHeight + gap * 2 + (sidebarHeight - totalGridHeight) / 2;
359
+ gridStartLeft = gap + (othersAreaWidth - totalGridWidth) / 2;
360
+ gridStartTop = effectivePosition === "top" ? gap + (othersAreaHeight - totalGridHeight) / 2 : mainHeight + gap * 2 + (othersAreaHeight - totalGridHeight) / 2;
585
361
  } else {
586
- gridStartLeft = sidebarPosition === "left" ? gap + (sidebarWidth - totalGridWidth) / 2 : mainWidth + gap * 2 + (sidebarWidth - totalGridWidth) / 2;
587
- gridStartTop = gap + (sidebarHeight - totalGridHeight) / 2;
362
+ gridStartLeft = effectivePosition === "left" ? gap + (othersAreaWidth - totalGridWidth) / 2 : mainWidth + gap * 2 + (othersAreaWidth - totalGridWidth) / 2;
363
+ gridStartTop = gap + (othersAreaHeight - totalGridHeight) / 2;
588
364
  }
589
- let sidebarIndex = 0;
365
+ let othersIndex = 0;
590
366
  for (let i = 0; i < count; i++) {
591
367
  if (i === pinnedIndex)
592
368
  continue;
593
- const isInVisibleRange = sidebarIndex >= startOthersIndex && sidebarIndex < endOthersIndex;
369
+ const isInVisibleRange = othersIndex >= startOthersIndex && othersIndex < endOthersIndex;
594
370
  if (isInVisibleRange) {
595
- const pageRelativeIndex = sidebarIndex - startOthersIndex;
371
+ const pageRelativeIndex = othersIndex - startOthersIndex;
596
372
  const row = Math.floor(pageRelativeIndex / thumbCols);
597
373
  const col = pageRelativeIndex % thumbCols;
598
374
  const itemsInLastRow = itemsOnPage % thumbCols || thumbCols;
@@ -601,9 +377,9 @@ function createSidebarGrid(options) {
601
377
  if (row === lastRowIndex && itemsInLastRow < thumbCols) {
602
378
  const rowWidth = itemsInLastRow * thumbWidth + (itemsInLastRow - 1) * gap;
603
379
  if (isVertical) {
604
- rowLeft = gap + (sidebarWidth - rowWidth) / 2;
380
+ rowLeft = gap + (othersAreaWidth - rowWidth) / 2;
605
381
  } else {
606
- rowLeft = (sidebarPosition === "left" ? gap : mainWidth + gap * 2) + (sidebarWidth - rowWidth) / 2;
382
+ rowLeft = (effectivePosition === "left" ? gap : mainWidth + gap * 2) + (othersAreaWidth - rowWidth) / 2;
607
383
  }
608
384
  }
609
385
  positions[i] = {
@@ -619,7 +395,7 @@ function createSidebarGrid(options) {
619
395
  dimensions: { width: 0, height: 0 }
620
396
  };
621
397
  }
622
- sidebarIndex++;
398
+ othersIndex++;
623
399
  }
624
400
  }
625
401
  const pagination = {
@@ -651,7 +427,7 @@ function createSidebarGrid(options) {
651
427
  height: mainItemHeight,
652
428
  rows: isVertical ? 2 : 1,
653
429
  cols: isVertical ? 1 : 2,
654
- layoutMode: "sidebar",
430
+ layoutMode: "gallery",
655
431
  getPosition: (index) => positions[index]?.position ?? { top: 0, left: 0 },
656
432
  getItemDimensions,
657
433
  isMainItem: (index) => index === pinnedIndex,
@@ -668,14 +444,18 @@ function createSidebarGrid(options) {
668
444
  },
669
445
  hiddenCount,
670
446
  getLastVisibleOthersIndex,
671
- getItemContentDimensions: createGetItemContentDimensions(getItemDimensions, options.itemAspectRatios, aspectRatio)
447
+ getItemContentDimensions: createGetItemContentDimensions(
448
+ getItemDimensions,
449
+ options.itemAspectRatios,
450
+ aspectRatio
451
+ )
672
452
  };
673
453
  }
674
454
  function createSpotlightGrid(options) {
675
- const { dimensions, gap, aspectRatio, pinnedIndex = 0, flexLayout = false, itemAspectRatios } = options;
455
+ const { dimensions, gap, aspectRatio, pinnedIndex = 0, itemAspectRatios } = options;
676
456
  const { width: W, height: H } = dimensions;
677
- const itemRatio = flexLayout && itemAspectRatios?.[pinnedIndex];
678
- const shouldFill = itemRatio === "fill" || itemRatio === "auto";
457
+ const itemRatio = itemAspectRatios?.[pinnedIndex];
458
+ const shouldFill = itemRatio === "auto";
679
459
  let spotWidth;
680
460
  let spotHeight;
681
461
  if (shouldFill) {
@@ -709,7 +489,11 @@ function createSpotlightGrid(options) {
709
489
  isItemVisible: (index) => index === pinnedIndex,
710
490
  hiddenCount: 0,
711
491
  getLastVisibleOthersIndex: () => -1,
712
- getItemContentDimensions: createGetItemContentDimensions(getItemDimensions, options.itemAspectRatios, aspectRatio)
492
+ getItemContentDimensions: createGetItemContentDimensions(
493
+ getItemDimensions,
494
+ options.itemAspectRatios,
495
+ aspectRatio
496
+ )
713
497
  };
714
498
  }
715
499
  function createDefaultPagination(count) {
@@ -757,23 +541,143 @@ function createEmptyMeetGridResult(layoutMode) {
757
541
  getItemContentDimensions: () => ({ width: 0, height: 0, offsetTop: 0, offsetLeft: 0 })
758
542
  };
759
543
  }
544
+ function hasMixedRatios(itemAspectRatios, defaultRatio) {
545
+ if (!itemAspectRatios || itemAspectRatios.length === 0)
546
+ return false;
547
+ const ratios = /* @__PURE__ */ new Set();
548
+ for (const r of itemAspectRatios) {
549
+ ratios.add(r ?? defaultRatio);
550
+ }
551
+ return ratios.size > 1;
552
+ }
553
+ function createFlexibleGalleryGrid(options) {
554
+ const { dimensions, gap, aspectRatio, count, itemAspectRatios = [] } = options;
555
+ if (count === 0) {
556
+ return createEmptyMeetGridResult("gallery");
557
+ }
558
+ const { width: W, height: H } = dimensions;
559
+ const availW = W - gap * 2;
560
+ const availH = H - gap * 2;
561
+ const itemWHRatios = [];
562
+ for (let i = 0; i < count; i++) {
563
+ const ratioStr = itemAspectRatios[i] ?? aspectRatio;
564
+ const hw = getAspectRatio(ratioStr);
565
+ itemWHRatios.push(1 / hw);
566
+ }
567
+ function packRows(rowHeight) {
568
+ const result = [];
569
+ let currentRow = [];
570
+ let rowW = 0;
571
+ for (let i = 0; i < count; i++) {
572
+ const itemW = itemWHRatios[i] * rowHeight;
573
+ const gapW = currentRow.length > 0 ? gap : 0;
574
+ if (currentRow.length > 0 && rowW + gapW + itemW > availW) {
575
+ result.push(currentRow);
576
+ currentRow = [i];
577
+ rowW = itemW;
578
+ } else {
579
+ currentRow.push(i);
580
+ rowW += gapW + itemW;
581
+ }
582
+ }
583
+ if (currentRow.length > 0) {
584
+ result.push(currentRow);
585
+ }
586
+ return result;
587
+ }
588
+ let lo = 20;
589
+ let hi = availH;
590
+ let bestRows = [];
591
+ for (let iter = 0; iter < 50; iter++) {
592
+ const mid = (lo + hi) / 2;
593
+ const rows = packRows(mid);
594
+ const totalH = rows.length * mid + (rows.length - 1) * gap;
595
+ if (totalH > availH) {
596
+ hi = mid;
597
+ } else {
598
+ lo = mid;
599
+ }
600
+ }
601
+ const idealRowH = (lo + hi) / 2;
602
+ bestRows = packRows(idealRowH);
603
+ if (bestRows.length > 1 && bestRows[bestRows.length - 1].length === 1) {
604
+ const lastItem = bestRows[bestRows.length - 1][0];
605
+ const prevRow = bestRows[bestRows.length - 2];
606
+ if (prevRow.length <= 4) {
607
+ prevRow.push(lastItem);
608
+ bestRows.pop();
609
+ }
610
+ }
611
+ const posMap = /* @__PURE__ */ new Map();
612
+ const rowCount = bestRows.length;
613
+ const rowHeights = [];
614
+ for (const row of bestRows) {
615
+ const totalUnitW = row.reduce((s, idx) => s + itemWHRatios[idx], 0);
616
+ const netW = availW - (row.length - 1) * gap;
617
+ rowHeights.push(netW / totalUnitW);
618
+ }
619
+ const totalRowH = rowHeights.reduce((s, h) => s + h, 0) + (rowCount - 1) * gap;
620
+ const globalScale = availH / totalRowH;
621
+ let currentTop = gap;
622
+ for (let ri = 0; ri < rowCount; ri++) {
623
+ const row = bestRows[ri];
624
+ const finalRowH = rowHeights[ri] * globalScale;
625
+ const totalUnitW = row.reduce((s, idx) => s + itemWHRatios[idx], 0);
626
+ const netW = availW - (row.length - 1) * gap;
627
+ const itemWidths = row.map((idx) => itemWHRatios[idx] / totalUnitW * netW);
628
+ let currentLeft = gap;
629
+ for (let ci = 0; ci < row.length; ci++) {
630
+ posMap.set(row[ci], {
631
+ position: { top: currentTop, left: currentLeft },
632
+ dimensions: { width: itemWidths[ci], height: finalRowH }
633
+ });
634
+ currentLeft += itemWidths[ci] + gap;
635
+ }
636
+ currentTop += finalRowH + gap;
637
+ }
638
+ const getPosition = (index) => {
639
+ return posMap.get(index)?.position ?? { top: -9999, left: -9999 };
640
+ };
641
+ const getItemDimensions = (index) => {
642
+ return posMap.get(index)?.dimensions ?? { width: 0, height: 0 };
643
+ };
644
+ const pagination = createDefaultPagination(count);
645
+ return {
646
+ width: availW,
647
+ height: availH,
648
+ rows: rowCount,
649
+ cols: rowCount > 0 ? Math.max(...bestRows.map((r) => r.length)) : 0,
650
+ layoutMode: "gallery",
651
+ getPosition,
652
+ getItemDimensions,
653
+ isMainItem: () => false,
654
+ pagination,
655
+ isItemVisible: () => true,
656
+ hiddenCount: 0,
657
+ getLastVisibleOthersIndex: () => -1,
658
+ getItemContentDimensions: createGetItemContentDimensions(
659
+ getItemDimensions,
660
+ itemAspectRatios,
661
+ aspectRatio
662
+ )
663
+ };
664
+ }
760
665
  function createMeetGrid(options) {
761
- const { layoutMode = "gallery", count, flexLayout = false } = options;
666
+ const { layoutMode = "gallery", count } = options;
762
667
  if (count === 0) {
763
668
  return createEmptyMeetGridResult(layoutMode);
764
669
  }
765
670
  switch (layoutMode) {
766
671
  case "spotlight":
767
672
  return createSpotlightGrid(options);
768
- case "sidebar":
769
- return createSidebarGrid(options);
770
673
  case "gallery":
771
674
  default: {
772
- const { maxItemsPerPage, currentPage, pinnedIndex, sidebarPosition = "right", dimensions, maxVisible = 0 } = options;
675
+ const { maxItemsPerPage, currentPage, pinnedIndex, maxVisible = 0 } = options;
773
676
  if (pinnedIndex !== void 0 && pinnedIndex >= 0 && pinnedIndex < count) {
774
- const isPortrait = dimensions.width < dimensions.height;
775
- const effectiveSidebarPosition = isPortrait ? "bottom" : sidebarPosition;
776
- return createSidebarGrid({ ...options, sidebarPosition: effectiveSidebarPosition });
677
+ return createFlexiblePinGrid(options);
678
+ }
679
+ if (hasMixedRatios(options.itemAspectRatios, options.aspectRatio)) {
680
+ return createFlexibleGalleryGrid(options);
777
681
  }
778
682
  let visibleCount = count;
779
683
  let hiddenCount = 0;
@@ -799,57 +703,6 @@ function createMeetGrid(options) {
799
703
  endIndex
800
704
  };
801
705
  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
- };
852
- }
853
706
  const grid = createGrid({ ...options, count: effectiveCount });
854
707
  const getPosition = (index) => {
855
708
  const relativeIndex = index - startIndex;
@@ -870,7 +723,11 @@ function createMeetGrid(options) {
870
723
  isItemVisible: (index) => index >= startIndex && index < endIndex,
871
724
  hiddenCount,
872
725
  getLastVisibleOthersIndex: () => hiddenCount > 0 ? lastVisibleIndex : -1,
873
- getItemContentDimensions: createGetItemContentDimensions(getItemDimensions, options.itemAspectRatios, options.aspectRatio)
726
+ getItemContentDimensions: createGetItemContentDimensions(
727
+ getItemDimensions,
728
+ options.itemAspectRatios,
729
+ options.aspectRatio
730
+ )
874
731
  };
875
732
  }
876
733
  }
@@ -892,4 +749,4 @@ function getSpringConfig(preset = "smooth") {
892
749
  };
893
750
  }
894
751
 
895
- export { calculateContentDimensions, calculateFlexLayout, calculateFlexStrip, createGrid, createGridItemPositioner, createMeetGrid, getAspectRatio, getGridItemDimensions, getSpringConfig, parseAspectRatio, springPresets };
752
+ export { calculateContentDimensions, createGrid, createGridItemPositioner, createMeetGrid, getAspectRatio, getGridItemDimensions, getSpringConfig, parseAspectRatio, springPresets };