feat: add FLYOUT_MENU_MAP data map and _buildFlyoutMenuItems builder

This commit is contained in:
Brian Krabach
2026-04-15 21:02:05 -07:00
parent 5c836c6108
commit e0700ef04d
2 changed files with 116 additions and 0 deletions
+76
View File
@@ -139,6 +139,82 @@ let _flyoutSessionKey = null;
let _flyoutSessionName = null; let _flyoutSessionName = null;
let _flyoutRemoteId = null; let _flyoutRemoteId = null;
/**
* Data map of menu item definitions keyed by view type.
* Each entry is an array of item config objects with:
* { label, action, className?, separator? }
* The 'user' view type gets the active view name injected at render time.
*/
var FLYOUT_MENU_MAP = {
'all': [
{ label: 'Add to View\u2026', action: 'add-to-view', className: 'flyout-menu__item--has-submenu' },
{ label: 'Hide', action: 'hide' },
{ separator: true },
{ label: 'Kill Session', action: 'kill', className: 'flyout-menu__item--danger' },
],
'user': [
{ label: 'Add to View\u2026', action: 'add-to-view', className: 'flyout-menu__item--has-submenu' },
{ label: 'Remove from {viewName}', action: 'remove-from-view' },
{ label: 'Hide', action: 'hide' },
{ separator: true },
{ label: 'Kill Session', action: 'kill', className: 'flyout-menu__item--danger' },
],
'hidden': [
{ label: 'Unhide', action: 'unhide' },
{ label: 'Unhide & Add to View\u2026', action: 'unhide-add-to-view', className: 'flyout-menu__item--has-submenu' },
{ separator: true },
{ label: 'Kill Session', action: 'kill', className: 'flyout-menu__item--danger' },
],
};
/**
* Build the flyout menu HTML string based on the active view type.
* Uses FLYOUT_MENU_MAP to generate items — no if/else chains.
* @returns {string} HTML for the menu items
*/
function _buildFlyoutMenuItems() {
// Determine view type: 'all', 'hidden', or 'user'
var viewType = _activeView;
if (viewType !== 'all' && viewType !== 'hidden') {
viewType = 'user';
}
var items = FLYOUT_MENU_MAP[viewType] || FLYOUT_MENU_MAP['all'];
var html = '';
for (var i = 0; i < items.length; i++) {
var item = items[i];
if (item.separator) {
html += '<div class="flyout-menu__separator" role="separator"></div>';
continue;
}
var label = item.label;
// Inject view name for "Remove from {viewName}"
if (label.indexOf('{viewName}') !== -1) {
var displayName = _activeView;
if (displayName.length > 20) {
displayName = displayName.substring(0, 20) + '\u2026';
}
label = label.replace('{viewName}', escapeHtml(displayName));
}
var cls = 'flyout-menu__item';
if (item.className) cls += ' ' + item.className;
var titleAttr = '';
if (item.action === 'remove-from-view' && _activeView && _activeView.length > 20) {
titleAttr = ' title="Remove from ' + escapeHtml(_activeView) + '"';
}
html += '<button class="' + cls + '" role="menuitem" data-action="' + item.action + '"' + titleAttr + '>';
html += label;
html += '</button>';
}
return html;
}
// ─── Settings state ─────────────────────────────────────────────────────────── // ─── Settings state ───────────────────────────────────────────────────────────
let _settingsOpen = false; let _settingsOpen = false;
let _serverSettings = null; let _serverSettings = null;
+40
View File
@@ -3748,3 +3748,43 @@ def test_old_tile_delete_handler_removed() -> None:
assert "tile-delete" not in bind_body, ( assert "tile-delete" not in bind_body, (
"The old .tile-delete delegated handler must be removed from bindStaticEventListeners" "The old .tile-delete delegated handler must be removed from bindStaticEventListeners"
) )
# ─── Task 4: Context-dependent menu items — FLYOUT_MENU_MAP ──────────────────
def test_flyout_menu_map_exists() -> None:
"""app.js must define a FLYOUT_MENU_MAP data structure."""
assert "FLYOUT_MENU_MAP" in _JS, (
"app.js must contain a FLYOUT_MENU_MAP data structure"
)
def test_flyout_menu_map_has_three_view_types() -> None:
"""FLYOUT_MENU_MAP must have keys for 'all', 'user', and 'hidden'."""
# The map should reference all three view types
map_section = _JS.split("FLYOUT_MENU_MAP")[1].split("};")[0]
assert "'all'" in map_section or '"all"' in map_section, (
"FLYOUT_MENU_MAP must include an 'all' key"
)
assert "'user'" in map_section or '"user"' in map_section, (
"FLYOUT_MENU_MAP must include a 'user' key"
)
assert "'hidden'" in map_section or '"hidden"' in map_section, (
"FLYOUT_MENU_MAP must include a 'hidden' key"
)
def test_build_flyout_menu_items_function_exists() -> None:
"""app.js must define a _buildFlyoutMenuItems function."""
assert "function _buildFlyoutMenuItems" in _JS, (
"app.js must contain a _buildFlyoutMenuItems function"
)
def test_build_flyout_uses_menu_map() -> None:
"""_buildFlyoutMenuItems must reference FLYOUT_MENU_MAP."""
fn_body = _JS.split("function _buildFlyoutMenuItems")[1].split("\nfunction ")[0]
assert "FLYOUT_MENU_MAP" in fn_body, (
"_buildFlyoutMenuItems must reference FLYOUT_MENU_MAP (data-driven, not if/else)"
)