Flexboxes: stacked, centered, wrapping, with color-matching sides and dynamic, synchronized width (see example)

Viewed 49

Basically, I want something that acts similar to the following snippet. Notice how adding an item to one flex row increases the width of both.

function initItems() {
    const numArr = Array.from(Array(3).keys());
  const items = numArr.map(() => "item");
  return items;
}

function FlexItems() {
    const [items, setItems] = React.useState(initItems);
  const addItem = () => setItems([...items, "item"]);
  
  return (
    <div className="flex-row">
      <button onClick={addItem}>Add Item</button>
      {items.map((item, idx)=><div className="item" key={idx}>{item}</div>)}
    </div>
  );
}

function StackedCenteredFlexItems() {
    return (
 <table>
  <tr>
    <td className="side first">.</td>
    <td className="center first"><FlexItems/></td>
    <td className="side first">.</td>
  </tr>
  <tr>
    <td className="side second">.</td>
    <td className="center second"><FlexItems/></td>
    <td className="side second">.</td>
  </tr>
</table>
  )
}

ReactDOM.render(
  <StackedCenteredFlexItems />,
  document.getElementById("react")
);
.flex-row {
  display: flex;
  /* flex-wrap: wrap; */
  border: 1px solid black;
}

table {
  width: 100%;
  border-collapse: collapse;
}

td.center {
  width: 1px;
  white-space: nowrap;
}

td.side {
  padding: 0;
  font-size: 1px;
  color: transparent;
}

.first {
   background: lightblue;
}

.second {
  background: lightgreen;
}

.item {
  margin: 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>

This solution is close, the active ingredient being that the flex rows are in the center cells of a table with this applied:

td.center {
  width: 1px;
  white-space: nowrap;
}

In that example flex-wrap is set to nowrap, however, because with wrap turned on, all the items in the flex rows just stack on top of one another. But I need the items to wrap in response to window size, like this:

function initItems() {
    const numArr = Array.from(Array(20).keys());
  const items = numArr.map(() => "item");
  return items;
}

function FlexItems({ className }) {
    const [items, setItems] = React.useState(initItems);
  const addItem = () => setItems([...items, "item"]);
  
  return (
    <div className={"flex-row " + className}>
      <button onClick={addItem}>Add Item</button>
      {items.map((item, idx)=><div className="item" key={idx}>{item}</div>)}
    </div>
  );
}


ReactDOM.render(
  <div>
    <FlexItems className="first" />
    <FlexItems className="second" />
  </div>,
  document.getElementById("react")
);
.flex-row {
  display: flex;
  flex-wrap: wrap;
}

.first {
   background: lightblue;
}

.second {
  background: lightgreen;
}

.item {
  margin: 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>

The first example has most of what I want:

  • The flex rows are centered in the window
  • When an item is added to one flex row, the width of both increases
  • To the left and right of each flex row, the background matches the flex row's color

The second example has the other thing:

  • Flex wrap responds to the edges of the window

How can I get all of it?

1 Answers
Related