UNPKG

@synergy-design-system/components

Version:
187 lines (184 loc) 6.09 kB
import { menu_styles_default } from "./chunk.M3HEPL2F.js"; import { component_styles_default } from "./chunk.2NT3B5WJ.js"; import { SynergyElement } from "./chunk.L2IYPRPF.js"; import { __decorateClass } from "./chunk.VK2FVWOF.js"; // src/components/menu/menu.component.ts import { html } from "lit"; import { query } from "lit/decorators.js"; import { state } from "lit/decorators.js"; var SynMenu = class extends SynergyElement { constructor() { super(...arguments); this.hasMenuItemsWithCheckmarks = false; this.checkmarkStyledItems = /* @__PURE__ */ new Set(); this.updateCheckMarksByChildPropChange = (e) => { e.stopImmediatePropagation(); this.handleUpdateCheckmarks(this.getAllItems()); }; } handleUpdateCheckmarks(items) { this.hasMenuItemsWithCheckmarks = items.some((item) => item.type === "checkbox" || item.loading); this.syncCheckmarkVisibility(items); } syncCheckmarkVisibility(items) { this.checkmarkStyledItems.forEach((item) => { if (!items.includes(item)) { item.style.removeProperty("--display-checkmark"); this.checkmarkStyledItems.delete(item); } }); if (this.hasMenuItemsWithCheckmarks) { items.forEach((item) => { item.style.removeProperty("--display-checkmark"); this.checkmarkStyledItems.delete(item); }); return; } items.forEach((item) => { item.style.setProperty("--display-checkmark", "none"); this.checkmarkStyledItems.add(item); }); } disconnectedCallback() { this.removeEventListener("syn-attributes-changed", this.updateCheckMarksByChildPropChange); this.checkmarkStyledItems.forEach((item) => item.style.removeProperty("--display-checkmark")); this.checkmarkStyledItems.clear(); } connectedCallback() { super.connectedCallback(); this.setAttribute("role", "menu"); this.addEventListener("syn-attributes-changed", this.updateCheckMarksByChildPropChange); } getMenuItemFromEvent(event) { const composedPath = event.composedPath(); const target = composedPath.find((el) => el instanceof HTMLElement && this.isMenuItem(el)); if (!target || !(target instanceof HTMLElement)) { return void 0; } const closestMenu = composedPath.find((el) => el instanceof Element && el.getAttribute("role") === "menu"); if (closestMenu !== this) { return void 0; } return target; } handleClick(event) { const item = this.getMenuItemFromEvent(event); if (!item) return; 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 = this.getMenuItemFromEvent(event); if (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 : ""); } getMenuItemsFromElement(element) { if (element.inert) { return []; } if (this.isMenuItem(element)) { return [element]; } if (element.tagName.toLowerCase() === "syn-menu") { return []; } return [...element.children].flatMap((child) => this.getMenuItemsFromElement(child)); } /** @internal Gets all slotted menu items, ignoring dividers, headers, and other elements. */ getAllItems() { return [...this.defaultSlot.assignedElements({ flatten: true })].flatMap((el) => this.getMenuItemsFromElement(el)); } /** * @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 @slotchange=${this.handleSlotChange} @click=${this.handleClick} @keydown=${this.handleKeyDown} @mousedown=${this.handleMouseDown} ></slot> `; } }; SynMenu.styles = [component_styles_default, menu_styles_default]; __decorateClass([ query("slot") ], SynMenu.prototype, "defaultSlot", 2); __decorateClass([ state() ], SynMenu.prototype, "hasMenuItemsWithCheckmarks", 2); export { SynMenu }; //# sourceMappingURL=chunk.VBEG3AW7.js.map