Table Layout rowspan and columnSpan

I want to make the various planogram designs using the tablelayout.js.

How can we achieve it .

Later i have to add the drag and drop functionality too in the cells.

I was trying with the normal table layout but it’s not working.

This is my sample nodeData

[
      { key: "LockerSet", isGroup: true, category: "LockerGroup", text: "Vertical Locker" },
      { key: 1, text: 'Left Door', category: 'locker', row: 0, col: 0, rowspan: 3, group: "LockerSet" },
      { key: 2, text: 'Right Top', category: 'locker', row: 0, col: 1, group: "LockerSet" },
      { key: 3, text: 'Right Middle', category: 'locker', row: 1, col: 1, group: "LockerSet" },
      { key: 4, text: 'Right Bottom', category: 'locker', row: 2, col: 1, group: "LockerSet" }
    ]

This is the normal grid and i am using layout as table layout JS

<!DOCTYPE html>
<html>
<head>
  <title>Simple Table with Empty Slots</title>
  <!-- Copyright 1998-2023 by Northwoods Software Corporation. -->
</head>
<body>
  <div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:600px"></div>
  <textarea id="mySavedModel" style="width:100%;height:400px">
{ "class": "GraphLinksModel",
  "nodeDataArray": [
{"key":1,"text":"Alpha","color":"lightblue","row":0,"col":0},
{"key":2,"text":"Beta","color":"orange","row":0,"col":1},
{"key":3,"text":"Gamma","color":"lightgreen","row":1,"col":0},
{"key":4,"text":"Delta","color":"pink","row":1,"col":1},
{"key":5,"text":"Epsilon","color":"lightblue","row":1,"col":3},
{"key":6,"text":"Zeta","color":"orange","row":2,"col":2},
{"key":7,"text":"Eta","color":"lightgreen","row":0,"col":3},
{"key":8,"text":"Theta","color":"pink","row":3,"col":1},
{"key":9,"text":"Iota","color":"lightblue","row":3,"col":3},
{"key":10,"text":"Kappa","color":"orange","row":2,"col":5},
{"key":11,"text":"Lambda","color":"lightgreen","row":2,"col":4},
{"key":12,"text":"Mu","color":"pink","row":2,"col":0},
{"category":"Empty","row":0,"col":2},
{"category":"Empty","row":0,"col":4},
{"category":"Empty","row":0,"col":5},
{"category":"Empty","row":1,"col":2},
{"category":"Empty","row":1,"col":4},
{"category":"Empty","row":1,"col":5},
{"category":"Empty","row":2,"col":1},
{"category":"Empty","row":2,"col":3},
{"category":"Empty","row":3,"col":0},
{"category":"Empty","row":3,"col":2},
{"category":"Empty","row":3,"col":4},
{"category":"Empty","row":3,"col":5}
]}
  </textarea>

  <script src="https://cdn.jsdelivr.net/npm/gojs/release/go.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/create-gojs-kit/dist/extensions/TableLayout.js"></script>
  <script id="code">
const CELLW = 120;
const CELLH = 120;
const TABLEW = 6;
const TABLEH = 4;

const myDiagram =
  new go.Diagram("myDiagramDiv", {
      layout: new TableLayout(),
      "draggingTool.isGridSnapEnabled": true,
      "draggingTool.gridSnapCellSize": new go.Size(CELLW, CELLH),
      "draggingTool.gridSnapCellSpot": go.Spot.Center,
      "SelectionMoved": e => {  // update the data.row and .col
        e.subject.each(node => {
          const oldrow = node.data.row || 0;
          const oldcol = node.data.col || 0;
          const row = Math.floor(node.location.y / CELLH);
          const col = Math.floor(node.location.x / CELLW);
          const oldpart = findPartAt(row, col);
          const m = e.diagram.model;
          if (oldpart && oldpart.category === "Empty") {
            m.set(node.data, "row", row);
            m.set(node.data, "col", col);
            m.set(oldpart.data, "row", oldrow);
            m.set(oldpart.data, "col", oldcol);
          }
          // else don't move this node!  the layout will move it back
        });
        e.diagram.layout.invalidateLayout();
      },
      "SelectionCopied": e => {  // update the data.row and .col
        e.subject.each(node => {
          const row = Math.floor(node.location.y / CELLH);
          const col = Math.floor(node.location.x / CELLW);
          const oldpart = findPartAt(row, col);
          const m = e.diagram.model;
          if (oldpart && oldpart.category === "Empty") {
            m.removeNodeData(oldpart.data);
            m.set(node.data, "row", row);
            m.set(node.data, "col", col);
          } else {
            m.removeNodeData(node.data);  // undo copy of this node
          }
        });
        e.diagram.layout.invalidateLayout();
      },
      "undoManager.isEnabled": true,
      "ModelChanged": e => {     // just for demonstration purposes,
        if (e.isTransactionFinished) {  // show the model data in the page's TextArea
          document.getElementById("mySavedModel").textContent = e.model.toJson();
        }
      }
    });

// don't use the infinite Diagram.grid, but just a fixed, limited grid in the "Grid" layer
myDiagram.add(
  new go.Part("Auto", {
      layerName: "Grid",
      location: new go.Point(0, 0), locationObjectName: "GRID"
    })
    .add(
      new go.Shape({ fill: null, stroke: "blue", strokeWidth: 2 }),
      new go.Panel("Grid", {
          name: "GRID",
          width: TABLEW * CELLW + 1, height: TABLEH * CELLH + 1,
          gridCellSize: new go.Size(CELLW, CELLH),
          margin: 4
        })
        .add(
          new go.Shape("LineH", { stroke: "gray" }),
          new go.Shape("LineV", { stroke: "gray" })
        )
    ));

// look up the Node or Empty Part that is at a particular row/column
function findPartAt(r, c) {
  const a = myDiagram.model.nodeDataArray;
  for (let i = 0; i < a.length; i++) {
    if (a[i].row === r && a[i].col === c) return myDiagram.findPartForData(a[i]);
  }
  return null;
}

myDiagram.nodeTemplate =
  new go.Node("Auto", {
      width: CELLW, height: CELLH,
      locationSpot: go.Spot.Center,
      minLocation: new go.Point(CELLW / 2, CELLH / 2),  // don't let nodes be moved beyond the table
      maxLocation: new go.Point(TABLEW * CELLW - CELLW / 2, TABLEH * CELLH - CELLH / 2)
    })
    .bind("row")
    .bind("column", "col")
    .bindObject("layerName", "isSelected", s => s ? "Foreground" : "")
    .add(
      new go.Shape({ fill: "white", stroke: null })
        .bind("fill", "color"),
      new go.TextBlock()
        .bind("text")
    );

myDiagram.nodeTemplateMap.add("Empty",
  new go.Part("Auto", {
      width: CELLW, height: CELLH,
      locationSpot: go.Spot.Center,
      layerName: "Background",
      selectable: false, movable: false, copyable: false, deletable: false
    })
    .bind("row")
    .bind("column", "col")
    .add(
      new go.Shape({ fill: "white", stroke: null, pickable: false }),
      new go.TextBlock("Drag & Drop\nSKU / Bin", {
        stroke: "gray", font: "italic 12pt sans-serif", textAlign: "center"
      })
    ));

myDiagram.model = go.Model.fromJson(document.getElementById("mySavedModel").textContent);
  </script>
</body>
</html>

Have you tried our Planogram sample? Planogram Editor: Drag-and-Drop items onto Shelves and Racks | GoJS Diagramming Library

In it the user can drag-and-drop any number of rectangular Parts. They can even be resized to be multiples of cells wide or tall. Even the Groups holding them can be resized.

Note that the Planogram sample does not use TableLayout.

Okay i will try this approach,
So we have to manually calculate the position of the each group and place it accordingly and i don’t need a resizable group or item i will be placing inside it also not resizable.

OK, if you don’t need the ability for the user to resize something, don’t set Part.resizable to true.

Is the data in your database positioned in terms of cell multiples? It is easy to implement data binding converter functions so that you can use your own coordinate system in your data.

Actually, an example is in the Gantt sample, where the user can dynamically change the horizontal scale without changing the Diagram.scale. Note how there are two binding conversion functions, both from source to target and (because it’s TwoWay) target to source, for both the position and the width properties.