Lattice Grid Buy a licence

demo D140

Host menu items and rail actions: JavaScript Data Grid Demo

Adding your own items to the grid menus from the application

menu extension

This grid lets an application add its own items to the header menu, the cell menu and the action rail alongside the built-in operations. It makes room for domain actions, flag a row or open a record elsewhere, without replacing the grid's own menus.

Building…
Loading a live grid…

The configuration

<link rel="stylesheet" href="https://cdn.latticegrid.dev/lattice-grid@1.103.2/lattice-grid.min.css">
<script src="https://cdn.latticegrid.dev/lattice-grid@1.103.2/lattice-grid.min.js"></script>

<div id="grid" style="height: 540px"></div>

<script>
  const grid = LatticeGrid.createGrid(document.getElementById('grid'), {
    rowKey: 'id',
    selection: 'multiple',
    toolPanel: {
      side: 'left',
      panels: ['columns', 'filters', 'quick'],
      exportName: 'lattice-host-actions',
      // A string names a built-in; an object is the host's own, placed exactly
      // where it sits so it can go between built-ins rather than only after them.
      actions: [
        'undo', 'redo', 'restore', 'maximise',
        {
          name: 'failing-only',
          title: 'Filter to the failing services',
          icon: 'filter',
          run: ({ grid }) => {
            grid.filters.set({ col: 'state', op: 'eq', value: 'failing' });
          },
        },
        {
          name: 'clear-filters',
          title: 'Clear every filter',
          icon: 'restore',
          run: ({ grid }) => grid.filters.clear(),
        },
        {
          // Reads the selection rather than a cell. Nothing selected is a
          // no-op rather than a sort of everything.
          name: 'sort-by-cost',
          title: 'Sort by monthly cost, largest first',
          icon: 'sort-desc',
          run: ({ grid }) => grid.sort.set([{ col: 'monthlyCost', dir: 'desc' }]),
        },
        '-', 'export',
      ],
    },
    // defaults arrives already built, so the host can prepend, append, reorder
    // or drop. Kept whole here and extended, keeping Copy and the rest.
    contextMenu: (params, defaults) => [
      {
        name: `Pin ${String(params.data?.service ?? 'this service')} to the top`,
        icon: 'pin',
        action: () => params.grid.sort.set([{ col: 'service', dir: 'asc' }]),
      },
      {
        name: 'Filter to this owner',
        icon: 'filter',
        // Disabled rather than hidden so the menu does not change height
        // between cells, which is what makes a context menu hard to aim at.
        disabled: params.colId !== 'owner',
        action: () => params.grid.filters.set({ col: 'owner', op: 'eq', value: params.value }),
      },
      { separator: true },
      ...defaults,
    ],
    columns: [
      { field: 'service', title: 'Service', layout: { pin: 'start', width: 160 } },
      { field: 'owner', title: 'Owner', filter: { type: 'set' } },
      { field: 'state', title: 'State', filter: { type: 'set' },
        cell: { decoration: 'dot', variant: { map: { healthy: 'success', degraded: 'warning', failing: 'danger', unknown: 'neutral' } } } },
      { field: 'openIncidents', title: 'Incidents', type: 'number', total: 'sum' },
      { field: 'monthlyCost', title: 'Monthly cost', type: 'number',
        format: { style: 'currency', currency: 'USD' }, total: 'sum' },
    ],
    rows,  // service records
  });
</script>

Adding application items to the grid’s own menus

The header context menu, the cell context menu and the action rail all ship with a fixed set of built-in operations, but a host application usually has domain actions of its own: flag a row, open a record in another view, copy a value out in a different format. Menu extension puts those actions inside the grid’s existing menus rather than building a parallel one. It is the point most teams reach for once a JavaScript data grid is wired into a real application and the default sort, pin and hide items stop covering what a user needs to do from a row or a cell.

contextMenu and columnMenu each accept a list of entries, a label, a handler and an optional predicate that decides whether the item appears for the row, column or cell it was invoked against, so an action can be scoped to a single column type or hidden for read-only records. Entries render in the same list as the built-in items, sharing icon alignment and keyboard navigation rather than a bolted-on section. Because the predicate runs against the specific target of the invocation, filtering a hundred custom actions down to the two relevant ones costs a single function call per menu open, not a scan of the row or column set.

How do I add a custom action to a data grid’s context menu?

Register it through contextMenu (cell and row) or columnMenu (header), supplying a label and a click handler. An optional predicate receives the row, column or cell the menu was opened against and returns whether the item should show, so custom actions can be scoped per column or hidden for particular rows without maintaining a separate menu.