Merge blocks created by svg path

My react web app has over 2000 blocks that created by svg path.

Users can select the blocks they need and merge them to a large block.

The sides of selected blocks must be adjacent.

The problem is I have no idea how to handle irregular block like A0+A1+A2+A4 in snippet.

const data = Array.from({ length: 9 }).map((_, i) => ({
  id: `A${i}`,
  x: (i % 3) * 300,
  y: Math.floor(i / 3) * 300,
  p: [{ node: "L", x: 300, y: 0 },{ node: "L", x: 300, y: 300 },{ node: "L", x: 0, y: 300 }]
}));
const expectResult = [
  {
    id: "A0", x: 0, y: 0,
    p: [{ node: "L", x: 900, y: 0 },{ node: "L", x: 900, y: 300 },{ node: "L", x: 600, y: 300 },{ node: "L", x: 600, y: 600 },{ node: "L", x: 300, y: 600 },{ node: "L", x: 300, y: 300 },{ node: "L", x: 0, y: 300 }],
  },
  {
    id: "A3", x: 0, y: 300,
    p: [{ node: "L", x: 300, y: 0 },{ node: "L", x: 300, y: 300 },{ node: "L", x: 600, y: 300 },{ node: "L", x: 600, y: 600 },{ node: "L", x: 0, y: 600 }],
  },
  {
    id: "A5", x: 600, y: 300,
    p: [{ node: "L", x: 300, y: 0 },{ node: "L", x: 300, y: 600 },{ node: "L", x: 0, y: 600 }],
  },
];

const App = () => {
  return (
    <div>
      original
      <svg width="100%" viewBox="-100 -100 1200 1200">
        {data.map((d) => (
          <Booth key={d.id} d={d} />
        ))}
      </svg>
      expected result after merge by selected blocks
      <svg width="100%" viewBox="-100 -100 1200 1200">
        {expectResult.map((d) => (
          <Booth key={d.id} d={d} />
        ))}
      </svg>
    </div>
  );
};

const drawPath = (path) => path.map((p) => (p.node === "L" ? `${p.node}${p.x} ${p.y}` : `${p.node}${p.x1} ${p.y1} ${p.x2} ${p.y2} ${p.x} ${p.y}`)).join("") + "Z";

const Booth = ({ d }) => {
  return (
    <g key={d.id} id={d.id} transform={`translate(${d.x},${d.y})`}>
      <path stroke={"black"} fill="none" strokeWidth={1} d={`M0 0${drawPath(d.p)}`} />
      <text y={150} fontSize={80}>
        {d.id}
      </text>
    </g>
  );
};
ReactDOM.createRoot(document.getElementById("app")).render(<App />);
body {
  margin: 0;
  padding: 0;
}
<script src="https://unpkg.com/react@18/umd/react.production.min.js" crossorigin="true"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js" crossorigin="true"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js" crossorigin="true"></script>
<div id="app"></div>

I tried to use min and max to calculate the new block’s shape, but it can’t handle irregular block.

const data = Array.from({ length: 9 }).map((_, i) => ({
  id: `A${i}`,
  x: (i % 3) * 300,
  y: Math.floor(i / 3) * 300,
  p: [
    { node: "L", x: 300, y: 0 },
    { node: "L", x: 300, y: 300 },
    { node: "L", x: 0, y: 300 },
  ],
}));

const areas = [
  { id: "A0", blocks: ["A0", "A1", "A2", "A4"] },
  { id: "A3", blocks: ["A3", "A6", "A7"] },
  { id: "A5", blocks: ["A5", "A8"] },
];
const mergeBlock = (blocksData) => {
  const filter = [];
  return blocksData
    .map((d1) => {
      const area = areas.find((d2) => d1.id === d2.id);
      const pos = blocksData.filter((d) => area && area.blocks.includes(d.id)).map((d) => ({ x: d.x, y: d.y }));
      const x = pos.length > 0 ? Math.min(...pos.map((d) => d.x)) : d1.x;
      const y = pos.length > 0 ? Math.min(...pos.map((d) => d.y)) : d1.y;
      const w = pos.length > 0 ? Math.max(...pos.map((d) => d.x)) - x + 300 : d1.w;
      const h = pos.length > 0 ? Math.max(...pos.map((d) => d.y)) - y + 300 : d1.h;
      filter.push(...(area ? area.blocks.filter((d) => d !== d1.id) : []));
      return {
        ...d1,
        x,
        y,
        p: [
          { node: "L", x: w, y: 0 },
          { node: "L", x: w, y: h },
          { node: "L", x: 0, y: h },
        ],
      };
    })
    .filter((d) => !filter.includes(d.id));
};

const App = () => {
  return (
    <div>
      <svg width="100%" viewBox="-100 -100 1200 1200">
        {mergeBlock(data).map((d) => (
          <Booth key={d.id} d={d} />
        ))}
      </svg>
    </div>
  );
};

const drawPath = (path) => path.map((p) => (p.node === "L" ? `${p.node}${p.x} ${p.y}` : `${p.node}${p.x1} ${p.y1} ${p.x2} ${p.y2} ${p.x} ${p.y}`)).join("") + "Z";

const Booth = ({ d }) => {
  return (
    <g key={d.id} id={d.id} transform={`translate(${d.x},${d.y})`}>
      <path stroke={"black"} fill="none" strokeWidth={1} d={`M0 0${drawPath(d.p)}`} />
      <text y={150} fontSize={80}>
        {d.id}
      </text>
    </g>
  );
};
ReactDOM.createRoot(document.getElementById("app")).render(<App />);
body {
  margin: 0;
  padding: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js" crossorigin="true"></script>
<div id="app"></div>