@grandbazar/ui-baza 1.7.0 → 1.9.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/bento.cjs ADDED
@@ -0,0 +1,294 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4
+
5
+ const BENTO_GRID_NAMES = ["mobile", "desktop"];
6
+ const BENTO_GRIDS = {
7
+ mobile: {
8
+ columns: 4,
9
+ presets: {
10
+ 1: ["4x2"],
11
+ 2: ["4x2", "4x2"],
12
+ 3: ["4x2", "2x2", "2x2"],
13
+ 4: ["4x2", "2x2", "2x1", "2x1"],
14
+ 5: ["4x2", "2x2", "2x2", "2x2", "2x2"]
15
+ },
16
+ rhythm: ["4x2", "2x2", "2x2", "2x2", "2x1", "2x1"]
17
+ },
18
+ desktop: {
19
+ columns: 12,
20
+ presets: {
21
+ 1: ["6x2"],
22
+ 2: ["6x2", "6x2"],
23
+ 3: ["4x2", "4x2", "4x2"],
24
+ 4: ["4x2", "4x2", "2x2", "2x2"],
25
+ 5: ["4x2", "2x2", "2x2", "4x1", "4x1"],
26
+ 6: ["4x2", "2x2", "6x2", "4x2", "4x2", "4x2"],
27
+ 7: ["4x2", "2x2", "6x2", "4x2", "4x2", "4x1", "4x1"],
28
+ 10: ["4x2", "2x2", "6x2", "4x2", "2x2", "2x2", "4x1", "4x1", "6x2", "6x2"]
29
+ },
30
+ rhythm: [
31
+ "4x2",
32
+ "2x2",
33
+ "6x2",
34
+ "4x2",
35
+ "2x2",
36
+ "2x2",
37
+ "4x1",
38
+ "4x1",
39
+ "6x2",
40
+ "2x2",
41
+ "4x2",
42
+ "2x2",
43
+ "2x2",
44
+ "4x2",
45
+ "4x1",
46
+ "4x1"
47
+ ]
48
+ }
49
+ };
50
+
51
+ const BENTO_TILE_SHAPES = ["square", "strip", "wide", "wideStrip", "large", "long", "picture"];
52
+ const BENTO_TILE_SHAPE_BY_SIZE = {
53
+ "1x1": "picture",
54
+ "2x1": "strip",
55
+ "2x2": "square",
56
+ "4x1": "wideStrip",
57
+ "4x2": "wide",
58
+ "6x2": "long",
59
+ "8x2": "long",
60
+ "8x3": "long",
61
+ "4x5": "large"
62
+ };
63
+
64
+ const BENTO_SIZES = ["1x1", "2x1", "2x2", "4x1", "4x2", "6x2", "8x2", "8x3", "4x5"];
65
+ const BENTO_SPANS = {
66
+ "1x1": { columns: 1, rows: 1 },
67
+ "2x1": { columns: 2, rows: 1 },
68
+ "2x2": { columns: 2, rows: 2 },
69
+ "4x1": { columns: 4, rows: 1 },
70
+ "4x2": { columns: 4, rows: 2 },
71
+ "6x2": { columns: 6, rows: 2 },
72
+ "8x2": { columns: 8, rows: 2 },
73
+ "8x3": { columns: 8, rows: 3 },
74
+ "4x5": { columns: 4, rows: 5 }
75
+ };
76
+ const BENTO_FALLBACKS = {
77
+ "1x1": ["2x1", "2x2"],
78
+ "2x1": ["2x2", "4x1", "4x2"],
79
+ "2x2": ["4x2", "2x1", "6x2"],
80
+ "4x1": ["4x2", "2x1", "2x2"],
81
+ "4x2": ["2x2", "4x1", "6x2", "8x2", "8x3"],
82
+ "6x2": ["4x2", "8x2", "8x3", "2x2"],
83
+ "8x2": ["8x3", "6x2", "4x2", "2x2"],
84
+ "8x3": ["8x2", "6x2", "4x2"],
85
+ "4x5": []
86
+ };
87
+ const BENTO_THIN_SIZES = ["2x1", "4x1"];
88
+ const BENTO_CONFIGURABLE_SIZES = {
89
+ mobile: ["2x2", "4x2", "4x1", "2x1", "1x1"],
90
+ desktop: ["2x2", "4x2", "6x2", "8x2", "8x3", "4x1"]
91
+ };
92
+ const BENTO_AUTO_SIZES = {
93
+ mobile: ["2x2", "2x1", "4x2", "4x1"],
94
+ desktop: ["2x2", "4x2", "4x1", "6x2", "8x2", "8x3"]
95
+ };
96
+ const BENTO_MOBILE_SIZE_BY_DESKTOP = {
97
+ "1x1": "1x1",
98
+ "2x1": "2x1",
99
+ "2x2": "2x2",
100
+ "4x1": "4x1",
101
+ "4x2": "4x2",
102
+ "6x2": "4x2",
103
+ "8x2": "4x2",
104
+ "8x3": "4x2",
105
+ "4x5": "4x5"
106
+ };
107
+ const BENTO_MAX_RESIZES = 5;
108
+ const BENTO_SEARCH_NODE_LIMIT = 3e4;
109
+
110
+ function toBentoSpan(size, columns) {
111
+ const span = BENTO_SPANS[size];
112
+ return { columns: Math.min(span.columns, columns), rows: span.rows };
113
+ }
114
+
115
+ function findHoleFreeBentoSizes(preferredSizes, slots, columns) {
116
+ const fullRow = (1 << columns) - 1;
117
+ const innerEdges = (1 << columns - 1) - 1;
118
+ let visitedCount = 0;
119
+ const candidatesAt = slots.map(({ allowedSizes }, index) => {
120
+ const preferred = preferredSizes[index];
121
+ const orderedSizes = preferred ? [preferred, ...BENTO_FALLBACKS[preferred], ...allowedSizes] : [...allowedSizes];
122
+ return orderedSizes.filter((size, at) => allowedSizes.includes(size) && orderedSizes.indexOf(size) === at).map((size) => ({ size, span: toBentoSpan(size, columns) }));
123
+ });
124
+ const oddSlotCounts = candidatesAt.reduceRight(
125
+ (after, candidates) => [(after[0] ?? 0) + Number(candidates.some(({ span }) => span.rows % 2)), ...after],
126
+ [0]
127
+ );
128
+ const remainderMasks = candidatesAt.reduceRight(
129
+ (after, candidates) => {
130
+ const next = after[0] ?? 0;
131
+ const reachable = candidates.reduce((mask, { span }) => {
132
+ const shift = span.columns * span.rows % columns;
133
+ return mask | (next << shift | next >>> columns - shift) & fullRow;
134
+ }, 0);
135
+ return [reachable, ...after];
136
+ },
137
+ [1]
138
+ );
139
+ const place = (rows, span) => {
140
+ const mask = (1 << span.columns) - 1;
141
+ for (let row = 0; ; row++) {
142
+ for (let column = 0; column + span.columns <= columns; column++) {
143
+ let isFree = true;
144
+ for (let offset = 0; offset < span.rows && isFree; offset++) {
145
+ isFree = !((rows[row + offset] ?? 0) & mask << column);
146
+ }
147
+ if (isFree) {
148
+ const nextRows = rows.slice();
149
+ for (let at = nextRows.length; at < row + span.rows; at++) {
150
+ nextRows.push(0);
151
+ }
152
+ for (let offset = 0; offset < span.rows; offset++) {
153
+ nextRows[row + offset] = (nextRows[row + offset] ?? 0) | mask << column;
154
+ }
155
+ let firstOpenRow = 0;
156
+ while (firstOpenRow < nextRows.length && nextRows[firstOpenRow] === fullRow) {
157
+ firstOpenRow++;
158
+ }
159
+ return nextRows.slice(firstOpenRow);
160
+ }
161
+ }
162
+ }
163
+ };
164
+ const isClosable = (index, rows, filledArea) => {
165
+ const missing = (columns - filledArea % columns) % columns;
166
+ const parity = rows.reduce((xor, taken) => xor ^ taken, 0);
167
+ let parityEdges = (parity ^ parity >>> 1) & innerEdges;
168
+ let parityEdgeCount = 0;
169
+ while (parityEdges) {
170
+ parityEdges &= parityEdges - 1;
171
+ parityEdgeCount++;
172
+ }
173
+ return Boolean((remainderMasks[index] ?? 0) & 1 << missing) && parityEdgeCount <= 2 * (oddSlotCounts[index] ?? 0);
174
+ };
175
+ const search = (index, rows, filledArea, resizesLeft, deadEnds) => {
176
+ if (index === slots.length) {
177
+ return rows.length ? null : [];
178
+ }
179
+ const key = `${index}:${resizesLeft}:${rows.join(",")}`;
180
+ if (deadEnds.has(key) || !isClosable(index, rows, filledArea) || ++visitedCount > BENTO_SEARCH_NODE_LIMIT) {
181
+ return null;
182
+ }
183
+ const allCandidates = candidatesAt[index] ?? [];
184
+ const candidates = resizesLeft ? allCandidates : allCandidates.slice(0, 1);
185
+ for (const [at, { size, span }] of candidates.entries()) {
186
+ const restSizes = search(
187
+ index + 1,
188
+ place(rows, span),
189
+ filledArea + span.columns * span.rows,
190
+ resizesLeft - Number(at > 0),
191
+ deadEnds
192
+ );
193
+ if (restSizes) {
194
+ return [size, ...restSizes];
195
+ }
196
+ }
197
+ deadEnds.add(key);
198
+ return null;
199
+ };
200
+ for (let resizeCount = 0; resizeCount <= BENTO_MAX_RESIZES; resizeCount++) {
201
+ const foundSizes = search(0, [], 0, resizeCount, /* @__PURE__ */ new Set());
202
+ if (foundSizes) {
203
+ return foundSizes;
204
+ }
205
+ }
206
+ return null;
207
+ }
208
+
209
+ function arrangeBento(slots, { columns, presets, rhythm }) {
210
+ const fitAllowed = (size, { allowedSizes }) => {
211
+ return [size, ...BENTO_FALLBACKS[size]].find((candidate) => allowedSizes.includes(candidate)) ?? allowedSizes[0] ?? size;
212
+ };
213
+ const isFixed = ({ allowedSizes }) => {
214
+ return allowedSizes.length === 1;
215
+ };
216
+ const flexibleIndexes = slots.flatMap((slot, index) => isFixed(slot) ? [] : [index]);
217
+ const presetSizes = presets[flexibleIndexes.length];
218
+ const layOut = ({ isPreferenceKept }) => {
219
+ return slots.map((slot, index) => {
220
+ const order = flexibleIndexes.indexOf(index);
221
+ const patternSize = presetSizes?.[order] ?? rhythm[order % rhythm.length];
222
+ const size = isFixed(slot) && slot.allowedSizes[0] || isPreferenceKept && slot.preferredSize || patternSize;
223
+ return fitAllowed(size ?? slot.preferredSize ?? slot.allowedSizes[0] ?? "2x2", slot);
224
+ });
225
+ };
226
+ const preferredSizes = layOut({ isPreferenceKept: true });
227
+ return findHoleFreeBentoSizes(preferredSizes, slots, columns) ?? findHoleFreeBentoSizes(layOut({ isPreferenceKept: false }), slots, columns) ?? preferredSizes;
228
+ }
229
+
230
+ function arrangeBentoGrids(slotsByGrid) {
231
+ const mobileSizes = arrangeBento(slotsByGrid.mobile, BENTO_GRIDS.mobile);
232
+ const desktopSizes = arrangeBento(slotsByGrid.desktop, BENTO_GRIDS.desktop);
233
+ return mobileSizes.flatMap((mobile, index) => {
234
+ const desktop = desktopSizes[index];
235
+ return desktop ? [{ mobile, desktop }] : [];
236
+ });
237
+ }
238
+
239
+ function countBentoHoles(sizes, columns) {
240
+ const takenRows = [];
241
+ let takenCellCount = 0;
242
+ const fits = (row, mask, rowSpan) => {
243
+ for (let offset = 0; offset < rowSpan; offset++) {
244
+ if ((takenRows[row + offset] ?? 0) & mask) {
245
+ return false;
246
+ }
247
+ }
248
+ return true;
249
+ };
250
+ for (const size of sizes) {
251
+ const span = toBentoSpan(size, columns);
252
+ const widthMask = (1 << span.columns) - 1;
253
+ let isPlaced = false;
254
+ for (let row = 0; !isPlaced; row++) {
255
+ for (let column = 0; column + span.columns <= columns && !isPlaced; column++) {
256
+ const mask = widthMask << column;
257
+ if (fits(row, mask, span.rows)) {
258
+ for (let offset = 0; offset < span.rows; offset++) {
259
+ takenRows[row + offset] = (takenRows[row + offset] ?? 0) | mask;
260
+ }
261
+ isPlaced = true;
262
+ }
263
+ }
264
+ }
265
+ takenCellCount += span.columns * span.rows;
266
+ }
267
+ return takenRows.length * columns - takenCellCount;
268
+ }
269
+
270
+ function toBentoSlot(grid, { allowedSizes, sizes, defaultSize }) {
271
+ const fixedSize = allowedSizes.length === 1 && allowedSizes[0] || sizes[grid];
272
+ if (fixedSize) {
273
+ return { preferredSize: fixedSize, allowedSizes: [fixedSize] };
274
+ }
275
+ const sizeFromDesktop = grid === "mobile" && sizes.desktop && BENTO_MOBILE_SIZE_BY_DESKTOP[sizes.desktop];
276
+ return {
277
+ preferredSize: sizeFromDesktop || defaultSize,
278
+ allowedSizes: BENTO_AUTO_SIZES[grid].filter((size) => allowedSizes.includes(size))
279
+ };
280
+ }
281
+
282
+ exports.BENTO_AUTO_SIZES = BENTO_AUTO_SIZES;
283
+ exports.BENTO_CONFIGURABLE_SIZES = BENTO_CONFIGURABLE_SIZES;
284
+ exports.BENTO_GRIDS = BENTO_GRIDS;
285
+ exports.BENTO_GRID_NAMES = BENTO_GRID_NAMES;
286
+ exports.BENTO_MOBILE_SIZE_BY_DESKTOP = BENTO_MOBILE_SIZE_BY_DESKTOP;
287
+ exports.BENTO_SIZES = BENTO_SIZES;
288
+ exports.BENTO_THIN_SIZES = BENTO_THIN_SIZES;
289
+ exports.BENTO_TILE_SHAPES = BENTO_TILE_SHAPES;
290
+ exports.BENTO_TILE_SHAPE_BY_SIZE = BENTO_TILE_SHAPE_BY_SIZE;
291
+ exports.arrangeBento = arrangeBento;
292
+ exports.arrangeBentoGrids = arrangeBentoGrids;
293
+ exports.countBentoHoles = countBentoHoles;
294
+ exports.toBentoSlot = toBentoSlot;
@@ -0,0 +1,54 @@
1
+ export declare function arrangeBento(slots: readonly TBentoSlot[], { columns, presets, rhythm }: TBentoGridRules): TBentoSize[];
2
+
3
+ export declare function arrangeBentoGrids(slotsByGrid: Record<TBentoGrid, readonly TBentoSlot[]>): TBentoSizes[];
4
+
5
+ export declare const BENTO_AUTO_SIZES: Record<TBentoGrid, readonly TBentoSize[]>;
6
+
7
+ export declare const BENTO_CONFIGURABLE_SIZES: Record<TBentoGrid, readonly TBentoSize[]>;
8
+
9
+ export declare const BENTO_GRID_NAMES: readonly ["mobile", "desktop"];
10
+
11
+ export declare const BENTO_GRIDS: Record<TBentoGrid, TBentoGridRules>;
12
+
13
+ export declare const BENTO_MOBILE_SIZE_BY_DESKTOP: Record<TBentoSize, TBentoSize>;
14
+
15
+ export declare const BENTO_SIZES: readonly ["1x1", "2x1", "2x2", "4x1", "4x2", "6x2", "8x2", "8x3", "4x5"];
16
+
17
+ export declare const BENTO_THIN_SIZES: readonly TBentoSize[];
18
+
19
+ export declare const BENTO_TILE_SHAPE_BY_SIZE: Record<TBentoSize, TBentoTileShape>;
20
+
21
+ export declare const BENTO_TILE_SHAPES: readonly ["square", "strip", "wide", "wideStrip", "large", "long", "picture"];
22
+
23
+ export declare function countBentoHoles(sizes: readonly TBentoSize[], columns: number): number;
24
+
25
+ declare type TArrayElement<T extends readonly unknown[]> = T[number];
26
+
27
+ export declare type TBentoGrid = TArrayElement<typeof BENTO_GRID_NAMES>;
28
+
29
+ export declare type TBentoGridRules = {
30
+ columns: number;
31
+ presets: Partial<Record<number, readonly TBentoSize[]>>;
32
+ rhythm: readonly TBentoSize[];
33
+ };
34
+
35
+ export declare type TBentoSize = TArrayElement<typeof BENTO_SIZES>;
36
+
37
+ export declare type TBentoSizes = Record<TBentoGrid, TBentoSize>;
38
+
39
+ export declare type TBentoSlot = {
40
+ preferredSize: TBentoSize | null;
41
+ allowedSizes: readonly TBentoSize[];
42
+ };
43
+
44
+ declare type TBentoSlotSource = {
45
+ allowedSizes: readonly TBentoSize[];
46
+ sizes: Partial<Record<TBentoGrid, TBentoSize | null>>;
47
+ defaultSize: TBentoSize | null;
48
+ };
49
+
50
+ export declare type TBentoTileShape = TArrayElement<typeof BENTO_TILE_SHAPES>;
51
+
52
+ export declare function toBentoSlot(grid: TBentoGrid, { allowedSizes, sizes, defaultSize }: TBentoSlotSource): TBentoSlot;
53
+
54
+ export { }
package/dist/bento.js ADDED
@@ -0,0 +1,278 @@
1
+ const BENTO_GRID_NAMES = ["mobile", "desktop"];
2
+ const BENTO_GRIDS = {
3
+ mobile: {
4
+ columns: 4,
5
+ presets: {
6
+ 1: ["4x2"],
7
+ 2: ["4x2", "4x2"],
8
+ 3: ["4x2", "2x2", "2x2"],
9
+ 4: ["4x2", "2x2", "2x1", "2x1"],
10
+ 5: ["4x2", "2x2", "2x2", "2x2", "2x2"]
11
+ },
12
+ rhythm: ["4x2", "2x2", "2x2", "2x2", "2x1", "2x1"]
13
+ },
14
+ desktop: {
15
+ columns: 12,
16
+ presets: {
17
+ 1: ["6x2"],
18
+ 2: ["6x2", "6x2"],
19
+ 3: ["4x2", "4x2", "4x2"],
20
+ 4: ["4x2", "4x2", "2x2", "2x2"],
21
+ 5: ["4x2", "2x2", "2x2", "4x1", "4x1"],
22
+ 6: ["4x2", "2x2", "6x2", "4x2", "4x2", "4x2"],
23
+ 7: ["4x2", "2x2", "6x2", "4x2", "4x2", "4x1", "4x1"],
24
+ 10: ["4x2", "2x2", "6x2", "4x2", "2x2", "2x2", "4x1", "4x1", "6x2", "6x2"]
25
+ },
26
+ rhythm: [
27
+ "4x2",
28
+ "2x2",
29
+ "6x2",
30
+ "4x2",
31
+ "2x2",
32
+ "2x2",
33
+ "4x1",
34
+ "4x1",
35
+ "6x2",
36
+ "2x2",
37
+ "4x2",
38
+ "2x2",
39
+ "2x2",
40
+ "4x2",
41
+ "4x1",
42
+ "4x1"
43
+ ]
44
+ }
45
+ };
46
+
47
+ const BENTO_TILE_SHAPES = ["square", "strip", "wide", "wideStrip", "large", "long", "picture"];
48
+ const BENTO_TILE_SHAPE_BY_SIZE = {
49
+ "1x1": "picture",
50
+ "2x1": "strip",
51
+ "2x2": "square",
52
+ "4x1": "wideStrip",
53
+ "4x2": "wide",
54
+ "6x2": "long",
55
+ "8x2": "long",
56
+ "8x3": "long",
57
+ "4x5": "large"
58
+ };
59
+
60
+ const BENTO_SIZES = ["1x1", "2x1", "2x2", "4x1", "4x2", "6x2", "8x2", "8x3", "4x5"];
61
+ const BENTO_SPANS = {
62
+ "1x1": { columns: 1, rows: 1 },
63
+ "2x1": { columns: 2, rows: 1 },
64
+ "2x2": { columns: 2, rows: 2 },
65
+ "4x1": { columns: 4, rows: 1 },
66
+ "4x2": { columns: 4, rows: 2 },
67
+ "6x2": { columns: 6, rows: 2 },
68
+ "8x2": { columns: 8, rows: 2 },
69
+ "8x3": { columns: 8, rows: 3 },
70
+ "4x5": { columns: 4, rows: 5 }
71
+ };
72
+ const BENTO_FALLBACKS = {
73
+ "1x1": ["2x1", "2x2"],
74
+ "2x1": ["2x2", "4x1", "4x2"],
75
+ "2x2": ["4x2", "2x1", "6x2"],
76
+ "4x1": ["4x2", "2x1", "2x2"],
77
+ "4x2": ["2x2", "4x1", "6x2", "8x2", "8x3"],
78
+ "6x2": ["4x2", "8x2", "8x3", "2x2"],
79
+ "8x2": ["8x3", "6x2", "4x2", "2x2"],
80
+ "8x3": ["8x2", "6x2", "4x2"],
81
+ "4x5": []
82
+ };
83
+ const BENTO_THIN_SIZES = ["2x1", "4x1"];
84
+ const BENTO_CONFIGURABLE_SIZES = {
85
+ mobile: ["2x2", "4x2", "4x1", "2x1", "1x1"],
86
+ desktop: ["2x2", "4x2", "6x2", "8x2", "8x3", "4x1"]
87
+ };
88
+ const BENTO_AUTO_SIZES = {
89
+ mobile: ["2x2", "2x1", "4x2", "4x1"],
90
+ desktop: ["2x2", "4x2", "4x1", "6x2", "8x2", "8x3"]
91
+ };
92
+ const BENTO_MOBILE_SIZE_BY_DESKTOP = {
93
+ "1x1": "1x1",
94
+ "2x1": "2x1",
95
+ "2x2": "2x2",
96
+ "4x1": "4x1",
97
+ "4x2": "4x2",
98
+ "6x2": "4x2",
99
+ "8x2": "4x2",
100
+ "8x3": "4x2",
101
+ "4x5": "4x5"
102
+ };
103
+ const BENTO_MAX_RESIZES = 5;
104
+ const BENTO_SEARCH_NODE_LIMIT = 3e4;
105
+
106
+ function toBentoSpan(size, columns) {
107
+ const span = BENTO_SPANS[size];
108
+ return { columns: Math.min(span.columns, columns), rows: span.rows };
109
+ }
110
+
111
+ function findHoleFreeBentoSizes(preferredSizes, slots, columns) {
112
+ const fullRow = (1 << columns) - 1;
113
+ const innerEdges = (1 << columns - 1) - 1;
114
+ let visitedCount = 0;
115
+ const candidatesAt = slots.map(({ allowedSizes }, index) => {
116
+ const preferred = preferredSizes[index];
117
+ const orderedSizes = preferred ? [preferred, ...BENTO_FALLBACKS[preferred], ...allowedSizes] : [...allowedSizes];
118
+ return orderedSizes.filter((size, at) => allowedSizes.includes(size) && orderedSizes.indexOf(size) === at).map((size) => ({ size, span: toBentoSpan(size, columns) }));
119
+ });
120
+ const oddSlotCounts = candidatesAt.reduceRight(
121
+ (after, candidates) => [(after[0] ?? 0) + Number(candidates.some(({ span }) => span.rows % 2)), ...after],
122
+ [0]
123
+ );
124
+ const remainderMasks = candidatesAt.reduceRight(
125
+ (after, candidates) => {
126
+ const next = after[0] ?? 0;
127
+ const reachable = candidates.reduce((mask, { span }) => {
128
+ const shift = span.columns * span.rows % columns;
129
+ return mask | (next << shift | next >>> columns - shift) & fullRow;
130
+ }, 0);
131
+ return [reachable, ...after];
132
+ },
133
+ [1]
134
+ );
135
+ const place = (rows, span) => {
136
+ const mask = (1 << span.columns) - 1;
137
+ for (let row = 0; ; row++) {
138
+ for (let column = 0; column + span.columns <= columns; column++) {
139
+ let isFree = true;
140
+ for (let offset = 0; offset < span.rows && isFree; offset++) {
141
+ isFree = !((rows[row + offset] ?? 0) & mask << column);
142
+ }
143
+ if (isFree) {
144
+ const nextRows = rows.slice();
145
+ for (let at = nextRows.length; at < row + span.rows; at++) {
146
+ nextRows.push(0);
147
+ }
148
+ for (let offset = 0; offset < span.rows; offset++) {
149
+ nextRows[row + offset] = (nextRows[row + offset] ?? 0) | mask << column;
150
+ }
151
+ let firstOpenRow = 0;
152
+ while (firstOpenRow < nextRows.length && nextRows[firstOpenRow] === fullRow) {
153
+ firstOpenRow++;
154
+ }
155
+ return nextRows.slice(firstOpenRow);
156
+ }
157
+ }
158
+ }
159
+ };
160
+ const isClosable = (index, rows, filledArea) => {
161
+ const missing = (columns - filledArea % columns) % columns;
162
+ const parity = rows.reduce((xor, taken) => xor ^ taken, 0);
163
+ let parityEdges = (parity ^ parity >>> 1) & innerEdges;
164
+ let parityEdgeCount = 0;
165
+ while (parityEdges) {
166
+ parityEdges &= parityEdges - 1;
167
+ parityEdgeCount++;
168
+ }
169
+ return Boolean((remainderMasks[index] ?? 0) & 1 << missing) && parityEdgeCount <= 2 * (oddSlotCounts[index] ?? 0);
170
+ };
171
+ const search = (index, rows, filledArea, resizesLeft, deadEnds) => {
172
+ if (index === slots.length) {
173
+ return rows.length ? null : [];
174
+ }
175
+ const key = `${index}:${resizesLeft}:${rows.join(",")}`;
176
+ if (deadEnds.has(key) || !isClosable(index, rows, filledArea) || ++visitedCount > BENTO_SEARCH_NODE_LIMIT) {
177
+ return null;
178
+ }
179
+ const allCandidates = candidatesAt[index] ?? [];
180
+ const candidates = resizesLeft ? allCandidates : allCandidates.slice(0, 1);
181
+ for (const [at, { size, span }] of candidates.entries()) {
182
+ const restSizes = search(
183
+ index + 1,
184
+ place(rows, span),
185
+ filledArea + span.columns * span.rows,
186
+ resizesLeft - Number(at > 0),
187
+ deadEnds
188
+ );
189
+ if (restSizes) {
190
+ return [size, ...restSizes];
191
+ }
192
+ }
193
+ deadEnds.add(key);
194
+ return null;
195
+ };
196
+ for (let resizeCount = 0; resizeCount <= BENTO_MAX_RESIZES; resizeCount++) {
197
+ const foundSizes = search(0, [], 0, resizeCount, /* @__PURE__ */ new Set());
198
+ if (foundSizes) {
199
+ return foundSizes;
200
+ }
201
+ }
202
+ return null;
203
+ }
204
+
205
+ function arrangeBento(slots, { columns, presets, rhythm }) {
206
+ const fitAllowed = (size, { allowedSizes }) => {
207
+ return [size, ...BENTO_FALLBACKS[size]].find((candidate) => allowedSizes.includes(candidate)) ?? allowedSizes[0] ?? size;
208
+ };
209
+ const isFixed = ({ allowedSizes }) => {
210
+ return allowedSizes.length === 1;
211
+ };
212
+ const flexibleIndexes = slots.flatMap((slot, index) => isFixed(slot) ? [] : [index]);
213
+ const presetSizes = presets[flexibleIndexes.length];
214
+ const layOut = ({ isPreferenceKept }) => {
215
+ return slots.map((slot, index) => {
216
+ const order = flexibleIndexes.indexOf(index);
217
+ const patternSize = presetSizes?.[order] ?? rhythm[order % rhythm.length];
218
+ const size = isFixed(slot) && slot.allowedSizes[0] || isPreferenceKept && slot.preferredSize || patternSize;
219
+ return fitAllowed(size ?? slot.preferredSize ?? slot.allowedSizes[0] ?? "2x2", slot);
220
+ });
221
+ };
222
+ const preferredSizes = layOut({ isPreferenceKept: true });
223
+ return findHoleFreeBentoSizes(preferredSizes, slots, columns) ?? findHoleFreeBentoSizes(layOut({ isPreferenceKept: false }), slots, columns) ?? preferredSizes;
224
+ }
225
+
226
+ function arrangeBentoGrids(slotsByGrid) {
227
+ const mobileSizes = arrangeBento(slotsByGrid.mobile, BENTO_GRIDS.mobile);
228
+ const desktopSizes = arrangeBento(slotsByGrid.desktop, BENTO_GRIDS.desktop);
229
+ return mobileSizes.flatMap((mobile, index) => {
230
+ const desktop = desktopSizes[index];
231
+ return desktop ? [{ mobile, desktop }] : [];
232
+ });
233
+ }
234
+
235
+ function countBentoHoles(sizes, columns) {
236
+ const takenRows = [];
237
+ let takenCellCount = 0;
238
+ const fits = (row, mask, rowSpan) => {
239
+ for (let offset = 0; offset < rowSpan; offset++) {
240
+ if ((takenRows[row + offset] ?? 0) & mask) {
241
+ return false;
242
+ }
243
+ }
244
+ return true;
245
+ };
246
+ for (const size of sizes) {
247
+ const span = toBentoSpan(size, columns);
248
+ const widthMask = (1 << span.columns) - 1;
249
+ let isPlaced = false;
250
+ for (let row = 0; !isPlaced; row++) {
251
+ for (let column = 0; column + span.columns <= columns && !isPlaced; column++) {
252
+ const mask = widthMask << column;
253
+ if (fits(row, mask, span.rows)) {
254
+ for (let offset = 0; offset < span.rows; offset++) {
255
+ takenRows[row + offset] = (takenRows[row + offset] ?? 0) | mask;
256
+ }
257
+ isPlaced = true;
258
+ }
259
+ }
260
+ }
261
+ takenCellCount += span.columns * span.rows;
262
+ }
263
+ return takenRows.length * columns - takenCellCount;
264
+ }
265
+
266
+ function toBentoSlot(grid, { allowedSizes, sizes, defaultSize }) {
267
+ const fixedSize = allowedSizes.length === 1 && allowedSizes[0] || sizes[grid];
268
+ if (fixedSize) {
269
+ return { preferredSize: fixedSize, allowedSizes: [fixedSize] };
270
+ }
271
+ const sizeFromDesktop = grid === "mobile" && sizes.desktop && BENTO_MOBILE_SIZE_BY_DESKTOP[sizes.desktop];
272
+ return {
273
+ preferredSize: sizeFromDesktop || defaultSize,
274
+ allowedSizes: BENTO_AUTO_SIZES[grid].filter((size) => allowedSizes.includes(size))
275
+ };
276
+ }
277
+
278
+ export { BENTO_AUTO_SIZES, BENTO_CONFIGURABLE_SIZES, BENTO_GRIDS, BENTO_GRID_NAMES, BENTO_MOBILE_SIZE_BY_DESKTOP, BENTO_SIZES, BENTO_THIN_SIZES, BENTO_TILE_SHAPES, BENTO_TILE_SHAPE_BY_SIZE, arrangeBento, arrangeBentoGrids, countBentoHoles, toBentoSlot };