@grandbazar/ui-baza 1.7.1 → 1.10.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,316 @@
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 countBentoHoles(sizes, columns) {
116
+ const takenRows = [];
117
+ let takenCellCount = 0;
118
+ const fits = (row, mask, rowSpan) => {
119
+ for (let offset = 0; offset < rowSpan; offset++) {
120
+ if ((takenRows[row + offset] ?? 0) & mask) {
121
+ return false;
122
+ }
123
+ }
124
+ return true;
125
+ };
126
+ for (const size of sizes) {
127
+ const span = toBentoSpan(size, columns);
128
+ const widthMask = (1 << span.columns) - 1;
129
+ let isPlaced = false;
130
+ for (let row = 0; !isPlaced; row++) {
131
+ for (let column = 0; column + span.columns <= columns && !isPlaced; column++) {
132
+ const mask = widthMask << column;
133
+ if (fits(row, mask, span.rows)) {
134
+ for (let offset = 0; offset < span.rows; offset++) {
135
+ takenRows[row + offset] = (takenRows[row + offset] ?? 0) | mask;
136
+ }
137
+ isPlaced = true;
138
+ }
139
+ }
140
+ }
141
+ takenCellCount += span.columns * span.rows;
142
+ }
143
+ return takenRows.length * columns - takenCellCount;
144
+ }
145
+
146
+ function findHoleFreeBentoSizes(preferredSizes, slots, columns) {
147
+ const fullRow = (1 << columns) - 1;
148
+ const innerEdges = (1 << columns - 1) - 1;
149
+ let visitedCount = 0;
150
+ const candidatesAt = slots.map(({ allowedSizes }, index) => {
151
+ const preferred = preferredSizes[index];
152
+ const orderedSizes = preferred ? [preferred, ...BENTO_FALLBACKS[preferred], ...allowedSizes] : [...allowedSizes];
153
+ return orderedSizes.filter((size, at) => allowedSizes.includes(size) && orderedSizes.indexOf(size) === at).map((size) => ({ size, span: toBentoSpan(size, columns) }));
154
+ });
155
+ const oddSlotCounts = candidatesAt.reduceRight(
156
+ (after, candidates) => [(after[0] ?? 0) + Number(candidates.some(({ span }) => span.rows % 2)), ...after],
157
+ [0]
158
+ );
159
+ const remainderMasks = candidatesAt.reduceRight(
160
+ (after, candidates) => {
161
+ const next = after[0] ?? 0;
162
+ const reachable = candidates.reduce((mask, { span }) => {
163
+ const shift = span.columns * span.rows % columns;
164
+ return mask | (next << shift | next >>> columns - shift) & fullRow;
165
+ }, 0);
166
+ return [reachable, ...after];
167
+ },
168
+ [1]
169
+ );
170
+ const place = (rows, span) => {
171
+ const mask = (1 << span.columns) - 1;
172
+ for (let row = 0; ; row++) {
173
+ for (let column = 0; column + span.columns <= columns; column++) {
174
+ let isFree = true;
175
+ for (let offset = 0; offset < span.rows && isFree; offset++) {
176
+ isFree = !((rows[row + offset] ?? 0) & mask << column);
177
+ }
178
+ if (isFree) {
179
+ const nextRows = rows.slice();
180
+ for (let at = nextRows.length; at < row + span.rows; at++) {
181
+ nextRows.push(0);
182
+ }
183
+ for (let offset = 0; offset < span.rows; offset++) {
184
+ nextRows[row + offset] = (nextRows[row + offset] ?? 0) | mask << column;
185
+ }
186
+ let firstOpenRow = 0;
187
+ while (firstOpenRow < nextRows.length && nextRows[firstOpenRow] === fullRow) {
188
+ firstOpenRow++;
189
+ }
190
+ return nextRows.slice(firstOpenRow);
191
+ }
192
+ }
193
+ }
194
+ };
195
+ const isClosable = (index, rows, filledArea) => {
196
+ const missing = (columns - filledArea % columns) % columns;
197
+ const parity = rows.reduce((xor, taken) => xor ^ taken, 0);
198
+ let parityEdges = (parity ^ parity >>> 1) & innerEdges;
199
+ let parityEdgeCount = 0;
200
+ while (parityEdges) {
201
+ parityEdges &= parityEdges - 1;
202
+ parityEdgeCount++;
203
+ }
204
+ return Boolean((remainderMasks[index] ?? 0) & 1 << missing) && parityEdgeCount <= 2 * (oddSlotCounts[index] ?? 0);
205
+ };
206
+ const search = (index, rows, filledArea, resizesLeft, deadEnds) => {
207
+ if (index === slots.length) {
208
+ return rows.length ? null : [];
209
+ }
210
+ const key = `${index}:${resizesLeft}:${rows.join(",")}`;
211
+ if (deadEnds.has(key) || !isClosable(index, rows, filledArea) || ++visitedCount > BENTO_SEARCH_NODE_LIMIT) {
212
+ return null;
213
+ }
214
+ const allCandidates = candidatesAt[index] ?? [];
215
+ const candidates = resizesLeft ? allCandidates : allCandidates.slice(0, 1);
216
+ for (const [at, { size, span }] of candidates.entries()) {
217
+ const restSizes = search(
218
+ index + 1,
219
+ place(rows, span),
220
+ filledArea + span.columns * span.rows,
221
+ resizesLeft - Number(at > 0),
222
+ deadEnds
223
+ );
224
+ if (restSizes) {
225
+ return [size, ...restSizes];
226
+ }
227
+ }
228
+ deadEnds.add(key);
229
+ return null;
230
+ };
231
+ for (let resizeCount = 0; resizeCount <= BENTO_MAX_RESIZES; resizeCount++) {
232
+ const foundSizes = search(0, [], 0, resizeCount, /* @__PURE__ */ new Set());
233
+ if (foundSizes) {
234
+ return foundSizes;
235
+ }
236
+ }
237
+ return null;
238
+ }
239
+
240
+ function isBentoDevMode() {
241
+ try {
242
+ return process.env.NODE_ENV !== "production";
243
+ } catch {
244
+ return false;
245
+ }
246
+ }
247
+
248
+ function arrangeBento(slots, { columns, presets, rhythm }) {
249
+ const fitAllowed = (size, { allowedSizes }) => {
250
+ return [size, ...BENTO_FALLBACKS[size]].find((candidate) => allowedSizes.includes(candidate)) ?? allowedSizes[0] ?? size;
251
+ };
252
+ const isFixed = ({ allowedSizes }) => {
253
+ return allowedSizes.length === 1;
254
+ };
255
+ const flexibleIndexes = slots.flatMap((slot, index) => isFixed(slot) ? [] : [index]);
256
+ const presetSizes = presets[flexibleIndexes.length];
257
+ const layOut = ({ isPreferenceKept }) => {
258
+ return slots.map((slot, index) => {
259
+ const order = flexibleIndexes.indexOf(index);
260
+ const patternSize = presetSizes?.[order] ?? rhythm[order % rhythm.length];
261
+ const size = isFixed(slot) && slot.allowedSizes[0] || isPreferenceKept && slot.preferredSize || patternSize;
262
+ return fitAllowed(size ?? slot.preferredSize ?? slot.allowedSizes[0] ?? "2x2", slot);
263
+ });
264
+ };
265
+ const preferredSizes = layOut({ isPreferenceKept: true });
266
+ const arrangedSizes = findHoleFreeBentoSizes(preferredSizes, slots, columns) ?? findHoleFreeBentoSizes(layOut({ isPreferenceKept: false }), slots, columns) ?? preferredSizes;
267
+ if (isBentoDevMode()) {
268
+ const holeCount = countBentoHoles(arrangedSizes, columns);
269
+ if (holeCount > 0) {
270
+ console.warn(
271
+ `arrangeBento: no hole-free arrangement on a ${columns}-column grid, ${holeCount} cell(s) left empty: ${arrangedSizes.join(", ")}`
272
+ );
273
+ }
274
+ }
275
+ return arrangedSizes;
276
+ }
277
+
278
+ function arrangeBentoGrids(slotsByGrid) {
279
+ const mobileSizes = arrangeBento(slotsByGrid.mobile, BENTO_GRIDS.mobile);
280
+ const desktopSizes = arrangeBento(slotsByGrid.desktop, BENTO_GRIDS.desktop);
281
+ return mobileSizes.flatMap((mobile, index) => {
282
+ const desktop = desktopSizes[index];
283
+ return desktop ? [{ mobile, desktop }] : [];
284
+ });
285
+ }
286
+
287
+ function toBentoSlot(grid, { allowedSizes, sizes, defaultSize, isAdjustable = false }) {
288
+ const givenSize = sizes[grid];
289
+ const fixedSize = allowedSizes.length === 1 && allowedSizes[0] || !isAdjustable && givenSize;
290
+ const autoSizes = BENTO_AUTO_SIZES[grid].filter((size) => allowedSizes.includes(size));
291
+ if (fixedSize) {
292
+ return { preferredSize: fixedSize, allowedSizes: [fixedSize] };
293
+ }
294
+ if (givenSize) {
295
+ return {
296
+ preferredSize: givenSize,
297
+ allowedSizes: autoSizes.includes(givenSize) ? autoSizes : [...autoSizes, givenSize]
298
+ };
299
+ }
300
+ const sizeFromDesktop = grid === "mobile" && sizes.desktop && BENTO_MOBILE_SIZE_BY_DESKTOP[sizes.desktop];
301
+ return { preferredSize: sizeFromDesktop || defaultSize, allowedSizes: autoSizes };
302
+ }
303
+
304
+ exports.BENTO_AUTO_SIZES = BENTO_AUTO_SIZES;
305
+ exports.BENTO_CONFIGURABLE_SIZES = BENTO_CONFIGURABLE_SIZES;
306
+ exports.BENTO_GRIDS = BENTO_GRIDS;
307
+ exports.BENTO_GRID_NAMES = BENTO_GRID_NAMES;
308
+ exports.BENTO_MOBILE_SIZE_BY_DESKTOP = BENTO_MOBILE_SIZE_BY_DESKTOP;
309
+ exports.BENTO_SIZES = BENTO_SIZES;
310
+ exports.BENTO_THIN_SIZES = BENTO_THIN_SIZES;
311
+ exports.BENTO_TILE_SHAPES = BENTO_TILE_SHAPES;
312
+ exports.BENTO_TILE_SHAPE_BY_SIZE = BENTO_TILE_SHAPE_BY_SIZE;
313
+ exports.arrangeBento = arrangeBento;
314
+ exports.arrangeBentoGrids = arrangeBentoGrids;
315
+ exports.countBentoHoles = countBentoHoles;
316
+ exports.toBentoSlot = toBentoSlot;
@@ -0,0 +1,55 @@
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
+ isAdjustable?: boolean;
49
+ };
50
+
51
+ export declare type TBentoTileShape = TArrayElement<typeof BENTO_TILE_SHAPES>;
52
+
53
+ export declare function toBentoSlot(grid: TBentoGrid, { allowedSizes, sizes, defaultSize, isAdjustable }: TBentoSlotSource): TBentoSlot;
54
+
55
+ export { }
package/dist/bento.js ADDED
@@ -0,0 +1,300 @@
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 countBentoHoles(sizes, columns) {
112
+ const takenRows = [];
113
+ let takenCellCount = 0;
114
+ const fits = (row, mask, rowSpan) => {
115
+ for (let offset = 0; offset < rowSpan; offset++) {
116
+ if ((takenRows[row + offset] ?? 0) & mask) {
117
+ return false;
118
+ }
119
+ }
120
+ return true;
121
+ };
122
+ for (const size of sizes) {
123
+ const span = toBentoSpan(size, columns);
124
+ const widthMask = (1 << span.columns) - 1;
125
+ let isPlaced = false;
126
+ for (let row = 0; !isPlaced; row++) {
127
+ for (let column = 0; column + span.columns <= columns && !isPlaced; column++) {
128
+ const mask = widthMask << column;
129
+ if (fits(row, mask, span.rows)) {
130
+ for (let offset = 0; offset < span.rows; offset++) {
131
+ takenRows[row + offset] = (takenRows[row + offset] ?? 0) | mask;
132
+ }
133
+ isPlaced = true;
134
+ }
135
+ }
136
+ }
137
+ takenCellCount += span.columns * span.rows;
138
+ }
139
+ return takenRows.length * columns - takenCellCount;
140
+ }
141
+
142
+ function findHoleFreeBentoSizes(preferredSizes, slots, columns) {
143
+ const fullRow = (1 << columns) - 1;
144
+ const innerEdges = (1 << columns - 1) - 1;
145
+ let visitedCount = 0;
146
+ const candidatesAt = slots.map(({ allowedSizes }, index) => {
147
+ const preferred = preferredSizes[index];
148
+ const orderedSizes = preferred ? [preferred, ...BENTO_FALLBACKS[preferred], ...allowedSizes] : [...allowedSizes];
149
+ return orderedSizes.filter((size, at) => allowedSizes.includes(size) && orderedSizes.indexOf(size) === at).map((size) => ({ size, span: toBentoSpan(size, columns) }));
150
+ });
151
+ const oddSlotCounts = candidatesAt.reduceRight(
152
+ (after, candidates) => [(after[0] ?? 0) + Number(candidates.some(({ span }) => span.rows % 2)), ...after],
153
+ [0]
154
+ );
155
+ const remainderMasks = candidatesAt.reduceRight(
156
+ (after, candidates) => {
157
+ const next = after[0] ?? 0;
158
+ const reachable = candidates.reduce((mask, { span }) => {
159
+ const shift = span.columns * span.rows % columns;
160
+ return mask | (next << shift | next >>> columns - shift) & fullRow;
161
+ }, 0);
162
+ return [reachable, ...after];
163
+ },
164
+ [1]
165
+ );
166
+ const place = (rows, span) => {
167
+ const mask = (1 << span.columns) - 1;
168
+ for (let row = 0; ; row++) {
169
+ for (let column = 0; column + span.columns <= columns; column++) {
170
+ let isFree = true;
171
+ for (let offset = 0; offset < span.rows && isFree; offset++) {
172
+ isFree = !((rows[row + offset] ?? 0) & mask << column);
173
+ }
174
+ if (isFree) {
175
+ const nextRows = rows.slice();
176
+ for (let at = nextRows.length; at < row + span.rows; at++) {
177
+ nextRows.push(0);
178
+ }
179
+ for (let offset = 0; offset < span.rows; offset++) {
180
+ nextRows[row + offset] = (nextRows[row + offset] ?? 0) | mask << column;
181
+ }
182
+ let firstOpenRow = 0;
183
+ while (firstOpenRow < nextRows.length && nextRows[firstOpenRow] === fullRow) {
184
+ firstOpenRow++;
185
+ }
186
+ return nextRows.slice(firstOpenRow);
187
+ }
188
+ }
189
+ }
190
+ };
191
+ const isClosable = (index, rows, filledArea) => {
192
+ const missing = (columns - filledArea % columns) % columns;
193
+ const parity = rows.reduce((xor, taken) => xor ^ taken, 0);
194
+ let parityEdges = (parity ^ parity >>> 1) & innerEdges;
195
+ let parityEdgeCount = 0;
196
+ while (parityEdges) {
197
+ parityEdges &= parityEdges - 1;
198
+ parityEdgeCount++;
199
+ }
200
+ return Boolean((remainderMasks[index] ?? 0) & 1 << missing) && parityEdgeCount <= 2 * (oddSlotCounts[index] ?? 0);
201
+ };
202
+ const search = (index, rows, filledArea, resizesLeft, deadEnds) => {
203
+ if (index === slots.length) {
204
+ return rows.length ? null : [];
205
+ }
206
+ const key = `${index}:${resizesLeft}:${rows.join(",")}`;
207
+ if (deadEnds.has(key) || !isClosable(index, rows, filledArea) || ++visitedCount > BENTO_SEARCH_NODE_LIMIT) {
208
+ return null;
209
+ }
210
+ const allCandidates = candidatesAt[index] ?? [];
211
+ const candidates = resizesLeft ? allCandidates : allCandidates.slice(0, 1);
212
+ for (const [at, { size, span }] of candidates.entries()) {
213
+ const restSizes = search(
214
+ index + 1,
215
+ place(rows, span),
216
+ filledArea + span.columns * span.rows,
217
+ resizesLeft - Number(at > 0),
218
+ deadEnds
219
+ );
220
+ if (restSizes) {
221
+ return [size, ...restSizes];
222
+ }
223
+ }
224
+ deadEnds.add(key);
225
+ return null;
226
+ };
227
+ for (let resizeCount = 0; resizeCount <= BENTO_MAX_RESIZES; resizeCount++) {
228
+ const foundSizes = search(0, [], 0, resizeCount, /* @__PURE__ */ new Set());
229
+ if (foundSizes) {
230
+ return foundSizes;
231
+ }
232
+ }
233
+ return null;
234
+ }
235
+
236
+ function isBentoDevMode() {
237
+ try {
238
+ return process.env.NODE_ENV !== "production";
239
+ } catch {
240
+ return false;
241
+ }
242
+ }
243
+
244
+ function arrangeBento(slots, { columns, presets, rhythm }) {
245
+ const fitAllowed = (size, { allowedSizes }) => {
246
+ return [size, ...BENTO_FALLBACKS[size]].find((candidate) => allowedSizes.includes(candidate)) ?? allowedSizes[0] ?? size;
247
+ };
248
+ const isFixed = ({ allowedSizes }) => {
249
+ return allowedSizes.length === 1;
250
+ };
251
+ const flexibleIndexes = slots.flatMap((slot, index) => isFixed(slot) ? [] : [index]);
252
+ const presetSizes = presets[flexibleIndexes.length];
253
+ const layOut = ({ isPreferenceKept }) => {
254
+ return slots.map((slot, index) => {
255
+ const order = flexibleIndexes.indexOf(index);
256
+ const patternSize = presetSizes?.[order] ?? rhythm[order % rhythm.length];
257
+ const size = isFixed(slot) && slot.allowedSizes[0] || isPreferenceKept && slot.preferredSize || patternSize;
258
+ return fitAllowed(size ?? slot.preferredSize ?? slot.allowedSizes[0] ?? "2x2", slot);
259
+ });
260
+ };
261
+ const preferredSizes = layOut({ isPreferenceKept: true });
262
+ const arrangedSizes = findHoleFreeBentoSizes(preferredSizes, slots, columns) ?? findHoleFreeBentoSizes(layOut({ isPreferenceKept: false }), slots, columns) ?? preferredSizes;
263
+ if (isBentoDevMode()) {
264
+ const holeCount = countBentoHoles(arrangedSizes, columns);
265
+ if (holeCount > 0) {
266
+ console.warn(
267
+ `arrangeBento: no hole-free arrangement on a ${columns}-column grid, ${holeCount} cell(s) left empty: ${arrangedSizes.join(", ")}`
268
+ );
269
+ }
270
+ }
271
+ return arrangedSizes;
272
+ }
273
+
274
+ function arrangeBentoGrids(slotsByGrid) {
275
+ const mobileSizes = arrangeBento(slotsByGrid.mobile, BENTO_GRIDS.mobile);
276
+ const desktopSizes = arrangeBento(slotsByGrid.desktop, BENTO_GRIDS.desktop);
277
+ return mobileSizes.flatMap((mobile, index) => {
278
+ const desktop = desktopSizes[index];
279
+ return desktop ? [{ mobile, desktop }] : [];
280
+ });
281
+ }
282
+
283
+ function toBentoSlot(grid, { allowedSizes, sizes, defaultSize, isAdjustable = false }) {
284
+ const givenSize = sizes[grid];
285
+ const fixedSize = allowedSizes.length === 1 && allowedSizes[0] || !isAdjustable && givenSize;
286
+ const autoSizes = BENTO_AUTO_SIZES[grid].filter((size) => allowedSizes.includes(size));
287
+ if (fixedSize) {
288
+ return { preferredSize: fixedSize, allowedSizes: [fixedSize] };
289
+ }
290
+ if (givenSize) {
291
+ return {
292
+ preferredSize: givenSize,
293
+ allowedSizes: autoSizes.includes(givenSize) ? autoSizes : [...autoSizes, givenSize]
294
+ };
295
+ }
296
+ const sizeFromDesktop = grid === "mobile" && sizes.desktop && BENTO_MOBILE_SIZE_BY_DESKTOP[sizes.desktop];
297
+ return { preferredSize: sizeFromDesktop || defaultSize, allowedSizes: autoSizes };
298
+ }
299
+
300
+ 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 };
package/dist/index.cjs CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4
4
 
5
+ const bento = require('./bento.cjs');
5
6
  const jsxRuntime = require('react/jsx-runtime');
6
7
  const React = require('react');
7
8
  const reactDom = require('react-dom');
@@ -3890,6 +3891,19 @@ function Tooltip(props) {
3890
3891
  }
3891
3892
  Tooltip.Content = TooltipContent;
3892
3893
 
3894
+ exports.BENTO_AUTO_SIZES = bento.BENTO_AUTO_SIZES;
3895
+ exports.BENTO_CONFIGURABLE_SIZES = bento.BENTO_CONFIGURABLE_SIZES;
3896
+ exports.BENTO_GRIDS = bento.BENTO_GRIDS;
3897
+ exports.BENTO_GRID_NAMES = bento.BENTO_GRID_NAMES;
3898
+ exports.BENTO_MOBILE_SIZE_BY_DESKTOP = bento.BENTO_MOBILE_SIZE_BY_DESKTOP;
3899
+ exports.BENTO_SIZES = bento.BENTO_SIZES;
3900
+ exports.BENTO_THIN_SIZES = bento.BENTO_THIN_SIZES;
3901
+ exports.BENTO_TILE_SHAPES = bento.BENTO_TILE_SHAPES;
3902
+ exports.BENTO_TILE_SHAPE_BY_SIZE = bento.BENTO_TILE_SHAPE_BY_SIZE;
3903
+ exports.arrangeBento = bento.arrangeBento;
3904
+ exports.arrangeBentoGrids = bento.arrangeBentoGrids;
3905
+ exports.countBentoHoles = bento.countBentoHoles;
3906
+ exports.toBentoSlot = bento.toBentoSlot;
3893
3907
  exports.Badge = Badge;
3894
3908
  exports.BadgeTabs = BadgeTabs;
3895
3909
  exports.Breadcrumbs = Breadcrumbs;
package/dist/index.d.ts CHANGED
@@ -6,6 +6,10 @@ import { JSX } from 'react/jsx-runtime';
6
6
  import { ReactPortal } from 'react';
7
7
  import { SetStateAction } from 'react';
8
8
 
9
+ export declare function arrangeBento(slots: readonly TBentoSlot[], { columns, presets, rhythm }: TBentoGridRules): TBentoSize[];
10
+
11
+ export declare function arrangeBentoGrids(slotsByGrid: Record<TBentoGrid, readonly TBentoSlot[]>): TBentoSizes[];
12
+
9
13
  /**
10
14
  * Badge displays a label or indicator with customizable color and size.
11
15
  * Supports multiple color schemes and display modes for different contexts.
@@ -50,6 +54,24 @@ declare function BadgeTabsItem({ className, children, isActive, mode, ...props }
50
54
 
51
55
  declare function BadgeTabsRoot({ className, children, ...props }: TBadgeTabsRootProps): JSX.Element;
52
56
 
57
+ export declare const BENTO_AUTO_SIZES: Record<TBentoGrid, readonly TBentoSize[]>;
58
+
59
+ export declare const BENTO_CONFIGURABLE_SIZES: Record<TBentoGrid, readonly TBentoSize[]>;
60
+
61
+ export declare const BENTO_GRID_NAMES: readonly ["mobile", "desktop"];
62
+
63
+ export declare const BENTO_GRIDS: Record<TBentoGrid, TBentoGridRules>;
64
+
65
+ export declare const BENTO_MOBILE_SIZE_BY_DESKTOP: Record<TBentoSize, TBentoSize>;
66
+
67
+ export declare const BENTO_SIZES: readonly ["1x1", "2x1", "2x2", "4x1", "4x2", "6x2", "8x2", "8x3", "4x5"];
68
+
69
+ export declare const BENTO_THIN_SIZES: readonly TBentoSize[];
70
+
71
+ export declare const BENTO_TILE_SHAPE_BY_SIZE: Record<TBentoSize, TBentoTileShape>;
72
+
73
+ export declare const BENTO_TILE_SHAPES: readonly ["square", "strip", "wide", "wideStrip", "large", "long", "picture"];
74
+
53
75
  declare function BreadcrumbItem({ children, className, isActive, asChild, ...props }: TBreadcrumbItemProps): JSX.Element;
54
76
 
55
77
  /**
@@ -138,6 +160,8 @@ declare const CHIP_MODES: readonly ["default", "glass", "white"];
138
160
 
139
161
  declare const CHIP_SHAPES: readonly ["default", "pill"];
140
162
 
163
+ export declare function countBentoHoles(sizes: readonly TBentoSize[], columns: number): number;
164
+
141
165
  export declare function Counter({ className, size, color, ...props }: TCounterProps): JSX.Element;
142
166
 
143
167
  declare const COUNTER_COLORS: readonly ["red", "accent", "neutral", "secondary", "white"];
@@ -776,6 +800,32 @@ declare type TBadgeTabsRootProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'c
776
800
  children: React.ReactNode;
777
801
  };
778
802
 
803
+ export declare type TBentoGrid = TArrayElement<typeof BENTO_GRID_NAMES>;
804
+
805
+ export declare type TBentoGridRules = {
806
+ columns: number;
807
+ presets: Partial<Record<number, readonly TBentoSize[]>>;
808
+ rhythm: readonly TBentoSize[];
809
+ };
810
+
811
+ export declare type TBentoSize = TArrayElement<typeof BENTO_SIZES>;
812
+
813
+ export declare type TBentoSizes = Record<TBentoGrid, TBentoSize>;
814
+
815
+ export declare type TBentoSlot = {
816
+ preferredSize: TBentoSize | null;
817
+ allowedSizes: readonly TBentoSize[];
818
+ };
819
+
820
+ declare type TBentoSlotSource = {
821
+ allowedSizes: readonly TBentoSize[];
822
+ sizes: Partial<Record<TBentoGrid, TBentoSize | null>>;
823
+ defaultSize: TBentoSize | null;
824
+ isAdjustable?: boolean;
825
+ };
826
+
827
+ export declare type TBentoTileShape = TArrayElement<typeof BENTO_TILE_SHAPES>;
828
+
779
829
  declare type TBreadcrumbItemProps = TAsChildProps<React.ButtonHTMLAttributes<HTMLButtonElement>> & {
780
830
  children: React.ReactNode;
781
831
  className?: string;
@@ -937,6 +987,8 @@ declare type TNotificationInlineProps = Omit<React.HTMLAttributes<HTMLDivElement
937
987
  onClose?: () => void;
938
988
  };
939
989
 
990
+ export declare function toBentoSlot(grid: TBentoGrid, { allowedSizes, sizes, defaultSize, isAdjustable }: TBentoSlotSource): TBentoSlot;
991
+
940
992
  /**
941
993
  * Tooltip is a compound component for displaying contextual hints.
942
994
  * Provides a trigger wrapper and content rendering for short descriptions.
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ 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 } from './bento.js';
1
2
  import { jsx, jsxs, Fragment as Fragment$1 } from 'react/jsx-runtime';
2
3
  import React, { isValidElement, cloneElement, Fragment, useMemo, useState, useCallback, createContext, useEffect, useRef, useContext, useId, Children } from 'react';
3
4
  import { createPortal } from 'react-dom';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grandbazar/ui-baza",
3
- "version": "1.7.1",
3
+ "version": "1.10.0",
4
4
  "description": "A lightweight and modular UI kit for building modern, accessible web interfaces. Designed for flexibility, scalability, and developer happiness.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -40,6 +40,16 @@
40
40
  "default": "./dist/index.cjs"
41
41
  }
42
42
  },
43
+ "./bento": {
44
+ "import": {
45
+ "types": "./dist/bento.d.ts",
46
+ "default": "./dist/bento.js"
47
+ },
48
+ "require": {
49
+ "types": "./dist/bento.d.ts",
50
+ "default": "./dist/bento.cjs"
51
+ }
52
+ },
43
53
  "./css": "./dist/style.css"
44
54
  },
45
55
  "scripts": {