Skip to content

Grid#

Info

See also a live demo.

Wunderbaum works as a treegrid out of the box if we specify column definitions.
In a treegrid, there is also general support for embedded input elements in column cells, like checkboxes, text fields, etc.

Note that the treegrid is not editable by default however. It does not even render cell content for columns other than the main (first) node column. This has to be implemented in the render(e) callback instead.
Wunderbaum does not implement fancy input controls. Rather think of it as a framework that makes it easy to use standard or custom HTML controls:
Create HTML controls in the render(e) callback and implement the change(e) event to enable editing.

Column Definitions#

Info

Column definitions are required to turn a plain Wunderbaum tree into a treegrid.

A list of column definitions is specified in the columns option. title and id are required. width is optional, but recommended.

The id is used to identify the column in the render event.
The special id "*" is used for the main node column with checkbox, connectors, icon, and title). It is required and must be the first column in the list.

The width is either specified in absolute pixels ("100px") or relative weights ("2.5"). Column widths default to "*", which is equivalent to "1.0"
Absolute widths are applied first, then the remaining space is distributed among the relative weights.

The classes property can be used to add CSS classes to the column header and cells.

The html property can be used to define cell markup that is rendered by default.

const tree = new Wunderbaum({
  ...
  columns: [
    { id: "*", title: "Product", width: "250px" },
    { id: "author", title: "Author", width: "200px" },
    { id: "year", title: "Year", width: "50px", classes: "wb-helper-end" },
    { id: "qty", title: "Qty", width: "50px", classes: "wb-helper-end" },
    {
      id: "price",
      title: "Price ($)",
      width: "80px",
      classes: "wb-helper-end",  // (1)
    },
    { id: "details", title: "Details", width: "*" },
  ],
  ...
});
  1. This classes are added to all header and row cells of that column. In this case: right align the content of the column.

Info

See also ColumnDefinition for details.

Rendering#

Wunderbaum renders the first column (the main node column) by default. To render additional columns, implement the render(e) callback.

The render event receives a WbRenderEventType object that contains useful properties for this purpose.

We can use the e.renderColInfosById property to iterate over all columns and render the content of each column.
This can be simplified by following the convention to name the column id after the node data property that should be rendered in that column.

const tree = new Wunderbaum({
  ...
  types: {},
  columns: [
    { id: "*", title: "Product", width: "250px" },
    { id: "author", title: "Author", width: "200px" },
    { id: "year", title: "Year", width: "50px", classes: "wb-helper-end" },
    { id: "qty", title: "Qty", width: "50px", classes: "wb-helper-end" },
    {
      id: "price",
      title: "Price ($)",
      width: "80px",
      classes: "wb-helper-end",
    },
    { id: "details", title: "Details", width: "*" },
  ],
  ...
  render: function (e) {
    const node = e.node;

    for (const col of Object.values(e.renderColInfosById)) {
      switch (col.id) {
        default:
          // Assumption: we named column.id === node.data.NAME
          col.elem.textContent = node.data[col.id];
          break;
      }
    }
  },
});

If we want to render formatted values, we can do this explicitly for each column:

const tree = new Wunderbaum({
  ...
  render: function (e) {
    const node = e.node;

    for (const col of Object.values(e.renderColInfosById)) {
      const val = node.data[col.id];

      switch (col.id) {
        case "date":
          if (val) {
            const dt = new Date(val);
            col.elem.textContent = dt.toISOString().slice(0, 10);
          } else {
            col.elem.textContent = "n.a.";
          }
          break;
        case "state":
          {
            const map = { h: "Happy", s: "Sad" };
            col.elem.textContent = map[val] || "n.a.";
          }
          break;
        case "avail":
          col.elem.textContent = val ? "Yes" : "No";
          break;
        default:
          // Assumption: we named column.id === node.data.NAME
          col.elem.textContent = val;
          break;
      }
    }
  },
});

Info

See the Edit Tutorial for examples how to render embedded controls.

Editing#

Editing cells — other than the node title column — is not supported by default. Instead we have to

  1. Implement the render(e) callback to render the cell's content as an HTML element that can be edited, like a text field, checkbox, etc.
  2. Implement the change(e) callback to update the node data when the user has finished editing a cell.

Info

See the Edit Tutorial for details.

A treegrid can have one of two navigation modes. We can toggle using the keyboard:

Row Mode ↔ Cell-Nav Mode

Info

See the Keyboard Tutorial for details.

Configuration and Customization#

Note

Todo.

const tree = new Wunderbaum({
  ...
  navigationModeOption: "startRow",  // | "cell" | "startCell" | "row"
  columns: [],
  ...
  // --- Events ---
  render: (e) => {
    // Return false to prevent default behavior
  }
  ...
  edit: {
    trigger: ["F2", "macEnter", ...],
    ...
  },
});
  • tree.setNavigationOption(mode: NavModeEnum)
  • tree.setColumn(colIdx: number|string, options?: SetColumnOptions)

Code Hacks#

Redefine Columns#

For example to append a new column:

tree.columns.push({
  title: "New Col",
  id: "col_" + sequence++,
  width: "100px",
});
tree.update("colStructure");

Add a Menu Button to the Column Header#

Add a filter button to the column header to toggle the filter mode:

const tree = new Wunderbaum({
  ...
  columns: [
    {
      title: "Title",
      menu: true,
      ...
    },
    ...
  ],
  buttonClick: (e) => {
    if (e.command === "menu") {
      alert("Open menu...");
    }
  },
  ...
});

Add a Sort Button to the Column Header#

Add a sort button to the column header and handle click events to toggle the order:

const tree = new Wunderbaum({
  ...
  columns: [
    {
      title: "Title",
      sortable: true, // or set in column definition
      ...
    },
    ...
  ],
  columnsSortable: true, // or set in column definition
  buttonClick: (e) => {
    if (e.command === "sort") {
      e.tree.sortByProperty({ colId: e.info.colId, updateColInfo: true });
    }
  },
  ...
});