Object menu
This page belongs to Data App developer guide. What each item does — the data dialog, the exports and the image capture — is in Data dialogs and exports.
Build a shared object menu early, before objects acquire separate event handlers. Give each object the menu actions it supports, including viewing and exporting data where available.
Default items
Section titled “Default items”Use this order and omit unsupported actions.
| Item | Offered on | Notes |
|---|---|---|
| Copy cell value | A table, when the pointer is over a cell | Copy the displayed text. |
| Show as table | Objects whose query your app owns | See Show an object’s data. |
| Export data as CSV | Same | |
| Export data as Excel | Same | Offer Excel alongside CSV. |
| Export image of this card / chart / table | Every object | A PNG of that one object as drawn. |
| Export image of the region | Every object inside a named region | For example, all the figure cards as one picture. |
No query insights item in a Data App. A Workbook’s menu offers the SQL and execution insights for an object. A Data App does not receive them (see What a Data App can reach), so omit this action.
Wire one listener for the whole document
Section titled “Wire one listener for the whole document”Objects in the header — filters, for example — live outside the board, so a listener scoped to the board gives them no menu. Listen on the document and scope by attribute:
document.addEventListener('contextmenu', (event) => { if (!(event.target instanceof Element)) return; const host = event.target.closest<HTMLElement>('[data-object-id]'); if (!host) return; event.preventDefault(); const spec = specsById.get(host.dataset.objectId!); const cell = event.target.closest('[role="gridcell"]'); openMenu(event.clientX, event.clientY, buildItems(spec, host, cell));});Mark every object’s host with its id, including hand-built figure cards, so one listener serves them
all. Tables render role="gridcell" cells, which is how the menu finds the cell under the pointer;
see Row structure.
Test event.target instanceof Element rather than relying on optional chaining: ?. guards a
nullish target, not a target such as window that has no closest method.
Scope by named region
Section titled “Scope by named region”Offer image export for the object itself and, when it belongs to a named group, the whole group. Declare groups in the markup:
<section class="charts" data-group="Charts">…</section><div class="kpi-row" data-group="KPI cards">…</div><section class="detail" data-group="Detail panel">…</section>Walk up with host.closest('[data-group]').
Name the scope in each menu label, for example “Export image of this card” and “Export image of KPI cards”.
Build it as a <dialog>
Section titled “Build it as a <dialog>”A modal <dialog>, opened with showModal(), gives you focus handling, Escape to close and a
backdrop. A click on the backdrop does not close it by default: add closedby="any" where the
browser supports it, or close it yourself when a click lands outside the menu’s box. Position it at
the pointer and flip it when it would leave the viewport.
- Read CSS rules that fail silently before styling it:
an unqualified
displayon a<dialog>leaves closed menus laid out over the page, catching clicks. - For an unavailable item with an explanatory tooltip, use
aria-disabled="true"so it can remain focusable. The item’s handler must check this state and ignore activation. - Close the menu before running the item. An item that opens a dialog must not race the menu’s own close.
- Style it from the chrome tokens, like the rest of the header’s popovers; see Keep the chrome consistent across themes. A minimum width around 216px matches Astrato’s own menus.