6.10.94-stable

<gui-table />

Virtualized table component for core::Table

Usage

Column based

const el = document.createElement('gui-table');
el.value = {
  meta: [
    { header: 'Date' },
    { header: 'Value'}
  ],
  cols: [
    [
      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.addEventListener('table-click', (ev) => {
  window.alert(`Clicked row=${ev.detail.rowIdx},col=${ev.detail.colIdx}`);
});

Row based

const el = document.createElement('gui-table');
el.value = {
  meta: [ 'Date', 'Value' ],
  rows: [
    [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.addEventListener('table-click', (ev) => {
  window.alert(`Clicked row=${ev.detail.rowIdx},col=${ev.detail.colIdx}`);
});

Object based

const el = document.createElement('gui-table');
el.value = [
  { 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.addEventListener('table-click', (ev) => {
  window.alert(`Clicked row=${ev.detail.rowIdx},col=${ev.detail.colIdx}`);
});

Under-the-hood, <gui-table /> uses <gui-value /> to render cells. You can change the properties passed to <gui-value /> by setting cellProps.

Global filtering

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

const el = document.createElement('div');

const filter = document.createElement('input');
filter.type = 'search';
filter.placeholder = 'Filter the table...';
filter.oninput = () => (table.filter = filter.value);

const table = document.createElement('gui-table');
table.value = {
  meta: ['Title', 'Author', 'Year Published', 'Pages'],
  rows: [
    ["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.addEventListener('table-click', (ev) => {
  window.alert(`Clicked row=${ev.detail.rowIdx},col=${ev.detail.colIdx}`);
});

el.append(filter, table);

Sort by column

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

const el = document.createElement('gui-table');
el.sortBy = [2, 'desc']; // 'asc', 'desc' or undefined for default order
el.value = {
  meta: ['Title', 'Author', 'Year Published', 'Pages'],
  rows: [
    ["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],
  ],
};

Filter by column

Table can be filtered by column using the filterColumns property.

const el = document.createElement('gui-table');
el.value = {
  meta: ['Title', 'Author', 'Year Published', 'Pages'],
  rows: [
    ["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],
  ],
};
el.filterColumns = ['the'];

Custom element by columns

By default, gui-table uses gui-value to render its cells. But that is not always what the user wants. Therefore, each column can define its own “tagName” to use for the rendering of its cells. This is done by setting cellTagNames, see the example below:

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.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>';
        this.title = value;
        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);
}

const el = document.createElement('gui-table');
el.cellTagNames = {
  2: 'arrow-icon', // give a custom tagname to use to render cells of column 2 (Sentiment)
};
el.value = {
  meta: [
    { header: 'Date' },
    { header: 'Value'},
    { header: 'Sentiment'},
  ],
  cols: [
    [
      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],
    [null, 'up', 'down', 'up'],
  ],
};

The “tagName” is the name a WebComponent is registered with as in:

customElements.define('tag-name', MyComponent);

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 two columns, the time index and an object, which is not very user friendly. So we extract the values that interest us into new columns.

We also leverage the ignoreCols property to hide the original object column.

Also works with Maps, node and Array types.

const el = document.createElement('div');
const tableWithoutMapping = document.createElement('gui-table');
const tableWithMapping = document.createElement('gui-table');
greycat.default.call('project::tableMappingExample').then((table) => {
  tableWithoutMapping.value = table;

  // Create a mapping to extract the values of the object column
  // first parameter is the index of the column to map
  // second param is the attribute to extract from the object
  const mappings = [core.TableColumnMapping.create(1, ["valA"]), core.TableColumnMapping.create(1, ["valB"])]

  core.Table.applyMappings(table, mappings).then((mappedTable) => {
    tableWithMapping.value = mappedTable;
  });
});
tableWithMapping.ignoreCols = [1] // Hide the original object column
el.append(tableWithoutMapping);
el.append(tableWithMapping);

Dynamic styling

const el = document.createElement('gui-table');
el.value = {
  meta: [
    { header: 'Date' },
    { header: 'Value'}
  ],
  cols: [
    [
      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.onrowupdate = (el, row) => {
  const value = row[1].value;
  if(value > 5){
    (el.children[1]).style.color = 'orange';
  } else if(value < 0){
    (el.children[1]).style.color = 'cyan';
  } 
};

Highlight selected row

Since the table is virtualized and you can sort and filter it, the selected row index is not the same as the original row index. To get the original row index, you can use the originalIndex property in the row values.

Here is an example of how to highlight the selected row:

const el = document.createElement('gui-table');
let selectedRowIdx = null;
el.value = {
  meta: [
    { header: 'Col 0'},
    { header: 'Col 1'}
  ],
  cols: [
    Array.from({ length: 100 }, (_, i) => i),
    Array.from({ length: 100 }, (_, i) => i * 2),
  ]
}
el.addEventListener('table-click', (ev) => {
  selectedRowIdx = ev.detail.row[0].originalIndex;
  el.update();
});
el.onrowupdate = (el, row) => {
  if(row[0].originalIndex === selectedRowIdx){
    el.style.backgroundColor = 'red';
  } else {
    el.style.backgroundColor = '';
  }

};

Events

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

TableClickEventDetail

export type TableClickEventDetail = {
  /**
   * The current row index. This is not necessarily the "original" row index.
   * If the table is filtered or sorted, it is the "current" row index.
   *
   * To know the "original" index, check the property `originalIndex` in the `row` values.
   */
  rowIdx: number;
  /**
   * Current column index.
   */
  colIdx: number;
  /**
   * The associated row values.
   */
  row: Value[];
};

Value

type Value = {
  /** The actual value for the cell */
  value: unknown;
  /**
   * The original index of the row in the column.
   * 
   * This is required because sorting/filtering changes indexing.
   */
  originalIndex: number;
};

Override cell properties

/**
 * A function called to compute the cell properties
 * that will be passed to the underlying `<gui-value />` component.
 */
export type CellProps = (
  row: Value[],
  value: unknown,
  rowIdx: number,
  colIdx: number,
) => ValueProps & { value: unknown };

type ValueProps =
  Omit<utils.StringifyProps, 'value' | 'dateFmt' | 'numFmt'>
  & Partial<Pick<GuiValueProps, 'linkify' | 'onClick'>>;