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 +21 -0
- package/README.md +439 -0
- package/dist/index.d.mts +408 -0
- package/dist/index.d.ts +408 -0
- package/dist/index.js +800 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +748 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +63 -0
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
|
+

|
|
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
|