demo D139
The cell context menu in htmx
An item that writes is never offered on a cell that cannot be written
cell:contextmenu
This grid offers per-value actions, copy, clear, comment or edit, on right-clicking a cell, and never offers a write action on a cell that cannot be written. It puts the right actions under the pointer, so the menu matches what a reader can actually do to that cell.
This is the htmx version. A server-rendered table is upgraded in place, so the page is readable before any script runs and gains the full grid when the module loads, which suits a progressively enhanced app.
The configuration
<link rel="stylesheet" href="https://cdn.latticegrid.dev/lattice-grid@1.108.0/lattice-grid.min.css">
<!-- A real, server-rendered table: readable with JavaScript off. data-field on
each header names the column that cell becomes, and data-id on each row is
the key the grid carries it under. -->
<table id="cell-context-menu-table" data-lattice-grid>
<thead>
<tr>
<th data-field="service">Service (read only)</th>
<th data-field="team">Team (read only)</th>
<th data-field="owner">Owner (editable)</th>
<th data-field="region">Region (editable)</th>
<th data-field="instances">Instances (edit: false)</th>
</tr>
</thead>
<tbody>
<tr data-id="1"><td>svc-00001</td><td>Search</td><td>k.yilmaz</td><td>Malmö</td><td>49</td></tr>
<tr data-id="2"><td>svc-00002</td><td>Mobile</td><td>m.svensson</td><td>Leeds</td><td>162</td></tr>
<tr data-id="3"><td>svc-00003</td><td>Identity</td><td>j.okafor</td><td>Zürich</td><td>20</td></tr>
<!-- the rest of the rows, rendered by your server -->
</tbody>
</table>
<script type="module">
import { hydrateTable } from 'https://cdn.latticegrid.dev/lattice-grid@1.108.0/modules/htmx.esm.min.js';
// The markup carries the rows; a server-rendered table's column types are
// declared in the config hydrateTable takes. The grid replaces the table
// where it stood.
const grid = hydrateTable(document.getElementById('cell-context-menu-table'), {
selection: 'multiple',
edit: { enabled: true },
// Right-click any cell for its menu. The actions offered fit the cell, so an
// editable cell offers the edit actions a read-only one leaves out.
columns: [
{ field: 'service', title: 'Service (read only)', layout: { pin: 'start', width: 190 } },
{ field: 'team', title: 'Team (read only)', layout: { width: 150 } },
{ field: 'owner', title: 'Owner (editable)', edit: true, layout: { width: 170 } },
{ field: 'region', title: 'Region (editable)', edit: true, layout: { flex: 1, min: 180 } },
{ field: 'instances', title: 'Instances (edit: false)', type: 'number', edit: false, layout: { width: 180 } },
],
});
</script>
Right-clicking a cell for a menu built from what that cell allows
A cell context menu is the natural place for per-value actions: copy, clear, comment, or edit, offered against the record under the pointer rather than as a permanent toolbar entry. The difficult part is not opening the menu but deciding its contents, since an item that writes is never offered on a cell that cannot be written. A read-only column, a cell disabled by a per-row rule, or a value a viewer lacks permission to change must each produce a shorter menu, not a full one with a disabled entry. Any JavaScript data grid handling mixed editable and locked columns needs this filtering done before the menu paints, not as a visual afterthought.
Lattice Grid exposes the interaction through the cell:contextmenu event, fired with the row, column, and value under the pointer, so a host application can inspect the same edit rules the grid uses and build a menu matching what the cell will accept. The default menu closes on outside click, on Escape, and once an item is chosen, and it is reachable without a mouse: the context-menu key and Shift+F10 open it against the focused cell. Menu construction runs once per invocation rather than being pre-built for every cell, so a wide grid with thousands of visible cells pays the permission-check cost only for the cell actually right-clicked.
Why does a cell’s context menu sometimes show fewer options than another cell in the same column?
Menu contents are computed per cell at the moment of the cell:contextmenu event, not fixed per column. A row-level rule, a permission check, or a value that fails validation can each remove a write action, so two cells in one column can legitimately offer different menus depending on the record behind them.