@thangdevalone/meeting-grid-layout-core 1.4.1

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.
@@ -0,0 +1,351 @@
1
+ /**
2
+ * Dimensions of an element (width and height in pixels)
3
+ */
4
+ interface GridDimensions {
5
+ width: number;
6
+ height: number;
7
+ }
8
+ /**
9
+ * Position of a grid item
10
+ */
11
+ interface Position {
12
+ top: number;
13
+ left: number;
14
+ }
15
+ /**
16
+ * Layout modes for the grid
17
+ * - gallery: Flexible grid that fills all available space. Supports pin mode with pinnedIndex.
18
+ * - spotlight: Single participant in focus, others hidden
19
+ */
20
+ type LayoutMode = 'gallery' | 'spotlight';
21
+ /**
22
+ * Options for creating a basic grid
23
+ */
24
+ interface GridOptions {
25
+ /** Aspect ratio in format "width:height" (e.g., "16:9", "4:3") */
26
+ aspectRatio: string;
27
+ /** Number of items in the grid */
28
+ count: number;
29
+ /** Container dimensions */
30
+ dimensions: GridDimensions;
31
+ /** Gap between items in pixels */
32
+ gap: number;
33
+ }
34
+ /**
35
+ * Aspect ratio configuration for individual items
36
+ * - string: Custom ratio like "16:9", "9:16", "4:3", "1:1"
37
+ * - 'auto': Use actual content dimensions (requires callback)
38
+ */
39
+ type ItemAspectRatio = string | 'auto';
40
+ /**
41
+ * Extended options for meet-style grid with layout modes
42
+ */
43
+ interface MeetGridOptions extends GridOptions {
44
+ /** Layout mode for the grid */
45
+ layoutMode?: LayoutMode;
46
+ /** Index of pinned/focused item (main participant for spotlight/pin modes) */
47
+ pinnedIndex?: number;
48
+ /**
49
+ * Position of "others" thumbnails when a participant is pinned.
50
+ * In portrait containers, this is forced to 'bottom'.
51
+ * @default 'right'
52
+ */
53
+ othersPosition?: 'left' | 'right' | 'top' | 'bottom';
54
+ /** Maximum items per page for pagination (0 = no pagination) */
55
+ maxItemsPerPage?: number;
56
+ /** Current page index (0-based) for pagination */
57
+ currentPage?: number;
58
+ /**
59
+ * Maximum visible items (0 = show all).
60
+ * - In gallery mode without pin: limits total items displayed
61
+ * - In gallery mode with pin: limits "others" thumbnails (pinned item always visible)
62
+ * When set, shows a '+X' indicator on the last visible item.
63
+ * @default 0
64
+ */
65
+ maxVisible?: number;
66
+ /** Current page for items (0-based), used when maxVisible > 0 for pagination */
67
+ currentVisiblePage?: number;
68
+ /**
69
+ * Per-item aspect ratio configurations (index-based)
70
+ * Allows different aspect ratios per participant:
71
+ * - Use "9:16" for mobile/portrait participants
72
+ * - Use "16:9" for desktop/landscape participants
73
+ * - Use undefined to inherit from global aspectRatio
74
+ * @example
75
+ * itemAspectRatios: ["16:9", "9:16", undefined]
76
+ */
77
+ itemAspectRatios?: (ItemAspectRatio | undefined)[];
78
+ /**
79
+ * Custom width for the floating PiP item in 2-person mode.
80
+ * When set, overrides the width resolved from floatBreakpoints.
81
+ */
82
+ floatWidth?: number;
83
+ /**
84
+ * Custom height for the floating PiP item in 2-person mode.
85
+ * When set, overrides the height resolved from floatBreakpoints.
86
+ */
87
+ floatHeight?: number;
88
+ /**
89
+ * Responsive breakpoints for the floating PiP in 2-person mode.
90
+ * When provided, PiP size auto-adjusts based on container width.
91
+ * Use `DEFAULT_FLOAT_BREAKPOINTS` as a starting point or define your own.
92
+ * `floatWidth`/`floatHeight` still override the resolved size when set.
93
+ *
94
+ * @example
95
+ * // Use default 5-level responsive breakpoints
96
+ * floatBreakpoints: DEFAULT_FLOAT_BREAKPOINTS
97
+ *
98
+ * // Custom breakpoints
99
+ * floatBreakpoints: [
100
+ * { minWidth: 0, width: 80, height: 110 },
101
+ * { minWidth: 600, width: 150, height: 200 },
102
+ * { minWidth: 1200, width: 250, height: 330 },
103
+ * ]
104
+ */
105
+ floatBreakpoints?: PipBreakpoint[];
106
+ }
107
+ /**
108
+ * Pagination info returned with grid result
109
+ */
110
+ interface PaginationInfo {
111
+ /** Whether pagination is enabled */
112
+ enabled: boolean;
113
+ /** Current page (0-based) */
114
+ currentPage: number;
115
+ /** Total number of pages */
116
+ totalPages: number;
117
+ /** Items shown on current page */
118
+ itemsOnPage: number;
119
+ /** Start index of items on current page */
120
+ startIndex: number;
121
+ /** End index of items on current page (exclusive) */
122
+ endIndex: number;
123
+ }
124
+ /**
125
+ * Result from grid calculations
126
+ */
127
+ interface GridResult {
128
+ /** Width of each grid item */
129
+ width: number;
130
+ /** Height of each grid item */
131
+ height: number;
132
+ /** Number of rows */
133
+ rows: number;
134
+ /** Number of columns */
135
+ cols: number;
136
+ /** Function to get position of item at index */
137
+ getPosition: (index: number) => Position;
138
+ }
139
+ /**
140
+ * Content dimensions result with positioning info
141
+ */
142
+ interface ContentDimensions extends GridDimensions {
143
+ /** Offset from cell top to center the content */
144
+ offsetTop: number;
145
+ /** Offset from cell left to center the content */
146
+ offsetLeft: number;
147
+ }
148
+ /**
149
+ * Responsive breakpoint configuration for PiP sizing.
150
+ * The system selects the breakpoint with the largest `minWidth` that is <= container width.
151
+ *
152
+ * @example
153
+ * // Custom breakpoints
154
+ * const breakpoints: PipBreakpoint[] = [
155
+ * { minWidth: 0, width: 80, height: 110 }, // Small mobile
156
+ * { minWidth: 480, width: 120, height: 160 }, // Mobile
157
+ * { minWidth: 768, width: 160, height: 215 }, // Tablet
158
+ * { minWidth: 1024, width: 200, height: 270 }, // Desktop
159
+ * ]
160
+ */
161
+ interface PipBreakpoint {
162
+ /** Minimum container width (px) for this breakpoint to apply */
163
+ minWidth: number;
164
+ /** PiP width at this breakpoint (px) */
165
+ width: number;
166
+ /** PiP height at this breakpoint (px) */
167
+ height: number;
168
+ }
169
+ /**
170
+ * Default responsive breakpoints for PiP sizing.
171
+ * Provides 5 levels from small mobile to large desktop.
172
+ *
173
+ * | Breakpoint | Container Width | PiP Size |
174
+ * | -------------- | --------------- | ---------- |
175
+ * | Small mobile | 0 – 479px | 100 × 135 |
176
+ * | Mobile/Tablet | 480 – 767px | 130 × 175 |
177
+ * | Tablet | 768 – 1023px | 160 × 215 |
178
+ * | Desktop | 1024 – 1439px | 180 × 240 |
179
+ * | Large Desktop | 1440px+ | 220 × 295 |
180
+ */
181
+ declare const DEFAULT_FLOAT_BREAKPOINTS: PipBreakpoint[];
182
+ /**
183
+ * Resolve PiP size from responsive breakpoints based on container width.
184
+ * Selects the breakpoint with the largest `minWidth` that is <= `containerWidth`.
185
+ *
186
+ * @param containerWidth - Current container width in pixels
187
+ * @param breakpoints - Array of PipBreakpoint configurations
188
+ * @returns Resolved { width, height } for the PiP
189
+ */
190
+ declare function resolveFloatSize(containerWidth: number, breakpoints: PipBreakpoint[]): GridDimensions;
191
+ /**
192
+ * Extended result for meet-style grid
193
+ */
194
+ interface MeetGridResult extends GridResult {
195
+ /** Layout mode used */
196
+ layoutMode: LayoutMode;
197
+ /** Get item cell dimensions (the grid cell size, may vary by index in some modes) */
198
+ getItemDimensions: (index: number) => GridDimensions;
199
+ /** Check if item is the main/featured item */
200
+ isMainItem: (index: number) => boolean;
201
+ /** Pagination info (if pagination is enabled) */
202
+ pagination: PaginationInfo;
203
+ /** Check if item should be visible on current page */
204
+ isItemVisible: (index: number) => boolean;
205
+ /**
206
+ * Number of hidden items (for '+X more' indicator).
207
+ * When maxVisible is set and there are more participants than allowed,
208
+ * this indicates how many are hidden.
209
+ */
210
+ hiddenCount: number;
211
+ /**
212
+ * Get the last visible item index in the "others" section.
213
+ * Returns -1 if no items are visible or if there's no "others" section.
214
+ * Useful for showing '+X more' indicator on the last visible item.
215
+ */
216
+ getLastVisibleOthersIndex: () => number;
217
+ /**
218
+ * Get the actual content dimensions within a cell.
219
+ * Use this when items have different aspect ratios (e.g., phone vs desktop).
220
+ * Returns dimensions fitted within the cell while maintaining the item's aspect ratio.
221
+ *
222
+ * @param index - The item index
223
+ * @param itemRatio - The item's aspect ratio ("16:9", "9:16", or undefined for cell dimensions)
224
+ * @returns Content dimensions with offset for centering within the cell
225
+ *
226
+ * @example
227
+ * // For a mobile participant (9:16)
228
+ * const content = grid.getItemContentDimensions(0, "9:16")
229
+ *
230
+ * // Apply in React:
231
+ * <div style={{
232
+ * width: content.width,
233
+ * height: content.height,
234
+ * marginTop: content.offsetTop,
235
+ * marginLeft: content.offsetLeft
236
+ * }}>
237
+ */
238
+ getItemContentDimensions: (index: number, itemRatio?: ItemAspectRatio) => ContentDimensions;
239
+ /**
240
+ * Index of the item that should be rendered as a floating PiP overlay.
241
+ * When set, the component layer (GridItem) should render this item as a
242
+ * draggable FloatingGridItem instead of a regular positioned item.
243
+ * Used automatically in 2-person mode (Zoom-style layout).
244
+ */
245
+ floatIndex?: number;
246
+ /**
247
+ * Dimensions for the floating PiP item.
248
+ * Only set when floatIndex is defined.
249
+ */
250
+ floatDimensions?: GridDimensions;
251
+ }
252
+ /**
253
+ * Parses the Aspect Ratio string to actual ratio (height/width)
254
+ * @param ratio The aspect ratio in the format of "width:height" (e.g., "16:9")
255
+ * @returns The parsed value of aspect ratio (height/width)
256
+ */
257
+ declare function getAspectRatio(ratio: string): number;
258
+ /**
259
+ * Parse aspect ratio to get width/height multiplier
260
+ */
261
+ declare function parseAspectRatio(ratio: string): {
262
+ widthRatio: number;
263
+ heightRatio: number;
264
+ };
265
+ /**
266
+ * Calculate content dimensions that fit within a cell while maintaining aspect ratio
267
+ * @param cellDimensions - The cell dimensions to fit content into
268
+ * @param itemRatio - The content's aspect ratio ("16:9", "9:16", etc.)
269
+ * @param defaultRatio - The default aspect ratio to use if itemRatio is undefined
270
+ * @returns Content dimensions with offset for centering
271
+ */
272
+ declare function calculateContentDimensions(cellDimensions: GridDimensions, itemRatio?: ItemAspectRatio, defaultRatio?: string): ContentDimensions;
273
+ /**
274
+ * Calculates grid item dimensions for items that can fit in a container.
275
+ * Adapted from: https://stackoverflow.com/a/28268965
276
+ */
277
+ declare function getGridItemDimensions({ count, dimensions, aspectRatio, gap }: GridOptions): {
278
+ width: number;
279
+ height: number;
280
+ rows: number;
281
+ cols: number;
282
+ };
283
+ /**
284
+ * Creates a utility function which helps you position grid items in a container.
285
+ */
286
+ declare function createGridItemPositioner({ parentDimensions, dimensions, rows, cols, count, gap, }: {
287
+ parentDimensions: GridDimensions;
288
+ dimensions: GridDimensions;
289
+ rows: number;
290
+ cols: number;
291
+ count: number;
292
+ gap: number;
293
+ }): (index: number) => Position;
294
+ /**
295
+ * Calculates data required for making a responsive grid.
296
+ */
297
+ declare function createGrid({ aspectRatio, count, dimensions, gap }: GridOptions): GridResult;
298
+ /**
299
+ * Create a meet-style grid with support for different layout modes.
300
+ * This is the main function for creating video conferencing-style layouts.
301
+ */
302
+ declare function createMeetGrid(options: MeetGridOptions): MeetGridResult;
303
+ /**
304
+ * Spring animation configuration presets
305
+ */
306
+ declare const springPresets: {
307
+ /** Snappy animations for UI interactions */
308
+ readonly snappy: {
309
+ readonly stiffness: 400;
310
+ readonly damping: 30;
311
+ };
312
+ /** Smooth animations for layout changes */
313
+ readonly smooth: {
314
+ readonly stiffness: 300;
315
+ readonly damping: 30;
316
+ };
317
+ /** Gentle animations for subtle effects */
318
+ readonly gentle: {
319
+ readonly stiffness: 200;
320
+ readonly damping: 25;
321
+ };
322
+ /** Bouncy animations for playful effects */
323
+ readonly bouncy: {
324
+ readonly stiffness: 400;
325
+ readonly damping: 15;
326
+ };
327
+ };
328
+ type SpringPreset = keyof typeof springPresets;
329
+ /**
330
+ * Get spring configuration for Motion animations
331
+ */
332
+ declare function getSpringConfig(preset?: SpringPreset): {
333
+ stiffness: 400;
334
+ damping: 30;
335
+ type: "spring";
336
+ } | {
337
+ stiffness: 300;
338
+ damping: 30;
339
+ type: "spring";
340
+ } | {
341
+ stiffness: 200;
342
+ damping: 25;
343
+ type: "spring";
344
+ } | {
345
+ stiffness: 400;
346
+ damping: 15;
347
+ type: "spring";
348
+ };
349
+
350
+ export { DEFAULT_FLOAT_BREAKPOINTS, calculateContentDimensions, createGrid, createGridItemPositioner, createMeetGrid, getAspectRatio, getGridItemDimensions, getSpringConfig, parseAspectRatio, resolveFloatSize, springPresets };
351
+ export type { ContentDimensions, GridDimensions, GridOptions, GridResult, ItemAspectRatio, LayoutMode, MeetGridOptions, MeetGridResult, PaginationInfo, PipBreakpoint, Position, SpringPreset };