@thangdevalone/meeting-grid-layout-core 1.4.1
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/LICENSE +44 -0
- package/README.md +68 -0
- package/dist/index.cjs +914 -0
- package/dist/index.d.cts +351 -0
- package/dist/index.d.mts +351 -0
- package/dist/index.d.ts +351 -0
- package/dist/index.mjs +902 -0
- package/package.json +50 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,902 @@
|
|
|
1
|
+
const DEFAULT_FLOAT_BREAKPOINTS = [
|
|
2
|
+
{ minWidth: 0, width: 100, height: 135 },
|
|
3
|
+
{ minWidth: 480, width: 130, height: 175 },
|
|
4
|
+
{ minWidth: 768, width: 160, height: 215 },
|
|
5
|
+
{ minWidth: 1024, width: 180, height: 240 },
|
|
6
|
+
{ minWidth: 1440, width: 220, height: 295 }
|
|
7
|
+
];
|
|
8
|
+
function resolveFloatSize(containerWidth, breakpoints) {
|
|
9
|
+
const sorted = [...breakpoints].sort((a, b) => b.minWidth - a.minWidth);
|
|
10
|
+
const match = sorted.find((bp) => containerWidth >= bp.minWidth);
|
|
11
|
+
if (match) {
|
|
12
|
+
return { width: match.width, height: match.height };
|
|
13
|
+
}
|
|
14
|
+
const smallest = sorted[sorted.length - 1];
|
|
15
|
+
return smallest ? { width: smallest.width, height: smallest.height } : { width: 120, height: 160 };
|
|
16
|
+
}
|
|
17
|
+
function getAspectRatio(ratio) {
|
|
18
|
+
const [width, height] = ratio.split(":");
|
|
19
|
+
if (!width || !height) {
|
|
20
|
+
throw new Error(
|
|
21
|
+
'meet-layout-grid: Invalid aspect ratio provided, expected format is "width:height".'
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
return Number.parseInt(height) / Number.parseInt(width);
|
|
25
|
+
}
|
|
26
|
+
function parseAspectRatio(ratio) {
|
|
27
|
+
const [width, height] = ratio.split(":").map(Number);
|
|
28
|
+
if (!width || !height || isNaN(width) || isNaN(height)) {
|
|
29
|
+
throw new Error(
|
|
30
|
+
'meet-layout-grid: Invalid aspect ratio provided, expected format is "width:height".'
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
return { widthRatio: width, heightRatio: height };
|
|
34
|
+
}
|
|
35
|
+
function calculateContentDimensions(cellDimensions, itemRatio, defaultRatio) {
|
|
36
|
+
const { width: cellW, height: cellH } = cellDimensions;
|
|
37
|
+
const effectiveRatio = itemRatio ?? (defaultRatio ? defaultRatio : void 0);
|
|
38
|
+
if (!effectiveRatio || effectiveRatio === "auto") {
|
|
39
|
+
return {
|
|
40
|
+
width: cellW,
|
|
41
|
+
height: cellH,
|
|
42
|
+
offsetTop: 0,
|
|
43
|
+
offsetLeft: 0
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
const ratio = getAspectRatio(effectiveRatio);
|
|
47
|
+
let contentW = cellW;
|
|
48
|
+
let contentH = contentW * ratio;
|
|
49
|
+
if (contentH > cellH) {
|
|
50
|
+
contentH = cellH;
|
|
51
|
+
contentW = contentH / ratio;
|
|
52
|
+
}
|
|
53
|
+
const offsetTop = (cellH - contentH) / 2;
|
|
54
|
+
const offsetLeft = (cellW - contentW) / 2;
|
|
55
|
+
return {
|
|
56
|
+
width: contentW,
|
|
57
|
+
height: contentH,
|
|
58
|
+
offsetTop,
|
|
59
|
+
offsetLeft
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function createGetItemContentDimensions(getItemDimensions, itemAspectRatios, defaultRatio) {
|
|
63
|
+
return (index, itemRatio) => {
|
|
64
|
+
const cellDimensions = getItemDimensions(index);
|
|
65
|
+
const effectiveRatio = itemRatio ?? itemAspectRatios?.[index] ?? defaultRatio;
|
|
66
|
+
return calculateContentDimensions(cellDimensions, effectiveRatio, defaultRatio);
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function getGridItemDimensions({ count, dimensions, aspectRatio, gap }) {
|
|
70
|
+
let { width: W, height: H } = dimensions;
|
|
71
|
+
if (W === 0 || H === 0 || count === 0) {
|
|
72
|
+
return { width: 0, height: 0, rows: 1, cols: 1 };
|
|
73
|
+
}
|
|
74
|
+
W -= gap * 2;
|
|
75
|
+
H -= gap * 2;
|
|
76
|
+
const s = gap;
|
|
77
|
+
const N = count;
|
|
78
|
+
const r = getAspectRatio(aspectRatio);
|
|
79
|
+
let w = 0;
|
|
80
|
+
let h = 0;
|
|
81
|
+
let a = 1;
|
|
82
|
+
let b = 1;
|
|
83
|
+
const minCols = 1;
|
|
84
|
+
const widths = [];
|
|
85
|
+
for (let n = 1; n <= N; n++) {
|
|
86
|
+
widths.push((W - s * (n - 1)) / n, (H - s * (n - 1)) / (n * r));
|
|
87
|
+
}
|
|
88
|
+
widths.sort((a2, b2) => b2 - a2);
|
|
89
|
+
for (const width of widths) {
|
|
90
|
+
w = width;
|
|
91
|
+
h = w * r;
|
|
92
|
+
a = Math.floor((W + s) / (w + s));
|
|
93
|
+
b = Math.floor((H + s) / (h + s));
|
|
94
|
+
if (a < minCols && N >= minCols) {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (a * b >= N) {
|
|
98
|
+
a = Math.max(minCols, Math.ceil(N / b));
|
|
99
|
+
b = Math.ceil(N / a);
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (a < minCols && N >= minCols) {
|
|
104
|
+
a = minCols;
|
|
105
|
+
b = Math.ceil(N / a);
|
|
106
|
+
w = (W - s * (a - 1)) / a;
|
|
107
|
+
h = w * r;
|
|
108
|
+
const requiredHeight = b * h + (b - 1) * s;
|
|
109
|
+
if (requiredHeight > H) {
|
|
110
|
+
const scale = H / requiredHeight;
|
|
111
|
+
h = h * scale;
|
|
112
|
+
w = h / r;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return { width: w, height: h, rows: b, cols: a };
|
|
116
|
+
}
|
|
117
|
+
function createGridItemPositioner({
|
|
118
|
+
parentDimensions,
|
|
119
|
+
dimensions,
|
|
120
|
+
rows,
|
|
121
|
+
cols,
|
|
122
|
+
count,
|
|
123
|
+
gap
|
|
124
|
+
}) {
|
|
125
|
+
const { width: W, height: H } = parentDimensions;
|
|
126
|
+
const { width: w, height: h } = dimensions;
|
|
127
|
+
const firstTop = (H - (h * rows + (rows - 1) * gap)) / 2;
|
|
128
|
+
let firstLeft = (W - (w * cols + (cols - 1) * gap)) / 2;
|
|
129
|
+
const topAdd = h + gap;
|
|
130
|
+
const leftAdd = w + gap;
|
|
131
|
+
const incompleteRowCols = count % cols;
|
|
132
|
+
const lastRowStartIndex = incompleteRowCols > 0 ? count - incompleteRowCols : count - cols;
|
|
133
|
+
function getPosition(index) {
|
|
134
|
+
const row = Math.floor(index / cols);
|
|
135
|
+
const col = index % cols;
|
|
136
|
+
const isInLastRow = incompleteRowCols > 0 && index >= lastRowStartIndex;
|
|
137
|
+
let leftOffset = firstLeft;
|
|
138
|
+
if (isInLastRow) {
|
|
139
|
+
const lastRowItemCount = incompleteRowCols;
|
|
140
|
+
const colInLastRow = index - lastRowStartIndex;
|
|
141
|
+
leftOffset = (W - (w * lastRowItemCount + (lastRowItemCount - 1) * gap)) / 2;
|
|
142
|
+
const top2 = firstTop + row * topAdd;
|
|
143
|
+
const left2 = leftOffset + colInLastRow * leftAdd;
|
|
144
|
+
return { top: top2, left: left2 };
|
|
145
|
+
}
|
|
146
|
+
const top = firstTop + row * topAdd;
|
|
147
|
+
const left = leftOffset + col * leftAdd;
|
|
148
|
+
return { top, left };
|
|
149
|
+
}
|
|
150
|
+
return getPosition;
|
|
151
|
+
}
|
|
152
|
+
function createGrid({ aspectRatio, count, dimensions, gap }) {
|
|
153
|
+
const { width, height, rows, cols } = getGridItemDimensions({
|
|
154
|
+
aspectRatio,
|
|
155
|
+
count,
|
|
156
|
+
dimensions,
|
|
157
|
+
gap
|
|
158
|
+
});
|
|
159
|
+
const getPosition = createGridItemPositioner({
|
|
160
|
+
parentDimensions: dimensions,
|
|
161
|
+
dimensions: { width, height },
|
|
162
|
+
rows,
|
|
163
|
+
cols,
|
|
164
|
+
count,
|
|
165
|
+
gap
|
|
166
|
+
});
|
|
167
|
+
return {
|
|
168
|
+
width,
|
|
169
|
+
height,
|
|
170
|
+
rows,
|
|
171
|
+
cols,
|
|
172
|
+
getPosition
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
function createFlexiblePinGrid(options) {
|
|
176
|
+
const {
|
|
177
|
+
dimensions,
|
|
178
|
+
gap,
|
|
179
|
+
aspectRatio,
|
|
180
|
+
count,
|
|
181
|
+
othersPosition = "right",
|
|
182
|
+
pinnedIndex = 0,
|
|
183
|
+
maxVisible = 0,
|
|
184
|
+
currentVisiblePage = 0
|
|
185
|
+
} = options;
|
|
186
|
+
if (count === 0) {
|
|
187
|
+
return createEmptyMeetGridResult("gallery");
|
|
188
|
+
}
|
|
189
|
+
if (count === 1) {
|
|
190
|
+
const { width: W2, height: H2 } = dimensions;
|
|
191
|
+
const mainWidth2 = W2 - gap * 2;
|
|
192
|
+
const mainHeight2 = H2 - gap * 2;
|
|
193
|
+
const getItemDimensions2 = () => ({ width: mainWidth2, height: mainHeight2 });
|
|
194
|
+
const pagination2 = createDefaultPagination(1);
|
|
195
|
+
return {
|
|
196
|
+
width: mainWidth2,
|
|
197
|
+
height: mainHeight2,
|
|
198
|
+
rows: 1,
|
|
199
|
+
cols: 1,
|
|
200
|
+
layoutMode: "gallery",
|
|
201
|
+
getPosition: () => ({ top: gap, left: gap }),
|
|
202
|
+
getItemDimensions: getItemDimensions2,
|
|
203
|
+
isMainItem: () => true,
|
|
204
|
+
pagination: pagination2,
|
|
205
|
+
isItemVisible: () => true,
|
|
206
|
+
hiddenCount: 0,
|
|
207
|
+
getLastVisibleOthersIndex: () => -1,
|
|
208
|
+
getItemContentDimensions: createGetItemContentDimensions(
|
|
209
|
+
getItemDimensions2,
|
|
210
|
+
options.itemAspectRatios,
|
|
211
|
+
aspectRatio
|
|
212
|
+
)
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
const { width: W, height: H } = dimensions;
|
|
216
|
+
const isPortrait = H > W;
|
|
217
|
+
const effectivePosition = isPortrait ? "bottom" : othersPosition;
|
|
218
|
+
const isVertical = effectivePosition === "bottom" || effectivePosition === "top";
|
|
219
|
+
const ratio = getAspectRatio(aspectRatio);
|
|
220
|
+
const totalOthers = count - 1;
|
|
221
|
+
const visibleOthers = maxVisible > 0 ? Math.min(maxVisible, totalOthers) : totalOthers;
|
|
222
|
+
const othersTotalPages = maxVisible > 0 ? Math.ceil(totalOthers / maxVisible) : 1;
|
|
223
|
+
const safeCurrentVisiblePage = Math.min(currentVisiblePage, Math.max(0, othersTotalPages - 1));
|
|
224
|
+
const startOthersIndex = safeCurrentVisiblePage * (maxVisible > 0 ? maxVisible : totalOthers);
|
|
225
|
+
const endOthersIndex = Math.min(startOthersIndex + visibleOthers, totalOthers);
|
|
226
|
+
const itemsOnPage = endOthersIndex - startOthersIndex;
|
|
227
|
+
const isPaginationMode = othersTotalPages > 1;
|
|
228
|
+
const isActivelyPaginating = isPaginationMode && currentVisiblePage > 0;
|
|
229
|
+
const hiddenCount = isActivelyPaginating ? 0 : totalOthers > itemsOnPage ? totalOthers - itemsOnPage + 1 : 0;
|
|
230
|
+
let mainWidth;
|
|
231
|
+
let mainHeight;
|
|
232
|
+
let othersAreaWidth;
|
|
233
|
+
let othersAreaHeight;
|
|
234
|
+
if (isVertical) {
|
|
235
|
+
const areaW = W - gap * 2;
|
|
236
|
+
const isMobilePin = W < 500;
|
|
237
|
+
if (isMobilePin && isPortrait && visibleOthers > 0) {
|
|
238
|
+
const mobileCols = Math.min(visibleOthers, 2);
|
|
239
|
+
const mobileRows = Math.ceil(visibleOthers / mobileCols);
|
|
240
|
+
let thumbW = (areaW - (mobileCols - 1) * gap) / mobileCols;
|
|
241
|
+
let thumbH = thumbW * ratio;
|
|
242
|
+
let neededH = mobileRows * thumbH + (mobileRows - 1) * gap + gap;
|
|
243
|
+
const maxOthersH = H * 0.7;
|
|
244
|
+
if (neededH > maxOthersH) {
|
|
245
|
+
neededH = maxOthersH;
|
|
246
|
+
const availThumbH = (neededH - (mobileRows - 1) * gap - gap * 2) / mobileRows;
|
|
247
|
+
thumbH = availThumbH;
|
|
248
|
+
thumbW = thumbH / ratio;
|
|
249
|
+
}
|
|
250
|
+
othersAreaHeight = neededH;
|
|
251
|
+
othersAreaWidth = areaW;
|
|
252
|
+
} else {
|
|
253
|
+
const othersRatio = isPortrait ? 1 : ratio;
|
|
254
|
+
let bestOthersH = 0;
|
|
255
|
+
let bestThumbArea = 0;
|
|
256
|
+
const maxRows = Math.min(3, visibleOthers || 1);
|
|
257
|
+
const maxOthersRatio = 0.5;
|
|
258
|
+
for (let rows = 1; rows <= maxRows; rows++) {
|
|
259
|
+
const cols = Math.ceil((visibleOthers || 1) / rows);
|
|
260
|
+
const thumbW = (areaW - (cols - 1) * gap) / cols;
|
|
261
|
+
const thumbH = thumbW * othersRatio;
|
|
262
|
+
const requiredH = rows * thumbH + (rows - 1) * gap + gap;
|
|
263
|
+
const areaRatio = requiredH / H;
|
|
264
|
+
if (areaRatio > maxOthersRatio)
|
|
265
|
+
continue;
|
|
266
|
+
if (thumbH < 40)
|
|
267
|
+
continue;
|
|
268
|
+
const thumbArea = thumbW * thumbH;
|
|
269
|
+
if (thumbArea > bestThumbArea) {
|
|
270
|
+
bestThumbArea = thumbArea;
|
|
271
|
+
bestOthersH = requiredH;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
if (bestOthersH === 0) {
|
|
275
|
+
bestOthersH = H * (isPortrait ? 0.25 : 0.2);
|
|
276
|
+
}
|
|
277
|
+
const minRatio = 0.12;
|
|
278
|
+
const maxRatio = 0.45;
|
|
279
|
+
if (bestOthersH / H < minRatio)
|
|
280
|
+
bestOthersH = H * minRatio;
|
|
281
|
+
else if (bestOthersH / H > maxRatio)
|
|
282
|
+
bestOthersH = H * maxRatio;
|
|
283
|
+
othersAreaHeight = bestOthersH;
|
|
284
|
+
othersAreaWidth = areaW;
|
|
285
|
+
}
|
|
286
|
+
mainHeight = H - othersAreaHeight - gap * 3;
|
|
287
|
+
mainWidth = areaW;
|
|
288
|
+
} else {
|
|
289
|
+
const areaH = H - gap * 2;
|
|
290
|
+
let bestOthersW = W * 0.2;
|
|
291
|
+
let bestScore = 0;
|
|
292
|
+
const maxCols = Math.min(3, visibleOthers || 1);
|
|
293
|
+
for (let cols = 1; cols <= maxCols; cols++) {
|
|
294
|
+
const rows = Math.ceil((visibleOthers || 1) / cols);
|
|
295
|
+
const thumbH = (areaH - (rows - 1) * gap) / rows;
|
|
296
|
+
const thumbW = thumbH / ratio;
|
|
297
|
+
const requiredW = cols * thumbW + (cols - 1) * gap + gap * 2;
|
|
298
|
+
const areaRatio = requiredW / W;
|
|
299
|
+
const thumbArea = thumbW * thumbH;
|
|
300
|
+
const mainAreaBonus = (1 - areaRatio) * 0.5;
|
|
301
|
+
const score = thumbArea * (1 + mainAreaBonus);
|
|
302
|
+
if (areaRatio >= 0.1 && areaRatio <= 0.4 && score > bestScore) {
|
|
303
|
+
bestOthersW = requiredW;
|
|
304
|
+
bestScore = score;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
if (bestOthersW / W < 0.1)
|
|
308
|
+
bestOthersW = W * 0.12;
|
|
309
|
+
else if (bestOthersW / W > 0.4)
|
|
310
|
+
bestOthersW = W * 0.35;
|
|
311
|
+
othersAreaWidth = bestOthersW;
|
|
312
|
+
othersAreaHeight = areaH;
|
|
313
|
+
mainWidth = W - othersAreaWidth - gap * 2;
|
|
314
|
+
mainHeight = areaH;
|
|
315
|
+
}
|
|
316
|
+
const mainItemWidth = mainWidth;
|
|
317
|
+
const mainItemHeight = mainHeight;
|
|
318
|
+
const positions = [];
|
|
319
|
+
let mainLeft;
|
|
320
|
+
let mainTop;
|
|
321
|
+
if (isVertical) {
|
|
322
|
+
mainLeft = gap + (mainWidth - mainItemWidth) / 2;
|
|
323
|
+
mainTop = effectivePosition === "top" ? othersAreaHeight + gap * 2 + (mainHeight - mainItemHeight) / 2 : gap + (mainHeight - mainItemHeight) / 2;
|
|
324
|
+
} else {
|
|
325
|
+
mainLeft = effectivePosition === "left" ? othersAreaWidth + gap * 2 + (mainWidth - mainItemWidth) / 2 : gap + (mainWidth - mainItemWidth) / 2;
|
|
326
|
+
mainTop = gap + (mainHeight - mainItemHeight) / 2;
|
|
327
|
+
}
|
|
328
|
+
positions[pinnedIndex] = {
|
|
329
|
+
position: { top: mainTop, left: mainLeft },
|
|
330
|
+
dimensions: { width: mainItemWidth, height: mainItemHeight }
|
|
331
|
+
};
|
|
332
|
+
{
|
|
333
|
+
const isMobileOthers = W < 500;
|
|
334
|
+
const othersRatio = isPortrait ? 1 : ratio;
|
|
335
|
+
let thumbCols = 1;
|
|
336
|
+
let thumbRows = 1;
|
|
337
|
+
let thumbWidth = 0;
|
|
338
|
+
let thumbHeight = 0;
|
|
339
|
+
if (visibleOthers > 0) {
|
|
340
|
+
if (isVertical) {
|
|
341
|
+
if (isMobileOthers && isPortrait) {
|
|
342
|
+
thumbCols = Math.min(visibleOthers, 2);
|
|
343
|
+
thumbRows = Math.ceil(visibleOthers / thumbCols);
|
|
344
|
+
const maxW = (othersAreaWidth - (thumbCols - 1) * gap) / thumbCols;
|
|
345
|
+
const maxH = (othersAreaHeight - (thumbRows - 1) * gap - gap) / thumbRows;
|
|
346
|
+
thumbWidth = maxW;
|
|
347
|
+
thumbHeight = thumbWidth * ratio;
|
|
348
|
+
if (thumbHeight > maxH) {
|
|
349
|
+
thumbHeight = maxH;
|
|
350
|
+
thumbWidth = thumbHeight / ratio;
|
|
351
|
+
}
|
|
352
|
+
} else {
|
|
353
|
+
let bestScore = -1;
|
|
354
|
+
for (let cols = 1; cols <= visibleOthers; cols++) {
|
|
355
|
+
const rows = Math.ceil(visibleOthers / cols);
|
|
356
|
+
const maxTileW = (othersAreaWidth - (cols - 1) * gap) / cols;
|
|
357
|
+
const maxTileH = (othersAreaHeight - (rows - 1) * gap) / rows;
|
|
358
|
+
let tileW = maxTileW;
|
|
359
|
+
let tileH = tileW * othersRatio;
|
|
360
|
+
if (tileH > maxTileH) {
|
|
361
|
+
tileH = maxTileH;
|
|
362
|
+
tileW = tileH / othersRatio;
|
|
363
|
+
}
|
|
364
|
+
const area = tileW * tileH;
|
|
365
|
+
const colsMultiplier = cols >= rows ? 1.5 : 0.5;
|
|
366
|
+
const score = area * colsMultiplier;
|
|
367
|
+
if (score > bestScore) {
|
|
368
|
+
bestScore = score;
|
|
369
|
+
thumbCols = cols;
|
|
370
|
+
thumbRows = rows;
|
|
371
|
+
thumbWidth = tileW;
|
|
372
|
+
thumbHeight = tileH;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
} else {
|
|
377
|
+
let bestScore = -1;
|
|
378
|
+
const targetRatio = 1 / ratio;
|
|
379
|
+
for (let rows = 1; rows <= visibleOthers; rows++) {
|
|
380
|
+
const cols = Math.ceil(visibleOthers / rows);
|
|
381
|
+
const maxTileH = (othersAreaHeight - (rows - 1) * gap) / rows;
|
|
382
|
+
const idealTileW = maxTileH * targetRatio;
|
|
383
|
+
const maxTileW = (othersAreaWidth - (cols - 1) * gap) / cols;
|
|
384
|
+
let tileW, tileH;
|
|
385
|
+
if (idealTileW <= maxTileW) {
|
|
386
|
+
tileW = idealTileW;
|
|
387
|
+
tileH = maxTileH;
|
|
388
|
+
} else {
|
|
389
|
+
tileW = maxTileW;
|
|
390
|
+
tileH = tileW / targetRatio;
|
|
391
|
+
}
|
|
392
|
+
const area = tileW * tileH * visibleOthers;
|
|
393
|
+
if (area > bestScore) {
|
|
394
|
+
bestScore = area;
|
|
395
|
+
thumbCols = cols;
|
|
396
|
+
thumbRows = rows;
|
|
397
|
+
thumbWidth = tileW;
|
|
398
|
+
thumbHeight = tileH;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
const totalGridWidth = thumbCols * thumbWidth + (thumbCols - 1) * gap;
|
|
404
|
+
const totalGridHeight = thumbRows * thumbHeight + (thumbRows - 1) * gap;
|
|
405
|
+
let gridStartLeft;
|
|
406
|
+
let gridStartTop;
|
|
407
|
+
if (isVertical) {
|
|
408
|
+
gridStartLeft = gap + (othersAreaWidth - totalGridWidth) / 2;
|
|
409
|
+
gridStartTop = effectivePosition === "top" ? gap + (othersAreaHeight - totalGridHeight) / 2 : mainHeight + gap * 2 + (othersAreaHeight - totalGridHeight) / 2;
|
|
410
|
+
} else {
|
|
411
|
+
gridStartLeft = effectivePosition === "left" ? gap + (othersAreaWidth - totalGridWidth) / 2 : mainWidth + gap * 2 + (othersAreaWidth - totalGridWidth) / 2;
|
|
412
|
+
gridStartTop = gap + (othersAreaHeight - totalGridHeight) / 2;
|
|
413
|
+
}
|
|
414
|
+
let othersIndex = 0;
|
|
415
|
+
for (let i = 0; i < count; i++) {
|
|
416
|
+
if (i === pinnedIndex)
|
|
417
|
+
continue;
|
|
418
|
+
const isInVisibleRange = othersIndex >= startOthersIndex && othersIndex < endOthersIndex;
|
|
419
|
+
if (isInVisibleRange) {
|
|
420
|
+
const pageRelativeIndex = othersIndex - startOthersIndex;
|
|
421
|
+
const row = Math.floor(pageRelativeIndex / thumbCols);
|
|
422
|
+
const col = pageRelativeIndex % thumbCols;
|
|
423
|
+
const itemsInLastRow = itemsOnPage % thumbCols || thumbCols;
|
|
424
|
+
let rowLeft = gridStartLeft;
|
|
425
|
+
const lastRowIndex = Math.ceil(itemsOnPage / thumbCols) - 1;
|
|
426
|
+
if (row === lastRowIndex && itemsInLastRow < thumbCols) {
|
|
427
|
+
const rowWidth = itemsInLastRow * thumbWidth + (itemsInLastRow - 1) * gap;
|
|
428
|
+
if (isVertical) {
|
|
429
|
+
rowLeft = gap + (othersAreaWidth - rowWidth) / 2;
|
|
430
|
+
} else {
|
|
431
|
+
rowLeft = (effectivePosition === "left" ? gap : mainWidth + gap * 2) + (othersAreaWidth - rowWidth) / 2;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
positions[i] = {
|
|
435
|
+
position: {
|
|
436
|
+
top: gridStartTop + row * (thumbHeight + gap),
|
|
437
|
+
left: rowLeft + col * (thumbWidth + gap)
|
|
438
|
+
},
|
|
439
|
+
dimensions: { width: thumbWidth, height: thumbHeight }
|
|
440
|
+
};
|
|
441
|
+
} else {
|
|
442
|
+
positions[i] = {
|
|
443
|
+
position: { top: -9999, left: -9999 },
|
|
444
|
+
dimensions: { width: 0, height: 0 }
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
othersIndex++;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
const pagination = {
|
|
451
|
+
enabled: maxVisible > 0 && totalOthers > maxVisible,
|
|
452
|
+
currentPage: safeCurrentVisiblePage,
|
|
453
|
+
totalPages: othersTotalPages,
|
|
454
|
+
itemsOnPage,
|
|
455
|
+
startIndex: startOthersIndex,
|
|
456
|
+
endIndex: endOthersIndex
|
|
457
|
+
};
|
|
458
|
+
const getItemDimensions = (index) => positions[index]?.dimensions ?? { width: 0, height: 0 };
|
|
459
|
+
const getLastVisibleOthersIndex = () => {
|
|
460
|
+
if (itemsOnPage === 0)
|
|
461
|
+
return -1;
|
|
462
|
+
let othersIdx = 0;
|
|
463
|
+
let lastVisibleOriginalIdx = -1;
|
|
464
|
+
for (let i = 0; i < count; i++) {
|
|
465
|
+
if (i === pinnedIndex)
|
|
466
|
+
continue;
|
|
467
|
+
if (othersIdx >= startOthersIndex && othersIdx < endOthersIndex) {
|
|
468
|
+
lastVisibleOriginalIdx = i;
|
|
469
|
+
}
|
|
470
|
+
othersIdx++;
|
|
471
|
+
}
|
|
472
|
+
return lastVisibleOriginalIdx;
|
|
473
|
+
};
|
|
474
|
+
return {
|
|
475
|
+
width: mainItemWidth,
|
|
476
|
+
height: mainItemHeight,
|
|
477
|
+
rows: isVertical ? 2 : 1,
|
|
478
|
+
cols: isVertical ? 1 : 2,
|
|
479
|
+
layoutMode: "gallery",
|
|
480
|
+
getPosition: (index) => positions[index]?.position ?? { top: 0, left: 0 },
|
|
481
|
+
getItemDimensions,
|
|
482
|
+
isMainItem: (index) => index === pinnedIndex,
|
|
483
|
+
pagination,
|
|
484
|
+
isItemVisible: (index) => {
|
|
485
|
+
if (index === pinnedIndex)
|
|
486
|
+
return true;
|
|
487
|
+
let sIdx = 0;
|
|
488
|
+
for (let i = 0; i < index; i++) {
|
|
489
|
+
if (i !== pinnedIndex)
|
|
490
|
+
sIdx++;
|
|
491
|
+
}
|
|
492
|
+
return sIdx >= startOthersIndex && sIdx < endOthersIndex;
|
|
493
|
+
},
|
|
494
|
+
hiddenCount,
|
|
495
|
+
getLastVisibleOthersIndex,
|
|
496
|
+
getItemContentDimensions: createGetItemContentDimensions(
|
|
497
|
+
getItemDimensions,
|
|
498
|
+
options.itemAspectRatios,
|
|
499
|
+
aspectRatio
|
|
500
|
+
)
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
function createSpotlightGrid(options) {
|
|
504
|
+
const { dimensions, gap, aspectRatio, pinnedIndex = 0, itemAspectRatios } = options;
|
|
505
|
+
const { width: W, height: H } = dimensions;
|
|
506
|
+
const itemRatio = itemAspectRatios?.[pinnedIndex];
|
|
507
|
+
const shouldFill = itemRatio === "auto";
|
|
508
|
+
let spotWidth;
|
|
509
|
+
let spotHeight;
|
|
510
|
+
if (shouldFill) {
|
|
511
|
+
spotWidth = W - gap * 2;
|
|
512
|
+
spotHeight = H - gap * 2;
|
|
513
|
+
} else {
|
|
514
|
+
const ratio = itemRatio ? getAspectRatio(itemRatio) : getAspectRatio(aspectRatio);
|
|
515
|
+
spotWidth = W - gap * 2;
|
|
516
|
+
spotHeight = spotWidth * ratio;
|
|
517
|
+
if (spotHeight > H - gap * 2) {
|
|
518
|
+
spotHeight = H - gap * 2;
|
|
519
|
+
spotWidth = spotHeight / ratio;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
const position = {
|
|
523
|
+
top: gap + (H - gap * 2 - spotHeight) / 2,
|
|
524
|
+
left: gap + (W - gap * 2 - spotWidth) / 2
|
|
525
|
+
};
|
|
526
|
+
const pagination = createDefaultPagination(1);
|
|
527
|
+
const getItemDimensions = (index) => index === pinnedIndex ? { width: spotWidth, height: spotHeight } : { width: 0, height: 0 };
|
|
528
|
+
return {
|
|
529
|
+
width: spotWidth,
|
|
530
|
+
height: spotHeight,
|
|
531
|
+
rows: 1,
|
|
532
|
+
cols: 1,
|
|
533
|
+
layoutMode: "spotlight",
|
|
534
|
+
getPosition: (index) => index === pinnedIndex ? position : { top: -9999, left: -9999 },
|
|
535
|
+
getItemDimensions,
|
|
536
|
+
isMainItem: (index) => index === pinnedIndex,
|
|
537
|
+
pagination,
|
|
538
|
+
isItemVisible: (index) => index === pinnedIndex,
|
|
539
|
+
hiddenCount: 0,
|
|
540
|
+
getLastVisibleOthersIndex: () => -1,
|
|
541
|
+
getItemContentDimensions: createGetItemContentDimensions(
|
|
542
|
+
getItemDimensions,
|
|
543
|
+
options.itemAspectRatios,
|
|
544
|
+
aspectRatio
|
|
545
|
+
)
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
function createDefaultPagination(count) {
|
|
549
|
+
return {
|
|
550
|
+
enabled: false,
|
|
551
|
+
currentPage: 0,
|
|
552
|
+
totalPages: 1,
|
|
553
|
+
itemsOnPage: count,
|
|
554
|
+
startIndex: 0,
|
|
555
|
+
endIndex: count
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
function createPaginationInfo(count, maxItemsPerPage, currentPage) {
|
|
559
|
+
if (!maxItemsPerPage || maxItemsPerPage <= 0 || maxItemsPerPage >= count) {
|
|
560
|
+
return createDefaultPagination(count);
|
|
561
|
+
}
|
|
562
|
+
const totalPages = Math.ceil(count / maxItemsPerPage);
|
|
563
|
+
const page = Math.min(Math.max(0, currentPage ?? 0), totalPages - 1);
|
|
564
|
+
const startIndex = page * maxItemsPerPage;
|
|
565
|
+
const endIndex = Math.min(startIndex + maxItemsPerPage, count);
|
|
566
|
+
return {
|
|
567
|
+
enabled: true,
|
|
568
|
+
currentPage: page,
|
|
569
|
+
totalPages,
|
|
570
|
+
itemsOnPage: endIndex - startIndex,
|
|
571
|
+
startIndex,
|
|
572
|
+
endIndex
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
function createEmptyMeetGridResult(layoutMode) {
|
|
576
|
+
const getItemDimensions = () => ({ width: 0, height: 0 });
|
|
577
|
+
return {
|
|
578
|
+
width: 0,
|
|
579
|
+
height: 0,
|
|
580
|
+
rows: 0,
|
|
581
|
+
cols: 0,
|
|
582
|
+
layoutMode,
|
|
583
|
+
getPosition: () => ({ top: 0, left: 0 }),
|
|
584
|
+
getItemDimensions,
|
|
585
|
+
isMainItem: () => false,
|
|
586
|
+
pagination: createDefaultPagination(0),
|
|
587
|
+
isItemVisible: () => false,
|
|
588
|
+
hiddenCount: 0,
|
|
589
|
+
getLastVisibleOthersIndex: () => -1,
|
|
590
|
+
getItemContentDimensions: () => ({ width: 0, height: 0, offsetTop: 0, offsetLeft: 0 })
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
function createFlexibleGalleryGrid(options) {
|
|
594
|
+
const {
|
|
595
|
+
dimensions,
|
|
596
|
+
gap,
|
|
597
|
+
aspectRatio,
|
|
598
|
+
count,
|
|
599
|
+
itemAspectRatios = [],
|
|
600
|
+
maxItemsPerPage,
|
|
601
|
+
currentPage,
|
|
602
|
+
maxVisible = 0
|
|
603
|
+
} = options;
|
|
604
|
+
if (count === 0) {
|
|
605
|
+
return createEmptyMeetGridResult("gallery");
|
|
606
|
+
}
|
|
607
|
+
const { width: W, height: H } = dimensions;
|
|
608
|
+
const availW = W - gap * 2;
|
|
609
|
+
const availH = H - gap * 2;
|
|
610
|
+
let visibleCount = count;
|
|
611
|
+
let hiddenCount = 0;
|
|
612
|
+
let startIndex = 0;
|
|
613
|
+
let endIndex = count;
|
|
614
|
+
if (maxItemsPerPage && maxItemsPerPage > 0) {
|
|
615
|
+
const pagInfo = createPaginationInfo(count, maxItemsPerPage, currentPage);
|
|
616
|
+
visibleCount = pagInfo.itemsOnPage;
|
|
617
|
+
startIndex = pagInfo.startIndex;
|
|
618
|
+
endIndex = pagInfo.endIndex;
|
|
619
|
+
} else if (maxVisible > 0 && count > maxVisible) {
|
|
620
|
+
visibleCount = maxVisible;
|
|
621
|
+
hiddenCount = count - maxVisible + 1;
|
|
622
|
+
startIndex = 0;
|
|
623
|
+
endIndex = maxVisible;
|
|
624
|
+
}
|
|
625
|
+
const pagination = maxItemsPerPage && maxItemsPerPage > 0 ? createPaginationInfo(count, maxItemsPerPage, currentPage) : {
|
|
626
|
+
enabled: false,
|
|
627
|
+
currentPage: 0,
|
|
628
|
+
totalPages: 1,
|
|
629
|
+
itemsOnPage: visibleCount,
|
|
630
|
+
startIndex,
|
|
631
|
+
endIndex
|
|
632
|
+
};
|
|
633
|
+
const visibleIndices = [];
|
|
634
|
+
for (let i = startIndex; i < endIndex; i++) {
|
|
635
|
+
visibleIndices.push(i);
|
|
636
|
+
}
|
|
637
|
+
const itemWHRatios = [];
|
|
638
|
+
for (const idx of visibleIndices) {
|
|
639
|
+
const ratioStr = itemAspectRatios[idx] ?? aspectRatio;
|
|
640
|
+
const hw = getAspectRatio(ratioStr);
|
|
641
|
+
itemWHRatios.push(1 / hw);
|
|
642
|
+
}
|
|
643
|
+
const effectiveCount = visibleIndices.length;
|
|
644
|
+
function computeTotalHeightForRows(numRows) {
|
|
645
|
+
const base = Math.floor(effectiveCount / numRows);
|
|
646
|
+
const extra = effectiveCount % numRows;
|
|
647
|
+
let totalH = 0;
|
|
648
|
+
let itemIdx = 0;
|
|
649
|
+
for (let r = 0; r < numRows; r++) {
|
|
650
|
+
const rowSize = base + (r < extra ? 1 : 0);
|
|
651
|
+
let totalUnitW = 0;
|
|
652
|
+
for (let i = 0; i < rowSize; i++) {
|
|
653
|
+
totalUnitW += itemWHRatios[itemIdx + i];
|
|
654
|
+
}
|
|
655
|
+
const netW = availW - (rowSize - 1) * gap;
|
|
656
|
+
totalH += netW / totalUnitW;
|
|
657
|
+
itemIdx += rowSize;
|
|
658
|
+
}
|
|
659
|
+
return totalH + (numRows - 1) * gap;
|
|
660
|
+
}
|
|
661
|
+
function distributeEvenly(numRows) {
|
|
662
|
+
const result = [];
|
|
663
|
+
const base = Math.floor(effectiveCount / numRows);
|
|
664
|
+
const extra = effectiveCount % numRows;
|
|
665
|
+
let idx = 0;
|
|
666
|
+
for (let r = 0; r < numRows; r++) {
|
|
667
|
+
const size = base + (r < extra ? 1 : 0);
|
|
668
|
+
result.push(Array.from({ length: size }, (_, i) => idx + i));
|
|
669
|
+
idx += size;
|
|
670
|
+
}
|
|
671
|
+
return result;
|
|
672
|
+
}
|
|
673
|
+
let bestRowCount = 1;
|
|
674
|
+
let bestDiff = Infinity;
|
|
675
|
+
const maxTryRows = Math.min(effectiveCount, Math.ceil(Math.sqrt(effectiveCount) * 2.5));
|
|
676
|
+
let prevTotalH = 0;
|
|
677
|
+
for (let numRows = 1; numRows <= maxTryRows; numRows++) {
|
|
678
|
+
if (Math.floor(effectiveCount / numRows) === 0)
|
|
679
|
+
break;
|
|
680
|
+
const totalH = computeTotalHeightForRows(numRows);
|
|
681
|
+
const diff = Math.abs(totalH - availH);
|
|
682
|
+
if (diff < bestDiff) {
|
|
683
|
+
bestDiff = diff;
|
|
684
|
+
bestRowCount = numRows;
|
|
685
|
+
}
|
|
686
|
+
if (numRows > 1 && totalH > availH && prevTotalH < availH) {
|
|
687
|
+
break;
|
|
688
|
+
}
|
|
689
|
+
prevTotalH = totalH;
|
|
690
|
+
}
|
|
691
|
+
const bestRows = distributeEvenly(bestRowCount);
|
|
692
|
+
const posMap = /* @__PURE__ */ new Map();
|
|
693
|
+
const rowCount = bestRows.length;
|
|
694
|
+
const rowHeights = [];
|
|
695
|
+
for (const row of bestRows) {
|
|
696
|
+
const totalUnitW = row.reduce((s, relIdx) => s + itemWHRatios[relIdx], 0);
|
|
697
|
+
const netW = availW - (row.length - 1) * gap;
|
|
698
|
+
rowHeights.push(netW / totalUnitW);
|
|
699
|
+
}
|
|
700
|
+
const totalRowH = rowHeights.reduce((s, h) => s + h, 0) + (rowCount - 1) * gap;
|
|
701
|
+
const globalScale = Math.min(1, availH / totalRowH);
|
|
702
|
+
const scaledTotalH = rowHeights.reduce((s, h) => s + h * globalScale, 0) + (rowCount - 1) * gap;
|
|
703
|
+
const verticalOffset = (availH - scaledTotalH) / 2;
|
|
704
|
+
let currentTop = gap + verticalOffset;
|
|
705
|
+
for (let ri = 0; ri < rowCount; ri++) {
|
|
706
|
+
const row = bestRows[ri];
|
|
707
|
+
const finalRowH = rowHeights[ri] * globalScale;
|
|
708
|
+
const totalUnitW = row.reduce((s, relIdx) => s + itemWHRatios[relIdx], 0);
|
|
709
|
+
const netW = availW - (row.length - 1) * gap;
|
|
710
|
+
const itemWidths = row.map((relIdx) => itemWHRatios[relIdx] / totalUnitW * netW * globalScale);
|
|
711
|
+
const scaledRowW = itemWidths.reduce((s, w) => s + w, 0) + (row.length - 1) * gap;
|
|
712
|
+
const horizontalOffset = (availW - scaledRowW) / 2;
|
|
713
|
+
let currentLeft = gap + horizontalOffset;
|
|
714
|
+
for (let ci = 0; ci < row.length; ci++) {
|
|
715
|
+
posMap.set(row[ci], {
|
|
716
|
+
position: { top: currentTop, left: currentLeft },
|
|
717
|
+
dimensions: { width: itemWidths[ci], height: finalRowH }
|
|
718
|
+
});
|
|
719
|
+
currentLeft += itemWidths[ci] + gap;
|
|
720
|
+
}
|
|
721
|
+
currentTop += finalRowH + gap;
|
|
722
|
+
}
|
|
723
|
+
const getPosition = (index) => {
|
|
724
|
+
const relativeIndex = index - startIndex;
|
|
725
|
+
if (relativeIndex < 0 || relativeIndex >= effectiveCount) {
|
|
726
|
+
return { top: -9999, left: -9999 };
|
|
727
|
+
}
|
|
728
|
+
return posMap.get(relativeIndex)?.position ?? { top: -9999, left: -9999 };
|
|
729
|
+
};
|
|
730
|
+
const getItemDimensions = (index) => {
|
|
731
|
+
const relativeIndex = index - startIndex;
|
|
732
|
+
if (relativeIndex < 0 || relativeIndex >= effectiveCount) {
|
|
733
|
+
return { width: 0, height: 0 };
|
|
734
|
+
}
|
|
735
|
+
return posMap.get(relativeIndex)?.dimensions ?? { width: 0, height: 0 };
|
|
736
|
+
};
|
|
737
|
+
const lastVisibleIndex = endIndex - 1;
|
|
738
|
+
return {
|
|
739
|
+
width: availW,
|
|
740
|
+
height: availH,
|
|
741
|
+
rows: rowCount,
|
|
742
|
+
cols: rowCount > 0 ? Math.max(...bestRows.map((r) => r.length)) : 0,
|
|
743
|
+
layoutMode: "gallery",
|
|
744
|
+
getPosition,
|
|
745
|
+
getItemDimensions,
|
|
746
|
+
isMainItem: () => false,
|
|
747
|
+
pagination,
|
|
748
|
+
isItemVisible: (index) => index >= startIndex && index < endIndex,
|
|
749
|
+
hiddenCount,
|
|
750
|
+
getLastVisibleOthersIndex: () => hiddenCount > 0 ? lastVisibleIndex : -1,
|
|
751
|
+
getItemContentDimensions: createGetItemContentDimensions(
|
|
752
|
+
getItemDimensions,
|
|
753
|
+
itemAspectRatios,
|
|
754
|
+
aspectRatio
|
|
755
|
+
)
|
|
756
|
+
};
|
|
757
|
+
}
|
|
758
|
+
function createMeetGrid(options) {
|
|
759
|
+
const { layoutMode = "gallery", count } = options;
|
|
760
|
+
if (count === 0) {
|
|
761
|
+
return createEmptyMeetGridResult(layoutMode);
|
|
762
|
+
}
|
|
763
|
+
switch (layoutMode) {
|
|
764
|
+
case "spotlight":
|
|
765
|
+
return createSpotlightGrid(options);
|
|
766
|
+
case "gallery":
|
|
767
|
+
default: {
|
|
768
|
+
const { maxItemsPerPage, currentPage, pinnedIndex, maxVisible = 0 } = options;
|
|
769
|
+
if (pinnedIndex !== void 0 && pinnedIndex >= 0 && pinnedIndex < count) {
|
|
770
|
+
return createFlexiblePinGrid(options);
|
|
771
|
+
}
|
|
772
|
+
if (count === 2) {
|
|
773
|
+
const { width: W, height: H } = options.dimensions;
|
|
774
|
+
const mainWidth = W;
|
|
775
|
+
const mainHeight = H;
|
|
776
|
+
let floatW;
|
|
777
|
+
let floatH;
|
|
778
|
+
if (options.floatBreakpoints) {
|
|
779
|
+
const resolved = resolveFloatSize(W, options.floatBreakpoints);
|
|
780
|
+
floatW = options.floatWidth ?? resolved.width;
|
|
781
|
+
floatH = options.floatHeight ?? resolved.height;
|
|
782
|
+
} else {
|
|
783
|
+
const isMobileSize = W < 500;
|
|
784
|
+
floatW = options.floatWidth ?? (isMobileSize ? 130 : 180);
|
|
785
|
+
floatH = options.floatHeight ?? (isMobileSize ? 175 : 240);
|
|
786
|
+
}
|
|
787
|
+
const pagination2 = createDefaultPagination(2);
|
|
788
|
+
const getItemDimensions2 = (index) => index === 0 ? { width: mainWidth, height: mainHeight } : { width: floatW, height: floatH };
|
|
789
|
+
return {
|
|
790
|
+
width: mainWidth,
|
|
791
|
+
height: mainHeight,
|
|
792
|
+
rows: 1,
|
|
793
|
+
cols: 1,
|
|
794
|
+
layoutMode: "gallery",
|
|
795
|
+
getPosition: (index) => index === 0 ? { top: 0, left: 0 } : { top: -9999, left: -9999 },
|
|
796
|
+
getItemDimensions: getItemDimensions2,
|
|
797
|
+
isMainItem: (index) => index === 0,
|
|
798
|
+
pagination: pagination2,
|
|
799
|
+
isItemVisible: () => true,
|
|
800
|
+
hiddenCount: 0,
|
|
801
|
+
getLastVisibleOthersIndex: () => -1,
|
|
802
|
+
getItemContentDimensions: createGetItemContentDimensions(
|
|
803
|
+
getItemDimensions2,
|
|
804
|
+
options.itemAspectRatios,
|
|
805
|
+
options.aspectRatio
|
|
806
|
+
),
|
|
807
|
+
floatIndex: 1,
|
|
808
|
+
floatDimensions: { width: floatW, height: floatH }
|
|
809
|
+
};
|
|
810
|
+
}
|
|
811
|
+
const isMobile = options.dimensions.width < 500;
|
|
812
|
+
if (isMobile && count > 1) {
|
|
813
|
+
const { width: cw, height: ch } = options.dimensions;
|
|
814
|
+
options = { ...options, aspectRatio: `${Math.round(cw)}:${Math.round(ch)}` };
|
|
815
|
+
}
|
|
816
|
+
if (options.itemAspectRatios && options.itemAspectRatios.length > 0) {
|
|
817
|
+
const ratios = /* @__PURE__ */ new Set();
|
|
818
|
+
for (const r of options.itemAspectRatios) {
|
|
819
|
+
ratios.add(r ?? options.aspectRatio);
|
|
820
|
+
}
|
|
821
|
+
if (ratios.size === 1) {
|
|
822
|
+
const sharedRatio = [...ratios][0];
|
|
823
|
+
if (sharedRatio !== options.aspectRatio) {
|
|
824
|
+
options = { ...options, aspectRatio: sharedRatio };
|
|
825
|
+
}
|
|
826
|
+
} else {
|
|
827
|
+
if (!isMobile) {
|
|
828
|
+
return createFlexibleGalleryGrid(options);
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
let visibleCount = count;
|
|
833
|
+
let hiddenCount = 0;
|
|
834
|
+
let startIndex = 0;
|
|
835
|
+
let endIndex = count;
|
|
836
|
+
if (maxItemsPerPage && maxItemsPerPage > 0) {
|
|
837
|
+
const pagination2 = createPaginationInfo(count, maxItemsPerPage, currentPage);
|
|
838
|
+
visibleCount = pagination2.itemsOnPage;
|
|
839
|
+
startIndex = pagination2.startIndex;
|
|
840
|
+
endIndex = pagination2.endIndex;
|
|
841
|
+
} else if (maxVisible > 0 && count > maxVisible) {
|
|
842
|
+
visibleCount = maxVisible;
|
|
843
|
+
hiddenCount = count - maxVisible + 1;
|
|
844
|
+
startIndex = 0;
|
|
845
|
+
endIndex = maxVisible;
|
|
846
|
+
}
|
|
847
|
+
const pagination = maxItemsPerPage && maxItemsPerPage > 0 ? createPaginationInfo(count, maxItemsPerPage, currentPage) : {
|
|
848
|
+
enabled: false,
|
|
849
|
+
currentPage: 0,
|
|
850
|
+
totalPages: 1,
|
|
851
|
+
itemsOnPage: visibleCount,
|
|
852
|
+
startIndex,
|
|
853
|
+
endIndex
|
|
854
|
+
};
|
|
855
|
+
const effectiveCount = visibleCount;
|
|
856
|
+
const grid = createGrid({ ...options, count: effectiveCount });
|
|
857
|
+
const getPosition = (index) => {
|
|
858
|
+
const relativeIndex = index - startIndex;
|
|
859
|
+
if (relativeIndex < 0 || relativeIndex >= effectiveCount) {
|
|
860
|
+
return { top: -9999, left: -9999 };
|
|
861
|
+
}
|
|
862
|
+
return grid.getPosition(relativeIndex);
|
|
863
|
+
};
|
|
864
|
+
const getItemDimensions = () => ({ width: grid.width, height: grid.height });
|
|
865
|
+
const lastVisibleIndex = endIndex - 1;
|
|
866
|
+
return {
|
|
867
|
+
...grid,
|
|
868
|
+
layoutMode: "gallery",
|
|
869
|
+
getPosition,
|
|
870
|
+
getItemDimensions,
|
|
871
|
+
isMainItem: () => false,
|
|
872
|
+
pagination,
|
|
873
|
+
isItemVisible: (index) => index >= startIndex && index < endIndex,
|
|
874
|
+
hiddenCount,
|
|
875
|
+
getLastVisibleOthersIndex: () => hiddenCount > 0 ? lastVisibleIndex : -1,
|
|
876
|
+
getItemContentDimensions: createGetItemContentDimensions(
|
|
877
|
+
getItemDimensions,
|
|
878
|
+
options.itemAspectRatios,
|
|
879
|
+
options.aspectRatio
|
|
880
|
+
)
|
|
881
|
+
};
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
const springPresets = {
|
|
886
|
+
/** Snappy animations for UI interactions */
|
|
887
|
+
snappy: { stiffness: 400, damping: 30 },
|
|
888
|
+
/** Smooth animations for layout changes */
|
|
889
|
+
smooth: { stiffness: 300, damping: 30 },
|
|
890
|
+
/** Gentle animations for subtle effects */
|
|
891
|
+
gentle: { stiffness: 200, damping: 25 },
|
|
892
|
+
/** Bouncy animations for playful effects */
|
|
893
|
+
bouncy: { stiffness: 400, damping: 15 }
|
|
894
|
+
};
|
|
895
|
+
function getSpringConfig(preset = "smooth") {
|
|
896
|
+
return {
|
|
897
|
+
type: "spring",
|
|
898
|
+
...springPresets[preset]
|
|
899
|
+
};
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
export { DEFAULT_FLOAT_BREAKPOINTS, calculateContentDimensions, createGrid, createGridItemPositioner, createMeetGrid, getAspectRatio, getGridItemDimensions, getSpringConfig, parseAspectRatio, resolveFloatSize, springPresets };
|