ov-components: Enhance layout handling by adding support for small elements and adjusting layout calculations

master
Carlos Santos 2025-12-19 17:17:25 +01:00
parent b266916f7f
commit 1b9162b6e5
1 changed files with 155 additions and 43 deletions

View File

@ -41,6 +41,7 @@ export interface ElementDimensions {
height: number; height: number;
width: number; width: number;
big?: boolean; big?: boolean;
small?: boolean;
} }
/** /**
@ -199,6 +200,7 @@ export class OpenViduLayout {
const elements = children.map((element) => { const elements = children.map((element) => {
const res = this.getChildDims(element); const res = this.getChildDims(element);
res.big = element.classList.contains(this.opts.bigClass); res.big = element.classList.contains(this.opts.bigClass);
res.small = element.classList.contains(this.opts.smallClass);
return res; return res;
}); });
@ -356,7 +358,7 @@ export class OpenViduLayout {
/** /**
* @hidden * @hidden
*/ */
private getChildDims(child: HTMLVideoElement | HTMLElement): { height: number; width: number; big?: boolean } { private getChildDims(child: HTMLVideoElement | HTMLElement): ElementDimensions {
if (child instanceof HTMLVideoElement) { if (child instanceof HTMLVideoElement) {
if (child.videoHeight && child.videoWidth) { if (child.videoHeight && child.videoWidth) {
return { return {
@ -542,12 +544,14 @@ export class OpenViduLayout {
let bigOffsetTop = 0; let bigOffsetTop = 0;
let bigOffsetLeft = 0; let bigOffsetLeft = 0;
const bigIndices: number[] = []; const bigIndices: number[] = [];
const smallIndices: number[] = [];
const normalIndices: number[] = [];
let bigBoxes: LayoutBox[] = []; let bigBoxes: LayoutBox[] = [];
let smallBoxes: LayoutBox[] = []; let smallBoxes: LayoutBox[] = [];
let areas: { big: LayoutArea | null; small: LayoutArea | null } = { big: null, small: null }; let normalBoxes: LayoutBox[] = [];
let areas: { big: LayoutArea | null; normal: LayoutArea | null; small: LayoutArea | null } = { big: null, normal: null, small: null };
// Move to Get Layout // Separate elements into three categories: big, small, and normal
const smallOnes = elements.filter((element) => !element.big);
const bigOnes = elements.filter((element, idx) => { const bigOnes = elements.filter((element, idx) => {
if (element.big) { if (element.big) {
bigIndices.push(idx); bigIndices.push(idx);
@ -555,20 +559,23 @@ export class OpenViduLayout {
} }
return false; return false;
}); });
//TODO: Habia un codigo personalizado que servía para const smallOnes = elements.filter((element, idx) => {
//TODO: tener videos grandes, pequeños y normales if (!element.big && element.small) {
//.filter((x) => !smallOnes.includes(x)); smallIndices.push(idx);
return true;
// const normalOnes: HTMLVideoElement[] = Array.prototype.filter }
// .call( return false;
// this.layoutContainer.querySelectorAll( });
// `#${id}>*:not(.${this.opts.bigClass})` const normalOnes = elements.filter((element, idx) => {
// ), if (!element.big && !element.small) {
// () => this.filterDisplayNone normalIndices.push(idx);
// ) return true;
// .filter((x) => !smallOnes.includes(x)); }
// this.attachElements(bigOnes, normalOnes, smallOnes); return false;
if (bigOnes.length > 0 && smallOnes.length > 0) { });
// Handle different layout scenarios based on element types
if (bigOnes.length > 0 && (normalOnes.length > 0 || smallOnes.length > 0)) {
// Scenario: Big elements with normal/small elements
let bigWidth; let bigWidth;
let bigHeight; let bigHeight;
let showBigFirst = bigFirst; let showBigFirst = bigFirst;
@ -615,7 +622,7 @@ export class OpenViduLayout {
maxRatio, maxRatio,
containerWidth, containerWidth,
containerHeight - bigHeight, containerHeight - bigHeight,
smallOnes.length, normalOnes.length + smallOnes.length,
smallMaxWidth, smallMaxWidth,
smallMaxHeight smallMaxHeight
); );
@ -670,7 +677,7 @@ export class OpenViduLayout {
maxRatio, maxRatio,
containerWidth - bigWidth, containerWidth - bigWidth,
containerHeight, containerHeight,
smallOnes.length, normalOnes.length + smallOnes.length,
smallMaxWidth, smallMaxWidth,
smallMaxHeight smallMaxHeight
); );
@ -691,7 +698,7 @@ export class OpenViduLayout {
width: bigWidth, width: bigWidth,
height: bigHeight height: bigHeight
}; };
areas.small = { areas.normal = {
top: offsetTop, top: offsetTop,
left: offsetLeft, left: offsetLeft,
width: containerWidth - offsetLeft, width: containerWidth - offsetLeft,
@ -704,7 +711,7 @@ export class OpenViduLayout {
width: bigWidth, width: bigWidth,
height: bigHeight height: bigHeight
}; };
areas.small = { areas.normal = {
top: 0, top: 0,
left: 0, left: 0,
width: containerWidth - offsetLeft, width: containerWidth - offsetLeft,
@ -719,8 +726,9 @@ export class OpenViduLayout {
width: containerWidth, width: containerWidth,
height: containerHeight height: containerHeight
}; };
} else { } else if (normalOnes.length > 0 || smallOnes.length > 0) {
areas.small = { // Only normal and/or small elements
areas.normal = {
top: offsetTop, top: offsetTop,
left: offsetLeft, left: offsetLeft,
width: containerWidth - offsetLeft, width: containerWidth - offsetLeft,
@ -746,36 +754,140 @@ export class OpenViduLayout {
bigOnes bigOnes
); );
} }
if (areas.small) { if (areas.normal) {
smallBoxes = this.getLayoutAux( // Calculate equivalent "normal-sized" count considering small elements take less space
// Treat each small element as taking up a fraction of a normal element's space
const smallElementSpaceFactor = 0.25; // Small elements take ~25% of normal element space
const equivalentNormalCount = normalOnes.length + (smallOnes.length * smallElementSpaceFactor);
// Calculate layout for all elements together as if they were normal-sized
const allNormalAreaElements = [...normalOnes, ...smallOnes];
if (allNormalAreaElements.length > 0) {
// Get dimensions as if all elements were normal-sized
const allBoxes = this.getLayoutAux(
{ {
containerWidth: areas.small.width, containerWidth: areas.normal.width,
containerHeight: areas.small.height, containerHeight: areas.normal.height,
offsetLeft: areas.small.left, offsetLeft: areas.normal.left,
offsetTop: areas.small.top, offsetTop: areas.normal.top,
fixedRatio, fixedRatio,
minRatio, minRatio,
maxRatio, maxRatio,
alignItems: areas.big ? smallAlignItems : alignItems, alignItems: areas.big ? smallAlignItems : alignItems,
maxWidth: areas.big ? smallMaxWidth : maxWidth, maxWidth: areas.big ? maxWidth : maxWidth,
maxHeight: areas.big ? smallMaxHeight : maxHeight, maxHeight: areas.big ? maxHeight : maxHeight,
scaleLastRow scaleLastRow
}, },
smallOnes allNormalAreaElements
); );
// Split boxes and adjust small elements
normalBoxes = allBoxes.slice(0, normalOnes.length);
const rawSmallBoxes = allBoxes.slice(normalOnes.length);
// Adjust small element boxes to use restricted dimensions and reposition them
// to utilize space efficiently
smallBoxes = rawSmallBoxes.map((box, idx) => {
// Calculate restricted size while maintaining aspect ratio
const restrictedWidth = Math.min(box.width, smallMaxWidth);
const restrictedHeight = Math.min(box.height, smallMaxHeight);
// Maintain the position but adjust size
return {
left: box.left,
top: box.top,
width: restrictedWidth,
height: restrictedHeight
};
});
// If there are small elements, try to compact them and redistribute space
if (smallOnes.length > 0 && normalOnes.length > 0) {
// Recalculate normal elements with more space since small elements take less
const adjustedDimensions = this.getBestDimensions(
minRatio,
maxRatio,
areas.normal.width,
areas.normal.height,
equivalentNormalCount,
areas.big ? maxWidth : maxWidth,
areas.big ? maxHeight : maxHeight
);
// If the adjusted dimensions give us bigger normal elements, recalculate
if (normalBoxes.length > 0 &&
adjustedDimensions.targetHeight > normalBoxes[0].height) {
normalBoxes = this.getLayoutAux(
{
containerWidth: areas.normal.width,
containerHeight: areas.normal.height,
offsetLeft: areas.normal.left,
offsetTop: areas.normal.top,
fixedRatio,
minRatio,
maxRatio,
alignItems: areas.big ? smallAlignItems : alignItems,
maxWidth: areas.big ? maxWidth : maxWidth,
maxHeight: areas.big ? maxHeight : maxHeight,
scaleLastRow
},
normalOnes
);
// Position small elements in remaining space (bottom or side)
const normalMaxBottom = normalBoxes.length > 0 ? Math.max(...normalBoxes.map(b => b.top + b.height)) : areas.normal.top;
const normalMaxRight = normalBoxes.length > 0 ? Math.max(...normalBoxes.map(b => b.left + b.width)) : areas.normal.left;
// Position small elements at the end of the layout
const spaceAtBottom = (areas.normal.top + areas.normal.height) - normalMaxBottom;
const spaceAtRight = (areas.normal.left + areas.normal.width) - normalMaxRight;
let smallStartX = areas.normal.left;
let smallStartY = normalMaxBottom;
let availableWidth = areas.normal.width;
// Choose best positioning based on available space
if (spaceAtBottom < smallMaxHeight && spaceAtRight >= smallMaxWidth) {
// Position to the right
smallStartX = normalMaxRight;
smallStartY = areas.normal.top;
availableWidth = spaceAtRight;
}
// Arrange small elements in a compact grid
const smallCols = Math.floor(availableWidth / smallMaxWidth);
smallBoxes = smallOnes.map((_, idx) => {
const col = idx % Math.max(1, smallCols);
const row = Math.floor(idx / Math.max(1, smallCols));
return {
left: smallStartX + (col * smallMaxWidth),
top: smallStartY + (row * smallMaxHeight),
width: smallMaxWidth,
height: smallMaxHeight
};
});
}
}
}
} }
const boxes: LayoutBox[] = []; const boxes: LayoutBox[] = [];
let bigBoxesIdx = 0; let bigBoxesIdx = 0;
let normalBoxesIdx = 0;
let smallBoxesIdx = 0; let smallBoxesIdx = 0;
// Rebuild the array in the right order based on where the bigIndices should be // Rebuild the array in the right order based on element types
elements.forEach((element, idx) => { elements.forEach((element, idx) => {
if (bigIndices.indexOf(idx) > -1) { if (bigIndices.indexOf(idx) > -1) {
boxes[idx] = bigBoxes[bigBoxesIdx]; boxes[idx] = bigBoxes[bigBoxesIdx];
bigBoxesIdx += 1; bigBoxesIdx += 1;
} else { } else if (smallIndices.indexOf(idx) > -1) {
boxes[idx] = smallBoxes[smallBoxesIdx]; boxes[idx] = smallBoxes[smallBoxesIdx];
smallBoxesIdx += 1; smallBoxesIdx += 1;
} else {
boxes[idx] = normalBoxes[normalBoxesIdx];
normalBoxesIdx += 1;
} }
}); });
return { boxes, areas }; return { boxes, areas };