7.3.292-stable Switch to dev

<gui-table />

Usage

Column based

const el = document.createElement('gui-table');
const table = gc.core.Table.create([
  [
    new Date('2019-01-01T15:15:00Z'),
    new Date('2019-01-02T15:15:00Z'),
    new Date('2020-01-03T15:15:00Z'),
    new Date('2021-01-04T15:15:00Z'),
  ],
  [-2.5, 1.458, 0.009, 5.64],
]);
el.value = table;
el.columns = [{ index: 0, header: 'Date' }, { index: 0, header: 'Value' }];
el.addEventListener('gui-table-click', (ev) => {
  window.alert(`Clicked row=${ev.detail.rowIdx},col=${ev.detail.colIdx}`);
});

Row based

const el = document.createElement('gui-table');
const table = gc.core.Table.fromRows([
  [new Date('2019-01-01T15:15:00Z'), -2.5],
  [new Date('2019-01-02T15:15:00Z'), 1.458],
  [new Date('2020-01-03T15:15:00Z'), 0.009],
  [new Date('2021-01-04T15:15:00Z'), 5.64],
]);
el.value = table;
el.columns = [{ index: 0, header: 'Date' }, { index: 0, header: 'Value' }];
el.addEventListener('gui-table-click', (ev) => {
  window.alert(`Clicked row=${ev.detail.rowIdx},col=${ev.detail.colIdx}`);
});

Object based

const el = document.createElement('gui-table');
const table = gc.core.Table.fromObjects([
  { Date: new Date('2019-01-01T15:15:00Z'), Value: -2.5 },
  { Date: new Date('2019-01-02T15:15:00Z'), Value: 1.458 },
  { Date: new Date('2020-01-03T15:15:00Z'), Value: 0.009 },
  { Date: new Date('2021-01-04T15:15:00Z'), Value: 5.64 },
]);
el.value = table;
el.addEventListener('gui-table-click', (ev) => {
  window.alert(`Clicked row=${ev.detail.rowIdx},col=${ev.detail.colIdx}`);
});

Global filtering

The following example shows how to add an input to filter the table:

const el = document.createElement('gui-table');
const table = gc.core.Table.fromRows([
  ['1984', 'George Orwell', 1949, 328],
  ['To Kill a Mockingbird', 'Harper Lee', 1960, 281],
  ['The Great Gatsby', 'F. Scott Fitzgerald', 1925, 180],
  ['One Hundred Years of Solitude', 'Gabriel Garcia Marquez', 1967, 417],
  ['Moby Dick', 'Herman Melville', 1851, 635],
  ['War and Peace', 'Leo Tolstoy', 1869, 1225],
  ['Pride and Prejudice', 'Jane Austen', 1813, 279],
  ['The Catcher in the Rye', 'J.D. Salinger', 1951, 214],
  ['The Hobbit', 'J.R.R. Tolkien', 1937, 310],
]);
table.headers = ['Title', 'Author', 'Year Published', 'Pages'];
el.value = table;
el.globalFilter = true;
el.addEventListener('gui-table-click', (ev) => {
  window.alert(`Clicked row=${ev.detail.rowIdx},col=${ev.detail.colIdx}`);
});

Sort by column

Table can specify the current column used for sorting by using the sortBy property.

const el = document.createElement('gui-table');
const table = gc.core.Table.fromRows([
  ['1984', 'George Orwell', 1949, 328],
  ['To Kill a Mockingbird', 'Harper Lee', 1960, 281],
  ['The Great Gatsby', 'F. Scott Fitzgerald', 1925, 180],
  ['One Hundred Years of Solitude', 'Gabriel Garcia Marquez', 1967, 417],
  ['Moby Dick', 'Herman Melville', 1851, 635],
  ['War and Peace', 'Leo Tolstoy', 1869, 1225],
  ['Pride and Prejudice', 'Jane Austen', 1813, 279],
  ['The Catcher in the Rye', 'J.D. Salinger', 1951, 214],
  ['The Hobbit', 'J.R.R. Tolkien', 1937, 310],
]);
table.headers = ['Title', 'Author', 'Year Published', 'Pages'];
el.value = table;
el.sortBy = [2, 'desc']; // 'asc', 'desc' or undefined for default order

Filter by column

Table can be filtered by column using the filterColumns property.

const el = document.createElement('gui-table');
const table = gc.core.Table.fromRows([
  ['1984', 'George Orwell', 1949, 328],
  ['To Kill a Mockingbird', 'Harper Lee', 1960, 281],
  ['The Great Gatsby', 'F. Scott Fitzgerald', 1925, 180],
  ['One Hundred Years of Solitude', 'Gabriel Garcia Marquez', 1967, 417],
  ['Moby Dick', 'Herman Melville', 1851, 635],
  ['War and Peace', 'Leo Tolstoy', 1869, 1225],
  ['Pride and Prejudice', 'Jane Austen', 1813, 279],
  ['The Catcher in the Rye', 'J.D. Salinger', 1951, 214],
  ['The Hobbit', 'J.R.R. Tolkien', 1937, 310],
]);
table.headers = ['Title', 'Author', 'Year Published', 'Pages'];
el.value = table;
el.filterColumns = ['the'];

Columns

By default, gui-table automatically displays all columns from the provided table using gui-value.

If you want finer control over which columns are shown or change their display order, you can set the columns property. The order of items in the columns array determines the display order in the table. Each column definition must at least specify the target table column via its index:

const tableEl = document.createElement('gui-table');
tableEl.value = myTable;
tableEl.columns = [
  { index: 4 }, // display myTable[4] as column 0
  { index: 2 }, // display myTable[2] as column 1
];

In this example:

  • Table column 4 is displayed first.
  • Table column 2 is displayed second.
  • All other columns are hidden.

Using Default Columns

If you only want to override or hide specific columns without redefining all of them, you can enable useDefaultColumns = true together with the columns property.

When useDefaultColumns is true:

  • The table preserves its natural column order.
  • Column definitions in columns are applied as overrides, not as a new order.

To hide a column in this mode, add hide: true for that column index:

tableEl.useDefaultColumns = true;
tableEl.columns = [
  { index: 2, hide: true }, // hides column 2
  { index: 4, /* override properties here if needed */ },
];

Cell

By default, gui-table uses gui-value to render its cells.
However, you can customize how cells are displayed for each column by overriding the cell property in the column definition.
The cell property accepts either:

  • A WebComponent tag name, or
  • A function used as a render callback.

If using a WebComponent tag, it must implement the AnyValueElement interface:

interface AnyValueElement<T> {
  get value(): T;
  set value(value: T);
}

gui-table will automatically set cell.value = ... with the actual cell value on each render.

Tag

In this example, the cell property is set to 'arrow-icon', a custom WebComponent that renders an arrow depending on the sentiment value:

const el = document.createElement('gui-table');
el.value = gc.core.Table.fromObjects([
  { date: new Date('2019-01-01T15:15:00Z'), value: -2.5, sentiment: null },
  { date: new Date('2019-01-02T15:15:00Z'), value: 1.458, sentiment: 'up' },
  { date: new Date('2020-01-03T15:15:00Z'), value: 0.009, sentiment: 'down' },
  { date: new Date('2021-01-04T15:15:00Z'), value: 5.64, sentiment: 'up' },
]);
el.useDefaultColumns = true;
el.columns = [{ index: 2, cell: 'arrow-icon' }];

class ArrowIcon extends HTMLElement {
  /** This will be called by `gui-table` to render the cell of column 2 */
  set value(value) {
    switch (value) {
      case 'up':
        this.title = value;
        this.innerHTML =
          '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="none"><path d="M12 6V18M12 6L7 11M12 6L17 11" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>';
        break;
      case 'down':
        this.innerHTML =
          '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="none"><path d="M12 6V18M12 18L7 13M12 18L17 13" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>';
        this.title = value;
        break;
      default:
        this.innerHTML =
          '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="none"><path d="M12 19H12.01M8.21704 7.69689C8.75753 6.12753 10.2471 5 12 5C14.2091 5 16 6.79086 16 9C16 10.6565 14.9931 12.0778 13.558 12.6852C12.8172 12.9988 12.4468 13.1556 12.3172 13.2767C12.1629 13.4209 12.1336 13.4651 12.061 13.6634C12 13.8299 12 14.0866 12 14.6L12 16" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>';
        this.title = 'unknown';
    }
  }
}

if (!customElements.get('arrow-icon')) {
  customElements.define('arrow-icon', ArrowIcon);
}

Function

If creating a WebComponent feels like overkill, you can also provide a function directly as cell. This function is used as the render callback of a dynamically created element:

const el = document.createElement('gui-table');
el.value = gc.core.Table.fromObjects([
  { date: new Date('2019-01-01T15:15:00Z'), value: -2.5, sentiment: null },
  { date: new Date('2019-01-02T15:15:00Z'), value: 1.458, sentiment: 'up' },
  { date: new Date('2020-01-03T15:15:00Z'), value: 0.009, sentiment: 'down' },
  { date: new Date('2021-01-04T15:15:00Z'), value: 5.64, sentiment: 'up' },
]);
el.useDefaultColumns = true;
el.columns = [{
  index: 2,
  cell: ({ value }) => {
    const el = document.createElement('div');
    switch (value) {
      case 'up':
        el.title = value;
        el.innerHTML =
          '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="none"><path d="M12 6V18M12 6L7 11M12 6L17 11" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>';
        break;
      case 'down':
        el.innerHTML =
          '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="none"><path d="M12 6V18M12 18L7 13M12 18L17 13" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>';
        el.title = value;
        break;
      default:
        el.innerHTML =
          '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="none"><path d="M12 19H12.01M8.21704 7.69689C8.75753 6.12753 10.2471 5 12 5C14.2091 5 16 6.79086 16 9C16 10.6565 14.9931 12.0778 13.558 12.6852C12.8172 12.9988 12.4468 13.1556 12.3172 13.2767C12.1629 13.4209 12.1336 13.4651 12.061 13.6634C12 13.8299 12 14.0866 12 14.6L12 16" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>';
        el.title = 'unknown';
        break;
    }
    return el;
  },
}];

Value

For simpler overrides, gui-table also provides the columns[index].value hook. This hook receives the raw cell value and can return anything. The returned value will then be passed to the underlying cell renderer (e.g. gui-value):

const el = document.createElement('gui-table');
el.value = gc.core.Table.fromObjects([
  { date: new Date('2019-01-01T15:15:00Z'), value: -2.5, sentiment: null },
  { date: new Date('2019-01-02T15:15:00Z'), value: 1.458, sentiment: 'up' },
  { date: new Date('2020-01-03T15:15:00Z'), value: 0.009, sentiment: 'down' },
  { date: new Date('2021-01-04T15:15:00Z'), value: 5.64, sentiment: 'up' },
]);
el.useDefaultColumns = true;
el.columns = [{
  index: 2,
  value: ({ value }) => {
    switch (value) {
      case 'up': return '↑';
      case 'down': return '↓';
      default: return 'N/A';
    }
  },
}];

Table Mappings

The mappings utility allows you to create a new table based on an existing table. The new table will have the same rows as the original table, but the columns will be mapped to new columns. This is useful when you want to create a new table with a subset of the columns of the original table.

In the example below, the original table has 3 columns where the 3rd one is an object, so we extract the values from that object into 2 new columns.

The mappings work with any Object, Map, Array and node.

const raw = document.createElement('gui-table');
raw.value = await gc.project.tableMappingExample();

const mapped = document.createElement('gui-table');
// Create the mappings to extract `container.inner.value_a` and `container.inner.value_b`
const mappings = [
  new gc.core.TableColumnMapping(gc.project.Container.$fields.inner, ['value_a']),
  new gc.core.TableColumnMapping(gc.project.Container.$fields.inner, ['value_b']),
];
mapped.value = await gc.core.Table.applyMappings(raw.table, mappings);
mapped.useDefaultColumns = true;
mapped.columns = [
  { index: gc.project.Container.$fields.inner, hide: true }, // hide the original column
  { index: gc.project.Container.$fields.inner + gc.project.Inner.$fields.value_a + 1, header: 'Value A' },
  { index: gc.project.Container.$fields.inner + gc.project.Inner.$fields.value_b + 1, header: 'Value B' },
];

const el = document.createElement('div');
el.className = 'gui-row';
el.style.gap = '20px';
el.appendChild(raw);
el.appendChild(mapped);
type Container {
    name: String;
    time: time;
    inner: Inner;
}

type Inner {
    value_a: int;
    value_b: int;
}

@expose
fn tableMappingExample(): Array<Container> {
    var arr = Array<Container> {};
    var now = time::now();
    for (var i = 0; i < 5; i++) {
        var container = Container {
            name: "container-${i}",
            time: now,
            inner: Inner {
                value_a: i * 2,
                value_b: i * 3,
            },
        };
        arr.add(container);
        now = now + 1min;
    }
    return arr;
}

Custom styles

const el = document.createElement('gui-table');
el.classList.add('gui-theme-light', 'custom-style');
el.value = [
  { ident: '189927-1', type: 'MTS', voltage: 20 },
  { ident: '142686-1', type: 'MTS', voltage: 15 },
  { ident: '192771-1', type: 'BTS', voltage:  7 },
];
el.rowHeight = 40;
el.columns = [
  { index: 0 },
  { index: 1 },
  {
    index: 2,
    value: ({ value }) => `${value} kV (MV)`,
  },
  {
    index: 0,
    header: '',
    filterable: false,
    sortable: false,
    cell: ({ value }) => {
      return gc.web.createElement('sl-icon-button', {
        name: 'eye',
        label: 'Details',
        onclick: () => window.alert(`Clicked: ${value}`),
      });
    }
  }
];
gui-table.custom-style {
  --table-head-height: 40px;
  --table-border-color: transparent;
}

gui-table.custom-style::part(title) {
  font-weight: bold;
}

CSS Variables

Variable Purpose Default Value
--icon-sort-default Icon displayed when column is not sorted unset
--icon-sort-asc Icon for ascending sort state
--icon-sort-desc Icon for descending sort state
--table-spacing General spacing/padding throughout table var(--spacing)
--table-bg-color Main table background color var(--bg-1)
--table-cell-bg-color Background color for table cells var(--base-1)
--table-border-color Color for table borders and dividers var(--border-color)
--table-filter-border-top-left-radius Top-left border radius for filter input var(--border-radius)
--table-filter-border-top-right-radius Top-right border radius for filter input var(--border-radius)
--table-subheader-color Color for column subheader text var(--text-muted)
--table-resizer-width Width of column resize handles 3px
--table-resizer-bg-color Background color of resize handles var(--primary)
--table-resizer-bg-color-hover Resize handle color on hover var(--primary-hover)
--table-head-height Height of table header row fit-content
--table-head-bg-color Background color of header cells var(--base-0)
--table-head-bg-color-hover Header cell background on hover var(--bg-1)
--table-head-border-color Border color for header cells var(--base-2)
--table-head-sorter-color-hover Sort icon color on hover var(--primary-hover)
--table-head-color-active Text color for active/sorted columns var(--accent-0)
--table-head-filter-bg-color-hover Filter button background on hover var(--primary-focus)

Slots

Slots Description
table The table container element
header The table header
header-cell For every header cells
title The header cell title
filter The global table input (optional)
filter-input The input of the header cells
sorter The header cell sorting icon
row For every row in the table body

Events

Name Detail Description
gui-table-click GuiTableEventDetail Triggered when a row is clicked
gui-table-dblclick GuiTableEventDetail Triggered when a row is double-clicked

GuiTableEventDetail

export type GuiTableEventDetail = {
  /** The clicked row index. */
  rowIdx: number;
  /** The clicked column index. */
  colIdx: number;
};