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/dist/index.d.mts
ADDED
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
import React, { ReactNode, CSSProperties, ComponentType } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Definition for a card component that can be rendered in the bento grid
|
|
5
|
+
* Users register their card components with this interface
|
|
6
|
+
*/
|
|
7
|
+
interface BentoCardDefinition<TProps = any> {
|
|
8
|
+
/** Unique identifier for this card type */
|
|
9
|
+
id: string;
|
|
10
|
+
/** The React component to render */
|
|
11
|
+
component: ComponentType<TProps>;
|
|
12
|
+
/** Default column span (defaults to 1) */
|
|
13
|
+
colSpan?: number;
|
|
14
|
+
/** Default row span (defaults to 1) */
|
|
15
|
+
rowSpan?: number;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Unified card definition that combines component, layout, and data mapping
|
|
19
|
+
* This is the preferred way to define cards - cleaner than separate arrays
|
|
20
|
+
* @template TData - The shape of your data source
|
|
21
|
+
* @template TProps - The props your card component expects
|
|
22
|
+
*/
|
|
23
|
+
interface UnifiedCardDefinition<TData = unknown, TProps = any> {
|
|
24
|
+
/** Unique identifier for this card */
|
|
25
|
+
id: string;
|
|
26
|
+
/** The React component to render */
|
|
27
|
+
component: ComponentType<TProps>;
|
|
28
|
+
/** Function that extracts props from your data for this card */
|
|
29
|
+
propsSelector: (data: TData) => TProps;
|
|
30
|
+
/** Default column span (defaults to 1) */
|
|
31
|
+
colSpan?: number;
|
|
32
|
+
/** Default row span (defaults to 1) */
|
|
33
|
+
rowSpan?: number;
|
|
34
|
+
/** Whether this card should be visible (defaults to true) */
|
|
35
|
+
visible?: boolean | ((data: TData) => boolean);
|
|
36
|
+
/** Whether this card is in a loading state */
|
|
37
|
+
loading?: boolean | ((data: TData) => boolean);
|
|
38
|
+
/** Custom loading component for this card */
|
|
39
|
+
loadingComponent?: ComponentType;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Position and size of a card in the grid
|
|
43
|
+
*/
|
|
44
|
+
interface CardPlacement {
|
|
45
|
+
/** Card definition ID to render */
|
|
46
|
+
cardId: string;
|
|
47
|
+
/** Starting column (1-indexed) */
|
|
48
|
+
col: number;
|
|
49
|
+
/** Starting row (1-indexed) */
|
|
50
|
+
row: number;
|
|
51
|
+
/** Number of columns to span (overrides card default) */
|
|
52
|
+
colSpan?: number;
|
|
53
|
+
/** Number of rows to span (overrides card default) */
|
|
54
|
+
rowSpan?: number;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Explicit slot-to-card mapping for preset layouts
|
|
58
|
+
* Allows users to specify which card goes in which slot by name
|
|
59
|
+
*/
|
|
60
|
+
interface PresetSlotMapping {
|
|
61
|
+
/** Slot index (0-based) or slot name if preset supports named slots */
|
|
62
|
+
slot: number | string;
|
|
63
|
+
/** Card ID to place in this slot */
|
|
64
|
+
cardId: string;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Complete layout configuration for the bento grid
|
|
68
|
+
*/
|
|
69
|
+
interface BentoLayoutConfig {
|
|
70
|
+
/** Number of columns in the grid */
|
|
71
|
+
columns: number;
|
|
72
|
+
/** Number of rows (optional - auto-calculated if not provided) */
|
|
73
|
+
rows?: number;
|
|
74
|
+
/** Gap between cards in pixels (applies to both row and column) */
|
|
75
|
+
gap?: number;
|
|
76
|
+
/** Gap between columns in pixels (overrides gap for columns) */
|
|
77
|
+
columnGap?: number;
|
|
78
|
+
/** Gap between rows in pixels (overrides gap for rows) */
|
|
79
|
+
rowGap?: number;
|
|
80
|
+
/** Card placements in the grid */
|
|
81
|
+
placements: CardPlacement[];
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Breakpoint configuration for responsive layouts
|
|
85
|
+
*/
|
|
86
|
+
interface BreakpointConfig {
|
|
87
|
+
/** Minimum viewport width for this breakpoint (in pixels) */
|
|
88
|
+
minWidth: number;
|
|
89
|
+
/** Layout to use at this breakpoint */
|
|
90
|
+
layout: BentoLayoutConfig | PresetLayoutName;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Responsive layout configuration
|
|
94
|
+
* Allows different layouts at different viewport widths
|
|
95
|
+
*/
|
|
96
|
+
interface ResponsiveLayoutConfig {
|
|
97
|
+
/** Default layout (used when no breakpoint matches) */
|
|
98
|
+
default: BentoLayoutConfig | PresetLayoutName;
|
|
99
|
+
/** Breakpoint-specific layouts (applied when viewport >= minWidth) */
|
|
100
|
+
breakpoints: BreakpointConfig[];
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Maps data from your data source to a specific card's props
|
|
104
|
+
* @template TData - The shape of your data source
|
|
105
|
+
*/
|
|
106
|
+
interface CardDataMapping<TData = unknown> {
|
|
107
|
+
/** Card definition ID this mapping applies to */
|
|
108
|
+
cardId: string;
|
|
109
|
+
/** Function that extracts props from your data for this card */
|
|
110
|
+
propsSelector: (data: TData) => Record<string, unknown>;
|
|
111
|
+
}
|
|
112
|
+
/** Available preset layout names */
|
|
113
|
+
type PresetLayoutName = "2x2" | "3x2" | "3x3" | "4x2" | "2x1-hero-left" | "2x1-hero-right" | "dashboard-9";
|
|
114
|
+
/**
|
|
115
|
+
* Preset layout definition
|
|
116
|
+
*/
|
|
117
|
+
interface PresetLayout {
|
|
118
|
+
name: PresetLayoutName;
|
|
119
|
+
columns: number;
|
|
120
|
+
rows: number;
|
|
121
|
+
/** Slot positions for cards - users just provide cardIds in order */
|
|
122
|
+
slots: Array<{
|
|
123
|
+
/** Optional name for this slot (e.g., "hero", "sidebar") */
|
|
124
|
+
name?: string;
|
|
125
|
+
col: number;
|
|
126
|
+
row: number;
|
|
127
|
+
colSpan?: number;
|
|
128
|
+
rowSpan?: number;
|
|
129
|
+
}>;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Props for the main BentoGrid component
|
|
133
|
+
* @template TData - The shape of your data source
|
|
134
|
+
*/
|
|
135
|
+
interface BentoGridProps<TData = unknown> {
|
|
136
|
+
/** Layout configuration, preset name, or responsive config */
|
|
137
|
+
layout: BentoLayoutConfig | PresetLayoutName | ResponsiveLayoutConfig;
|
|
138
|
+
/** Registered card definitions */
|
|
139
|
+
cards: BentoCardDefinition[];
|
|
140
|
+
/** Your data source */
|
|
141
|
+
data: TData;
|
|
142
|
+
/** Maps your data to each card's props */
|
|
143
|
+
dataMapping: CardDataMapping<TData>[];
|
|
144
|
+
/** Additional CSS class for the grid container */
|
|
145
|
+
className?: string;
|
|
146
|
+
/** Additional inline styles for the grid container */
|
|
147
|
+
style?: CSSProperties;
|
|
148
|
+
/** Custom card wrapper component */
|
|
149
|
+
cardWrapper?: ComponentType<CardWrapperProps>;
|
|
150
|
+
/** Called when a card fails to render */
|
|
151
|
+
onCardError?: (cardId: string, error: Error) => void;
|
|
152
|
+
/** Accessible label for the grid region */
|
|
153
|
+
ariaLabel?: string;
|
|
154
|
+
/** ID of element that labels this grid */
|
|
155
|
+
ariaLabelledBy?: string;
|
|
156
|
+
/** Enable enter/exit animations for cards */
|
|
157
|
+
animated?: boolean;
|
|
158
|
+
/** Animation duration in milliseconds (default: 300) */
|
|
159
|
+
animationDuration?: number;
|
|
160
|
+
/** CSS class for grid cells */
|
|
161
|
+
cellClassName?: string | ((cardId: string) => string);
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Props for the unified BentoGrid component (preferred API)
|
|
165
|
+
* Combines cards and data mapping into a single prop
|
|
166
|
+
* @template TData - The shape of your data source
|
|
167
|
+
*/
|
|
168
|
+
interface UnifiedBentoGridProps<TData = unknown> {
|
|
169
|
+
/** Layout configuration, preset name, or responsive config */
|
|
170
|
+
layout: BentoLayoutConfig | PresetLayoutName | ResponsiveLayoutConfig;
|
|
171
|
+
/** Unified card definitions with components and data selectors */
|
|
172
|
+
cards: UnifiedCardDefinition<TData, any>[];
|
|
173
|
+
/** Your data source */
|
|
174
|
+
data: TData;
|
|
175
|
+
/** Additional CSS class for the grid container */
|
|
176
|
+
className?: string;
|
|
177
|
+
/** Additional inline styles for the grid container */
|
|
178
|
+
style?: CSSProperties;
|
|
179
|
+
/** Custom card wrapper component */
|
|
180
|
+
cardWrapper?: ComponentType<CardWrapperProps>;
|
|
181
|
+
/** Called when a card fails to render */
|
|
182
|
+
onCardError?: (cardId: string, error: Error) => void;
|
|
183
|
+
/** Accessible label for the grid region */
|
|
184
|
+
ariaLabel?: string;
|
|
185
|
+
/** ID of element that labels this grid */
|
|
186
|
+
ariaLabelledBy?: string;
|
|
187
|
+
/** Enable enter/exit animations for cards */
|
|
188
|
+
animated?: boolean;
|
|
189
|
+
/** Animation duration in milliseconds (default: 300) */
|
|
190
|
+
animationDuration?: number;
|
|
191
|
+
/** Global loading component for all cards */
|
|
192
|
+
loadingComponent?: ComponentType;
|
|
193
|
+
/** CSS class for grid cells */
|
|
194
|
+
cellClassName?: string | ((cardId: string) => string);
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Props passed to the card wrapper component
|
|
198
|
+
*/
|
|
199
|
+
interface CardWrapperProps {
|
|
200
|
+
children: ReactNode;
|
|
201
|
+
cardId: string;
|
|
202
|
+
className?: string;
|
|
203
|
+
style?: CSSProperties;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Props for the default BentoCard wrapper
|
|
207
|
+
*/
|
|
208
|
+
interface BentoCardProps {
|
|
209
|
+
children: ReactNode;
|
|
210
|
+
className?: string;
|
|
211
|
+
style?: CSSProperties;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* BentoGrid - A flexible, configurable bento grid layout system
|
|
216
|
+
*
|
|
217
|
+
* @example
|
|
218
|
+
* ```tsx
|
|
219
|
+
* <BentoGrid
|
|
220
|
+
* layout="3x3"
|
|
221
|
+
* cards={[
|
|
222
|
+
* { id: 'stats', component: StatsCard },
|
|
223
|
+
* { id: 'chart', component: ChartCard },
|
|
224
|
+
* ]}
|
|
225
|
+
* data={myData}
|
|
226
|
+
* dataMapping={[
|
|
227
|
+
* { cardId: 'stats', propsSelector: (d) => ({ count: d.total }) },
|
|
228
|
+
* { cardId: 'chart', propsSelector: (d) => ({ points: d.chartData }) },
|
|
229
|
+
* ]}
|
|
230
|
+
* />
|
|
231
|
+
* ```
|
|
232
|
+
*/
|
|
233
|
+
declare function BentoGrid<TData>({ layout, cards, data, dataMapping, className, style, cardWrapper, onCardError, ariaLabel, ariaLabelledBy, animated, animationDuration, cellClassName, }: BentoGridProps<TData>): React.ReactElement;
|
|
234
|
+
/**
|
|
235
|
+
* UnifiedBentoGrid - Cleaner API that combines cards and data mapping
|
|
236
|
+
*
|
|
237
|
+
* This is the preferred way to use BentoGrid. Instead of separate `cards` and
|
|
238
|
+
* `dataMapping` arrays, you define everything in one place.
|
|
239
|
+
*
|
|
240
|
+
* @example
|
|
241
|
+
* ```tsx
|
|
242
|
+
* <UnifiedBentoGrid
|
|
243
|
+
* layout="3x2"
|
|
244
|
+
* cards={[
|
|
245
|
+
* {
|
|
246
|
+
* id: 'stats',
|
|
247
|
+
* component: StatsCard,
|
|
248
|
+
* propsSelector: (d) => ({ count: d.total, label: 'Users' }),
|
|
249
|
+
* colSpan: 2,
|
|
250
|
+
* },
|
|
251
|
+
* {
|
|
252
|
+
* id: 'chart',
|
|
253
|
+
* component: ChartCard,
|
|
254
|
+
* propsSelector: (d) => ({ data: d.chartData }),
|
|
255
|
+
* visible: (d) => d.chartData.length > 0,
|
|
256
|
+
* loading: (d) => d.isLoading,
|
|
257
|
+
* },
|
|
258
|
+
* ]}
|
|
259
|
+
* data={myData}
|
|
260
|
+
* animated
|
|
261
|
+
* />
|
|
262
|
+
* ```
|
|
263
|
+
*/
|
|
264
|
+
declare function UnifiedBentoGrid<TData>({ layout, cards, data, className, style, cardWrapper, onCardError, ariaLabel, ariaLabelledBy, animated, animationDuration, loadingComponent: GlobalLoadingComponent, cellClassName, }: UnifiedBentoGridProps<TData>): React.ReactElement;
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Default card wrapper component
|
|
268
|
+
* Provides basic styling - users can override with their own wrapper
|
|
269
|
+
* Uses CSS custom properties for easy theming
|
|
270
|
+
*/
|
|
271
|
+
declare const BentoCard: React.FC<BentoCardProps>;
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Check if a layout config is a responsive config
|
|
275
|
+
*/
|
|
276
|
+
declare function isResponsiveConfig(layout: BentoLayoutConfig | PresetLayoutName | ResponsiveLayoutConfig): layout is ResponsiveLayoutConfig;
|
|
277
|
+
/**
|
|
278
|
+
* Hook to handle responsive layouts based on viewport width
|
|
279
|
+
* Returns the appropriate layout for the current viewport
|
|
280
|
+
*
|
|
281
|
+
* @example
|
|
282
|
+
* ```tsx
|
|
283
|
+
* const layout = useResponsiveLayout({
|
|
284
|
+
* default: "2x2",
|
|
285
|
+
* breakpoints: [
|
|
286
|
+
* { minWidth: 768, layout: "3x2" },
|
|
287
|
+
* { minWidth: 1024, layout: "4x2" },
|
|
288
|
+
* ],
|
|
289
|
+
* });
|
|
290
|
+
* ```
|
|
291
|
+
*/
|
|
292
|
+
declare function useResponsiveLayout(config: BentoLayoutConfig | PresetLayoutName | ResponsiveLayoutConfig): BentoLayoutConfig | PresetLayoutName;
|
|
293
|
+
/**
|
|
294
|
+
* Hook to track window width with debouncing
|
|
295
|
+
*/
|
|
296
|
+
declare function useWindowWidth(debounceMs?: number): number;
|
|
297
|
+
/**
|
|
298
|
+
* Hook to create card definitions from an object map
|
|
299
|
+
* Provides a cleaner API for defining cards
|
|
300
|
+
*
|
|
301
|
+
* @example
|
|
302
|
+
* ```tsx
|
|
303
|
+
* const cards = useCardDefinitions({
|
|
304
|
+
* stats: { component: StatsCard, colSpan: 2 },
|
|
305
|
+
* chart: { component: ChartCard },
|
|
306
|
+
* list: { component: ListCard, rowSpan: 2 },
|
|
307
|
+
* });
|
|
308
|
+
* ```
|
|
309
|
+
*/
|
|
310
|
+
declare function useCardDefinitions<T extends Record<string, Omit<BentoCardDefinition<any>, "id">>>(definitions: T): BentoCardDefinition<any>[];
|
|
311
|
+
/**
|
|
312
|
+
* Hook to create data mappings from an object map
|
|
313
|
+
* Provides a cleaner API for mapping data to cards
|
|
314
|
+
*
|
|
315
|
+
* @example
|
|
316
|
+
* ```tsx
|
|
317
|
+
* const dataMapping = useDataMapping<MyDataType>({
|
|
318
|
+
* stats: (data) => ({ count: data.total, label: 'Items' }),
|
|
319
|
+
* chart: (data) => ({ points: data.chartData }),
|
|
320
|
+
* });
|
|
321
|
+
* ```
|
|
322
|
+
*/
|
|
323
|
+
declare function useDataMapping<TData>(mappings: Record<string, (data: TData) => Record<string, unknown>>): CardDataMapping<TData>[];
|
|
324
|
+
/**
|
|
325
|
+
* Hook to create a layout configuration
|
|
326
|
+
* Handles both preset names and custom configs
|
|
327
|
+
*
|
|
328
|
+
* @example
|
|
329
|
+
* ```tsx
|
|
330
|
+
* // Using preset
|
|
331
|
+
* const layout = useLayout("3x3", ["stats", "chart", "list", ...]);
|
|
332
|
+
*
|
|
333
|
+
* // Using custom config
|
|
334
|
+
* const layout = useLayout({
|
|
335
|
+
* columns: 3,
|
|
336
|
+
* placements: [
|
|
337
|
+
* { cardId: "stats", col: 1, row: 1, colSpan: 2 },
|
|
338
|
+
* { cardId: "chart", col: 3, row: 1 },
|
|
339
|
+
* ]
|
|
340
|
+
* });
|
|
341
|
+
* ```
|
|
342
|
+
*/
|
|
343
|
+
declare function useLayout(config: PresetLayoutName | BentoLayoutConfig, cardIds?: string[]): BentoLayoutConfig;
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Create a simple grid layout where cards are placed sequentially
|
|
347
|
+
* Useful for quick prototyping
|
|
348
|
+
*
|
|
349
|
+
* @example
|
|
350
|
+
* ```ts
|
|
351
|
+
* const layout = createSimpleLayout(3, ["a", "b", "c", "d", "e", "f"]);
|
|
352
|
+
* // Creates a 3-column grid with cards placed left-to-right, top-to-bottom
|
|
353
|
+
* ```
|
|
354
|
+
*/
|
|
355
|
+
declare function createSimpleLayout(columns: number, cardIds: string[], gap?: number): BentoLayoutConfig;
|
|
356
|
+
/**
|
|
357
|
+
* Layout builder for fluent API
|
|
358
|
+
*
|
|
359
|
+
* @example
|
|
360
|
+
* ```ts
|
|
361
|
+
* const layout = layoutBuilder(3)
|
|
362
|
+
* .gap(16)
|
|
363
|
+
* .columnGap(20)
|
|
364
|
+
* .rowGap(12)
|
|
365
|
+
* .place("hero", 1, 1, { colSpan: 2, rowSpan: 2 })
|
|
366
|
+
* .place("stats", 3, 1)
|
|
367
|
+
* .place("chart", 3, 2)
|
|
368
|
+
* .build();
|
|
369
|
+
* ```
|
|
370
|
+
*/
|
|
371
|
+
declare function layoutBuilder(columns: number): {
|
|
372
|
+
gap(value: number): /*elided*/ any;
|
|
373
|
+
columnGap(value: number): /*elided*/ any;
|
|
374
|
+
rowGap(value: number): /*elided*/ any;
|
|
375
|
+
rows(value: number): /*elided*/ any;
|
|
376
|
+
place(cardId: string, col: number, row: number, options?: {
|
|
377
|
+
colSpan?: number;
|
|
378
|
+
rowSpan?: number;
|
|
379
|
+
}): /*elided*/ any;
|
|
380
|
+
build(): BentoLayoutConfig;
|
|
381
|
+
};
|
|
382
|
+
/**
|
|
383
|
+
* Validate a layout configuration
|
|
384
|
+
* Returns array of validation errors (empty if valid)
|
|
385
|
+
*/
|
|
386
|
+
declare function validateLayout(layout: BentoLayoutConfig): string[];
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Preset layout definitions
|
|
390
|
+
* Users can use these by name instead of defining custom layouts
|
|
391
|
+
*/
|
|
392
|
+
declare const PRESET_LAYOUTS: Record<PresetLayoutName, PresetLayout>;
|
|
393
|
+
/**
|
|
394
|
+
* Convert a preset name to a full layout config with card IDs
|
|
395
|
+
* Supports both legacy array-based assignment and explicit slot mapping
|
|
396
|
+
*/
|
|
397
|
+
declare function presetToLayout(presetName: PresetLayoutName, cardIdsOrMapping: string[] | PresetSlotMapping[]): BentoLayoutConfig;
|
|
398
|
+
/**
|
|
399
|
+
* Get available slot names for a preset layout
|
|
400
|
+
* Useful for discovering what slots are available
|
|
401
|
+
*/
|
|
402
|
+
declare function getPresetSlotNames(presetName: PresetLayoutName): string[];
|
|
403
|
+
/**
|
|
404
|
+
* Check if a value is a preset layout name
|
|
405
|
+
*/
|
|
406
|
+
declare function isPresetName(value: unknown): value is PresetLayoutName;
|
|
407
|
+
|
|
408
|
+
export { BentoCard, type BentoCardDefinition, type BentoCardProps, BentoGrid, type BentoGridProps, type BentoLayoutConfig, type BreakpointConfig, type CardDataMapping, type CardPlacement, type CardWrapperProps, PRESET_LAYOUTS, type PresetLayout, type PresetLayoutName, type PresetSlotMapping, type ResponsiveLayoutConfig, UnifiedBentoGrid, type UnifiedBentoGridProps, type UnifiedCardDefinition, createSimpleLayout, getPresetSlotNames, isPresetName, isResponsiveConfig, layoutBuilder, presetToLayout, useCardDefinitions, useDataMapping, useLayout, useResponsiveLayout, useWindowWidth, validateLayout };
|