JSFiddle - React, Tailwind, and code Playground

by lid0

HTML

<pre>
  process overlap of blocks and push overlaps down a line until no overlaps on all lines.
  we start with a flat list of items.
</pre>
<div id="container" style="position:relative">


</div>

CSS

.line {
  background: #cfcfcf;
  border: 1px solid gray;
  height: 30px;
}

.box {
  opacity: 0.8;
  color: white;
  font-size: 12px;
  margin-top: 3px;
  display: inline-block;
  border: 1px solid black;
  background: #246222;
  width: 10px;
  height: 20px;
  position: absolute;
}

/**  **/
.line:nth-child(2) .box:nth-child(odd) {
  margin-top: 5px;
  background: red;
}



.message.before  {
  margin-top:10px;
  background:yellow;
}

.message.after  {
  margin-top:10px;
  background:yellow;
}

JavaScript

/**
@author: lidlanca 2021 march 29

process a list of items.
list must be ordered by x property in ASC order.
item requires {x,w} properties. 

the function will return the items in lines.
where overlapping items are pushed down to the next line until there are not overlapps. 


Given a list of items:
[ {id:A, x:0, w:10}, {id:B,x:7,w:10} ]

processItems() will return:

[
  [{id:A, x:0, w:10}],
  [{id:B, x:7, w:10} ]
]



Which can then visually be rendered   |---A---|  and |---B---| before processing:
|---A--|-|--B---|

and after processing, line by line rendering
|---A---|
      |---B---|


**/
function processItems(items) {

  var lines = []


  var done = false
  var line = [...items] // start with the whole list of items
  do {
    done = true

    // each iteration we will process the last line, and populate
    // items that can stay in the line into currentLine
    // and items that overlap push to the next line. 
    var currentLine = []
    var nextLine = []

    // if the line we are processing has one or less items. 
    // we break, there are no processing to do.
    if (line.length <= 1 && lines.length > 0) {
      if (lines.length == 0) {
        // if its the first line we are processing, we need to add the line to the result object
        // and then we can exit.
        lines.push(line)
      }
      break
    }

    // push first element of the line to currentLine
    currentLine.push(line[0])
    var prevNonOverlap = line[0] // initialize as first non overlapping item of the line
    // iterate on the remainin items of the line.
    for (i = 1; i < line.length; i++) {
      // check if current item overlap with last non overlapping item.
      if (
        itemsIntersect(prevNonOverlap, line[i])
        /* prevNonOverlap.x + prevNonOverlap.w >= line[i].x &&
        prevNonOverlap.x + prevNonOverlap.w <= line[i].x + line[i].w */
      ) {
        // item overlap with last non overlapping item. push item to nextLine.
       ...