/**
* @license
* Copyright 2023 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { CloseReason, createDefaultCloseMenuEvent, isClosableKey, } from './shared.js';
/**
* A controller that provides most functionality of an element that implements
* the MenuItem interface.
*/
export class MenuItemController {
/**
* @param host The MenuItem in which to attach this controller to.
* @param config The object that configures this controller's behavior.
*/
constructor(host, config) {
this.host = host;
this.internalTypeaheadText = null;
/**
* Bind this click listener to the interactive element. Handles closing the
* menu.
*/
this.onClick = () => {
if (this.host.keepOpen)
return;
this.host.dispatchEvent(createDefaultCloseMenuEvent(this.host, {
kind: CloseReason.CLICK_SELECTION,
}));
};
/**
* Bind this click listener to the interactive element. Handles closing the
* menu.
*/
this.onKeydown = (event) => {
// Check if the interactive element is an anchor tag. If so, click it.
if (this.host.href && event.code === 'Enter') {
const interactiveElement = this.getInteractiveElement();
if (interactiveElement instanceof HTMLAnchorElement) {
interactiveElement.click();
}
}
if (event.defaultPrevented)
return;
// If the host has keepOpen = true we should ignore clicks & Space/Enter,
// however we always maintain the ability to close a menu with a explicit
// `escape` keypress.
const keyCode = event.code;
if (this.host.keepOpen && keyCode !== 'Escape')
return;
if (isClosableKey(keyCode)) {
event.preventDefault();
this.host.dispatchEvent(createDefaultCloseMenuEvent(this.host, {
kind: CloseReason.KEYDOWN,
key: keyCode,
}));
}
};
this.getHeadlineElements = config.getHeadlineElements;
this.getSupportingTextElements = config.getSupportingTextElements;
this.getDefaultElements = config.getDefaultElements;
this.getInteractiveElement = config.getInteractiveElement;
this.host.addController(this);
}
/**
* The text that is selectable via typeahead. If not set, defaults to the
* innerText of the item slotted into the `"headline"` slot, and if there are
* no slotted elements into headline, then it checks the _default_ slot, and
* then the `"supporting-text"` slot if nothing is in _default_.
*/
get typeaheadText() {
if (this.internalTypeaheadText !== null) {
return this.internalTypeaheadText;
}
const headlineElements = this.getHeadlineElements();
const textParts = [];
headlineElements.forEach((headlineElement) => {
if (headlineElement.textContent && headlineElement.textContent.trim()) {
textParts.push(headlineElement.textContent.trim());
}
});
// If there are no headline elements, check the default slot's text content
if (textParts.length === 0) {
this.getDefaultElements().forEach((defaultElement) => {
if (defaultElement.textContent && defaultElement.textContent.trim()) {
textParts.push(defaultElement.textContent.trim());
}
});
}
// If there are no headline nor default slot elements, check the
//supporting-text slot's text content
if (textParts.length === 0) {
this.getSupportingTextElements().forEach((supportingTextElement) => {
if (supportingTextElement.textContent &&
supportingTextElement.textContent.trim()) {
textParts.push(supportingTextElement.textContent.trim());
}
});
}
return textParts.join(' ');
}
/**
* The recommended tag name to render as the list item.
*/
get tagName() {
const type = this.host.type;
switch (type) {
case 'link':
return 'a';
case 'button':
return 'button';
default:
case 'menuitem':
case 'option':
return 'li';
}
}
/**
* The recommended role of the menu item.
*/
get role() {
return this.host.type === 'option' ? 'option' : 'menuitem';
}
hostConnected() {
this.host.toggleAttribute('md-menu-item', true);
}
hostUpdate() {
if (this.host.href) {
this.host.type = 'link';
}
}
/**
* Use to set the typeaheadText when it changes.
*/
setTypeaheadText(text) {
this.internalTypeaheadText = text;
}
}
//# sourceMappingURL=menuItemController.js.map