bento-grid-builder 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,439 @@
1
+ # bento-grid-builder
2
+
3
+ A flexible, configurable bento grid layout system for React. Create beautiful dashboard layouts with minimal configuration.
4
+
5
+ ![Bento Grid Preview](assets/bento-preview.png)
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install bento-grid-builder
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ```tsx
16
+ import { BentoGrid } from "bento-grid-builder";
17
+
18
+ // Define your card components
19
+ const StatsCard = ({ count, label }) => (
20
+ <div>
21
+ <h2>{count}</h2>
22
+ <p>{label}</p>
23
+ </div>
24
+ );
25
+
26
+ const ChartCard = ({ data }) => <MyChart data={data} />;
27
+
28
+ // Use the grid
29
+ function Dashboard() {
30
+ const data = { totalUsers: 1234, chartData: [...] };
31
+
32
+ return (
33
+ <BentoGrid
34
+ layout="3x2"
35
+ cards={[
36
+ { id: "stats", component: StatsCard, colSpan: 2 },
37
+ { id: "chart", component: ChartCard },
38
+ ]}
39
+ data={data}
40
+ dataMapping={[
41
+ { cardId: "stats", propsSelector: (d) => ({ count: d.totalUsers, label: "Users" }) },
42
+ { cardId: "chart", propsSelector: (d) => ({ data: d.chartData }) },
43
+ ]}
44
+ />
45
+ );
46
+ }
47
+ ```
48
+
49
+ ## Preset Layouts
50
+
51
+ Use built-in presets for common layouts:
52
+
53
+ - `"2x2"` - 2 columns, 2 rows (4 cards)
54
+ - `"3x2"` - 3 columns, 2 rows (6 cards)
55
+ - `"3x3"` - 3 columns, 3 rows (9 cards)
56
+ - `"4x2"` - 4 columns, 2 rows (8 cards)
57
+ - `"2x1-hero-left"` - Hero card on left spanning 2 rows
58
+ - `"2x1-hero-right"` - Hero card on right spanning 2 rows
59
+ - `"dashboard-9"` - 9-card dashboard with mixed spans
60
+
61
+ ```tsx
62
+ <BentoGrid
63
+ layout="dashboard-9"
64
+ cards={cards}
65
+ data={data}
66
+ dataMapping={mapping}
67
+ />
68
+ ```
69
+
70
+ ## Custom Layouts
71
+
72
+ Define your own layout with full control:
73
+
74
+ ```tsx
75
+ const customLayout = {
76
+ columns: 4,
77
+ gap: 16,
78
+ placements: [
79
+ { cardId: "hero", col: 1, row: 1, colSpan: 2, rowSpan: 2 },
80
+ { cardId: "stats1", col: 3, row: 1 },
81
+ { cardId: "stats2", col: 4, row: 1 },
82
+ { cardId: "chart", col: 3, row: 2, colSpan: 2 },
83
+ ],
84
+ };
85
+
86
+ <BentoGrid
87
+ layout={customLayout}
88
+ cards={cards}
89
+ data={data}
90
+ dataMapping={mapping}
91
+ />;
92
+ ```
93
+
94
+ ## Responsive Layouts
95
+
96
+ Define different layouts for different viewport widths:
97
+
98
+ ```tsx
99
+ <BentoGrid
100
+ layout={{
101
+ default: "2x2", // Mobile
102
+ breakpoints: [
103
+ { minWidth: 768, layout: "3x2" }, // Tablet
104
+ { minWidth: 1024, layout: "4x2" }, // Desktop
105
+ ],
106
+ }}
107
+ cards={cards}
108
+ data={data}
109
+ dataMapping={mapping}
110
+ />
111
+ ```
112
+
113
+ Or use the hook directly:
114
+
115
+ ```tsx
116
+ import { useResponsiveLayout } from "bento-grid-builder";
117
+
118
+ const layout = useResponsiveLayout({
119
+ default: "2x2",
120
+ breakpoints: [
121
+ { minWidth: 768, layout: "3x2" },
122
+ { minWidth: 1024, layout: customDesktopLayout },
123
+ ],
124
+ });
125
+ ```
126
+
127
+ ## Layout Builder
128
+
129
+ Use the fluent API for building layouts:
130
+
131
+ ```tsx
132
+ import { layoutBuilder } from "bento-grid-builder";
133
+
134
+ const layout = layoutBuilder(3)
135
+ .gap(16)
136
+ .place("hero", 1, 1, { colSpan: 2, rowSpan: 2 })
137
+ .place("stats", 3, 1)
138
+ .place("chart", 3, 2)
139
+ .build();
140
+ ```
141
+
142
+ ## Hooks API
143
+
144
+ Cleaner syntax with hooks:
145
+
146
+ ```tsx
147
+ import {
148
+ BentoGrid,
149
+ useCardDefinitions,
150
+ useDataMapping,
151
+ } from "bento-grid-builder";
152
+
153
+ function Dashboard({ data }) {
154
+ const cards = useCardDefinitions({
155
+ stats: { component: StatsCard, colSpan: 2 },
156
+ chart: { component: ChartCard },
157
+ list: { component: ListCard, rowSpan: 2 },
158
+ });
159
+
160
+ const dataMapping = useDataMapping({
161
+ stats: (d) => ({ count: d.total, label: "Items" }),
162
+ chart: (d) => ({ points: d.chartData }),
163
+ list: (d) => ({ items: d.recentItems }),
164
+ });
165
+
166
+ return (
167
+ <BentoGrid
168
+ layout="3x2"
169
+ cards={cards}
170
+ data={data}
171
+ dataMapping={dataMapping}
172
+ />
173
+ );
174
+ }
175
+ ```
176
+
177
+ ## Custom Card Wrapper
178
+
179
+ Override the default card styling:
180
+
181
+ ```tsx
182
+ const MyCardWrapper = ({ children, cardId }) => (
183
+ <div className={`my-card my-card--${cardId}`}>{children}</div>
184
+ );
185
+
186
+ <BentoGrid
187
+ layout="3x3"
188
+ cards={cards}
189
+ data={data}
190
+ dataMapping={mapping}
191
+ cardWrapper={MyCardWrapper}
192
+ />;
193
+ ```
194
+
195
+ ## Styling
196
+
197
+ The grid uses CSS custom properties for layout, which works seamlessly with both regular CSS and Tailwind CSS.
198
+
199
+ ### CSS Custom Properties
200
+
201
+ Customize the grid appearance with CSS variables:
202
+
203
+ ```css
204
+ .bento-grid {
205
+ --bento-card-bg: #ffffff;
206
+ --bento-card-radius: 12px;
207
+ --bento-card-padding: 16px;
208
+ --bento-card-border: 1px solid #e5e7eb;
209
+ }
210
+
211
+ /* Dark mode */
212
+ .dark .bento-grid {
213
+ --bento-card-bg: #1f2937;
214
+ --bento-card-border: 1px solid #374151;
215
+ }
216
+ ```
217
+
218
+ ### Tailwind CSS
219
+
220
+ Tailwind classes work automatically - no special configuration needed. The grid sets CSS custom properties via inline styles, and Tailwind utility classes override the CSS rules directly:
221
+
222
+ ```tsx
223
+ <UnifiedBentoGrid
224
+ layout={layout}
225
+ cards={cards}
226
+ data={data}
227
+ // Tailwind classes override the default grid styles
228
+ className="grid grid-cols-3 gap-4"
229
+ cellClassName="bg-white dark:bg-gray-800 rounded-xl p-4 shadow-sm border border-gray-200"
230
+ />
231
+ ```
232
+
233
+ Dynamic classes per card:
234
+
235
+ ```tsx
236
+ <UnifiedBentoGrid
237
+ layout={layout}
238
+ cards={cards}
239
+ data={data}
240
+ className="grid grid-cols-3 gap-4"
241
+ cellClassName={(cardId) => {
242
+ if (cardId === "hero")
243
+ return "col-span-2 row-span-2 bg-gradient-to-br from-blue-600 to-purple-600 rounded-xl p-4";
244
+ return "bg-white rounded-xl p-4 border";
245
+ }}
246
+ />
247
+ ```
248
+
249
+ Data attributes are added to cells for CSS targeting:
250
+
251
+ ```css
252
+ /* Target by card ID */
253
+ .bento-cell[data-card-id="hero"] {
254
+ @apply bg-blue-600;
255
+ }
256
+ ```
257
+
258
+ ### Separate Row/Column Gaps
259
+
260
+ ```tsx
261
+ const layout = layoutBuilder(3)
262
+ .gap(16) // default for both
263
+ .columnGap(24) // override column gap
264
+ .rowGap(12) // override row gap
265
+ .build();
266
+
267
+ // Or in config object
268
+ const layout = {
269
+ columns: 3,
270
+ gap: 16,
271
+ columnGap: 24,
272
+ rowGap: 12,
273
+ placements: [...],
274
+ };
275
+ ```
276
+
277
+ Or pass styles directly:
278
+
279
+ ```tsx
280
+ <BentoGrid
281
+ layout="3x3"
282
+ cards={cards}
283
+ data={data}
284
+ dataMapping={mapping}
285
+ style={{ maxWidth: 1200, margin: "0 auto" }}
286
+ />
287
+ ```
288
+
289
+ ## Accessibility
290
+
291
+ The grid includes built-in accessibility features:
292
+
293
+ ```tsx
294
+ <BentoGrid
295
+ layout="3x3"
296
+ cards={cards}
297
+ data={data}
298
+ dataMapping={mapping}
299
+ ariaLabel="Sales dashboard"
300
+ />
301
+
302
+ // Or reference an external label
303
+ <h2 id="dashboard-title">Sales Dashboard</h2>
304
+ <BentoGrid
305
+ layout="3x3"
306
+ cards={cards}
307
+ data={data}
308
+ dataMapping={mapping}
309
+ ariaLabelledBy="dashboard-title"
310
+ />
311
+ ```
312
+
313
+ ## Error Handling
314
+
315
+ Handle card render errors gracefully:
316
+
317
+ ```tsx
318
+ <BentoGrid
319
+ layout="3x3"
320
+ cards={cards}
321
+ data={data}
322
+ dataMapping={mapping}
323
+ onCardError={(cardId, error) => {
324
+ console.error(`Card ${cardId} failed:`, error);
325
+ }}
326
+ />
327
+ ```
328
+
329
+ ## Loading States
330
+
331
+ Show loading placeholders for cards (UnifiedBentoGrid only):
332
+
333
+ ```tsx
334
+ <UnifiedBentoGrid
335
+ layout="3x2"
336
+ cards={[
337
+ {
338
+ id: "stats",
339
+ component: StatsCard,
340
+ propsSelector: (d) => ({ count: d.total }),
341
+ loading: (d) => d.isLoading, // Function or boolean
342
+ },
343
+ {
344
+ id: "chart",
345
+ component: ChartCard,
346
+ propsSelector: (d) => ({ data: d.chartData }),
347
+ loading: false,
348
+ loadingComponent: CustomSkeleton, // Per-card loading component
349
+ },
350
+ ]}
351
+ data={myData}
352
+ loadingComponent={GlobalSkeleton} // Default for all cards
353
+ />
354
+ ```
355
+
356
+ ## Animations
357
+
358
+ Enable fade-in animations for cards:
359
+
360
+ ```tsx
361
+ <BentoGrid
362
+ layout="3x3"
363
+ cards={cards}
364
+ data={data}
365
+ dataMapping={mapping}
366
+ animated
367
+ animationDuration={300} // milliseconds
368
+ />
369
+ ```
370
+
371
+ Customize animation via CSS:
372
+
373
+ ```css
374
+ .bento-grid {
375
+ --bento-animation-duration: 400ms;
376
+ }
377
+
378
+ /* Override the animation entirely */
379
+ .bento-cell-animated {
380
+ animation: my-custom-animation 0.5s ease-out;
381
+ }
382
+ ```
383
+
384
+ ## TypeScript
385
+
386
+ Full TypeScript support with generics:
387
+
388
+ ```tsx
389
+ interface MyData {
390
+ users: number;
391
+ chartData: Point[];
392
+ }
393
+
394
+ <BentoGrid<MyData>
395
+ layout="3x2"
396
+ cards={cards}
397
+ data={myData}
398
+ dataMapping={[
399
+ { cardId: "stats", propsSelector: (d) => ({ count: d.users }) },
400
+ ]}
401
+ />;
402
+ ```
403
+
404
+ ## API Reference
405
+
406
+ ### BentoGrid Props
407
+
408
+ | Prop | Type | Description |
409
+ | ------------- | ---------------------------------------- | ---------------------------------------- |
410
+ | `layout` | `BentoLayoutConfig \| PresetLayoutName` | Grid layout configuration or preset name |
411
+ | `cards` | `BentoCardDefinition[]` | Array of card definitions |
412
+ | `data` | `TData` | Your data source |
413
+ | `dataMapping` | `CardDataMapping<TData>[]` | Maps data to card props |
414
+ | `className` | `string` | Additional CSS class |
415
+ | `style` | `CSSProperties` | Inline styles |
416
+ | `cardWrapper` | `ComponentType<CardWrapperProps>` | Custom card wrapper |
417
+ | `onCardError` | `(cardId: string, error: Error) => void` | Error callback |
418
+
419
+ ### BentoCardDefinition
420
+
421
+ | Property | Type | Description |
422
+ | ----------- | --------------- | ------------------------- |
423
+ | `id` | `string` | Unique card identifier |
424
+ | `component` | `ComponentType` | React component to render |
425
+ | `colSpan` | `number` | Default column span (1) |
426
+ | `rowSpan` | `number` | Default row span (1) |
427
+
428
+ ### BentoLayoutConfig
429
+
430
+ | Property | Type | Description |
431
+ | ------------ | ----------------- | ------------------------------------------- |
432
+ | `columns` | `number` | Number of grid columns |
433
+ | `rows` | `number` | Number of rows (auto-calculated if omitted) |
434
+ | `gap` | `number` | Gap between cards in pixels |
435
+ | `placements` | `CardPlacement[]` | Card positions |
436
+
437
+ ## License
438
+
439
+ MIT