feat(shell): put every menu in the app through the one surface
Fourteen call sites, one tag name each and nothing else -- which is what menu-surface's shape buys: the host's panel is slotted into whichever presentation is up, so no item model, no keyboard model and no styling moved. The 48px rows come from contextMenuStyles, the one stylesheet every one of these hosts already includes, because the panel is the host's own light DOM and only the host's stylesheet can reach it. Two of the fourteen were found by the source sweep rather than by the conversion: queue-panel's add-to-playlist popup, which is a real menu. now-playing's cover preview is allowlisted instead -- it is a hover affordance in the bottom bar, so a touch device never opens it and nothing clips it. The playlist submenu had to come too, and that is the one place this change made something worse before it made it better. It is a placement="right-start" flyout anchored to its row, and making the menu full-width moved that anchor to x=0 -- so the flip put the picker at x -182 to 0, entirely off-screen, and "Add to Playlist" led nowhere at all. Before the change the row started at x~245 and the same flip landed on screen. It is a sheet now and stacks over the first, which is also why menu-shown does not re-assert focus while it is open. The three hosts that do not use ContextMenuController -- page-header's overflow menu, playlist-view's hand-rolled menu, queue-panel's picker -- bind menu-dismiss themselves, or Escape would close the sheet and leave their own open flag set. page-header is included deliberately: the clipping does not bite there, since it opens downward from the top of a full-height view, but on a phone every action of an overflowing page lives in that menu at wa-dropdown-item defaults. One surface, so there is no second answer to what a menu looks like.
This commit is contained in:
@@ -9,7 +9,8 @@ import {
|
||||
} from 'lit/decorators.js';
|
||||
import '@awesome.me/webawesome/dist/components/icon/icon.js';
|
||||
import '@awesome.me/webawesome/dist/components/popup/popup.js';
|
||||
import type WaPopup from '@awesome.me/webawesome/dist/components/popup/popup.js';
|
||||
import type { MenuSurface } from '../menu-surface/menu-surface';
|
||||
import '../menu-surface/menu-surface';
|
||||
import '@awesome.me/webawesome/dist/components/dropdown-item/dropdown-item.js';
|
||||
import { QueueController } from '@store/controllers/queue-controller';
|
||||
import { creditStore } from '@store/credit-store';
|
||||
@@ -34,7 +35,7 @@ import {
|
||||
contextMenuStyles,
|
||||
isContextMenuKey,
|
||||
} from '@utils/context-menu-controller.js';
|
||||
import type { ContextMenuHost } from '@utils/context-menu-controller.js';
|
||||
import type { ContextMenuHost, MenuTarget } from '@utils/context-menu-controller.js';
|
||||
import { focusRovingRow, nextRovingIndex } from '@utils/roving-rows';
|
||||
import { FavoritesController } from '@store/controllers/favorites-controller';
|
||||
import {
|
||||
@@ -140,13 +141,13 @@ export class QueuePanel
|
||||
private delegationAttached = false;
|
||||
|
||||
@query('#add-to-playlist-popup')
|
||||
private addToPlaylistPopup!: WaPopup;
|
||||
private addToPlaylistPopup!: MenuSurface;
|
||||
|
||||
@query('#context-menu')
|
||||
private contextMenuPopup!: WaPopup;
|
||||
private contextMenuPopup!: MenuSurface;
|
||||
|
||||
@query('#playlist-submenu')
|
||||
private playlistSubmenuPopup!: WaPopup;
|
||||
private playlistSubmenuPopup!: MenuSurface;
|
||||
|
||||
/** Unsubscribes the credit-arrival repaint. */
|
||||
private creditsUnsub?: () => void;
|
||||
@@ -305,11 +306,11 @@ export class QueuePanel
|
||||
// ContextMenuHost interface
|
||||
// =================================================================
|
||||
|
||||
getContextMenuPopup(): WaPopup | undefined {
|
||||
getContextMenuPopup(): MenuTarget | undefined {
|
||||
return this.contextMenuPopup;
|
||||
}
|
||||
|
||||
getPlaylistSubmenuPopup(): WaPopup | undefined {
|
||||
getPlaylistSubmenuPopup(): MenuTarget | undefined {
|
||||
return this.playlistSubmenuPopup;
|
||||
}
|
||||
|
||||
@@ -1092,7 +1093,7 @@ export class QueuePanel
|
||||
}
|
||||
}
|
||||
|
||||
private closePlaylistPicker() {
|
||||
private closePlaylistPicker = () => {
|
||||
if (!this.playlistPickerOpen) return;
|
||||
|
||||
this.playlistPickerOpen = false;
|
||||
@@ -1102,7 +1103,7 @@ export class QueuePanel
|
||||
if (popup) {
|
||||
popup.active = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private onPlaylistActionComplete = () => {
|
||||
this.closePlaylistPicker();
|
||||
@@ -2042,9 +2043,11 @@ export class QueuePanel
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<wa-popup
|
||||
<menu-surface
|
||||
id="add-to-playlist-popup"
|
||||
label="Add to playlist"
|
||||
placement="bottom-end"
|
||||
@menu-dismiss=${this.closePlaylistPicker}
|
||||
.active=${this.playlistPickerOpen}
|
||||
>
|
||||
${this.playlistPickerOpen
|
||||
@@ -2060,7 +2063,7 @@ export class QueuePanel
|
||||
></playlist-picker>
|
||||
`
|
||||
: nothing}
|
||||
</wa-popup>
|
||||
</menu-surface>
|
||||
|
||||
<div
|
||||
class="list-area"
|
||||
@@ -2103,11 +2106,8 @@ export class QueuePanel
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<wa-popup
|
||||
<menu-surface
|
||||
id="context-menu"
|
||||
placement="bottom-start"
|
||||
flip
|
||||
shift
|
||||
.active=${this.ctxMenu.contextMenuOpen}
|
||||
>
|
||||
${this.ctxMenu.contextMenuOpen
|
||||
@@ -2195,13 +2195,12 @@ export class QueuePanel
|
||||
</div>
|
||||
`
|
||||
: nothing}
|
||||
</wa-popup>
|
||||
</menu-surface>
|
||||
|
||||
<wa-popup
|
||||
<menu-surface
|
||||
id="playlist-submenu"
|
||||
label="Add to playlist"
|
||||
placement="right-start"
|
||||
flip
|
||||
shift
|
||||
.active=${this.ctxMenu.playlistSubmenuOpen}
|
||||
>
|
||||
${this.ctxMenu.playlistSubmenuOpen &&
|
||||
@@ -2224,7 +2223,7 @@ export class QueuePanel
|
||||
</div>
|
||||
`
|
||||
: nothing}
|
||||
</wa-popup>
|
||||
</menu-surface>
|
||||
|
||||
<track-details></track-details>
|
||||
`;
|
||||
|
||||
Reference in New Issue
Block a user