@thangdevalone/meet-layout-grid-react 1.1.0 → 1.2.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/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # @thangdevalone/meet-layout-grid-react
2
2
 
3
- React bindings for meet-layout-grid with Motion animations.
3
+ React 18+ bindings for meet-layout-grid with Motion animations.
4
+
5
+ > For full documentation, layout modes, and API reference, see the [main README](https://github.com/thangdevalone/meet-layout-grid#readme).
4
6
 
5
7
  ## Installation
6
8
 
@@ -8,19 +10,14 @@ React bindings for meet-layout-grid with Motion animations.
8
10
  npm install @thangdevalone/meet-layout-grid-react
9
11
  ```
10
12
 
11
- ## Quick start
13
+ ## Quick Start
12
14
 
13
15
  ```tsx
14
16
  import { GridContainer, GridItem } from '@thangdevalone/meet-layout-grid-react'
15
17
 
16
18
  function MeetingGrid({ participants }) {
17
19
  return (
18
- <GridContainer
19
- aspectRatio="16:9"
20
- gap={8}
21
- layoutMode="gallery"
22
- count={participants.length}
23
- >
20
+ <GridContainer aspectRatio="16:9" gap={8} layoutMode="gallery" count={participants.length}>
24
21
  {participants.map((p, index) => (
25
22
  <GridItem key={p.id} index={index}>
26
23
  <VideoTile participant={p} />
@@ -35,75 +32,64 @@ function MeetingGrid({ participants }) {
35
32
 
36
33
  ### `<GridContainer>`
37
34
 
38
- Wraps the grid, computes layout, provides context.
39
-
40
- ```tsx
41
- <GridContainer
42
- aspectRatio="16:9"
43
- gap={8}
44
- count={6}
45
- layoutMode="gallery"
46
- pinnedIndex={0}
47
- sidebarPosition="right"
48
- sidebarRatio={0.25}
49
- springPreset="smooth"
50
- >
51
- {children}
52
- </GridContainer>
53
- ```
35
+ Wraps the grid, computes layout, and provides context to children.
36
+
37
+ | Prop | Type | Default | Description |
38
+ | ------------------ | -------------- | ----------- | ------------------------------ |
39
+ | `aspectRatio` | `string` | `'16:9'` | Default tile aspect ratio |
40
+ | `gap` | `number` | `8` | Gap between tiles (px) |
41
+ | `count` | `number` | required | Number of items |
42
+ | `layoutMode` | `LayoutMode` | `'gallery'` | `gallery` \| `spotlight` |
43
+ | `pinnedIndex` | `number` | - | Pinned participant index |
44
+ | `othersPosition` | `string` | `'right'` | Thumbnail position in pin mode |
45
+ | `springPreset` | `SpringPreset` | `'smooth'` | Animation preset |
46
+ | `maxItemsPerPage` | `number` | - | Max items per page |
47
+ | `currentPage` | `number` | `0` | Current page |
48
+ | `maxVisible` | `number` | - | Max visible in others area |
49
+ | `itemAspectRatios` | `array` | - | Per-item aspect ratios |
54
50
 
55
51
  ### `<GridItem>`
56
52
 
57
- One grid cell; uses Motion for layout animation.
58
-
59
- ```tsx
60
- <GridItem index={0} disableAnimation={false}>
61
- {content}
62
- </GridItem>
63
- ```
64
-
65
- ## Hooks
53
+ Single grid cell with Motion layout animation.
66
54
 
67
- ### `useGridDimensions(ref)`
55
+ | Prop | Type | Default | Description |
56
+ | ------------------ | --------- | -------- | ------------------------ |
57
+ | `index` | `number` | required | Item index |
58
+ | `disableAnimation` | `boolean` | `false` | Disable layout animation |
68
59
 
69
- Uses ResizeObserver to track element size.
60
+ Render prop: `{({ contentDimensions, isLastVisibleOther, hiddenCount }) => ...}`
70
61
 
71
- ```tsx
72
- const ref = useRef<HTMLDivElement>(null)
73
- const dimensions = useGridDimensions(ref)
74
- // { width: number, height: number }
75
- ```
62
+ ### `<FloatingGridItem>`
76
63
 
77
- ### `useMeetGrid(options)`
64
+ Draggable Picture-in-Picture overlay with corner snapping.
78
65
 
79
- Compute grid layout yourself.
66
+ | Prop | Type | Default | Description |
67
+ | ---------------- | ---------- | ---------------- | ---------------------------- |
68
+ | `width` | `number` | - | Floating item width |
69
+ | `height` | `number` | - | Floating item height |
70
+ | `anchor` | `string` | `'bottom-right'` | Corner anchor position |
71
+ | `visible` | `boolean` | `true` | Show/hide the floating item |
72
+ | `edgePadding` | `number` | - | Padding from container edges |
73
+ | `borderRadius` | `number` | - | Border radius |
74
+ | `onAnchorChange` | `function` | - | Callback when anchor changes |
80
75
 
81
- ```tsx
82
- const grid = useMeetGrid({
83
- dimensions: { width: 800, height: 600 },
84
- count: 6,
85
- aspectRatio: '16:9',
86
- gap: 8,
87
- layoutMode: 'sidebar',
88
- sidebarPosition: 'bottom', // speaker-like layout
89
- })
90
-
91
- const { top, left } = grid.getPosition(index)
92
- const { width, height } = grid.getItemDimensions(index)
93
- ```
76
+ ### `<GridOverlay>`
94
77
 
95
- ## Layout modes
78
+ Full-grid overlay for screen sharing, whiteboard, etc.
96
79
 
97
- - **gallery** Same-size tiles (use `pinnedIndex` to pin a participant)
98
- - **spotlight** Single participant
99
- - **sidebar** Main + thumbnails (use `sidebarPosition: 'bottom'` for speaker-like layout)
80
+ | Prop | Type | Default | Description |
81
+ | ----------------- | --------- | ------- | ------------------------ |
82
+ | `visible` | `boolean` | `false` | Show/hide the overlay |
83
+ | `backgroundColor` | `string` | - | Overlay background color |
100
84
 
101
- ## Animation presets
85
+ ## Hooks
102
86
 
103
- - `snappy` — Fast feedback
104
- - `smooth` Layout changes (default)
105
- - `gentle` Subtle
106
- - `bouncy` Slight overshoot
87
+ | Hook | Description |
88
+ | ------------------------ | ----------------------------------------- |
89
+ | `useGridDimensions(ref)` | Track element size via ResizeObserver |
90
+ | `useMeetGrid(options)` | Compute grid layout programmatically |
91
+ | `useGridContext()` | Access grid context from child components |
92
+ | `useGridAnimation()` | Access animation config from context |
107
93
 
108
94
  ## License
109
95
 
package/dist/index.cjs CHANGED
@@ -57,13 +57,11 @@ function useMeetGrid(options) {
57
57
  options.gap,
58
58
  options.layoutMode,
59
59
  options.pinnedIndex,
60
- options.sidebarPosition,
61
- options.sidebarRatio,
60
+ options.othersPosition,
62
61
  options.maxItemsPerPage,
63
62
  options.currentPage,
64
63
  options.maxVisible,
65
64
  options.currentVisiblePage,
66
- options.flexLayout,
67
65
  itemAspectRatiosKey
68
66
  ]);
69
67
  }
@@ -71,128 +69,104 @@ function useGridAnimation(preset = "smooth") {
71
69
  return React.useMemo(() => meetLayoutGridCore.getSpringConfig(preset), [preset]);
72
70
  }
73
71
 
74
- const GridContainer = React.forwardRef(
75
- function GridContainer2({
76
- children,
77
- aspectRatio = "16:9",
78
- gap = 8,
79
- count,
80
- layoutMode = "gallery",
72
+ const GridContainer = React.forwardRef(function GridContainer2({
73
+ children,
74
+ aspectRatio = "16:9",
75
+ gap = 8,
76
+ count,
77
+ layoutMode = "gallery",
78
+ pinnedIndex,
79
+ othersPosition,
80
+ springPreset = "smooth",
81
+ style,
82
+ className,
83
+ maxItemsPerPage,
84
+ currentPage,
85
+ maxVisible,
86
+ currentVisiblePage,
87
+ itemAspectRatios,
88
+ ...props
89
+ }, forwardedRef) {
90
+ const internalRef = React.useRef(null);
91
+ const ref = forwardedRef || internalRef;
92
+ const dimensions = useGridDimensions(ref);
93
+ const childCount = count ?? React__default.Children.count(children);
94
+ const gridOptions = {
95
+ dimensions,
96
+ count: childCount,
97
+ aspectRatio,
98
+ gap,
99
+ layoutMode,
81
100
  pinnedIndex,
82
- sidebarPosition,
83
- sidebarRatio,
84
- springPreset = "smooth",
85
- style,
86
- className,
101
+ othersPosition,
87
102
  maxItemsPerPage,
88
103
  currentPage,
89
104
  maxVisible,
90
105
  currentVisiblePage,
91
- itemAspectRatios,
92
- flexLayout,
93
- ...props
94
- }, forwardedRef) {
95
- const internalRef = React.useRef(null);
96
- const ref = forwardedRef || internalRef;
97
- const dimensions = useGridDimensions(ref);
98
- const childCount = count ?? React__default.Children.count(children);
99
- const gridOptions = {
100
- dimensions,
101
- count: childCount,
102
- aspectRatio,
103
- gap,
104
- layoutMode,
105
- pinnedIndex,
106
- sidebarPosition,
107
- sidebarRatio,
108
- maxItemsPerPage,
109
- currentPage,
110
- maxVisible,
111
- currentVisiblePage,
112
- itemAspectRatios,
113
- flexLayout
114
- };
115
- const grid = useMeetGrid(gridOptions);
116
- const containerStyle = {
117
- position: "relative",
118
- width: "100%",
119
- height: "100%",
120
- overflow: "hidden",
121
- ...style
122
- };
123
- return /* @__PURE__ */ React__default.createElement(GridContext.Provider, { value: { dimensions, grid, springPreset } }, /* @__PURE__ */ React__default.createElement("div", { ref, style: containerStyle, className, ...props }, children));
106
+ itemAspectRatios
107
+ };
108
+ const grid = useMeetGrid(gridOptions);
109
+ const containerStyle = {
110
+ position: "relative",
111
+ width: "100%",
112
+ height: "100%",
113
+ overflow: "hidden",
114
+ ...style
115
+ };
116
+ return /* @__PURE__ */ React__default.createElement(GridContext.Provider, { value: { dimensions, grid, springPreset } }, /* @__PURE__ */ React__default.createElement("div", { ref, style: containerStyle, className, ...props }, children));
117
+ });
118
+ const GridItem = React.forwardRef(function GridItem2({
119
+ index,
120
+ children,
121
+ itemAspectRatio,
122
+ transition: customTransition,
123
+ disableAnimation = false,
124
+ className,
125
+ style,
126
+ ...props
127
+ }, ref) {
128
+ const { grid, springPreset } = useGridContext();
129
+ if (!grid) {
130
+ return null;
124
131
  }
125
- );
126
- const GridItem = React.forwardRef(
127
- function GridItem2({
128
- index,
129
- children,
130
- itemAspectRatio,
131
- transition: customTransition,
132
- disableAnimation = false,
133
- className,
134
- style,
135
- ...props
136
- }, ref) {
137
- const { grid, springPreset } = useGridContext();
138
- if (!grid) {
139
- return null;
140
- }
141
- if (!grid.isItemVisible(index)) {
142
- return null;
143
- }
144
- const { top, left } = grid.getPosition(index);
145
- const { width, height } = grid.getItemDimensions(index);
146
- const contentDimensions = grid.getItemContentDimensions(index, itemAspectRatio);
147
- const isMain = grid.isMainItem(index);
148
- if (grid.layoutMode === "spotlight" && !isMain) {
149
- return null;
150
- }
151
- const springConfig = meetLayoutGridCore.getSpringConfig(springPreset);
152
- const transition = customTransition ?? {
153
- type: springConfig.type,
154
- stiffness: springConfig.stiffness,
155
- damping: springConfig.damping
156
- };
157
- const animatedStyle = {
158
- position: "absolute",
159
- width,
160
- height,
161
- top,
162
- left
163
- };
164
- const lastVisibleOthersIndex = grid.getLastVisibleOthersIndex();
165
- const isLastVisibleOther = index === lastVisibleOthersIndex;
166
- const hiddenCount = grid.hiddenCount;
167
- const renderChildren = () => {
168
- if (typeof children === "function") {
169
- return children({ contentDimensions, isLastVisibleOther, hiddenCount });
170
- }
171
- return children;
172
- };
173
- if (disableAnimation) {
174
- return /* @__PURE__ */ React__default.createElement(
175
- "div",
176
- {
177
- ref,
178
- style: { ...animatedStyle, ...style },
179
- className,
180
- "data-grid-index": index,
181
- "data-grid-main": isMain,
182
- ...props
183
- },
184
- renderChildren()
185
- );
132
+ if (!grid.isItemVisible(index)) {
133
+ return null;
134
+ }
135
+ const { top, left } = grid.getPosition(index);
136
+ const { width, height } = grid.getItemDimensions(index);
137
+ const contentDimensions = grid.getItemContentDimensions(index, itemAspectRatio);
138
+ const isMain = grid.isMainItem(index);
139
+ if (grid.layoutMode === "spotlight" && !isMain) {
140
+ return null;
141
+ }
142
+ const springConfig = meetLayoutGridCore.getSpringConfig(springPreset);
143
+ const transition = customTransition ?? {
144
+ type: springConfig.type,
145
+ stiffness: springConfig.stiffness,
146
+ damping: springConfig.damping
147
+ };
148
+ const animatedStyle = {
149
+ position: "absolute",
150
+ width,
151
+ height,
152
+ top,
153
+ left
154
+ };
155
+ const lastVisibleOthersIndex = grid.getLastVisibleOthersIndex();
156
+ const isLastVisibleOther = index === lastVisibleOthersIndex;
157
+ const hiddenCount = grid.hiddenCount;
158
+ const renderChildren = () => {
159
+ if (typeof children === "function") {
160
+ return children({ contentDimensions, isLastVisibleOther, hiddenCount });
186
161
  }
162
+ return children;
163
+ };
164
+ if (disableAnimation) {
187
165
  return /* @__PURE__ */ React__default.createElement(
188
- react.motion.div,
166
+ "div",
189
167
  {
190
168
  ref,
191
- layout: true,
192
- initial: false,
193
- animate: animatedStyle,
194
- transition,
195
- style,
169
+ style: { ...animatedStyle, ...style },
196
170
  className,
197
171
  "data-grid-index": index,
198
172
  "data-grid-main": isMain,
@@ -201,7 +175,23 @@ const GridItem = React.forwardRef(
201
175
  renderChildren()
202
176
  );
203
177
  }
204
- );
178
+ return /* @__PURE__ */ React__default.createElement(
179
+ react.motion.div,
180
+ {
181
+ ref,
182
+ layout: true,
183
+ initial: false,
184
+ animate: animatedStyle,
185
+ transition,
186
+ style,
187
+ className,
188
+ "data-grid-index": index,
189
+ "data-grid-main": isMain,
190
+ ...props
191
+ },
192
+ renderChildren()
193
+ );
194
+ });
205
195
  const FloatingGridItem = React.forwardRef(
206
196
  function FloatingGridItem2({
207
197
  children,
@@ -224,20 +214,26 @@ const FloatingGridItem = React.forwardRef(
224
214
  const x = react.useMotionValue(0);
225
215
  const y = react.useMotionValue(0);
226
216
  const [isInitialized, setIsInitialized] = React__default.useState(false);
227
- const getCornerPosition = React__default.useCallback((corner) => {
228
- const padding = edgePadding + initialPosition.x;
229
- switch (corner) {
230
- case "top-left":
231
- return { x: padding, y: padding };
232
- case "top-right":
233
- return { x: dimensions.width - width - padding, y: padding };
234
- case "bottom-left":
235
- return { x: padding, y: dimensions.height - height - padding };
236
- case "bottom-right":
237
- default:
238
- return { x: dimensions.width - width - padding, y: dimensions.height - height - padding };
239
- }
240
- }, [dimensions.width, dimensions.height, width, height, edgePadding, initialPosition.x]);
217
+ const getCornerPosition = React__default.useCallback(
218
+ (corner) => {
219
+ const padding = edgePadding + initialPosition.x;
220
+ switch (corner) {
221
+ case "top-left":
222
+ return { x: padding, y: padding };
223
+ case "top-right":
224
+ return { x: dimensions.width - width - padding, y: padding };
225
+ case "bottom-left":
226
+ return { x: padding, y: dimensions.height - height - padding };
227
+ case "bottom-right":
228
+ default:
229
+ return {
230
+ x: dimensions.width - width - padding,
231
+ y: dimensions.height - height - padding
232
+ };
233
+ }
234
+ },
235
+ [dimensions.width, dimensions.height, width, height, edgePadding, initialPosition.x]
236
+ );
241
237
  React__default.useEffect(() => {
242
238
  if (dimensions.width > 0 && dimensions.height > 0 && !isInitialized) {
243
239
  const pos = getCornerPosition(currentAnchor);
@@ -321,29 +317,27 @@ const FloatingGridItem = React.forwardRef(
321
317
  );
322
318
  }
323
319
  );
324
- const GridOverlay = React.forwardRef(
325
- function GridOverlay2({ visible = true, backgroundColor = "rgba(0,0,0,0.5)", children, style, ...props }, ref) {
326
- if (!visible)
327
- return null;
328
- return /* @__PURE__ */ React__default.createElement(
329
- "div",
330
- {
331
- ref,
332
- style: {
333
- position: "absolute",
334
- inset: 0,
335
- display: "flex",
336
- alignItems: "center",
337
- justifyContent: "center",
338
- backgroundColor,
339
- ...style
340
- },
341
- ...props
320
+ const GridOverlay = React.forwardRef(function GridOverlay2({ visible = true, backgroundColor = "rgba(0,0,0,0.5)", children, style, ...props }, ref) {
321
+ if (!visible)
322
+ return null;
323
+ return /* @__PURE__ */ React__default.createElement(
324
+ "div",
325
+ {
326
+ ref,
327
+ style: {
328
+ position: "absolute",
329
+ inset: 0,
330
+ display: "flex",
331
+ alignItems: "center",
332
+ justifyContent: "center",
333
+ backgroundColor,
334
+ ...style
342
335
  },
343
- children
344
- );
345
- }
346
- );
336
+ ...props
337
+ },
338
+ children
339
+ );
340
+ });
347
341
 
348
342
  exports.createGrid = meetLayoutGridCore.createGrid;
349
343
  exports.createGridItemPositioner = meetLayoutGridCore.createGridItemPositioner;
package/dist/index.d.cts CHANGED
@@ -59,12 +59,14 @@ interface GridContainerProps extends Omit<HTMLAttributes<HTMLDivElement>, 'child
59
59
  count?: number;
60
60
  /** Layout mode */
61
61
  layoutMode?: LayoutMode;
62
- /** Index of pinned/focused item (main participant for spotlight/sidebar modes) */
62
+ /** Index of pinned/focused item (main participant for pin/spotlight modes) */
63
63
  pinnedIndex?: number;
64
- /** Sidebar position */
65
- sidebarPosition?: 'left' | 'right' | 'top' | 'bottom';
66
- /** Sidebar ratio (0-1) */
67
- sidebarRatio?: number;
64
+ /**
65
+ * Position of "others" thumbnails when a participant is pinned.
66
+ * In portrait containers, this is forced to 'bottom'.
67
+ * @default 'right'
68
+ */
69
+ othersPosition?: 'left' | 'right' | 'top' | 'bottom';
68
70
  /** Spring animation preset */
69
71
  springPreset?: SpringPreset;
70
72
  /** Custom container style */
@@ -75,23 +77,16 @@ interface GridContainerProps extends Omit<HTMLAttributes<HTMLDivElement>, 'child
75
77
  maxItemsPerPage?: number;
76
78
  /** Current page index (0-based) for pagination */
77
79
  currentPage?: number;
78
- /** Maximum visible items (0 = show all). In gallery mode: limits all items. In sidebar: limits "others". */
80
+ /** Maximum visible items (0 = show all). In gallery mode without pin: limits all items. With pin: limits "others". */
79
81
  maxVisible?: number;
80
82
  /** Current page for visible items (0-based), used when maxVisible > 0 */
81
83
  currentVisiblePage?: number;
82
84
  /**
83
85
  * Per-item aspect ratio configurations.
84
- * Use different ratios for mobile (9:16), desktop (16:9), or whiteboard (fill).
85
- * @example ['16:9', '9:16', 'fill', undefined]
86
+ * Use different ratios for mobile (9:16), desktop (16:9).
87
+ * @example ['16:9', '9:16', undefined]
86
88
  */
87
89
  itemAspectRatios?: (ItemAspectRatio | undefined)[];
88
- /**
89
- * Enable flexible cell sizing based on item aspect ratios.
90
- * When true, portrait items (9:16) get narrower cells, landscape items (16:9) get wider cells.
91
- * Items are packed into rows intelligently.
92
- * @default false
93
- */
94
- flexLayout?: boolean;
95
90
  }
96
91
  /**
97
92
  * Container component for the meet grid.
package/dist/index.d.mts CHANGED
@@ -59,12 +59,14 @@ interface GridContainerProps extends Omit<HTMLAttributes<HTMLDivElement>, 'child
59
59
  count?: number;
60
60
  /** Layout mode */
61
61
  layoutMode?: LayoutMode;
62
- /** Index of pinned/focused item (main participant for spotlight/sidebar modes) */
62
+ /** Index of pinned/focused item (main participant for pin/spotlight modes) */
63
63
  pinnedIndex?: number;
64
- /** Sidebar position */
65
- sidebarPosition?: 'left' | 'right' | 'top' | 'bottom';
66
- /** Sidebar ratio (0-1) */
67
- sidebarRatio?: number;
64
+ /**
65
+ * Position of "others" thumbnails when a participant is pinned.
66
+ * In portrait containers, this is forced to 'bottom'.
67
+ * @default 'right'
68
+ */
69
+ othersPosition?: 'left' | 'right' | 'top' | 'bottom';
68
70
  /** Spring animation preset */
69
71
  springPreset?: SpringPreset;
70
72
  /** Custom container style */
@@ -75,23 +77,16 @@ interface GridContainerProps extends Omit<HTMLAttributes<HTMLDivElement>, 'child
75
77
  maxItemsPerPage?: number;
76
78
  /** Current page index (0-based) for pagination */
77
79
  currentPage?: number;
78
- /** Maximum visible items (0 = show all). In gallery mode: limits all items. In sidebar: limits "others". */
80
+ /** Maximum visible items (0 = show all). In gallery mode without pin: limits all items. With pin: limits "others". */
79
81
  maxVisible?: number;
80
82
  /** Current page for visible items (0-based), used when maxVisible > 0 */
81
83
  currentVisiblePage?: number;
82
84
  /**
83
85
  * Per-item aspect ratio configurations.
84
- * Use different ratios for mobile (9:16), desktop (16:9), or whiteboard (fill).
85
- * @example ['16:9', '9:16', 'fill', undefined]
86
+ * Use different ratios for mobile (9:16), desktop (16:9).
87
+ * @example ['16:9', '9:16', undefined]
86
88
  */
87
89
  itemAspectRatios?: (ItemAspectRatio | undefined)[];
88
- /**
89
- * Enable flexible cell sizing based on item aspect ratios.
90
- * When true, portrait items (9:16) get narrower cells, landscape items (16:9) get wider cells.
91
- * Items are packed into rows intelligently.
92
- * @default false
93
- */
94
- flexLayout?: boolean;
95
90
  }
96
91
  /**
97
92
  * Container component for the meet grid.
package/dist/index.d.ts CHANGED
@@ -59,12 +59,14 @@ interface GridContainerProps extends Omit<HTMLAttributes<HTMLDivElement>, 'child
59
59
  count?: number;
60
60
  /** Layout mode */
61
61
  layoutMode?: LayoutMode;
62
- /** Index of pinned/focused item (main participant for spotlight/sidebar modes) */
62
+ /** Index of pinned/focused item (main participant for pin/spotlight modes) */
63
63
  pinnedIndex?: number;
64
- /** Sidebar position */
65
- sidebarPosition?: 'left' | 'right' | 'top' | 'bottom';
66
- /** Sidebar ratio (0-1) */
67
- sidebarRatio?: number;
64
+ /**
65
+ * Position of "others" thumbnails when a participant is pinned.
66
+ * In portrait containers, this is forced to 'bottom'.
67
+ * @default 'right'
68
+ */
69
+ othersPosition?: 'left' | 'right' | 'top' | 'bottom';
68
70
  /** Spring animation preset */
69
71
  springPreset?: SpringPreset;
70
72
  /** Custom container style */
@@ -75,23 +77,16 @@ interface GridContainerProps extends Omit<HTMLAttributes<HTMLDivElement>, 'child
75
77
  maxItemsPerPage?: number;
76
78
  /** Current page index (0-based) for pagination */
77
79
  currentPage?: number;
78
- /** Maximum visible items (0 = show all). In gallery mode: limits all items. In sidebar: limits "others". */
80
+ /** Maximum visible items (0 = show all). In gallery mode without pin: limits all items. With pin: limits "others". */
79
81
  maxVisible?: number;
80
82
  /** Current page for visible items (0-based), used when maxVisible > 0 */
81
83
  currentVisiblePage?: number;
82
84
  /**
83
85
  * Per-item aspect ratio configurations.
84
- * Use different ratios for mobile (9:16), desktop (16:9), or whiteboard (fill).
85
- * @example ['16:9', '9:16', 'fill', undefined]
86
+ * Use different ratios for mobile (9:16), desktop (16:9).
87
+ * @example ['16:9', '9:16', undefined]
86
88
  */
87
89
  itemAspectRatios?: (ItemAspectRatio | undefined)[];
88
- /**
89
- * Enable flexible cell sizing based on item aspect ratios.
90
- * When true, portrait items (9:16) get narrower cells, landscape items (16:9) get wider cells.
91
- * Items are packed into rows intelligently.
92
- * @default false
93
- */
94
- flexLayout?: boolean;
95
90
  }
96
91
  /**
97
92
  * Container component for the meet grid.
package/dist/index.mjs CHANGED
@@ -52,13 +52,11 @@ function useMeetGrid(options) {
52
52
  options.gap,
53
53
  options.layoutMode,
54
54
  options.pinnedIndex,
55
- options.sidebarPosition,
56
- options.sidebarRatio,
55
+ options.othersPosition,
57
56
  options.maxItemsPerPage,
58
57
  options.currentPage,
59
58
  options.maxVisible,
60
59
  options.currentVisiblePage,
61
- options.flexLayout,
62
60
  itemAspectRatiosKey
63
61
  ]);
64
62
  }
@@ -66,128 +64,104 @@ function useGridAnimation(preset = "smooth") {
66
64
  return useMemo(() => getSpringConfig(preset), [preset]);
67
65
  }
68
66
 
69
- const GridContainer = forwardRef(
70
- function GridContainer2({
71
- children,
72
- aspectRatio = "16:9",
73
- gap = 8,
74
- count,
75
- layoutMode = "gallery",
67
+ const GridContainer = forwardRef(function GridContainer2({
68
+ children,
69
+ aspectRatio = "16:9",
70
+ gap = 8,
71
+ count,
72
+ layoutMode = "gallery",
73
+ pinnedIndex,
74
+ othersPosition,
75
+ springPreset = "smooth",
76
+ style,
77
+ className,
78
+ maxItemsPerPage,
79
+ currentPage,
80
+ maxVisible,
81
+ currentVisiblePage,
82
+ itemAspectRatios,
83
+ ...props
84
+ }, forwardedRef) {
85
+ const internalRef = useRef(null);
86
+ const ref = forwardedRef || internalRef;
87
+ const dimensions = useGridDimensions(ref);
88
+ const childCount = count ?? React.Children.count(children);
89
+ const gridOptions = {
90
+ dimensions,
91
+ count: childCount,
92
+ aspectRatio,
93
+ gap,
94
+ layoutMode,
76
95
  pinnedIndex,
77
- sidebarPosition,
78
- sidebarRatio,
79
- springPreset = "smooth",
80
- style,
81
- className,
96
+ othersPosition,
82
97
  maxItemsPerPage,
83
98
  currentPage,
84
99
  maxVisible,
85
100
  currentVisiblePage,
86
- itemAspectRatios,
87
- flexLayout,
88
- ...props
89
- }, forwardedRef) {
90
- const internalRef = useRef(null);
91
- const ref = forwardedRef || internalRef;
92
- const dimensions = useGridDimensions(ref);
93
- const childCount = count ?? React.Children.count(children);
94
- const gridOptions = {
95
- dimensions,
96
- count: childCount,
97
- aspectRatio,
98
- gap,
99
- layoutMode,
100
- pinnedIndex,
101
- sidebarPosition,
102
- sidebarRatio,
103
- maxItemsPerPage,
104
- currentPage,
105
- maxVisible,
106
- currentVisiblePage,
107
- itemAspectRatios,
108
- flexLayout
109
- };
110
- const grid = useMeetGrid(gridOptions);
111
- const containerStyle = {
112
- position: "relative",
113
- width: "100%",
114
- height: "100%",
115
- overflow: "hidden",
116
- ...style
117
- };
118
- return /* @__PURE__ */ React.createElement(GridContext.Provider, { value: { dimensions, grid, springPreset } }, /* @__PURE__ */ React.createElement("div", { ref, style: containerStyle, className, ...props }, children));
101
+ itemAspectRatios
102
+ };
103
+ const grid = useMeetGrid(gridOptions);
104
+ const containerStyle = {
105
+ position: "relative",
106
+ width: "100%",
107
+ height: "100%",
108
+ overflow: "hidden",
109
+ ...style
110
+ };
111
+ return /* @__PURE__ */ React.createElement(GridContext.Provider, { value: { dimensions, grid, springPreset } }, /* @__PURE__ */ React.createElement("div", { ref, style: containerStyle, className, ...props }, children));
112
+ });
113
+ const GridItem = forwardRef(function GridItem2({
114
+ index,
115
+ children,
116
+ itemAspectRatio,
117
+ transition: customTransition,
118
+ disableAnimation = false,
119
+ className,
120
+ style,
121
+ ...props
122
+ }, ref) {
123
+ const { grid, springPreset } = useGridContext();
124
+ if (!grid) {
125
+ return null;
119
126
  }
120
- );
121
- const GridItem = forwardRef(
122
- function GridItem2({
123
- index,
124
- children,
125
- itemAspectRatio,
126
- transition: customTransition,
127
- disableAnimation = false,
128
- className,
129
- style,
130
- ...props
131
- }, ref) {
132
- const { grid, springPreset } = useGridContext();
133
- if (!grid) {
134
- return null;
135
- }
136
- if (!grid.isItemVisible(index)) {
137
- return null;
138
- }
139
- const { top, left } = grid.getPosition(index);
140
- const { width, height } = grid.getItemDimensions(index);
141
- const contentDimensions = grid.getItemContentDimensions(index, itemAspectRatio);
142
- const isMain = grid.isMainItem(index);
143
- if (grid.layoutMode === "spotlight" && !isMain) {
144
- return null;
145
- }
146
- const springConfig = getSpringConfig(springPreset);
147
- const transition = customTransition ?? {
148
- type: springConfig.type,
149
- stiffness: springConfig.stiffness,
150
- damping: springConfig.damping
151
- };
152
- const animatedStyle = {
153
- position: "absolute",
154
- width,
155
- height,
156
- top,
157
- left
158
- };
159
- const lastVisibleOthersIndex = grid.getLastVisibleOthersIndex();
160
- const isLastVisibleOther = index === lastVisibleOthersIndex;
161
- const hiddenCount = grid.hiddenCount;
162
- const renderChildren = () => {
163
- if (typeof children === "function") {
164
- return children({ contentDimensions, isLastVisibleOther, hiddenCount });
165
- }
166
- return children;
167
- };
168
- if (disableAnimation) {
169
- return /* @__PURE__ */ React.createElement(
170
- "div",
171
- {
172
- ref,
173
- style: { ...animatedStyle, ...style },
174
- className,
175
- "data-grid-index": index,
176
- "data-grid-main": isMain,
177
- ...props
178
- },
179
- renderChildren()
180
- );
127
+ if (!grid.isItemVisible(index)) {
128
+ return null;
129
+ }
130
+ const { top, left } = grid.getPosition(index);
131
+ const { width, height } = grid.getItemDimensions(index);
132
+ const contentDimensions = grid.getItemContentDimensions(index, itemAspectRatio);
133
+ const isMain = grid.isMainItem(index);
134
+ if (grid.layoutMode === "spotlight" && !isMain) {
135
+ return null;
136
+ }
137
+ const springConfig = getSpringConfig(springPreset);
138
+ const transition = customTransition ?? {
139
+ type: springConfig.type,
140
+ stiffness: springConfig.stiffness,
141
+ damping: springConfig.damping
142
+ };
143
+ const animatedStyle = {
144
+ position: "absolute",
145
+ width,
146
+ height,
147
+ top,
148
+ left
149
+ };
150
+ const lastVisibleOthersIndex = grid.getLastVisibleOthersIndex();
151
+ const isLastVisibleOther = index === lastVisibleOthersIndex;
152
+ const hiddenCount = grid.hiddenCount;
153
+ const renderChildren = () => {
154
+ if (typeof children === "function") {
155
+ return children({ contentDimensions, isLastVisibleOther, hiddenCount });
181
156
  }
157
+ return children;
158
+ };
159
+ if (disableAnimation) {
182
160
  return /* @__PURE__ */ React.createElement(
183
- motion.div,
161
+ "div",
184
162
  {
185
163
  ref,
186
- layout: true,
187
- initial: false,
188
- animate: animatedStyle,
189
- transition,
190
- style,
164
+ style: { ...animatedStyle, ...style },
191
165
  className,
192
166
  "data-grid-index": index,
193
167
  "data-grid-main": isMain,
@@ -196,7 +170,23 @@ const GridItem = forwardRef(
196
170
  renderChildren()
197
171
  );
198
172
  }
199
- );
173
+ return /* @__PURE__ */ React.createElement(
174
+ motion.div,
175
+ {
176
+ ref,
177
+ layout: true,
178
+ initial: false,
179
+ animate: animatedStyle,
180
+ transition,
181
+ style,
182
+ className,
183
+ "data-grid-index": index,
184
+ "data-grid-main": isMain,
185
+ ...props
186
+ },
187
+ renderChildren()
188
+ );
189
+ });
200
190
  const FloatingGridItem = forwardRef(
201
191
  function FloatingGridItem2({
202
192
  children,
@@ -219,20 +209,26 @@ const FloatingGridItem = forwardRef(
219
209
  const x = useMotionValue(0);
220
210
  const y = useMotionValue(0);
221
211
  const [isInitialized, setIsInitialized] = React.useState(false);
222
- const getCornerPosition = React.useCallback((corner) => {
223
- const padding = edgePadding + initialPosition.x;
224
- switch (corner) {
225
- case "top-left":
226
- return { x: padding, y: padding };
227
- case "top-right":
228
- return { x: dimensions.width - width - padding, y: padding };
229
- case "bottom-left":
230
- return { x: padding, y: dimensions.height - height - padding };
231
- case "bottom-right":
232
- default:
233
- return { x: dimensions.width - width - padding, y: dimensions.height - height - padding };
234
- }
235
- }, [dimensions.width, dimensions.height, width, height, edgePadding, initialPosition.x]);
212
+ const getCornerPosition = React.useCallback(
213
+ (corner) => {
214
+ const padding = edgePadding + initialPosition.x;
215
+ switch (corner) {
216
+ case "top-left":
217
+ return { x: padding, y: padding };
218
+ case "top-right":
219
+ return { x: dimensions.width - width - padding, y: padding };
220
+ case "bottom-left":
221
+ return { x: padding, y: dimensions.height - height - padding };
222
+ case "bottom-right":
223
+ default:
224
+ return {
225
+ x: dimensions.width - width - padding,
226
+ y: dimensions.height - height - padding
227
+ };
228
+ }
229
+ },
230
+ [dimensions.width, dimensions.height, width, height, edgePadding, initialPosition.x]
231
+ );
236
232
  React.useEffect(() => {
237
233
  if (dimensions.width > 0 && dimensions.height > 0 && !isInitialized) {
238
234
  const pos = getCornerPosition(currentAnchor);
@@ -316,28 +312,26 @@ const FloatingGridItem = forwardRef(
316
312
  );
317
313
  }
318
314
  );
319
- const GridOverlay = forwardRef(
320
- function GridOverlay2({ visible = true, backgroundColor = "rgba(0,0,0,0.5)", children, style, ...props }, ref) {
321
- if (!visible)
322
- return null;
323
- return /* @__PURE__ */ React.createElement(
324
- "div",
325
- {
326
- ref,
327
- style: {
328
- position: "absolute",
329
- inset: 0,
330
- display: "flex",
331
- alignItems: "center",
332
- justifyContent: "center",
333
- backgroundColor,
334
- ...style
335
- },
336
- ...props
315
+ const GridOverlay = forwardRef(function GridOverlay2({ visible = true, backgroundColor = "rgba(0,0,0,0.5)", children, style, ...props }, ref) {
316
+ if (!visible)
317
+ return null;
318
+ return /* @__PURE__ */ React.createElement(
319
+ "div",
320
+ {
321
+ ref,
322
+ style: {
323
+ position: "absolute",
324
+ inset: 0,
325
+ display: "flex",
326
+ alignItems: "center",
327
+ justifyContent: "center",
328
+ backgroundColor,
329
+ ...style
337
330
  },
338
- children
339
- );
340
- }
341
- );
331
+ ...props
332
+ },
333
+ children
334
+ );
335
+ });
342
336
 
343
337
  export { FloatingGridItem, GridContainer, GridContext, GridItem, GridOverlay, useGridAnimation, useGridContext, useGridDimensions, useMeetGrid };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thangdevalone/meet-layout-grid-react",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "React integration for meet-layout-grid with Motion animations",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -42,7 +42,7 @@
42
42
  },
43
43
  "dependencies": {
44
44
  "motion": "^11.15.0",
45
- "@thangdevalone/meet-layout-grid-core": "1.1.0"
45
+ "@thangdevalone/meet-layout-grid-core": "1.2.0"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@types/react": "^18.2.0",