Action Menu: one panel per list
A list whose rows share one menu renders its entries once. ws_menu_panel prints the panel as a template, and each row's Action Menu names it with panel.
When to Use
Use when:
Every row of a list has the same menu, give or take a few entries a row may lack: a list of workshops, of team members, of files. Render the entries once with
ws_menu_panel, give each row ws_menu($label, [], ['panel' => $id]), and drop the entries a row lacks in a ws-menu-open listener. The page carries one set of entries instead of one per row.
Don't use when:
There is one menu, or the rows' menus differ in more than a few removable entries: give each
ws_menu its own items. The panel's mode must match the menus that use it.
Example
Two rows, one panel. Open either menu: the panel is cloned in after that row's trigger, and removed when it closes.
document.addEventListener('ws-menu-open', function (e) {
const id = e.target.dataset.planId; // the row's own facts, on its menu
e.detail.panel.querySelectorAll('a[href]').forEach(a => a.href += '&plan=' + id);
});Accessibility
| Concern | Behavior |
|---|---|
| At rest | The template is inert, so the page holds no entries and the trigger promises no aria-controls it cannot keep. |
| Open | The clone is the panel ws_menu renders: its list is role="menu", named by the row's trigger, and the trigger's aria-controls names it while it is in the page. Keys, focus and dismissal are ws_menu's. |
| Closed | The clone is removed and aria-controls with it; the next open clones afresh against the row as it is then. |
Files
| File | Purpose |
|---|---|
includes/components/helpers.php | ws_menu_panel(), and ws_menu()'s panel option |
includes/components/menu.php | The template both render through |
includes/components/components.js | wsMenuClonePanel(): the clone on open, removed by wsMenuClose() |