diff --git a/src/cockpitMarkup.test.mjs b/src/cockpitMarkup.test.mjs index 034cf3d00..70641642c 100644 --- a/src/cockpitMarkup.test.mjs +++ b/src/cockpitMarkup.test.mjs @@ -633,17 +633,22 @@ test('Cockpit side surfaces behave as two single-expanded accordions', () => { ); assert.match( radioBindings, - /'gev:cockpit-context-expanded'[\s\S]*?setPanelCollapsed\('data-panel', true\);/, + /'gev:cockpit-context-expanded'[\s\S]*?setPanelCollapsed\('data-panel', true\);\s*this\.actions\.setPanelCollapsed\('utilities-panel', true\);/, ); assert.match( ui, - /!nextCollapsed\s*&&\s*this\.cockpitView\?\.active\s*&&\s*panelId === 'data-panel'[\s\S]*?_cockpitContextCollapsedForDataPanel =\s*!this\.cockpitView\.contextCollapsed[\s\S]*?this\.cockpitView\.setContextCollapsed\(true\);/, + /!nextCollapsed\s*&&\s*this\.cockpitView\?\.active\s*&&\s*COCKPIT_LEFT_PANEL_IDS\.has\(panelId\)[\s\S]*?_cockpitContextCollapsedForDataPanel =\s*!this\.cockpitView\.contextCollapsed[\s\S]*?this\.cockpitView\.setContextCollapsed\(true\);/, ); assert.match( ui, - /nextCollapsed\s*&&\s*this\.cockpitView\?\.active\s*&&\s*panelId === 'data-panel'[\s\S]*?_cockpitContextCollapsedForDataPanel[\s\S]*?this\.cockpitView\.setContextCollapsed\(false\);/, + /nextCollapsed\s*&&\s*this\.cockpitView\?\.active\s*&&\s*COCKPIT_LEFT_PANEL_IDS\.has\(panelId\)[\s\S]*?_cockpitContextCollapsedForDataPanel[\s\S]*?this\.cockpitView\.setContextCollapsed\(false\);/, 'closing Data Layers must restore Contact only after an automatic collapse', ); + assert.match( + ui, + /const COCKPIT_LEFT_PANEL_IDS = new Set\(\['data-panel', 'utilities-panel'\]\);/, + 'Utilities folds Contact away in Cockpit as Data Layers does', + ); assert.match( setContextCollapsed.toString(), /const wasCollapsed = this\.contextCollapsed;[\s\S]*?this\.active && wasCollapsed && !this\.contextCollapsed[\s\S]*?'gev:cockpit-context-expanded'/, diff --git a/src/ui/cockpitLayout.js b/src/ui/cockpitLayout.js index c0a00299d..2c5300b8a 100644 --- a/src/ui/cockpitLayout.js +++ b/src/ui/cockpitLayout.js @@ -84,8 +84,9 @@ export function syncContextLayout() { return; } const defaultTop = Math.max(255, Math.min(300, window.innerHeight * 0.28)); + // An open left panel Cockpit keeps (Data Layers or Utilities). const dataPanel = document.querySelector( - '#left-panel-stack > #data-panel:not(.collapsed)', + '#left-panel-stack > :is(#data-panel, #utilities-panel):not(.collapsed)', ); let desiredTop = defaultTop; if (this.contextCollapsed && dataPanel) { diff --git a/src/ui/cyberTheme.test.mjs b/src/ui/cyberTheme.test.mjs index eabd3df38..75746b6e3 100644 --- a/src/ui/cyberTheme.test.mjs +++ b/src/ui/cyberTheme.test.mjs @@ -854,7 +854,7 @@ test('Cyber Cockpit keeps Data Layers inside its bounded rail', () => { ); assert.match( cyberStyles, - /body\.cockpit-mode #left-panel-stack > #data-panel \{[\s\S]*?max-height: min\(64vh, 610px\);/, + /body\.cockpit-mode\s+#left-panel-stack\s+> :is\(#data-panel, #utilities-panel\) \{[\s\S]*?max-height: min\(64vh, 610px\);/, ); }); diff --git a/src/ui/panelChrome.js b/src/ui/panelChrome.js index f2adef3bf..e83083865 100644 --- a/src/ui/panelChrome.js +++ b/src/ui/panelChrome.js @@ -25,6 +25,11 @@ const SHARE_PANEL_STATE_SPECS = Object.freeze([ { id: 'pp-toggles' }, { id: 'param-slider-panel' }, ]); +/** + * The left panels Cockpit keeps (Data Layers, and Utilities for Directions): + * opening one there folds the Cockpit context rail away until it closes. + */ +const COCKPIT_LEFT_PANEL_IDS = new Set(['data-panel', 'utilities-panel']); /** The command dock trays with a pin button. */ const PINNABLE_PANEL_IDS = new Set( SHARE_PANEL_STATE_SPECS.filter((spec) => spec.pinnable).map( @@ -497,7 +502,7 @@ export class PanelChrome { if ( !nextCollapsed && this.cockpitView?.active && - panelId === 'data-panel' + COCKPIT_LEFT_PANEL_IDS.has(panelId) ) { this._cockpitContextCollapsedForDataPanel = !this.cockpitView.contextCollapsed; @@ -528,7 +533,7 @@ export class PanelChrome { if ( nextCollapsed && this.cockpitView?.active && - panelId === 'data-panel' && + COCKPIT_LEFT_PANEL_IDS.has(panelId) && this._cockpitContextCollapsedForDataPanel ) { this._cockpitContextCollapsedForDataPanel = false; diff --git a/src/ui/radioBindings.js b/src/ui/radioBindings.js index 05a211537..ac3c7c151 100644 --- a/src/ui/radioBindings.js +++ b/src/ui/radioBindings.js @@ -380,7 +380,9 @@ export function bindRadioControls() { window, 'gev:cockpit-context-expanded', () => { + // Contact takes the room the open left panel had. this.actions.setPanelCollapsed('data-panel', true); + this.actions.setPanelCollapsed('utilities-panel', true); }, tunerListenerOptions, ); diff --git a/src/ui/styles/cyber.css b/src/ui/styles/cyber.css index dbaf12146..4957cd769 100644 --- a/src/ui/styles/cyber.css +++ b/src/ui/styles/cyber.css @@ -2771,7 +2771,10 @@ border-inline-start: 0; } -:root[data-ui-theme='cyber'] body.cockpit-mode #left-panel-stack > #data-panel { +:root[data-ui-theme='cyber'] + body.cockpit-mode + #left-panel-stack + > :is(#data-panel, #utilities-panel) { --panel-expanded-width: var(--cyber-panel-width); width: var(--cyber-panel-width); max-height: min(64vh, 610px); @@ -2780,7 +2783,7 @@ :root[data-ui-theme='cyber'] body.cockpit-mode #left-panel-stack - > #data-panel.collapsed { + > :is(#data-panel, #utilities-panel).collapsed { width: var(--cyber-panel-collapsed-width); } @@ -2813,7 +2816,7 @@ :root[data-ui-theme='cyber'] body.cockpit-mode #left-panel-stack - > #data-panel:not(.collapsed) { + > :is(#data-panel, #utilities-panel):not(.collapsed) { flex: 0 0 var(--cyber-cockpit-panel-height, 40vh); height: var(--cyber-cockpit-panel-height, 40vh); max-height: var(--cyber-cockpit-panel-height, 40vh);