@synergy-design-system/components
Version:
This package provides the base of the Synergy Design System as native web components. It uses [lit](https://www.lit.dev) and parts of [shoelace](https://shoelace.style/). Synergy officially supports the latest two versions of all major browsers (as define
164 lines (161 loc) • 5.34 kB
JavaScript
import {
menu_custom_styles_default
} from "./chunk.3IQ2QEPZ.js";
import {
menu_styles_default
} from "./chunk.C2CWYCOU.js";
import {
component_styles_default
} from "./chunk.NLYVOJGK.js";
import {
SynergyElement
} from "./chunk.3THJTCRO.js";
import {
__decorateClass
} from "./chunk.Z4XV3SMG.js";
// src/components/menu/menu.component.ts
import { html } from "lit";
import { query } from "lit/decorators.js";
import { state } from "lit/decorators.js";
import { classMap } from "lit/directives/class-map.js";
var SynMenu = class extends SynergyElement {
constructor() {
super(...arguments);
this.hasMenuItemsWithCheckmarks = false;
this.updateCheckMarksByChildPropChange = (e) => {
e.stopImmediatePropagation();
this.handleUpdateCheckmarks(this.getAllItems());
};
}
handleUpdateCheckmarks(items) {
this.hasMenuItemsWithCheckmarks = items.some((item) => item.type === "checkbox" || item.loading);
}
disconnectedCallback() {
this.removeEventListener("syn-attributes-changed", this.updateCheckMarksByChildPropChange);
}
connectedCallback() {
super.connectedCallback();
this.setAttribute("role", "menu");
this.addEventListener("syn-attributes-changed", this.updateCheckMarksByChildPropChange);
}
handleClick(event) {
const menuItemTypes = ["menuitem", "menuitemcheckbox"];
const composedPath = event.composedPath();
const target = composedPath.find((el) => {
var _a;
return menuItemTypes.includes(((_a = el == null ? void 0 : el.getAttribute) == null ? void 0 : _a.call(el, "role")) || "");
});
if (!target) return;
const closestMenu = composedPath.find((el) => {
var _a;
return ((_a = el == null ? void 0 : el.getAttribute) == null ? void 0 : _a.call(el, "role")) === "menu";
});
const clickHasSubmenu = closestMenu !== this;
if (clickHasSubmenu) return;
const item = target;
if (item.type === "checkbox") {
item.checked = !item.checked;
}
this.emit("syn-select", { detail: { item } });
}
handleKeyDown(event) {
if (event.key === "Enter" || event.key === " ") {
const item = this.getCurrentItem();
event.preventDefault();
event.stopPropagation();
item == null ? void 0 : item.click();
} else if (["ArrowDown", "ArrowUp", "Home", "End"].includes(event.key)) {
const items = this.getAllItems();
const activeItem = this.getCurrentItem();
let index = activeItem ? items.indexOf(activeItem) : 0;
if (items.length > 0) {
event.preventDefault();
event.stopPropagation();
if (event.key === "ArrowDown") {
index++;
} else if (event.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;
}
this.setCurrentItem(items[index]);
items[index].focus();
}
}
}
handleMouseDown(event) {
const target = event.target;
if (this.isMenuItem(target)) {
this.setCurrentItem(target);
}
}
handleSlotChange() {
const items = this.getAllItems();
this.handleUpdateCheckmarks(items);
if (items.length > 0) {
this.setCurrentItem(items[0]);
}
}
isMenuItem(item) {
var _a;
return item.tagName.toLowerCase() === "syn-menu-item" || ["menuitem", "menuitemcheckbox", "menuitemradio"].includes((_a = item.getAttribute("role")) != null ? _a : "");
}
/** @internal Gets all slotted menu items, ignoring dividers, headers, and other elements. */
getAllItems() {
return [...this.defaultSlot.assignedElements({ flatten: true })].filter((el) => {
if (el.inert || !this.isMenuItem(el)) {
return false;
}
return true;
});
}
/**
* @internal Gets the current menu item, which is the menu item that has `tabindex="0"` within the roving tab index.
* The menu item may or may not have focus, but for keyboard interaction purposes it's considered the "active" item.
*/
getCurrentItem() {
return this.getAllItems().find((i) => i.getAttribute("tabindex") === "0");
}
/**
* @internal Sets the current menu item to the specified element. This sets `tabindex="0"` on the target element and
* `tabindex="-1"` to all other items. This method must be called prior to setting focus on a menu item.
*/
setCurrentItem(item) {
const items = this.getAllItems();
items.forEach((i) => {
i.setAttribute("tabindex", i === item ? "0" : "-1");
});
}
render() {
return html`
<slot
class=${classMap({
"menu--no-checkmarks": !this.hasMenuItemsWithCheckmarks
})}
=${this.handleSlotChange}
=${this.handleClick}
=${this.handleKeyDown}
=${this.handleMouseDown}
></slot>
`;
}
};
SynMenu.styles = [component_styles_default, menu_styles_default, menu_custom_styles_default];
__decorateClass([
query("slot")
], SynMenu.prototype, "defaultSlot", 2);
__decorateClass([
state()
], SynMenu.prototype, "hasMenuItemsWithCheckmarks", 2);
export {
SynMenu
};
//# sourceMappingURL=chunk.2UFVMHUX.js.map