mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-15 15:52:21 +00:00
fix(ui): right rail tabs: review fixes, launcher in the strip, strip never scrolls on common windows
Review fixes P1.1-P2.7: a panel hidden at mount gets its tab when revealed; any live opening of a rail panel makes it the owner, restores do not; a short lane scrolls the strip with a thin scrollbar and brings a newly opened tab into view once; the join and the panel cover stay inside the lane and the fillet shrinks to the room left; a share link carries an owner-collapsed peer as collapsed so the recipient opens the sender's panel; border-box on phones; the strip's column folds into the panel width at 721-760px; the focus ring draws inside the tab. The compact Radio launcher moves from Context's header into a tool slot under the tabs on desktop (back into the header for the phone sheet) and always toggles the compact player, also while Context is open; the player's Details button leads to the Radio section. The jump-to-section role, its two labels and the #804 closings of the player on Context expansion and rail layout passes are gone with it. The rail panels' own collapse arrows are hidden on desktop: the tab opens and closes a panel, Escape inside closes it and returns focus to the tab. The tabs are denser and the rail climbs toward the HUD corner above its left-rail baseline when the strip's column is taller than the lane, so the strip no longer scrolls on windows down to about 680px tall; the measuring pass keeps the strip's scroll offset. scripts/qa-radio.mjs expectations follow the launcher's new role. Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
1 parent
ff017c99ca
commit
bff1d0eb29
24 files changed
+804
-379
No files matched your search
+1
-83
@@ -109,94 +109,12 @@ export class RadioControls {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bring the embedded Radio section into the expanded Context scroller.
|
||||
* This never changes Radio power, playback, selection, or Context mode.
|
||||
* @param {{focusTarget?: HTMLElement|null}} [options]
|
||||
* @returns {Promise<boolean>} Whether the internal scroller moved.
|
||||
*/
|
||||
async _revealRadioPanelInsideContext({ focusTarget = null } = {}) {
|
||||
const contextPanel = document.getElementById('global-context-panel');
|
||||
const scroller =
|
||||
(document.documentElement?.dataset.uiTheme === 'cyber' &&
|
||||
contextPanel?.querySelector('.cyber-panel-body')) ||
|
||||
contextPanel?.querySelector('.global-context-panel-inner');
|
||||
if (
|
||||
!contextPanel ||
|
||||
contextPanel.classList.contains('collapsed') ||
|
||||
!scroller ||
|
||||
!this._radioPanel ||
|
||||
this._radioPanel.classList.contains('collapsed')
|
||||
)
|
||||
return false;
|
||||
|
||||
await new Promise((resolve) =>
|
||||
requestAnimationFrame(() => requestAnimationFrame(resolve)),
|
||||
);
|
||||
if (
|
||||
this.destroyed ||
|
||||
contextPanel.classList.contains('collapsed') ||
|
||||
this._radioPanel.classList.contains('collapsed')
|
||||
)
|
||||
return false;
|
||||
|
||||
const viewport = scroller.getBoundingClientRect();
|
||||
const radioRect = this._radioPanel.getBoundingClientRect();
|
||||
const desired = scroller.scrollTop + radioRect.top - viewport.top - 10;
|
||||
const next = Math.min(
|
||||
Math.max(0, scroller.scrollHeight - scroller.clientHeight),
|
||||
Math.max(0, desired),
|
||||
);
|
||||
const moved = Math.abs(next - scroller.scrollTop) >= 1;
|
||||
if (moved) {
|
||||
const reducedMotion = window.matchMedia?.(
|
||||
'(prefers-reduced-motion: reduce)',
|
||||
)?.matches;
|
||||
scroller.scrollTo({
|
||||
top: next,
|
||||
behavior: reducedMotion ? 'auto' : 'smooth',
|
||||
});
|
||||
}
|
||||
focusTarget?.focus?.({ preventScroll: true });
|
||||
return moved;
|
||||
}
|
||||
|
||||
/** Keep the launcher truthful for the active theme and Context route. */
|
||||
/** Keep the launcher's ARIA state matched to the compact player. */
|
||||
_syncContextRadioLauncherState() {
|
||||
if (this.destroyed || !this._contextRadioToggleBtn) return;
|
||||
const contextPanel = globalThis.document?.getElementById(
|
||||
'global-context-panel',
|
||||
);
|
||||
const contextExpanded = Boolean(
|
||||
contextPanel && !contextPanel.classList.contains('collapsed'),
|
||||
);
|
||||
if (contextExpanded) {
|
||||
const radioExpanded = Boolean(
|
||||
this._radioPanel && !this._radioPanel.classList.contains('collapsed'),
|
||||
);
|
||||
this._contextRadioToggleBtn.setAttribute('aria-controls', 'radio-panel');
|
||||
this._contextRadioToggleBtn.setAttribute(
|
||||
'aria-expanded',
|
||||
String(radioExpanded),
|
||||
);
|
||||
const label = t(
|
||||
radioExpanded
|
||||
? msg('Go to expanded Radio section')
|
||||
: msg('Expand Radio section in Context'),
|
||||
);
|
||||
this._contextRadioToggleBtn.setAttribute('aria-label', label);
|
||||
this._contextRadioToggleBtn.title = label;
|
||||
return;
|
||||
}
|
||||
// This launcher toggles the compact player even when the detailed panel
|
||||
// is open elsewhere. Its ARIA state must describe that same disclosure.
|
||||
const compactOpen = Boolean(
|
||||
this._contextRadioDock?.classList.contains('disclosure-open'),
|
||||
);
|
||||
this._contextRadioToggleBtn.setAttribute(
|
||||
'aria-controls',
|
||||
'context-radio-mini',
|
||||
);
|
||||
this._contextRadioToggleBtn.setAttribute(
|
||||
'aria-expanded',
|
||||
String(compactOpen),
|
||||
|
||||
Reference in new issue
Block a user