javascriptcssreactjsflexboxvideocall

How to break a flex into new row for dynamic data


I am working on a video calling application using reactjs. LocalTile component contains video of local user and RemoteTiles is returning videos of all remote users. My desired layout is like for 3 users:

===video1=video2===
=======video3======

for 4 users:

===video1=video2===
===video3=video4===

for 5 users:

===video1=video2=video3===
=======video4=video5======



<div id='gallery-parent' style={{ display: 'flex', flex: '1 1', flexDirection: 'column', gap: '1rem', position: 'relative' }}>
       <div style={{ alignItems: 'stretch',  display: 'flex', flex: '1 1', gap: '1rem',justifyContent:'center'}}>
         <LocalTile />
         <RemoteTiles />
       </div>                    
</div>

its not working for me as all users are rendering in same column and shrinking videos.


Solution

  • I'm not 100% sure how you'd implement this in your larger codebase, but this is how I'd do it in vanilla JS. After the list of callers is rendered, you can grab the one just past halfway and insert a non-shrinking full-width element before it which will force the wrap to happen.

    This little example utility allows n number of users in the input box and will split them as expected

    function updateHandler(){
      if(input.value === '') return;
      //else
      const n = parseInt(input.value),
        half = Math.ceil(n/2)
      output.innerHTML = Array.apply(null, Array(n)).map(() => `<div class="caller"></div>`).join('')
      
      const halfwayElem = output.querySelectorAll('.caller')[half],
        splitter = document.createElement('hr')
        
      splitter.className = 'callers-divider'
      
      output.insertBefore(splitter, halfwayElem)
    }
    
    updateHandler()
    input.oninput = updateHandler
    #output {
      width: 100%;
      display: flex;
      flex-wrap: wrap;
     }
     .caller {
      flex: 1;
      border: 1px solid black;
      margin: 8px;
      aspect-ratio: 4/3;
     }
     .callers-divider {
      display: block;
      width: 100%;
      flex-shrink: 0;
    }
    <label>Number of users connected: <input id="input" value="5" /></label>
    <div id="output"></div>

    Alternatively, if you don't want the second row to take up the full width and just center the two callers, it can be done with CSS instead of DOM manipulation. This uses the bitwise operator n ^ 1 to determine if n is odd and if so, bump it up to the next even number

    function updateHandler(){
      if(input.value === '') return;
      //else
      let n = parseInt(input.value)
        
      output.innerHTML = Array.apply(null, Array(n)).map(() => `<div class="caller"></div>`).join('')
      
      const shiftedN = n ^ 1
      if(n !== shiftedN){
        ++n
      }
      
      const callerWidth = (200/n) + '%'
      output.style.setProperty('--caller-width', callerWidth)
    }
    
    updateHandler()
    input.oninput = updateHandler
    #output {
      width: 100%;
      display: flex;
      flex-wrap: wrap;
      justify-content: center;
     }
     .caller {
      border: 1px solid black;
      aspect-ratio: 4/3;
      box-sizing: border-box;
      width: var(--caller-width);
     }
    <label>Number of users connected: <input id="input" value="5" /></label>
    <div id="output"></div>