var __accessCheck = (obj, member, msg) => {
if (!member.has(obj))
throw TypeError("Cannot " + msg);
};
var __privateGet = (obj, member, getter) => {
__accessCheck(obj, member, "read from private field");
return getter ? getter.call(obj) : member.get(obj);
};
var __privateAdd = (obj, member, value) => {
if (member.has(obj))
throw TypeError("Cannot add the same private member more than once");
member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
};
var __privateSet = (obj, member, value, setter) => {
__accessCheck(obj, member, "write to private field");
setter ? setter.call(obj, value) : member.set(obj, value);
return value;
};
var __privateMethod = (obj, member, method) => {
__accessCheck(obj, member, "access private method");
return method;
};
var _mediaController, _previouslyFocused, _invokerElement, _previousItems, _mutationObserver, _isPopover, _cssRule, _handleSlotChange, handleSlotChange_fn, _handleMenuItems, _updateLayoutStyle, updateLayoutStyle_fn, _handleInvoke, handleInvoke_fn, _handleOpen, handleOpen_fn, _handleClosed, handleClosed_fn, _handleBoundsResize, _handleMenuResize, _positionMenu, positionMenu_fn, _resizeMenu, resizeMenu_fn, _handleClick, handleClick_fn, _backButtonElement, backButtonElement_get, _handleToggle, handleToggle_fn, _checkSubmenuHasExpanded, checkSubmenuHasExpanded_fn, _handleFocusOut, handleFocusOut_fn, _handleKeyDown, handleKeyDown_fn, _getItem, getItem_fn, _getTabItem, getTabItem_fn, _setTabItem, setTabItem_fn, _selectItem, selectItem_fn;
import { MediaStateReceiverAttributes } from "../constants.js";
import { globalThis, document } from "../utils/server-safe-globals.js";
import { computePosition } from "../utils/anchor-utils.js";
import { observeResize, unobserveResize } from "../utils/resize-observer.js";
import { ToggleEvent, InvokeEvent } from "../utils/events.js";
import {
getActiveElement,
containsComposedNode,
closestComposedNode,
insertCSSRule,
getMediaController,
getAttributeMediaController,
getDocumentOrShadowRoot
} from "../utils/element-utils.js";
function createMenuItem({
type,
text,
value,
checked
}) {
const item = document.createElement(
"media-chrome-menu-item"
);
item.type = type != null ? type : "";
item.part.add("menu-item");
if (type)
item.part.add(type);
item.value = value;
item.checked = checked;
const label = document.createElement("span");
label.textContent = text;
item.append(label);
return item;
}
function createIndicator(el, name) {
let customIndicator = el.querySelector(`:scope > [slot="${name}"]`);
if ((customIndicator == null ? void 0 : customIndicator.nodeName) == "SLOT")
customIndicator = customIndicator.assignedElements({ flatten: true })[0];
if (customIndicator) {
customIndicator = customIndicator.cloneNode(true);
return customIndicator;
}
const fallbackIndicator = el.shadowRoot.querySelector(
`[name="${name}"] > svg`
);
if (fallbackIndicator) {
return fallbackIndicator.cloneNode(true);
}
return "";
}
const template = document.createElement("template");
template.innerHTML = /*html*/
`
`;
const Attributes = {
STYLE: "style",
HIDDEN: "hidden",
DISABLED: "disabled",
ANCHOR: "anchor"
};
class MediaChromeMenu extends globalThis.HTMLElement {
constructor() {
super();
__privateAdd(this, _handleSlotChange);
/**
* Sets the layout style for the menu.
* It can be a row or column layout. e.g. playback-rate-menu
*/
__privateAdd(this, _updateLayoutStyle);
__privateAdd(this, _handleInvoke);
__privateAdd(this, _handleOpen);
__privateAdd(this, _handleClosed);
/**
* Updates the popover menu position based on the anchor element.
* @param {number} [menuWidth]
*/
__privateAdd(this, _positionMenu);
/**
* Resize this menu to fit the submenu.
* @param {boolean} animate
*/
__privateAdd(this, _resizeMenu);
__privateAdd(this, _handleClick);
__privateAdd(this, _backButtonElement);
/**
* Handle the toggle event of submenus.
* Closes all other open submenus when opening a submenu.
* Resizes this menu to fit the submenu.
*
* @param {ToggleEvent} event
*/
__privateAdd(this, _handleToggle);
/**
* Check if any submenu is expanded and update the container class accordingly.
* When the CSS :has() selector is supported, this can be done with CSS only.
*/
__privateAdd(this, _checkSubmenuHasExpanded);
__privateAdd(this, _handleFocusOut);
__privateAdd(this, _handleKeyDown);
__privateAdd(this, _getItem);
__privateAdd(this, _getTabItem);
__privateAdd(this, _setTabItem);
__privateAdd(this, _selectItem);
__privateAdd(this, _mediaController, null);
__privateAdd(this, _previouslyFocused, null);
__privateAdd(this, _invokerElement, null);
__privateAdd(this, _previousItems, /* @__PURE__ */ new Set());
__privateAdd(this, _mutationObserver, void 0);
__privateAdd(this, _isPopover, false);
__privateAdd(this, _cssRule, null);
/**
* Fires an event when a menu item is added or removed.
* This is needed to update the description slot of an ancestor menu item.
*/
__privateAdd(this, _handleMenuItems, () => {
const previousItems = __privateGet(this, _previousItems);
const currentItems = new Set(this.items);
for (const item of previousItems) {
if (!currentItems.has(item)) {
this.dispatchEvent(new CustomEvent("removemenuitem", { detail: item }));
}
}
for (const item of currentItems) {
if (!previousItems.has(item)) {
this.dispatchEvent(new CustomEvent("addmenuitem", { detail: item }));
}
}
__privateSet(this, _previousItems, currentItems);
});
__privateAdd(this, _handleBoundsResize, () => {
__privateMethod(this, _positionMenu, positionMenu_fn).call(this);
__privateMethod(this, _resizeMenu, resizeMenu_fn).call(this, false);
});
__privateAdd(this, _handleMenuResize, () => {
__privateMethod(this, _positionMenu, positionMenu_fn).call(this);
});
if (!this.shadowRoot) {
this.attachShadow({ mode: "open" });
this.nativeEl = this.constructor.template.content.cloneNode(true);
this.shadowRoot.append(this.nativeEl);
}
this.container = this.shadowRoot.querySelector("#container");
this.defaultSlot = this.shadowRoot.querySelector(
"slot:not([name])"
);
this.shadowRoot.addEventListener("slotchange", this);
__privateSet(this, _mutationObserver, new MutationObserver(__privateGet(this, _handleMenuItems)));
__privateGet(this, _mutationObserver).observe(this.defaultSlot, { childList: true });
}
static get observedAttributes() {
return [
Attributes.DISABLED,
Attributes.HIDDEN,
Attributes.STYLE,
Attributes.ANCHOR,
MediaStateReceiverAttributes.MEDIA_CONTROLLER
];
}
static formatMenuItemText(text) {
return text;
}
enable() {
this.addEventListener("click", this);
this.addEventListener("focusout", this);
this.addEventListener("keydown", this);
this.addEventListener("invoke", this);
this.addEventListener("toggle", this);
}
disable() {
this.removeEventListener("click", this);
this.removeEventListener("focusout", this);
this.removeEventListener("keyup", this);
this.removeEventListener("invoke", this);
this.removeEventListener("toggle", this);
}
handleEvent(event) {
switch (event.type) {
case "slotchange":
__privateMethod(this, _handleSlotChange, handleSlotChange_fn).call(this, event);
break;
case "invoke":
__privateMethod(this, _handleInvoke, handleInvoke_fn).call(this, event);
break;
case "click":
__privateMethod(this, _handleClick, handleClick_fn).call(this, event);
break;
case "toggle":
__privateMethod(this, _handleToggle, handleToggle_fn).call(this, event);
break;
case "focusout":
__privateMethod(this, _handleFocusOut, handleFocusOut_fn).call(this, event);
break;
case "keydown":
__privateMethod(this, _handleKeyDown, handleKeyDown_fn).call(this, event);
break;
}
}
connectedCallback() {
var _a, _b;
__privateSet(this, _cssRule, insertCSSRule(this.shadowRoot, ":host"));
__privateMethod(this, _updateLayoutStyle, updateLayoutStyle_fn).call(this);
if (!this.hasAttribute("disabled")) {
this.enable();
}
if (!this.role) {
this.role = "menu";
}
__privateSet(this, _mediaController, getAttributeMediaController(this));
(_b = (_a = __privateGet(this, _mediaController)) == null ? void 0 : _a.associateElement) == null ? void 0 : _b.call(_a, this);
if (!this.hidden) {
observeResize(getBoundsElement(this), __privateGet(this, _handleBoundsResize));
observeResize(this, __privateGet(this, _handleMenuResize));
}
}
disconnectedCallback() {
var _a, _b;
unobserveResize(getBoundsElement(this), __privateGet(this, _handleBoundsResize));
unobserveResize(this, __privateGet(this, _handleMenuResize));
this.disable();
(_b = (_a = __privateGet(this, _mediaController)) == null ? void 0 : _a.unassociateElement) == null ? void 0 : _b.call(_a, this);
__privateSet(this, _mediaController, null);
}
attributeChangedCallback(attrName, oldValue, newValue) {
var _a, _b, _c, _d;
if (attrName === Attributes.HIDDEN && newValue !== oldValue) {
if (!__privateGet(this, _isPopover))
__privateSet(this, _isPopover, true);
if (this.hidden) {
__privateMethod(this, _handleClosed, handleClosed_fn).call(this);
} else {
__privateMethod(this, _handleOpen, handleOpen_fn).call(this);
}
this.dispatchEvent(
new ToggleEvent({
oldState: this.hidden ? "open" : "closed",
newState: this.hidden ? "closed" : "open",
bubbles: true
})
);
} else if (attrName === MediaStateReceiverAttributes.MEDIA_CONTROLLER) {
if (oldValue) {
(_b = (_a = __privateGet(this, _mediaController)) == null ? void 0 : _a.unassociateElement) == null ? void 0 : _b.call(_a, this);
__privateSet(this, _mediaController, null);
}
if (newValue && this.isConnected) {
__privateSet(this, _mediaController, getAttributeMediaController(this));
(_d = (_c = __privateGet(this, _mediaController)) == null ? void 0 : _c.associateElement) == null ? void 0 : _d.call(_c, this);
}
} else if (attrName === Attributes.DISABLED && newValue !== oldValue) {
if (newValue == null) {
this.enable();
} else {
this.disable();
}
} else if (attrName === Attributes.STYLE && newValue !== oldValue) {
__privateMethod(this, _updateLayoutStyle, updateLayoutStyle_fn).call(this);
}
}
formatMenuItemText(text, data) {
return this.constructor.formatMenuItemText(text, data);
}
get anchor() {
return this.getAttribute("anchor");
}
set anchor(value) {
this.setAttribute("anchor", `${value}`);
}
/**
* Returns the anchor element when it is a floating menu.
*/
get anchorElement() {
var _a;
if (this.anchor) {
return (_a = getDocumentOrShadowRoot(this)) == null ? void 0 : _a.querySelector(`#${this.anchor}`);
}
return null;
}
/**
* Returns the menu items.
*/
get items() {
return this.defaultSlot.assignedElements({ flatten: true }).filter(isMenuItem);
}
get radioGroupItems() {
return this.items.filter((item) => item.role === "menuitemradio");
}
get checkedItems() {
return this.items.filter((item) => item.checked);
}
get value() {
var _a, _b;
return (_b = (_a = this.checkedItems[0]) == null ? void 0 : _a.value) != null ? _b : "";
}
set value(newValue) {
const item = this.items.find((item2) => item2.value === newValue);
if (!item)
return;
__privateMethod(this, _selectItem, selectItem_fn).call(this, item);
}
focus() {
__privateSet(this, _previouslyFocused, getActiveElement());
if (this.items.length) {
__privateMethod(this, _setTabItem, setTabItem_fn).call(this, this.items[0]);
this.items[0].focus();
return;
}
const focusable = this.querySelector(
'[autofocus], [tabindex]:not([tabindex="-1"]), [role="menu"]'
);
focusable == null ? void 0 : focusable.focus();
}
handleSelect(event) {
var _a;
const item = __privateMethod(this, _getItem, getItem_fn).call(this, event);
if (!item)
return;
__privateMethod(this, _selectItem, selectItem_fn).call(this, item, item.type === "checkbox");
if (__privateGet(this, _invokerElement) && !this.hidden) {
(_a = __privateGet(this, _previouslyFocused)) == null ? void 0 : _a.focus();
this.hidden = true;
}
}
get keysUsed() {
return [
"Enter",
"Escape",
"Tab",
" ",
"ArrowDown",
"ArrowUp",
"Home",
"End"
];
}
handleMove(event) {
var _a, _b;
const { key } = event;
const items = this.items;
const currentItem = (_b = (_a = __privateMethod(this, _getItem, getItem_fn).call(this, event)) != null ? _a : __privateMethod(this, _getTabItem, getTabItem_fn).call(this)) != null ? _b : items[0];
const currentIndex = items.indexOf(currentItem);
let index = Math.max(0, currentIndex);
if (key === "ArrowDown") {
index++;
} else if (key === "ArrowUp") {
index--;
} else if (event.key === "Home") {
index = 0;
} else if (event.key === "End") {
index = items.length - 1;
}
if (index < 0) {
index = items.length - 1;
}
if (index > items.length - 1) {
index = 0;
}
__privateMethod(this, _setTabItem, setTabItem_fn).call(this, items[index]);
items[index].focus();
}
}
_mediaController = new WeakMap();
_previouslyFocused = new WeakMap();
_invokerElement = new WeakMap();
_previousItems = new WeakMap();
_mutationObserver = new WeakMap();
_isPopover = new WeakMap();
_cssRule = new WeakMap();
_handleSlotChange = new WeakSet();
handleSlotChange_fn = function(event) {
const slot = event.target;
for (const node of slot.assignedNodes({ flatten: true })) {
if (node.nodeType === 3 && node.textContent.trim() === "") {
node.remove();
}
}
if (["header", "title"].includes(slot.name)) {
const header = this.shadowRoot.querySelector(
'slot[name="header"]'
);
header.hidden = slot.assignedNodes().length === 0;
}
if (!slot.name) {
__privateGet(this, _handleMenuItems).call(this);
}
};
_handleMenuItems = new WeakMap();
_updateLayoutStyle = new WeakSet();
updateLayoutStyle_fn = function() {
var _a;
const layoutRowStyle = this.shadowRoot.querySelector("#layout-row");
const menuLayout = (_a = getComputedStyle(this).getPropertyValue("--media-menu-layout")) == null ? void 0 : _a.trim();
layoutRowStyle.setAttribute("media", menuLayout === "row" ? "" : "width:0");
};
_handleInvoke = new WeakSet();
handleInvoke_fn = function(event) {
__privateSet(this, _invokerElement, event.relatedTarget);
if (!containsComposedNode(this, event.relatedTarget)) {
this.hidden = !this.hidden;
}
};
_handleOpen = new WeakSet();
handleOpen_fn = function() {
var _a;
(_a = __privateGet(this, _invokerElement)) == null ? void 0 : _a.setAttribute("aria-expanded", "true");
this.addEventListener("transitionend", () => this.focus(), { once: true });
observeResize(getBoundsElement(this), __privateGet(this, _handleBoundsResize));
observeResize(this, __privateGet(this, _handleMenuResize));
};
_handleClosed = new WeakSet();
handleClosed_fn = function() {
var _a;
(_a = __privateGet(this, _invokerElement)) == null ? void 0 : _a.setAttribute("aria-expanded", "false");
unobserveResize(getBoundsElement(this), __privateGet(this, _handleBoundsResize));
unobserveResize(this, __privateGet(this, _handleMenuResize));
};
_handleBoundsResize = new WeakMap();
_handleMenuResize = new WeakMap();
_positionMenu = new WeakSet();
positionMenu_fn = function(menuWidth) {
if (this.hasAttribute("mediacontroller") && !this.anchor)
return;
if (this.hidden || !this.anchorElement)
return;
const { x, y } = computePosition({
anchor: this.anchorElement,
floating: this,
placement: "top-start"
});
menuWidth != null ? menuWidth : menuWidth = this.offsetWidth;
const bounds = getBoundsElement(this);
const boundsRect = bounds.getBoundingClientRect();
const right = boundsRect.width - x - menuWidth;
const bottom = boundsRect.height - y - this.offsetHeight;
const { style } = __privateGet(this, _cssRule);
style.setProperty("position", "absolute");
style.setProperty("right", `${Math.max(0, right)}px`);
style.setProperty("--_menu-bottom", `${bottom}px`);
const computedStyle = getComputedStyle(this);
const isBottomCalc = style.getPropertyValue("--_menu-bottom") === computedStyle.bottom;
const realBottom = isBottomCalc ? bottom : parseFloat(computedStyle.bottom);
const maxHeight = boundsRect.height - realBottom - parseFloat(computedStyle.marginBottom);
this.style.setProperty("--_menu-max-height", `${maxHeight}px`);
};
_resizeMenu = new WeakSet();
resizeMenu_fn = function(animate) {
const expandedMenuItem = this.querySelector(
'[role="menuitem"][aria-haspopup][aria-expanded="true"]'
);
const expandedSubmenu = expandedMenuItem == null ? void 0 : expandedMenuItem.querySelector(
'[role="menu"]'
);
const { style } = __privateGet(this, _cssRule);
if (!animate) {
style.setProperty("--media-menu-transition-in", "none");
}
if (expandedSubmenu) {
const height = expandedSubmenu.offsetHeight;
const width = Math.max(
expandedSubmenu.offsetWidth,
expandedMenuItem.offsetWidth
);
this.style.setProperty("min-width", `${width}px`);
this.style.setProperty("min-height", `${height}px`);
__privateMethod(this, _positionMenu, positionMenu_fn).call(this, width);
} else {
this.style.removeProperty("min-width");
this.style.removeProperty("min-height");
__privateMethod(this, _positionMenu, positionMenu_fn).call(this);
}
style.removeProperty("--media-menu-transition-in");
};
_handleClick = new WeakSet();
handleClick_fn = function(event) {
var _a;
event.stopPropagation();
if (event.composedPath().includes(__privateGet(this, _backButtonElement, backButtonElement_get))) {
(_a = __privateGet(this, _previouslyFocused)) == null ? void 0 : _a.focus();
this.hidden = true;
return;
}
const item = __privateMethod(this, _getItem, getItem_fn).call(this, event);
if (!item || item.hasAttribute("disabled"))
return;
__privateMethod(this, _setTabItem, setTabItem_fn).call(this, item);
this.handleSelect(event);
};
_backButtonElement = new WeakSet();
backButtonElement_get = function() {
var _a;
const headerSlot = this.shadowRoot.querySelector(
'slot[name="header"]'
);
return (_a = headerSlot.assignedElements({ flatten: true })) == null ? void 0 : _a.find(
(el) => el.matches('button[part~="back"]')
);
};
_handleToggle = new WeakSet();
handleToggle_fn = function(event) {
if (event.target === this)
return;
__privateMethod(this, _checkSubmenuHasExpanded, checkSubmenuHasExpanded_fn).call(this);
const menuItemsWithSubmenu = Array.from(
this.querySelectorAll('[role="menuitem"][aria-haspopup]')
);
for (const item of menuItemsWithSubmenu) {
if (item.invokeTargetElement == event.target)
continue;
if (event.newState == "open" && item.getAttribute("aria-expanded") == "true" && !item.invokeTargetElement.hidden) {
item.invokeTargetElement.dispatchEvent(
new InvokeEvent({ relatedTarget: item })
);
}
}
for (const item of menuItemsWithSubmenu) {
item.setAttribute("aria-expanded", `${!item.submenuElement.hidden}`);
}
__privateMethod(this, _resizeMenu, resizeMenu_fn).call(this, true);
};
_checkSubmenuHasExpanded = new WeakSet();
checkSubmenuHasExpanded_fn = function() {
const selector = '[role="menuitem"] > [role="menu"]:not([hidden])';
const expandedMenuItem = this.querySelector(selector);
this.container.classList.toggle("has-expanded", !!expandedMenuItem);
};
_handleFocusOut = new WeakSet();
handleFocusOut_fn = function(event) {
var _a;
if (!containsComposedNode(this, event.relatedTarget)) {
if (__privateGet(this, _isPopover)) {
(_a = __privateGet(this, _previouslyFocused)) == null ? void 0 : _a.focus();
}
if (__privateGet(this, _invokerElement) && __privateGet(this, _invokerElement) !== event.relatedTarget && !this.hidden) {
this.hidden = true;
}
}
};
_handleKeyDown = new WeakSet();
handleKeyDown_fn = function(event) {
var _a, _b, _c, _d, _e;
const { key, ctrlKey, altKey, metaKey } = event;
if (ctrlKey || altKey || metaKey) {
return;
}
if (!this.keysUsed.includes(key)) {
return;
}
event.preventDefault();
event.stopPropagation();
if (key === "Tab") {
if (__privateGet(this, _isPopover)) {
this.hidden = true;
return;
}
if (event.shiftKey) {
(_b = (_a = this.previousElementSibling) == null ? void 0 : _a.focus) == null ? void 0 : _b.call(_a);
} else {
(_d = (_c = this.nextElementSibling) == null ? void 0 : _c.focus) == null ? void 0 : _d.call(_c);
}
this.blur();
} else if (key === "Escape") {
(_e = __privateGet(this, _previouslyFocused)) == null ? void 0 : _e.focus();
if (__privateGet(this, _isPopover)) {
this.hidden = true;
}
} else if (key === "Enter" || key === " ") {
this.handleSelect(event);
} else {
this.handleMove(event);
}
};
_getItem = new WeakSet();
getItem_fn = function(event) {
return event.composedPath().find((el) => {
return ["menuitemradio", "menuitemcheckbox"].includes(
el.role
);
});
};
_getTabItem = new WeakSet();
getTabItem_fn = function() {
return this.items.find((item) => item.tabIndex === 0);
};
_setTabItem = new WeakSet();
setTabItem_fn = function(tabItem) {
for (const item of this.items) {
item.tabIndex = item === tabItem ? 0 : -1;
}
};
_selectItem = new WeakSet();
selectItem_fn = function(item, toggle) {
const oldCheckedItems = [...this.checkedItems];
if (item.type === "radio") {
this.radioGroupItems.forEach((el) => el.checked = false);
}
if (toggle) {
item.checked = !item.checked;
} else {
item.checked = true;
}
if (this.checkedItems.some((opt, i) => opt != oldCheckedItems[i])) {
this.dispatchEvent(
new Event("change", { bubbles: true, composed: true })
);
}
};
MediaChromeMenu.template = template;
function isMenuItem(element) {
return ["menuitem", "menuitemradio", "menuitemcheckbox"].includes(
element == null ? void 0 : element.role
);
}
function getBoundsElement(host) {
var _a;
return (_a = host.getAttribute("bounds") ? closestComposedNode(host, `#${host.getAttribute("bounds")}`) : getMediaController(host) || host.parentElement) != null ? _a : host;
}
if (!globalThis.customElements.get("media-chrome-menu")) {
globalThis.customElements.define("media-chrome-menu", MediaChromeMenu);
}
var media_chrome_menu_default = MediaChromeMenu;
export {
Attributes,
MediaChromeMenu,
createIndicator,
createMenuItem,
media_chrome_menu_default as default
};