@thangdevalone/meeting-grid-layout-vue 1.5.11 → 1.9.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
@@ -52,9 +52,7 @@ Wraps the grid and provides layout via `provide`/`inject`.
52
52
  | `max-visible` | `number` | `0` | Max visible in others area (0 = show all) |
53
53
  | `current-visible-page` | `number` | `0` | Current page for visible items (when max-visible > 0) |
54
54
  | `item-aspect-ratios` | `(ItemAspectRatio \| undefined)[]` | - | Per-item aspect ratios |
55
- | `float-width` | `number` | - | Width of the auto-float PiP (2-person mode). Overrides breakpoints. |
56
- | `float-height` | `number` | - | Height of the auto-float PiP (2-person mode). Overrides breakpoints. |
57
- | `float-breakpoints` | `PipBreakpoint[]` | - | Responsive breakpoints for auto-float PiP (see [Responsive PiP](#responsive-pip)) |
55
+ | `float-size` | `FloatSize` | `'medium'` | Responsive size for auto-float PiP: `'small' \| 'medium' \| 'large'` |
58
56
  | `pip-index` | `number` | `1` | Which participant (0 or 1) is the floating PiP in 2-person mode |
59
57
  | `pin-only` | `boolean` | `false` | Mobile/tablet pin-only mode: page 0 = pinned full-screen, page 1+ = others gallery (≤1024px) |
60
58
  | `disable-float` | `boolean` | `false` | Disable Floating PiP in 2-person mode; shows standard gallery grid instead |
@@ -91,9 +89,8 @@ Draggable Picture-in-Picture overlay with corner snapping.
91
89
 
92
90
  | Prop | Type | Default | Description |
93
91
  | ------------------ | ------------------------------------------------------------- | -------------------------------- | ------------------------------------------------ |
94
- | `width` | `number` | `120` | Floating item width (px). Overridden by `breakpoints`. |
95
- | `height` | `number` | `160` | Floating item height (px). Overridden by `breakpoints`. |
96
- | `breakpoints` | `PipBreakpoint[]` | - | Responsive breakpoints (see [Responsive PiP](#responsive-pip)) |
92
+ | `size` | `'small' \| 'medium' \| 'large'` | - | Responsive pre-defined sizes (see [Responsive PiP](#responsive-pip)) |
93
+ | `aspect-ratio` | `string` | `'16:9'` | Aspect ratio of the floating PiP item |
97
94
  | `initial-position` | `{ x: number; y: number }` | `{ x: 16, y: 16 }` | Extra offset from anchor corner |
98
95
  | `anchor` | `'top-left' \| 'top-right' \| 'bottom-left' \| 'bottom-right'` | `'bottom-right'` | Which corner to snap/anchor the item |
99
96
  | `visible` | `boolean` | `true` | Whether the floating item is visible |
@@ -118,21 +115,21 @@ Full-grid overlay for screen sharing, whiteboard, etc.
118
115
 
119
116
  ## Responsive PiP
120
117
 
121
- PiP auto-adjusts size based on container width via breakpoints:
118
+ PiP auto-adjusts size based on container width via pre-defined sizes:
122
119
 
123
120
  ```vue
124
121
  <script setup>
125
- import { FloatingGridItem, GridContainer, GridItem, DEFAULT_FLOAT_BREAKPOINTS } from '@thangdevalone/meeting-grid-layout-vue'
122
+ import { FloatingGridItem, GridContainer, GridItem } from '@thangdevalone/meeting-grid-layout-vue'
126
123
  </script>
127
124
 
128
125
  <template>
129
126
  <!-- Standalone — auto-responsive -->
130
- <FloatingGridItem :breakpoints="DEFAULT_FLOAT_BREAKPOINTS">
127
+ <FloatingGridItem size="large">
131
128
  <VideoTile />
132
129
  </FloatingGridItem>
133
130
 
134
131
  <!-- Auto-float in 2-person mode -->
135
- <GridContainer :count="2" :float-breakpoints="DEFAULT_FLOAT_BREAKPOINTS">
132
+ <GridContainer :count="2" float-size="large">
136
133
  <GridItem v-for="(p, i) in participants" :key="p.id" :index="i">
137
134
  <VideoTile :participant="p" />
138
135
  </GridItem>
@@ -140,7 +137,7 @@ import { FloatingGridItem, GridContainer, GridItem, DEFAULT_FLOAT_BREAKPOINTS }
140
137
  </template>
141
138
  ```
142
139
 
143
- > See the [main README](https://github.com/thangdevalone/meeting-grid-layout#floating-pip-picture-in-picture) for default breakpoint table, custom breakpoints, and `resolveFloatSize` utility.
140
+ > See the [main README](https://github.com/thangdevalone/meeting-grid-layout#floating-pip-picture-in-picture) for full documentation on `floatSize`.
144
141
 
145
142
  ## Composables
146
143
 
@@ -157,12 +154,11 @@ import { FloatingGridItem, GridContainer, GridItem, DEFAULT_FLOAT_BREAKPOINTS }
157
154
  | --------------------------- | ---------- | ------------------------------------------------ |
158
155
  | `createMeetGrid` | function | Create grid layout (Vanilla JS) |
159
156
  | `createGrid` | function | Low-level grid computation |
160
- | `resolveFloatSize` | function | Resolve PiP size for a given container width |
161
- | `DEFAULT_FLOAT_BREAKPOINTS` | constant | Ready-made 5-level responsive PiP breakpoints |
157
+ | `resolveFloatWidth` | function | Resolve PiP width for a given container width |
162
158
  | `getSpringConfig` | function | Get spring config from preset name |
163
159
  | `springPresets` | object | All spring presets |
164
160
  | `getAspectRatio` | function | Parse aspect ratio string |
165
- | `PipBreakpoint` | type | Breakpoint definition type |
161
+ | `FloatSize` | type | Float size enum (`'small'`, `'medium'`, `'large'`) |
166
162
  | `ContentDimensions` | type | Content dimensions with offset |
167
163
 
168
164
  ## License
package/dist/index.cjs CHANGED
@@ -124,24 +124,13 @@ const GridContainer = vue.defineComponent({
124
124
  type: Array,
125
125
  default: void 0
126
126
  },
127
- /** Custom width for the floating PiP item in 2-person mode */
128
- floatWidth: {
129
- type: Number,
130
- default: void 0
131
- },
132
- /** Custom height for the floating PiP item in 2-person mode */
133
- floatHeight: {
134
- type: Number,
135
- default: void 0
136
- },
137
127
  /**
138
- * Responsive breakpoints for the floating PiP in 2-person mode.
139
- * When provided, PiP size auto-adjusts based on container width.
140
- * Use `DEFAULT_FLOAT_BREAKPOINTS` for a ready-made 5-level responsive config.
128
+ * Pre-defined responsive size for the floating PiP window.
129
+ * 'small', 'medium', or 'large'.
141
130
  */
142
- floatBreakpoints: {
143
- type: Array,
144
- default: void 0
131
+ floatSize: {
132
+ type: String,
133
+ default: "medium"
145
134
  },
146
135
  /**
147
136
  * Index of the participant to show as the floating PiP in 2-person mode.
@@ -183,6 +172,14 @@ const GridContainer = vue.defineComponent({
183
172
  type: Boolean,
184
173
  default: false
185
174
  },
175
+ /**
176
+ * If true, forces the GridItem wrappers to maintain their aspect ratio exactly.
177
+ * @default false
178
+ */
179
+ forceAspectRatio: {
180
+ type: Boolean,
181
+ default: false
182
+ },
186
183
  /** HTML tag to render */
187
184
  tag: {
188
185
  type: String,
@@ -205,19 +202,19 @@ const GridContainer = vue.defineComponent({
205
202
  maxVisible: props.maxVisible,
206
203
  currentVisiblePage: props.currentVisiblePage,
207
204
  itemAspectRatios: props.itemAspectRatios,
208
- floatWidth: props.floatWidth,
209
- floatHeight: props.floatHeight,
210
- floatBreakpoints: props.floatBreakpoints,
205
+ floatSize: props.floatSize,
211
206
  pipIndex: props.pipIndex,
212
207
  pinOnly: props.pinOnly,
213
- disableFloat: props.disableFloat
208
+ disableFloat: props.disableFloat,
209
+ forceAspectRatio: props.forceAspectRatio
214
210
  }));
215
211
  const grid = useMeetGrid(gridOptions);
216
212
  vue.provide(GridContextKey, {
217
213
  grid,
218
214
  springPreset: vue.toRef(props, "springPreset"),
219
215
  dimensions,
220
- disableAnimation: vue.toRef(props, "disableAnimation")
216
+ disableAnimation: vue.toRef(props, "disableAnimation"),
217
+ forceAspectRatio: vue.toRef(props, "forceAspectRatio")
221
218
  });
222
219
  return () => vue.h(
223
220
  props.tag,
@@ -264,15 +261,40 @@ const GridItem = vue.defineComponent({
264
261
  console.warn("GridItem must be used inside a GridContainer");
265
262
  return () => null;
266
263
  }
267
- const { grid, springPreset, dimensions: containerDimensions, disableAnimation: containerDisableAnimation } = context;
264
+ const { grid, springPreset, dimensions: containerDimensions, disableAnimation: containerDisableAnimation, forceAspectRatio } = context;
268
265
  const noAnimation = vue.computed(() => containerDisableAnimation.value || props.disableAnimation);
269
- const position = vue.computed(() => grid.value.getPosition(props.index));
270
- const dimensions = vue.computed(() => grid.value.getItemDimensions(props.index));
271
- const contentDimensions = vue.computed(
272
- () => grid.value.getItemContentDimensions(props.index, props.itemAspectRatio)
273
- );
274
266
  const isMain = vue.computed(() => grid.value.isMainItem(props.index));
275
267
  const isVisible = vue.computed(() => grid.value.isItemVisible(props.index));
268
+ const isFloat = vue.computed(() => grid.value.floatIndex === props.index);
269
+ const shouldForceRatio = vue.computed(() => forceAspectRatio.value || isFloat.value);
270
+ const position = vue.computed(() => {
271
+ const pos = grid.value.getPosition(props.index);
272
+ if (shouldForceRatio.value && !isFloat.value) {
273
+ const contentDims = grid.value.getItemContentDimensions(props.index, props.itemAspectRatio);
274
+ return {
275
+ top: pos.top + contentDims.offsetTop,
276
+ left: pos.left + contentDims.offsetLeft
277
+ };
278
+ }
279
+ return pos;
280
+ });
281
+ const dimensions = vue.computed(() => {
282
+ if (shouldForceRatio.value && !isFloat.value) {
283
+ const contentDims = grid.value.getItemContentDimensions(props.index, props.itemAspectRatio);
284
+ return {
285
+ width: contentDims.width,
286
+ height: contentDims.height
287
+ };
288
+ }
289
+ return grid.value.getItemDimensions(props.index);
290
+ });
291
+ const contentDimensions = vue.computed(() => {
292
+ const dims = grid.value.getItemContentDimensions(props.index, props.itemAspectRatio);
293
+ if (shouldForceRatio.value) {
294
+ return { ...dims, offsetTop: 0, offsetLeft: 0 };
295
+ }
296
+ return dims;
297
+ });
276
298
  const isHidden = vue.computed(() => {
277
299
  if (grid.value.layoutMode === "spotlight" && !isMain.value)
278
300
  return true;
@@ -280,8 +302,10 @@ const GridItem = vue.defineComponent({
280
302
  return true;
281
303
  return false;
282
304
  });
283
- const isFloat = vue.computed(() => grid.value.floatIndex === props.index);
284
- const floatDims = vue.computed(() => grid.value.floatDimensions ?? { width: 120, height: 160 });
305
+ const floatDims = vue.computed(() => {
306
+ const dims = grid.value.getItemContentDimensions(props.index, props.itemAspectRatio);
307
+ return { width: dims.width, height: dims.height };
308
+ });
285
309
  const floatAnchor = vue.ref(
286
310
  "bottom-right"
287
311
  );
@@ -339,20 +363,21 @@ const GridItem = vue.defineComponent({
339
363
  { immediate: true }
340
364
  );
341
365
  vue.watch(
342
- [floatAnchor, () => containerDimensions.value.width, () => containerDimensions.value.height],
343
- ([, w, h2]) => {
344
- if (isFloat.value && floatInitialized.value && w > 0 && h2 > 0) {
345
- const pos = getFloatCornerPos(floatAnchor.value);
346
- if (noAnimation.value) {
347
- x.set(pos.x);
348
- y.set(pos.y);
349
- } else {
350
- const springCfg = { type: "spring", stiffness: 400, damping: 30 };
351
- motionV.animate(x, pos.x, springCfg);
352
- motionV.animate(y, pos.y, springCfg);
353
- }
366
+ () => [
367
+ floatAnchor.value,
368
+ containerDimensions.value.width,
369
+ containerDimensions.value.height,
370
+ floatDims.value.width,
371
+ floatDims.value.height
372
+ ],
373
+ ([newAnchor, cw, ch]) => {
374
+ if (isFloat.value && floatInitialized.value && cw > 0 && ch > 0) {
375
+ const pos = getFloatCornerPos(newAnchor);
376
+ x.set(pos.x);
377
+ y.set(pos.y);
354
378
  }
355
- }
379
+ },
380
+ { flush: "post" }
356
381
  );
357
382
  const isLastVisibleOther = vue.computed(() => {
358
383
  const lastVisibleOthersIndex = grid.value.getLastVisibleOthersIndex();
@@ -564,14 +589,16 @@ const FloatingGridItem = vue.defineComponent({
564
589
  default: 160
565
590
  },
566
591
  /**
567
- * Responsive breakpoints for PiP sizing.
568
- * When provided, width/height auto-adjust based on container width.
569
- * Overrides the fixed `width`/`height` props.
570
- * Use `DEFAULT_FLOAT_BREAKPOINTS` for a ready-made 5-level responsive config.
592
+ * Pre-defined responsive size for the floating item.
593
+ * 'small', 'medium', or 'large'.
571
594
  */
572
- breakpoints: {
573
- type: Array,
574
- default: void 0
595
+ size: {
596
+ type: String,
597
+ default: "medium"
598
+ },
599
+ aspectRatio: {
600
+ type: String,
601
+ default: "16:9"
575
602
  },
576
603
  /** Initial position (x, y from container edges) */
577
604
  initialPosition: {
@@ -614,10 +641,13 @@ const FloatingGridItem = vue.defineComponent({
614
641
  const { dimensions, disableAnimation: containerDisableAnimation } = context;
615
642
  const currentAnchor = vue.ref(props.anchor);
616
643
  const effectiveSize = vue.computed(() => {
617
- if (props.breakpoints && props.breakpoints.length > 0 && dimensions.value.width > 0) {
618
- return meetingGridLayoutCore.resolveFloatSize(dimensions.value.width, props.breakpoints);
619
- }
620
- return { width: props.width, height: props.height };
644
+ const hwRatio = meetingGridLayoutCore.getAspectRatio(props.aspectRatio);
645
+ const baseSize = meetingGridLayoutCore.resolveFloatWidth(dimensions.value.width, props.size);
646
+ const baseArea = baseSize * baseSize;
647
+ return {
648
+ width: Math.sqrt(baseArea / hwRatio),
649
+ height: Math.sqrt(baseArea * hwRatio)
650
+ };
621
651
  });
622
652
  const x = motionV.useMotionValue(0);
623
653
  const y = motionV.useMotionValue(0);
@@ -672,41 +702,24 @@ const FloatingGridItem = vue.defineComponent({
672
702
  { immediate: true }
673
703
  );
674
704
  vue.watch(
675
- [
676
- () => props.anchor,
677
- () => containerDimensions.value.width,
678
- () => containerDimensions.value.height
705
+ () => [
706
+ props.anchor,
707
+ containerDimensions.value.width,
708
+ containerDimensions.value.height,
709
+ effectiveSize.value.width,
710
+ effectiveSize.value.height
679
711
  ],
680
- ([newAnchor, w, h2]) => {
681
- if (isInitialized.value && w > 0 && h2 > 0 && newAnchor !== currentAnchor.value) {
682
- currentAnchor.value = newAnchor;
683
- const pos = getCornerPosition(newAnchor);
684
- if (containerDisableAnimation.value) {
685
- x.set(pos.x);
686
- y.set(pos.y);
687
- } else {
688
- const springCfg = { type: "spring", stiffness: 400, damping: 30 };
689
- motionV.animate(x, pos.x, springCfg);
690
- motionV.animate(y, pos.y, springCfg);
691
- }
692
- }
693
- }
694
- );
695
- vue.watch(
696
- [() => effectiveSize.value.width, () => effectiveSize.value.height],
697
- () => {
698
- if (isInitialized.value && containerDimensions.value.width > 0 && containerDimensions.value.height > 0) {
699
- const pos = getCornerPosition(currentAnchor.value);
700
- if (containerDisableAnimation) {
701
- x.set(pos.x);
702
- y.set(pos.y);
703
- } else {
704
- const springCfg = { type: "spring", stiffness: 400, damping: 30 };
705
- motionV.animate(x, pos.x, springCfg);
706
- motionV.animate(y, pos.y, springCfg);
712
+ ([newAnchor, cw, ch]) => {
713
+ if (isInitialized.value && cw > 0 && ch > 0) {
714
+ if (newAnchor !== currentAnchor.value) {
715
+ currentAnchor.value = newAnchor;
707
716
  }
717
+ const pos = getCornerPosition(newAnchor);
718
+ x.set(pos.x);
719
+ y.set(pos.y);
708
720
  }
709
- }
721
+ },
722
+ { flush: "post" }
710
723
  );
711
724
  return () => {
712
725
  const dims = containerDimensions.value;
@@ -776,6 +789,7 @@ exports.createMeetGrid = meetingGridLayoutCore.createMeetGrid;
776
789
  exports.getAspectRatio = meetingGridLayoutCore.getAspectRatio;
777
790
  exports.getGridItemDimensions = meetingGridLayoutCore.getGridItemDimensions;
778
791
  exports.getSpringConfig = meetingGridLayoutCore.getSpringConfig;
792
+ exports.resolveFloatWidth = meetingGridLayoutCore.resolveFloatWidth;
779
793
  exports.springPresets = meetingGridLayoutCore.springPresets;
780
794
  exports.FloatingGridItem = FloatingGridItem;
781
795
  exports.GridContainer = GridContainer;
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as _thangdevalone_meeting_grid_layout_core from '@thangdevalone/meeting-grid-layout-core';
2
- import { GridDimensions, MeetGridOptions, MeetGridResult, SpringPreset, LayoutMode, ItemAspectRatio, PipBreakpoint } from '@thangdevalone/meeting-grid-layout-core';
3
- export { ContentDimensions, GridDimensions, GridOptions, ItemAspectRatio, LayoutMode, MeetGridOptions, MeetGridResult, Position, SpringPreset, createGrid, createGridItemPositioner, createMeetGrid, getAspectRatio, getGridItemDimensions, getSpringConfig, springPresets } from '@thangdevalone/meeting-grid-layout-core';
2
+ import { GridDimensions, MeetGridOptions, MeetGridResult, SpringPreset, LayoutMode, ItemAspectRatio, FloatSize } from '@thangdevalone/meeting-grid-layout-core';
3
+ export { ContentDimensions, FloatSize, GridDimensions, GridOptions, ItemAspectRatio, LayoutMode, MeetGridOptions, MeetGridResult, Position, SpringPreset, createGrid, createGridItemPositioner, createMeetGrid, getAspectRatio, getGridItemDimensions, getSpringConfig, resolveFloatWidth, springPresets } from '@thangdevalone/meeting-grid-layout-core';
4
4
  import * as vue from 'vue';
5
5
  import { Ref, ComputedRef, InjectionKey, PropType } from 'vue';
6
6
 
@@ -55,6 +55,7 @@ interface GridContextValue {
55
55
  springPreset: Ref<SpringPreset>;
56
56
  dimensions: Ref<GridDimensions>;
57
57
  disableAnimation: Ref<boolean>;
58
+ forceAspectRatio: Ref<boolean>;
58
59
  }
59
60
  declare const GridContextKey: InjectionKey<GridContextValue>;
60
61
  declare const GridContainer: vue.DefineComponent<vue.ExtractPropTypes<{
@@ -126,24 +127,13 @@ declare const GridContainer: vue.DefineComponent<vue.ExtractPropTypes<{
126
127
  type: PropType<(ItemAspectRatio | undefined)[]>;
127
128
  default: undefined;
128
129
  };
129
- /** Custom width for the floating PiP item in 2-person mode */
130
- floatWidth: {
131
- type: NumberConstructor;
132
- default: undefined;
133
- };
134
- /** Custom height for the floating PiP item in 2-person mode */
135
- floatHeight: {
136
- type: NumberConstructor;
137
- default: undefined;
138
- };
139
130
  /**
140
- * Responsive breakpoints for the floating PiP in 2-person mode.
141
- * When provided, PiP size auto-adjusts based on container width.
142
- * Use `DEFAULT_FLOAT_BREAKPOINTS` for a ready-made 5-level responsive config.
131
+ * Pre-defined responsive size for the floating PiP window.
132
+ * 'small', 'medium', or 'large'.
143
133
  */
144
- floatBreakpoints: {
145
- type: PropType<PipBreakpoint[]>;
146
- default: undefined;
134
+ floatSize: {
135
+ type: PropType<FloatSize>;
136
+ default: string;
147
137
  };
148
138
  /**
149
139
  * Index of the participant to show as the floating PiP in 2-person mode.
@@ -185,6 +175,14 @@ declare const GridContainer: vue.DefineComponent<vue.ExtractPropTypes<{
185
175
  type: BooleanConstructor;
186
176
  default: boolean;
187
177
  };
178
+ /**
179
+ * If true, forces the GridItem wrappers to maintain their aspect ratio exactly.
180
+ * @default false
181
+ */
182
+ forceAspectRatio: {
183
+ type: BooleanConstructor;
184
+ default: boolean;
185
+ };
188
186
  /** HTML tag to render */
189
187
  tag: {
190
188
  type: StringConstructor;
@@ -261,24 +259,13 @@ declare const GridContainer: vue.DefineComponent<vue.ExtractPropTypes<{
261
259
  type: PropType<(ItemAspectRatio | undefined)[]>;
262
260
  default: undefined;
263
261
  };
264
- /** Custom width for the floating PiP item in 2-person mode */
265
- floatWidth: {
266
- type: NumberConstructor;
267
- default: undefined;
268
- };
269
- /** Custom height for the floating PiP item in 2-person mode */
270
- floatHeight: {
271
- type: NumberConstructor;
272
- default: undefined;
273
- };
274
262
  /**
275
- * Responsive breakpoints for the floating PiP in 2-person mode.
276
- * When provided, PiP size auto-adjusts based on container width.
277
- * Use `DEFAULT_FLOAT_BREAKPOINTS` for a ready-made 5-level responsive config.
263
+ * Pre-defined responsive size for the floating PiP window.
264
+ * 'small', 'medium', or 'large'.
278
265
  */
279
- floatBreakpoints: {
280
- type: PropType<PipBreakpoint[]>;
281
- default: undefined;
266
+ floatSize: {
267
+ type: PropType<FloatSize>;
268
+ default: string;
282
269
  };
283
270
  /**
284
271
  * Index of the participant to show as the floating PiP in 2-person mode.
@@ -320,6 +307,14 @@ declare const GridContainer: vue.DefineComponent<vue.ExtractPropTypes<{
320
307
  type: BooleanConstructor;
321
308
  default: boolean;
322
309
  };
310
+ /**
311
+ * If true, forces the GridItem wrappers to maintain their aspect ratio exactly.
312
+ * @default false
313
+ */
314
+ forceAspectRatio: {
315
+ type: BooleanConstructor;
316
+ default: boolean;
317
+ };
323
318
  /** HTML tag to render */
324
319
  tag: {
325
320
  type: StringConstructor;
@@ -337,13 +332,12 @@ declare const GridContainer: vue.DefineComponent<vue.ExtractPropTypes<{
337
332
  maxVisible: number;
338
333
  currentVisiblePage: number;
339
334
  itemAspectRatios: (string | undefined)[];
340
- floatWidth: number;
341
- floatHeight: number;
342
- floatBreakpoints: PipBreakpoint[];
335
+ floatSize: FloatSize;
343
336
  pipIndex: number;
344
337
  pinOnly: boolean;
345
338
  disableFloat: boolean;
346
339
  disableAnimation: boolean;
340
+ forceAspectRatio: boolean;
347
341
  tag: string;
348
342
  }, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
349
343
  declare const GridItem: vue.DefineComponent<vue.ExtractPropTypes<{
@@ -435,14 +429,16 @@ declare const FloatingGridItem: vue.DefineComponent<vue.ExtractPropTypes<{
435
429
  default: number;
436
430
  };
437
431
  /**
438
- * Responsive breakpoints for PiP sizing.
439
- * When provided, width/height auto-adjust based on container width.
440
- * Overrides the fixed `width`/`height` props.
441
- * Use `DEFAULT_FLOAT_BREAKPOINTS` for a ready-made 5-level responsive config.
432
+ * Pre-defined responsive size for the floating item.
433
+ * 'small', 'medium', or 'large'.
442
434
  */
443
- breakpoints: {
444
- type: PropType<PipBreakpoint[]>;
445
- default: undefined;
435
+ size: {
436
+ type: PropType<"small" | "medium" | "large">;
437
+ default: string;
438
+ };
439
+ aspectRatio: {
440
+ type: StringConstructor;
441
+ default: string;
446
442
  };
447
443
  /** Initial position (x, y from container edges) */
448
444
  initialPosition: {
@@ -494,14 +490,16 @@ declare const FloatingGridItem: vue.DefineComponent<vue.ExtractPropTypes<{
494
490
  default: number;
495
491
  };
496
492
  /**
497
- * Responsive breakpoints for PiP sizing.
498
- * When provided, width/height auto-adjust based on container width.
499
- * Overrides the fixed `width`/`height` props.
500
- * Use `DEFAULT_FLOAT_BREAKPOINTS` for a ready-made 5-level responsive config.
493
+ * Pre-defined responsive size for the floating item.
494
+ * 'small', 'medium', or 'large'.
501
495
  */
502
- breakpoints: {
503
- type: PropType<PipBreakpoint[]>;
504
- default: undefined;
496
+ size: {
497
+ type: PropType<"small" | "medium" | "large">;
498
+ default: string;
499
+ };
500
+ aspectRatio: {
501
+ type: StringConstructor;
502
+ default: string;
505
503
  };
506
504
  /** Initial position (x, y from container edges) */
507
505
  initialPosition: {
@@ -542,13 +540,14 @@ declare const FloatingGridItem: vue.DefineComponent<vue.ExtractPropTypes<{
542
540
  }>> & Readonly<{
543
541
  onAnchorChange?: ((...args: any[]) => any) | undefined;
544
542
  }>, {
543
+ aspectRatio: string;
545
544
  borderRadius: number;
546
545
  boxShadow: string;
547
546
  height: number;
548
547
  width: number;
549
548
  anchor: "top-left" | "top-right" | "bottom-left" | "bottom-right";
549
+ size: "small" | "medium" | "large";
550
550
  visible: boolean;
551
- breakpoints: PipBreakpoint[];
552
551
  initialPosition: {
553
552
  x: number;
554
553
  y: number;
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as _thangdevalone_meeting_grid_layout_core from '@thangdevalone/meeting-grid-layout-core';
2
- import { GridDimensions, MeetGridOptions, MeetGridResult, SpringPreset, LayoutMode, ItemAspectRatio, PipBreakpoint } from '@thangdevalone/meeting-grid-layout-core';
3
- export { ContentDimensions, GridDimensions, GridOptions, ItemAspectRatio, LayoutMode, MeetGridOptions, MeetGridResult, Position, SpringPreset, createGrid, createGridItemPositioner, createMeetGrid, getAspectRatio, getGridItemDimensions, getSpringConfig, springPresets } from '@thangdevalone/meeting-grid-layout-core';
2
+ import { GridDimensions, MeetGridOptions, MeetGridResult, SpringPreset, LayoutMode, ItemAspectRatio, FloatSize } from '@thangdevalone/meeting-grid-layout-core';
3
+ export { ContentDimensions, FloatSize, GridDimensions, GridOptions, ItemAspectRatio, LayoutMode, MeetGridOptions, MeetGridResult, Position, SpringPreset, createGrid, createGridItemPositioner, createMeetGrid, getAspectRatio, getGridItemDimensions, getSpringConfig, resolveFloatWidth, springPresets } from '@thangdevalone/meeting-grid-layout-core';
4
4
  import * as vue from 'vue';
5
5
  import { Ref, ComputedRef, InjectionKey, PropType } from 'vue';
6
6
 
@@ -55,6 +55,7 @@ interface GridContextValue {
55
55
  springPreset: Ref<SpringPreset>;
56
56
  dimensions: Ref<GridDimensions>;
57
57
  disableAnimation: Ref<boolean>;
58
+ forceAspectRatio: Ref<boolean>;
58
59
  }
59
60
  declare const GridContextKey: InjectionKey<GridContextValue>;
60
61
  declare const GridContainer: vue.DefineComponent<vue.ExtractPropTypes<{
@@ -126,24 +127,13 @@ declare const GridContainer: vue.DefineComponent<vue.ExtractPropTypes<{
126
127
  type: PropType<(ItemAspectRatio | undefined)[]>;
127
128
  default: undefined;
128
129
  };
129
- /** Custom width for the floating PiP item in 2-person mode */
130
- floatWidth: {
131
- type: NumberConstructor;
132
- default: undefined;
133
- };
134
- /** Custom height for the floating PiP item in 2-person mode */
135
- floatHeight: {
136
- type: NumberConstructor;
137
- default: undefined;
138
- };
139
130
  /**
140
- * Responsive breakpoints for the floating PiP in 2-person mode.
141
- * When provided, PiP size auto-adjusts based on container width.
142
- * Use `DEFAULT_FLOAT_BREAKPOINTS` for a ready-made 5-level responsive config.
131
+ * Pre-defined responsive size for the floating PiP window.
132
+ * 'small', 'medium', or 'large'.
143
133
  */
144
- floatBreakpoints: {
145
- type: PropType<PipBreakpoint[]>;
146
- default: undefined;
134
+ floatSize: {
135
+ type: PropType<FloatSize>;
136
+ default: string;
147
137
  };
148
138
  /**
149
139
  * Index of the participant to show as the floating PiP in 2-person mode.
@@ -185,6 +175,14 @@ declare const GridContainer: vue.DefineComponent<vue.ExtractPropTypes<{
185
175
  type: BooleanConstructor;
186
176
  default: boolean;
187
177
  };
178
+ /**
179
+ * If true, forces the GridItem wrappers to maintain their aspect ratio exactly.
180
+ * @default false
181
+ */
182
+ forceAspectRatio: {
183
+ type: BooleanConstructor;
184
+ default: boolean;
185
+ };
188
186
  /** HTML tag to render */
189
187
  tag: {
190
188
  type: StringConstructor;
@@ -261,24 +259,13 @@ declare const GridContainer: vue.DefineComponent<vue.ExtractPropTypes<{
261
259
  type: PropType<(ItemAspectRatio | undefined)[]>;
262
260
  default: undefined;
263
261
  };
264
- /** Custom width for the floating PiP item in 2-person mode */
265
- floatWidth: {
266
- type: NumberConstructor;
267
- default: undefined;
268
- };
269
- /** Custom height for the floating PiP item in 2-person mode */
270
- floatHeight: {
271
- type: NumberConstructor;
272
- default: undefined;
273
- };
274
262
  /**
275
- * Responsive breakpoints for the floating PiP in 2-person mode.
276
- * When provided, PiP size auto-adjusts based on container width.
277
- * Use `DEFAULT_FLOAT_BREAKPOINTS` for a ready-made 5-level responsive config.
263
+ * Pre-defined responsive size for the floating PiP window.
264
+ * 'small', 'medium', or 'large'.
278
265
  */
279
- floatBreakpoints: {
280
- type: PropType<PipBreakpoint[]>;
281
- default: undefined;
266
+ floatSize: {
267
+ type: PropType<FloatSize>;
268
+ default: string;
282
269
  };
283
270
  /**
284
271
  * Index of the participant to show as the floating PiP in 2-person mode.
@@ -320,6 +307,14 @@ declare const GridContainer: vue.DefineComponent<vue.ExtractPropTypes<{
320
307
  type: BooleanConstructor;
321
308
  default: boolean;
322
309
  };
310
+ /**
311
+ * If true, forces the GridItem wrappers to maintain their aspect ratio exactly.
312
+ * @default false
313
+ */
314
+ forceAspectRatio: {
315
+ type: BooleanConstructor;
316
+ default: boolean;
317
+ };
323
318
  /** HTML tag to render */
324
319
  tag: {
325
320
  type: StringConstructor;
@@ -337,13 +332,12 @@ declare const GridContainer: vue.DefineComponent<vue.ExtractPropTypes<{
337
332
  maxVisible: number;
338
333
  currentVisiblePage: number;
339
334
  itemAspectRatios: (string | undefined)[];
340
- floatWidth: number;
341
- floatHeight: number;
342
- floatBreakpoints: PipBreakpoint[];
335
+ floatSize: FloatSize;
343
336
  pipIndex: number;
344
337
  pinOnly: boolean;
345
338
  disableFloat: boolean;
346
339
  disableAnimation: boolean;
340
+ forceAspectRatio: boolean;
347
341
  tag: string;
348
342
  }, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
349
343
  declare const GridItem: vue.DefineComponent<vue.ExtractPropTypes<{
@@ -435,14 +429,16 @@ declare const FloatingGridItem: vue.DefineComponent<vue.ExtractPropTypes<{
435
429
  default: number;
436
430
  };
437
431
  /**
438
- * Responsive breakpoints for PiP sizing.
439
- * When provided, width/height auto-adjust based on container width.
440
- * Overrides the fixed `width`/`height` props.
441
- * Use `DEFAULT_FLOAT_BREAKPOINTS` for a ready-made 5-level responsive config.
432
+ * Pre-defined responsive size for the floating item.
433
+ * 'small', 'medium', or 'large'.
442
434
  */
443
- breakpoints: {
444
- type: PropType<PipBreakpoint[]>;
445
- default: undefined;
435
+ size: {
436
+ type: PropType<"small" | "medium" | "large">;
437
+ default: string;
438
+ };
439
+ aspectRatio: {
440
+ type: StringConstructor;
441
+ default: string;
446
442
  };
447
443
  /** Initial position (x, y from container edges) */
448
444
  initialPosition: {
@@ -494,14 +490,16 @@ declare const FloatingGridItem: vue.DefineComponent<vue.ExtractPropTypes<{
494
490
  default: number;
495
491
  };
496
492
  /**
497
- * Responsive breakpoints for PiP sizing.
498
- * When provided, width/height auto-adjust based on container width.
499
- * Overrides the fixed `width`/`height` props.
500
- * Use `DEFAULT_FLOAT_BREAKPOINTS` for a ready-made 5-level responsive config.
493
+ * Pre-defined responsive size for the floating item.
494
+ * 'small', 'medium', or 'large'.
501
495
  */
502
- breakpoints: {
503
- type: PropType<PipBreakpoint[]>;
504
- default: undefined;
496
+ size: {
497
+ type: PropType<"small" | "medium" | "large">;
498
+ default: string;
499
+ };
500
+ aspectRatio: {
501
+ type: StringConstructor;
502
+ default: string;
505
503
  };
506
504
  /** Initial position (x, y from container edges) */
507
505
  initialPosition: {
@@ -542,13 +540,14 @@ declare const FloatingGridItem: vue.DefineComponent<vue.ExtractPropTypes<{
542
540
  }>> & Readonly<{
543
541
  onAnchorChange?: ((...args: any[]) => any) | undefined;
544
542
  }>, {
543
+ aspectRatio: string;
545
544
  borderRadius: number;
546
545
  boxShadow: string;
547
546
  height: number;
548
547
  width: number;
549
548
  anchor: "top-left" | "top-right" | "bottom-left" | "bottom-right";
549
+ size: "small" | "medium" | "large";
550
550
  visible: boolean;
551
- breakpoints: PipBreakpoint[];
552
551
  initialPosition: {
553
552
  x: number;
554
553
  y: number;
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as _thangdevalone_meeting_grid_layout_core from '@thangdevalone/meeting-grid-layout-core';
2
- import { GridDimensions, MeetGridOptions, MeetGridResult, SpringPreset, LayoutMode, ItemAspectRatio, PipBreakpoint } from '@thangdevalone/meeting-grid-layout-core';
3
- export { ContentDimensions, GridDimensions, GridOptions, ItemAspectRatio, LayoutMode, MeetGridOptions, MeetGridResult, Position, SpringPreset, createGrid, createGridItemPositioner, createMeetGrid, getAspectRatio, getGridItemDimensions, getSpringConfig, springPresets } from '@thangdevalone/meeting-grid-layout-core';
2
+ import { GridDimensions, MeetGridOptions, MeetGridResult, SpringPreset, LayoutMode, ItemAspectRatio, FloatSize } from '@thangdevalone/meeting-grid-layout-core';
3
+ export { ContentDimensions, FloatSize, GridDimensions, GridOptions, ItemAspectRatio, LayoutMode, MeetGridOptions, MeetGridResult, Position, SpringPreset, createGrid, createGridItemPositioner, createMeetGrid, getAspectRatio, getGridItemDimensions, getSpringConfig, resolveFloatWidth, springPresets } from '@thangdevalone/meeting-grid-layout-core';
4
4
  import * as vue from 'vue';
5
5
  import { Ref, ComputedRef, InjectionKey, PropType } from 'vue';
6
6
 
@@ -55,6 +55,7 @@ interface GridContextValue {
55
55
  springPreset: Ref<SpringPreset>;
56
56
  dimensions: Ref<GridDimensions>;
57
57
  disableAnimation: Ref<boolean>;
58
+ forceAspectRatio: Ref<boolean>;
58
59
  }
59
60
  declare const GridContextKey: InjectionKey<GridContextValue>;
60
61
  declare const GridContainer: vue.DefineComponent<vue.ExtractPropTypes<{
@@ -126,24 +127,13 @@ declare const GridContainer: vue.DefineComponent<vue.ExtractPropTypes<{
126
127
  type: PropType<(ItemAspectRatio | undefined)[]>;
127
128
  default: undefined;
128
129
  };
129
- /** Custom width for the floating PiP item in 2-person mode */
130
- floatWidth: {
131
- type: NumberConstructor;
132
- default: undefined;
133
- };
134
- /** Custom height for the floating PiP item in 2-person mode */
135
- floatHeight: {
136
- type: NumberConstructor;
137
- default: undefined;
138
- };
139
130
  /**
140
- * Responsive breakpoints for the floating PiP in 2-person mode.
141
- * When provided, PiP size auto-adjusts based on container width.
142
- * Use `DEFAULT_FLOAT_BREAKPOINTS` for a ready-made 5-level responsive config.
131
+ * Pre-defined responsive size for the floating PiP window.
132
+ * 'small', 'medium', or 'large'.
143
133
  */
144
- floatBreakpoints: {
145
- type: PropType<PipBreakpoint[]>;
146
- default: undefined;
134
+ floatSize: {
135
+ type: PropType<FloatSize>;
136
+ default: string;
147
137
  };
148
138
  /**
149
139
  * Index of the participant to show as the floating PiP in 2-person mode.
@@ -185,6 +175,14 @@ declare const GridContainer: vue.DefineComponent<vue.ExtractPropTypes<{
185
175
  type: BooleanConstructor;
186
176
  default: boolean;
187
177
  };
178
+ /**
179
+ * If true, forces the GridItem wrappers to maintain their aspect ratio exactly.
180
+ * @default false
181
+ */
182
+ forceAspectRatio: {
183
+ type: BooleanConstructor;
184
+ default: boolean;
185
+ };
188
186
  /** HTML tag to render */
189
187
  tag: {
190
188
  type: StringConstructor;
@@ -261,24 +259,13 @@ declare const GridContainer: vue.DefineComponent<vue.ExtractPropTypes<{
261
259
  type: PropType<(ItemAspectRatio | undefined)[]>;
262
260
  default: undefined;
263
261
  };
264
- /** Custom width for the floating PiP item in 2-person mode */
265
- floatWidth: {
266
- type: NumberConstructor;
267
- default: undefined;
268
- };
269
- /** Custom height for the floating PiP item in 2-person mode */
270
- floatHeight: {
271
- type: NumberConstructor;
272
- default: undefined;
273
- };
274
262
  /**
275
- * Responsive breakpoints for the floating PiP in 2-person mode.
276
- * When provided, PiP size auto-adjusts based on container width.
277
- * Use `DEFAULT_FLOAT_BREAKPOINTS` for a ready-made 5-level responsive config.
263
+ * Pre-defined responsive size for the floating PiP window.
264
+ * 'small', 'medium', or 'large'.
278
265
  */
279
- floatBreakpoints: {
280
- type: PropType<PipBreakpoint[]>;
281
- default: undefined;
266
+ floatSize: {
267
+ type: PropType<FloatSize>;
268
+ default: string;
282
269
  };
283
270
  /**
284
271
  * Index of the participant to show as the floating PiP in 2-person mode.
@@ -320,6 +307,14 @@ declare const GridContainer: vue.DefineComponent<vue.ExtractPropTypes<{
320
307
  type: BooleanConstructor;
321
308
  default: boolean;
322
309
  };
310
+ /**
311
+ * If true, forces the GridItem wrappers to maintain their aspect ratio exactly.
312
+ * @default false
313
+ */
314
+ forceAspectRatio: {
315
+ type: BooleanConstructor;
316
+ default: boolean;
317
+ };
323
318
  /** HTML tag to render */
324
319
  tag: {
325
320
  type: StringConstructor;
@@ -337,13 +332,12 @@ declare const GridContainer: vue.DefineComponent<vue.ExtractPropTypes<{
337
332
  maxVisible: number;
338
333
  currentVisiblePage: number;
339
334
  itemAspectRatios: (string | undefined)[];
340
- floatWidth: number;
341
- floatHeight: number;
342
- floatBreakpoints: PipBreakpoint[];
335
+ floatSize: FloatSize;
343
336
  pipIndex: number;
344
337
  pinOnly: boolean;
345
338
  disableFloat: boolean;
346
339
  disableAnimation: boolean;
340
+ forceAspectRatio: boolean;
347
341
  tag: string;
348
342
  }, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
349
343
  declare const GridItem: vue.DefineComponent<vue.ExtractPropTypes<{
@@ -435,14 +429,16 @@ declare const FloatingGridItem: vue.DefineComponent<vue.ExtractPropTypes<{
435
429
  default: number;
436
430
  };
437
431
  /**
438
- * Responsive breakpoints for PiP sizing.
439
- * When provided, width/height auto-adjust based on container width.
440
- * Overrides the fixed `width`/`height` props.
441
- * Use `DEFAULT_FLOAT_BREAKPOINTS` for a ready-made 5-level responsive config.
432
+ * Pre-defined responsive size for the floating item.
433
+ * 'small', 'medium', or 'large'.
442
434
  */
443
- breakpoints: {
444
- type: PropType<PipBreakpoint[]>;
445
- default: undefined;
435
+ size: {
436
+ type: PropType<"small" | "medium" | "large">;
437
+ default: string;
438
+ };
439
+ aspectRatio: {
440
+ type: StringConstructor;
441
+ default: string;
446
442
  };
447
443
  /** Initial position (x, y from container edges) */
448
444
  initialPosition: {
@@ -494,14 +490,16 @@ declare const FloatingGridItem: vue.DefineComponent<vue.ExtractPropTypes<{
494
490
  default: number;
495
491
  };
496
492
  /**
497
- * Responsive breakpoints for PiP sizing.
498
- * When provided, width/height auto-adjust based on container width.
499
- * Overrides the fixed `width`/`height` props.
500
- * Use `DEFAULT_FLOAT_BREAKPOINTS` for a ready-made 5-level responsive config.
493
+ * Pre-defined responsive size for the floating item.
494
+ * 'small', 'medium', or 'large'.
501
495
  */
502
- breakpoints: {
503
- type: PropType<PipBreakpoint[]>;
504
- default: undefined;
496
+ size: {
497
+ type: PropType<"small" | "medium" | "large">;
498
+ default: string;
499
+ };
500
+ aspectRatio: {
501
+ type: StringConstructor;
502
+ default: string;
505
503
  };
506
504
  /** Initial position (x, y from container edges) */
507
505
  initialPosition: {
@@ -542,13 +540,14 @@ declare const FloatingGridItem: vue.DefineComponent<vue.ExtractPropTypes<{
542
540
  }>> & Readonly<{
543
541
  onAnchorChange?: ((...args: any[]) => any) | undefined;
544
542
  }>, {
543
+ aspectRatio: string;
545
544
  borderRadius: number;
546
545
  boxShadow: string;
547
546
  height: number;
548
547
  width: number;
549
548
  anchor: "top-left" | "top-right" | "bottom-left" | "bottom-right";
549
+ size: "small" | "medium" | "large";
550
550
  visible: boolean;
551
- breakpoints: PipBreakpoint[];
552
551
  initialPosition: {
553
552
  x: number;
554
553
  y: number;
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { createMeetGrid, getSpringConfig, resolveFloatSize } from '@thangdevalone/meeting-grid-layout-core';
2
- export { createGrid, createGridItemPositioner, createMeetGrid, getAspectRatio, getGridItemDimensions, getSpringConfig, springPresets } from '@thangdevalone/meeting-grid-layout-core';
1
+ import { createMeetGrid, getSpringConfig, getAspectRatio, resolveFloatWidth } from '@thangdevalone/meeting-grid-layout-core';
2
+ export { createGrid, createGridItemPositioner, createMeetGrid, getAspectRatio, getGridItemDimensions, getSpringConfig, resolveFloatWidth, springPresets } from '@thangdevalone/meeting-grid-layout-core';
3
3
  import { useResizeObserver } from '@vueuse/core';
4
4
  import { ref, onMounted, computed, defineComponent, provide, toRef, h, inject, watch } from 'vue';
5
5
  import { useMotionValue, animate, motion } from 'motion-v';
@@ -123,24 +123,13 @@ const GridContainer = defineComponent({
123
123
  type: Array,
124
124
  default: void 0
125
125
  },
126
- /** Custom width for the floating PiP item in 2-person mode */
127
- floatWidth: {
128
- type: Number,
129
- default: void 0
130
- },
131
- /** Custom height for the floating PiP item in 2-person mode */
132
- floatHeight: {
133
- type: Number,
134
- default: void 0
135
- },
136
126
  /**
137
- * Responsive breakpoints for the floating PiP in 2-person mode.
138
- * When provided, PiP size auto-adjusts based on container width.
139
- * Use `DEFAULT_FLOAT_BREAKPOINTS` for a ready-made 5-level responsive config.
127
+ * Pre-defined responsive size for the floating PiP window.
128
+ * 'small', 'medium', or 'large'.
140
129
  */
141
- floatBreakpoints: {
142
- type: Array,
143
- default: void 0
130
+ floatSize: {
131
+ type: String,
132
+ default: "medium"
144
133
  },
145
134
  /**
146
135
  * Index of the participant to show as the floating PiP in 2-person mode.
@@ -182,6 +171,14 @@ const GridContainer = defineComponent({
182
171
  type: Boolean,
183
172
  default: false
184
173
  },
174
+ /**
175
+ * If true, forces the GridItem wrappers to maintain their aspect ratio exactly.
176
+ * @default false
177
+ */
178
+ forceAspectRatio: {
179
+ type: Boolean,
180
+ default: false
181
+ },
185
182
  /** HTML tag to render */
186
183
  tag: {
187
184
  type: String,
@@ -204,19 +201,19 @@ const GridContainer = defineComponent({
204
201
  maxVisible: props.maxVisible,
205
202
  currentVisiblePage: props.currentVisiblePage,
206
203
  itemAspectRatios: props.itemAspectRatios,
207
- floatWidth: props.floatWidth,
208
- floatHeight: props.floatHeight,
209
- floatBreakpoints: props.floatBreakpoints,
204
+ floatSize: props.floatSize,
210
205
  pipIndex: props.pipIndex,
211
206
  pinOnly: props.pinOnly,
212
- disableFloat: props.disableFloat
207
+ disableFloat: props.disableFloat,
208
+ forceAspectRatio: props.forceAspectRatio
213
209
  }));
214
210
  const grid = useMeetGrid(gridOptions);
215
211
  provide(GridContextKey, {
216
212
  grid,
217
213
  springPreset: toRef(props, "springPreset"),
218
214
  dimensions,
219
- disableAnimation: toRef(props, "disableAnimation")
215
+ disableAnimation: toRef(props, "disableAnimation"),
216
+ forceAspectRatio: toRef(props, "forceAspectRatio")
220
217
  });
221
218
  return () => h(
222
219
  props.tag,
@@ -263,15 +260,40 @@ const GridItem = defineComponent({
263
260
  console.warn("GridItem must be used inside a GridContainer");
264
261
  return () => null;
265
262
  }
266
- const { grid, springPreset, dimensions: containerDimensions, disableAnimation: containerDisableAnimation } = context;
263
+ const { grid, springPreset, dimensions: containerDimensions, disableAnimation: containerDisableAnimation, forceAspectRatio } = context;
267
264
  const noAnimation = computed(() => containerDisableAnimation.value || props.disableAnimation);
268
- const position = computed(() => grid.value.getPosition(props.index));
269
- const dimensions = computed(() => grid.value.getItemDimensions(props.index));
270
- const contentDimensions = computed(
271
- () => grid.value.getItemContentDimensions(props.index, props.itemAspectRatio)
272
- );
273
265
  const isMain = computed(() => grid.value.isMainItem(props.index));
274
266
  const isVisible = computed(() => grid.value.isItemVisible(props.index));
267
+ const isFloat = computed(() => grid.value.floatIndex === props.index);
268
+ const shouldForceRatio = computed(() => forceAspectRatio.value || isFloat.value);
269
+ const position = computed(() => {
270
+ const pos = grid.value.getPosition(props.index);
271
+ if (shouldForceRatio.value && !isFloat.value) {
272
+ const contentDims = grid.value.getItemContentDimensions(props.index, props.itemAspectRatio);
273
+ return {
274
+ top: pos.top + contentDims.offsetTop,
275
+ left: pos.left + contentDims.offsetLeft
276
+ };
277
+ }
278
+ return pos;
279
+ });
280
+ const dimensions = computed(() => {
281
+ if (shouldForceRatio.value && !isFloat.value) {
282
+ const contentDims = grid.value.getItemContentDimensions(props.index, props.itemAspectRatio);
283
+ return {
284
+ width: contentDims.width,
285
+ height: contentDims.height
286
+ };
287
+ }
288
+ return grid.value.getItemDimensions(props.index);
289
+ });
290
+ const contentDimensions = computed(() => {
291
+ const dims = grid.value.getItemContentDimensions(props.index, props.itemAspectRatio);
292
+ if (shouldForceRatio.value) {
293
+ return { ...dims, offsetTop: 0, offsetLeft: 0 };
294
+ }
295
+ return dims;
296
+ });
275
297
  const isHidden = computed(() => {
276
298
  if (grid.value.layoutMode === "spotlight" && !isMain.value)
277
299
  return true;
@@ -279,8 +301,10 @@ const GridItem = defineComponent({
279
301
  return true;
280
302
  return false;
281
303
  });
282
- const isFloat = computed(() => grid.value.floatIndex === props.index);
283
- const floatDims = computed(() => grid.value.floatDimensions ?? { width: 120, height: 160 });
304
+ const floatDims = computed(() => {
305
+ const dims = grid.value.getItemContentDimensions(props.index, props.itemAspectRatio);
306
+ return { width: dims.width, height: dims.height };
307
+ });
284
308
  const floatAnchor = ref(
285
309
  "bottom-right"
286
310
  );
@@ -338,20 +362,21 @@ const GridItem = defineComponent({
338
362
  { immediate: true }
339
363
  );
340
364
  watch(
341
- [floatAnchor, () => containerDimensions.value.width, () => containerDimensions.value.height],
342
- ([, w, h2]) => {
343
- if (isFloat.value && floatInitialized.value && w > 0 && h2 > 0) {
344
- const pos = getFloatCornerPos(floatAnchor.value);
345
- if (noAnimation.value) {
346
- x.set(pos.x);
347
- y.set(pos.y);
348
- } else {
349
- const springCfg = { type: "spring", stiffness: 400, damping: 30 };
350
- animate(x, pos.x, springCfg);
351
- animate(y, pos.y, springCfg);
352
- }
365
+ () => [
366
+ floatAnchor.value,
367
+ containerDimensions.value.width,
368
+ containerDimensions.value.height,
369
+ floatDims.value.width,
370
+ floatDims.value.height
371
+ ],
372
+ ([newAnchor, cw, ch]) => {
373
+ if (isFloat.value && floatInitialized.value && cw > 0 && ch > 0) {
374
+ const pos = getFloatCornerPos(newAnchor);
375
+ x.set(pos.x);
376
+ y.set(pos.y);
353
377
  }
354
- }
378
+ },
379
+ { flush: "post" }
355
380
  );
356
381
  const isLastVisibleOther = computed(() => {
357
382
  const lastVisibleOthersIndex = grid.value.getLastVisibleOthersIndex();
@@ -563,14 +588,16 @@ const FloatingGridItem = defineComponent({
563
588
  default: 160
564
589
  },
565
590
  /**
566
- * Responsive breakpoints for PiP sizing.
567
- * When provided, width/height auto-adjust based on container width.
568
- * Overrides the fixed `width`/`height` props.
569
- * Use `DEFAULT_FLOAT_BREAKPOINTS` for a ready-made 5-level responsive config.
591
+ * Pre-defined responsive size for the floating item.
592
+ * 'small', 'medium', or 'large'.
570
593
  */
571
- breakpoints: {
572
- type: Array,
573
- default: void 0
594
+ size: {
595
+ type: String,
596
+ default: "medium"
597
+ },
598
+ aspectRatio: {
599
+ type: String,
600
+ default: "16:9"
574
601
  },
575
602
  /** Initial position (x, y from container edges) */
576
603
  initialPosition: {
@@ -613,10 +640,13 @@ const FloatingGridItem = defineComponent({
613
640
  const { dimensions, disableAnimation: containerDisableAnimation } = context;
614
641
  const currentAnchor = ref(props.anchor);
615
642
  const effectiveSize = computed(() => {
616
- if (props.breakpoints && props.breakpoints.length > 0 && dimensions.value.width > 0) {
617
- return resolveFloatSize(dimensions.value.width, props.breakpoints);
618
- }
619
- return { width: props.width, height: props.height };
643
+ const hwRatio = getAspectRatio(props.aspectRatio);
644
+ const baseSize = resolveFloatWidth(dimensions.value.width, props.size);
645
+ const baseArea = baseSize * baseSize;
646
+ return {
647
+ width: Math.sqrt(baseArea / hwRatio),
648
+ height: Math.sqrt(baseArea * hwRatio)
649
+ };
620
650
  });
621
651
  const x = useMotionValue(0);
622
652
  const y = useMotionValue(0);
@@ -671,41 +701,24 @@ const FloatingGridItem = defineComponent({
671
701
  { immediate: true }
672
702
  );
673
703
  watch(
674
- [
675
- () => props.anchor,
676
- () => containerDimensions.value.width,
677
- () => containerDimensions.value.height
704
+ () => [
705
+ props.anchor,
706
+ containerDimensions.value.width,
707
+ containerDimensions.value.height,
708
+ effectiveSize.value.width,
709
+ effectiveSize.value.height
678
710
  ],
679
- ([newAnchor, w, h2]) => {
680
- if (isInitialized.value && w > 0 && h2 > 0 && newAnchor !== currentAnchor.value) {
681
- currentAnchor.value = newAnchor;
682
- const pos = getCornerPosition(newAnchor);
683
- if (containerDisableAnimation.value) {
684
- x.set(pos.x);
685
- y.set(pos.y);
686
- } else {
687
- const springCfg = { type: "spring", stiffness: 400, damping: 30 };
688
- animate(x, pos.x, springCfg);
689
- animate(y, pos.y, springCfg);
690
- }
691
- }
692
- }
693
- );
694
- watch(
695
- [() => effectiveSize.value.width, () => effectiveSize.value.height],
696
- () => {
697
- if (isInitialized.value && containerDimensions.value.width > 0 && containerDimensions.value.height > 0) {
698
- const pos = getCornerPosition(currentAnchor.value);
699
- if (containerDisableAnimation) {
700
- x.set(pos.x);
701
- y.set(pos.y);
702
- } else {
703
- const springCfg = { type: "spring", stiffness: 400, damping: 30 };
704
- animate(x, pos.x, springCfg);
705
- animate(y, pos.y, springCfg);
711
+ ([newAnchor, cw, ch]) => {
712
+ if (isInitialized.value && cw > 0 && ch > 0) {
713
+ if (newAnchor !== currentAnchor.value) {
714
+ currentAnchor.value = newAnchor;
706
715
  }
716
+ const pos = getCornerPosition(newAnchor);
717
+ x.set(pos.x);
718
+ y.set(pos.y);
707
719
  }
708
- }
720
+ },
721
+ { flush: "post" }
709
722
  );
710
723
  return () => {
711
724
  const dims = containerDimensions.value;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thangdevalone/meeting-grid-layout-vue",
3
- "version": "1.5.11",
3
+ "version": "1.9.0",
4
4
  "description": "Vue 3 integration for meeting-grid-layout with Motion animations",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -43,7 +43,7 @@
43
43
  "dependencies": {
44
44
  "@vueuse/core": "^10.7.0",
45
45
  "motion-v": "^1.0.0",
46
- "@thangdevalone/meeting-grid-layout-core": "1.5.11"
46
+ "@thangdevalone/meeting-grid-layout-core": "1.9.0"
47
47
  },
48
48
  "devDependencies": {
49
49
  "vue": "^3.4.0",