diff --git a/focus/internal/focus-ring.ts b/focus/internal/focus-ring.ts index a48ab3d01b..e8c494296a 100644 --- a/focus/internal/focus-ring.ts +++ b/focus/internal/focus-ring.ts @@ -4,13 +4,15 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {LitElement} from 'lit'; +import {LitElement, PropertyValues} from 'lit'; import {property} from 'lit/decorators.js'; import {Attachable, AttachableController} from '../../internal/controller/attachable-controller.js'; /** * Events that the focus ring listens to. + * + * @fires visibility-changed Fired whenever `visible` changes. */ const EVENTS = ['focusin', 'focusout', 'pointerdown']; @@ -80,6 +82,15 @@ export class FocusRing extends LitElement implements Attachable { next?.addEventListener(event, this); } } + + override update(changed: PropertyValues) { + if (changed.has('visible')) { + // This logic can be removed once the `:has` selector has been introduced + // to Firefox. This is necessary to allow correct submenu styles. + this.dispatchEvent(new Event('visibility-changed')); + } + super.update(changed); + } } const HANDLED_BY_FOCUS_RING = Symbol('handledByFocusRing'); diff --git a/list/internal/listitem/list-item.ts b/list/internal/listitem/list-item.ts index 360692acb2..e2266e34a2 100644 --- a/list/internal/listitem/list-item.ts +++ b/list/internal/listitem/list-item.ts @@ -161,9 +161,17 @@ export class ListItemEl extends LitElement implements ListItem { * Handles rendering of the focus ring. */ protected renderFocusRing(): TemplateResult|typeof nothing { - return html``; + return html` + `; } + protected onFocusRingVisibilityChanged(e: Event) {} + /** * Classes applied to the list item root. */ diff --git a/menu/internal/menuitem/_menu-item.scss b/menu/internal/menuitem/_menu-item.scss index c10bb04085..e8e9b67778 100644 --- a/menu/internal/menuitem/_menu-item.scss +++ b/menu/internal/menuitem/_menu-item.scss @@ -47,7 +47,7 @@ } // Set the ripple opacity to 0 if there is a submenu that is hovered. - .list-item:has(.submenu:hover) { + .submenu-hover { // Have to use ripple theme directly because :has selector in this case does // not work in this case with the :has selector, thus we cannot override the // custom props set in :host diff --git a/menu/internal/menuitem/forced-colors-styles.scss b/menu/internal/menuitem/forced-colors-styles.scss index 8fc7d4c60a..2bbced2497 100644 --- a/menu/internal/menuitem/forced-colors-styles.scss +++ b/menu/internal/menuitem/forced-colors-styles.scss @@ -10,7 +10,7 @@ // Show double border only when selected, and the current list item does not // have a focus ring on it. - :host([selected]) .list-item:not(:has(.focus-ring[visible]))::before { + :host([selected]) .list-item:not(.has-focus-ring)::before { content: ''; position: absolute; inset: 0; diff --git a/menu/internal/menuitem/menu-item.ts b/menu/internal/menuitem/menu-item.ts index 4be2638c31..162209e768 100644 --- a/menu/internal/menuitem/menu-item.ts +++ b/menu/internal/menuitem/menu-item.ts @@ -4,8 +4,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {property} from 'lit/decorators.js'; +import {property, state} from 'lit/decorators.js'; +import type {MdFocusRing} from '../../../focus/md-focus-ring.js'; import {ListItemEl, ListItemRole} from '../../../list/internal/listitem/list-item.js'; import {CLOSE_REASON, DefaultCloseMenuEvent, isClosableKey, MenuItem} from '../shared.js'; @@ -26,6 +27,8 @@ export class MenuItemEl extends ListItemEl implements MenuItem { */ @property({type: Boolean, attribute: 'keep-open'}) keepOpen = false; + @state() protected hasFocusRing = false; + /** * Used for overriding e.g. sub-menu-item. */ @@ -40,6 +43,18 @@ export class MenuItemEl extends ListItemEl implements MenuItem { new DefaultCloseMenuEvent(this, {kind: CLOSE_REASON.CLICK_SELECTION})); } + protected override getRenderClasses() { + return { + ...super.getRenderClasses(), + 'has-focus-ring': this.hasFocusRing, + }; + } + + protected override onFocusRingVisibilityChanged(e: Event) { + const focusRing = e.target as MdFocusRing; + this.hasFocusRing = focusRing.visible; + } + protected override onKeydown(event: KeyboardEvent) { if (this.keepOpen) return; const keyCode = event.code; diff --git a/menu/internal/submenuitem/sub-menu-item.ts b/menu/internal/submenuitem/sub-menu-item.ts index 7ab300db5e..dda6c13c12 100644 --- a/menu/internal/submenuitem/sub-menu-item.ts +++ b/menu/internal/submenuitem/sub-menu-item.ts @@ -5,7 +5,7 @@ */ import {html} from 'lit'; -import {property, queryAssignedElements} from 'lit/decorators.js'; +import {property, queryAssignedElements, state} from 'lit/decorators.js'; import {List} from '../../../list/internal/list.js'; import {Corner, Menu} from '../menu.js'; @@ -53,6 +53,8 @@ export class SubMenuItem extends MenuItemEl { */ @property({type: Boolean, reflect: true}) selected = false; + @state() protected submenuHover = false; + @queryAssignedElements({slot: 'submenu', flatten: true}) private readonly menus!: Menu[]; @@ -105,6 +107,10 @@ export class SubMenuItem extends MenuItemEl { this.show(); } + protected override getRenderClasses() { + return {...super.getRenderClasses(), 'submenu-hover': this.submenuHover}; + } + /** * On item keydown handles opening the submenu. */ @@ -149,6 +155,8 @@ export class SubMenuItem extends MenuItemEl { private renderSubMenu() { return html`