@greypan/web-ui 5.0.0 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/context-menu/index.ts"],"sourcesContent":["import { computePosition, shift } from '@floating-ui/dom'\nimport { html, LitElement, unsafeCSS } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport '@/components/dropdown-divider'\nimport '@/components/dropdown-header'\nimport '@/components/dropdown-item'\nimport { UserChangeController } from '@/shared/events/user-change'\nimport { createMenuPortalOverlay, type MenuPortalOverlay } from '@/shared/menu-portal/menu-portal'\nimport {\n findFocusedMenuItem,\n focusMenuItem,\n getEnabledMenuItems,\n getMenuChildren,\n getMenuItemFromEvent,\n getMovableMenuSubtrees,\n hideNestedMenuChildren,\n moveMenuChildren\n} from '@/shared/menu-portal/menu-tree'\nimport { hideOverlayPresence, showOverlayPresence } from '@/shared/overlay/presence'\nimport { defineScrollLockLease } from '@/shared/scroll-lock/scroll-lock'\n\nimport style from './style.css?inline'\n\nconst MARKER_TEXT = 'wui-context-menu-item'\n\n@customElement('web-ui-context-menu')\nexport class WebUiContextMenu extends LitElement {\n static override styles = unsafeCSS(style)\n\n @property({ type: Boolean, reflect: true }) disabled = false\n @property({ type: Boolean, reflect: true, attribute: 'no-scroll-lock' }) noScrollLock = false\n\n @state() private _isOpen = false\n @state() private _x = 0\n @state() private _y = 0\n\n private _activeSubmenus: MenuPortalOverlay[] = []\n private _activeSubmenuItems: HTMLElement[] = []\n // 子菜单 dialog 定位的唯一写者代数(按 panel 键控):同帧关闭→重开复用同一 panel\n // 时只允许最新一次定位写入;不同层级子菜单各有 panel,互不作废。\n private readonly _submenuPositionEpochs = new WeakMap<HTMLElement, number>()\n private readonly _closingSubmenus = new Map<HTMLElement, MenuPortalOverlay>()\n private _submenuTimer?: ReturnType<typeof setTimeout>\n private _ignoreOutsideClick = false\n private _ignoreOutsideClickTimer?: ReturnType<typeof setTimeout>\n private _hoverCleanupFns: (() => void)[] = []\n private _menu?: MenuPortalOverlay\n private readonly _menuItemAnchors = new Map<HTMLElement, Comment>()\n private readonly _scrollLock = defineScrollLockLease().make()\n private readonly _userOpenChange = new UserChangeController()\n private _restoreFocusTarget?: HTMLElement\n private _shouldOpenInstantly = true\n private _refreshScheduled = false\n // 菜单打开期间宿主可能不经重定位直接改写子内容(网络推送、定时器等),\n // 新节点缺隐藏 slot 会可见叠加到菜单上;观察 portal 内容并在下一帧重新同步。\n private readonly _contentObserver = new MutationObserver(() => {\n if (this._isOpen) this._scheduleRefresh()\n })\n\n get isOpen(): boolean {\n return this._isOpen\n }\n\n override connectedCallback() {\n super.connectedCallback()\n this._hideMenuItems()\n this.addEventListener('contextmenu', this._onContextMenu)\n this.addEventListener('keydown', this._onKeydown)\n this.addEventListener('click', this._onMenuClick)\n document.addEventListener('click', this._onClickOutside)\n document.addEventListener('contextmenu', this._onContextMenuOutside)\n document.addEventListener('wheel', this._onWheel, { capture: true, passive: false })\n document.addEventListener('touchmove', this._onTouchMove, { capture: true, passive: false })\n document.addEventListener('keydown', this._onDocumentKeydown)\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback()\n this.removeEventListener('contextmenu', this._onContextMenu)\n this.removeEventListener('keydown', this._onKeydown)\n this.removeEventListener('click', this._onMenuClick)\n document.removeEventListener('click', this._onClickOutside)\n document.removeEventListener('contextmenu', this._onContextMenuOutside)\n document.removeEventListener('wheel', this._onWheel, true)\n document.removeEventListener('touchmove', this._onTouchMove, true)\n document.removeEventListener('keydown', this._onDocumentKeydown)\n this._contentObserver.disconnect()\n clearTimeout(this._submenuTimer)\n clearTimeout(this._ignoreOutsideClickTimer)\n this._hoverCleanupFns.forEach(cleanup => cleanup())\n this._scrollLock.release()\n this._returnItemsToSlot()\n this._menu?.panel.remove()\n this._menu = undefined\n this._closeSubmenusFrom(0, true)\n this._restoreClosingSubmenus()\n // 脱离文档即视为关闭:否则重连后 _isOpen 仍为 true 而 _menu 已清空,\n // 下次 openAt 会走已打开分支静默失败,菜单无法再打开。\n this._isOpen = false\n this._restoreFocusTarget = undefined\n }\n\n protected override updated(changed: Map<string, unknown>) {\n // Lit 的已排队更新可在卸载后执行,不能让已失效实例重新获取全局滚动锁。\n if (!this.isConnected) {\n this._syncScrollLock(false)\n return\n }\n\n if (changed.has('_isOpen')) {\n if (this._isOpen) {\n this._syncScrollLock()\n if (!this._menu) {\n this._menu = createMenuPortalOverlay('context-menu', this)\n this._menu.panel.setAttribute('role', 'menu')\n this._menu.panel.setAttribute('aria-label', '上下文菜单')\n this._menu.panel.addEventListener('click', this._onMenuClick)\n }\n // 父项始终留在 menu.content 内,观察它即可覆盖各级子菜单在打开期间的内容重建。\n this._contentObserver.observe(this._menu.content, { childList: true, subtree: true })\n requestAnimationFrame(() => {\n if (!this._isOpen || !this._menu) return\n this._refreshMenu()\n showOverlayPresence(this._menu.panel, { isInstant: this._shouldOpenInstantly })\n this._shouldOpenInstantly = true\n this._focusFirstItem()\n })\n } else {\n this._syncScrollLock(false)\n this._contentObserver.disconnect()\n this._refreshScheduled = false\n void this._closeMenuAfterPresence()\n }\n if (this._userOpenChange.consume()) this._dispatchChange(this._isOpen)\n }\n if (changed.has('noScrollLock')) this._syncScrollLock()\n }\n\n /**\n * 在指定视口坐标打开菜单。\n * @param x 水平坐标(px)。\n * @param y 垂直坐标(px)。\n * @returns 无返回值;命令式打开不派发 `open-change` 事件。\n */\n openAt(x: number, y: number) {\n this._openAt(x, y, true)\n }\n\n private _openAt(x: number, y: number, isInstant: boolean): boolean {\n if (this.disabled) return false\n this._x = x\n this._y = y\n this._shouldOpenInstantly = isInstant\n this._ignoreCurrentOutsideClick()\n if (this._isOpen) {\n this._closeSubmenusFrom(0, true)\n this._restoreClosingSubmenus()\n this._scheduleRefresh()\n return false\n }\n this._restoreFocusTarget ??= document.activeElement instanceof HTMLElement ? document.activeElement : undefined\n this._isOpen = true\n return true\n }\n\n /**\n * 关闭菜单。\n * @returns 无返回值;命令式关闭不派发 `open-change` 事件。\n */\n close() {\n if (!this._isOpen) return\n this._isOpen = false\n }\n\n private readonly _closeFromUser = () => {\n if (!this._isOpen) return\n this._userOpenChange.mark()\n this.close()\n }\n\n // 同一帧内多次内容变化只刷新一次,避免观察者与刷新自身 append 形成循环。\n private _scheduleRefresh() {\n if (this._refreshScheduled || !this._isOpen) return\n this._refreshScheduled = true\n requestAnimationFrame(() => {\n this._refreshScheduled = false\n if (!this._isOpen || !this._menu) return\n this._refreshMenu()\n })\n }\n\n // 重新同步 portal 内容并重定位;fresh-open、重定位与观察者触发的刷新共用。\n private _refreshMenu() {\n if (!this._menu) return\n this._setupMenuItems()\n this._positionMenu()\n this._bindLevelHovers()\n }\n\n private _positionMenu() {\n const panel = this._menu?.panel\n if (!panel) return\n\n panel.style.visibility = 'hidden'\n panel.style.display = ''\n\n // 普通 overlay root 不经过 transformed containing block,保留轻量的同步定位。\n // 只有 panel 已进入 open native dialog 时才需要 Floating UI 解析坐标。\n if (!(panel.parentElement instanceof HTMLDialogElement && panel.parentElement.open)) {\n this._positionMenuInViewport(panel)\n return\n }\n\n void computePosition({ getBoundingClientRect: () => new DOMRect(this._x, this._y, 0, 0) }, panel, {\n strategy: 'fixed',\n placement: 'bottom-start',\n // crossAxis 必须显式开启:bottom-start 的 sideAxis 为 y,默认只钳制 x,\n // 视口下缘打开时菜单底部会溢出且无法滚动进入视野。\n middleware: [shift({ padding: 8, crossAxis: true })]\n }).then(({ x, y, middlewareData }) => {\n if (!this._isOpen || this._menu?.panel !== panel) return\n // dialog 相对坐标不能与 viewport 的 _x/_y 比较推导 origin(原点非零时几乎恒判\n // right/bottom);shift 数据是该坐标系内的钳制位移增量,负值即被推向该轴起点侧。\n const shiftX = middlewareData.shift?.x ?? 0\n const shiftY = middlewareData.shift?.y ?? 0\n this._applyMenuPosition(panel, x, y, shiftX < 0 ? 'right' : 'left', shiftY < 0 ? 'bottom' : 'top')\n })\n }\n\n private _positionMenuInViewport(panel: HTMLElement) {\n const { width, height } = panel.getBoundingClientRect()\n const vw = window.innerWidth\n const vh = window.innerHeight\n\n let x = this._x\n let y = this._y\n\n if (x + width > vw) x = vw - width - 8\n if (y + height > vh) y = vh - height - 8\n if (x < 0) x = 8\n if (y < 0) y = 8\n\n this._applyMenuPosition(panel, x, y)\n }\n\n private _applyMenuPosition(\n panel: HTMLElement,\n x: number,\n y: number,\n horizontalOrigin?: 'left' | 'right',\n verticalOrigin?: 'top' | 'bottom'\n ) {\n panel.style.left = `${x}px`\n panel.style.top = `${y}px`\n // 非 dialog 路径保持 viewport 坐标比较;dialog 路径由调用方传入 shift 推导值。\n const horizontal = horizontalOrigin ?? (x < this._x ? 'right' : 'left')\n const vertical = verticalOrigin ?? (y < this._y ? 'bottom' : 'top')\n panel.style.setProperty('--wui-internal-overlay-transform-origin', `${vertical} ${horizontal}`)\n panel.style.visibility = ''\n }\n\n private _focusFirstItem() {\n const items = this._menu?.content.querySelectorAll<HTMLElement>('web-ui-dropdown-item:not([disabled])')\n const firstItem = items?.[0]\n if (firstItem) {\n this._focusMenuItem(firstItem)\n }\n }\n\n private _setupMenuItems() {\n const menu = this._menu\n if (!menu) return\n const content = menu.content\n\n this._syncManagedItems(content)\n this._hideMenuItems()\n // 宿主可能在菜单打开期间动态改写 slot 子内容(如切换上下文后重建嵌套子项),\n // 新节点没有隐藏 slot 会落入父项默认 slot 可见渲染并叠到一级菜单上;\n // 因此对已移入 content 的嵌套子项也要重新隐藏。\n hideNestedMenuChildren(content, 'context-menu-hidden')\n\n // 框架 v-if 注释锚点的复位独立于元素重排:无论元素序是否已变都要执行。\n // 若挂在「元素序已变才重排」之下,顺序恰好正确时会跳过复位,锚点漂移无法收敛,\n // 而 Vue 下次翻转正依赖锚点引导新分支项的插入点。\n const anchors = this._captureFrameworkAnchors(content)\n this._orderMenuItems(content)\n this._restoreFrameworkAnchors(content, anchors)\n }\n\n /**\n * 捕获框架 v-if 注释锚点相对其后继元素的绑定,供重排后复位。\n * 后继以 nextElementSibling 解析(跳过注释/文本,多个相邻锚点绑定同一后继)。\n * 锚点在 content 末尾、无后继元素时记为 null(尾部锚点,复位时保持末尾)。\n */\n private _captureFrameworkAnchors(content: HTMLElement): Map<Comment, HTMLElement | null> {\n const anchors = new Map<Comment, HTMLElement | null>()\n for (const node of Array.from(content.childNodes)) {\n if (node instanceof Comment && node.textContent !== MARKER_TEXT) {\n const nextEl = node.nextElementSibling\n anchors.set(node, nextEl instanceof HTMLElement ? nextEl : null)\n }\n }\n return anchors\n }\n\n /**\n * 把锚点复位到捕获时其后继元素之前(尾部锚点保持末尾)。\n * 元素重排用 insertBefore/appendChild 不会移动注释,锚点必须显式复位,\n * 否则脱离模板位置后 Vue 下次 v-if 翻转会把新分支项插到错误插入点。\n */\n private _restoreFrameworkAnchors(content: HTMLElement, anchors: Map<Comment, HTMLElement | null>) {\n for (const [anchor, successor] of anchors) {\n if (anchor.parentNode !== content) continue\n if (successor && successor.parentNode === content) {\n if (anchor.nextElementSibling !== successor) content.insertBefore(anchor, successor)\n } else {\n // 尾部锚点:重排可能把元素 append 到末尾越过锚点,恢复其末尾位置。\n content.appendChild(anchor)\n }\n }\n }\n\n /**\n * 全集 reconcile:以「content 现有元素 ∪ 宿主元素」为托管全集。\n * 框架(如 Vue 的 v-if 翻转)可能不经宿主直接改写 portal 内节点——卸载旧项留下\n * 注释锚点、把新项直接插进 content——它们都会被这里收编(补 marker + 补隐藏),\n * 已被框架删除/移出两处的条目则被清理。\n */\n private _syncManagedItems(content: HTMLElement) {\n // 宿主与 portal 面板分别位于不同容器,结构上不相交且收集只看直接子层,\n // 两个集合不会重复。\n const inContent = getMovableMenuSubtrees(content)\n const inHost = getMovableMenuSubtrees(this)\n const managed = new Set([...inContent, ...inHost])\n\n // 清理已失效条目:托管元素既不在 content 也不在宿主,说明已被框架删除。\n // 仍在 content 的元素必被上面收集进 managed,故这里只需处理宿主侧。\n this._menuItemAnchors.forEach((marker, subtree) => {\n if (managed.has(subtree)) return\n marker.remove()\n this._menuItemAnchors.delete(subtree)\n })\n\n // 确保每个托管元素都有 marker:框架直接插入 content 的项在此收编。\n // content 项按其当前 DOM 序(框架语义序)**反向**处理,新 marker 插到后继项 marker 之前——\n // 这样补建的 marker 序无需重排即可与 content 对齐;宿主项的 marker 原地补插。\n const ensureMarker = (subtree: HTMLElement, inHost: boolean, before?: Comment) => {\n const existing = this._menuItemAnchors.get(subtree)\n if (existing && existing.parentNode === this) return existing\n existing?.remove()\n const marker = document.createComment(MARKER_TEXT)\n if (inHost) {\n this.insertBefore(marker, subtree)\n } else if (before && before.parentNode === this) {\n this.insertBefore(marker, before)\n } else {\n this.appendChild(marker)\n }\n this._menuItemAnchors.set(subtree, marker)\n return marker\n }\n for (let index = inContent.length - 1; index >= 0; index--) {\n const subtree = inContent[index]\n const next = inContent[index + 1]\n const nextMarker = next ? this._menuItemAnchors.get(next) : undefined\n ensureMarker(subtree, false, nextMarker)\n }\n for (const subtree of inHost) {\n ensureMarker(subtree, true)\n }\n\n // 宿主中尚存的托管项移入 content。\n for (const subtree of inHost) {\n content.appendChild(subtree)\n }\n }\n\n private _orderMenuItems(content: HTMLElement) {\n const targetItems: HTMLElement[] = []\n for (const node of Array.from(this.childNodes)) {\n if (node.nodeType !== Node.COMMENT_NODE || node.textContent !== MARKER_TEXT) continue\n this._menuItemAnchors.forEach((marker, subtree) => {\n if (marker !== node || subtree.parentNode !== content) return\n targetItems.push(subtree)\n })\n }\n\n if (targetItems.length === 0) return\n\n const currentItems = Array.from(content.children)\n if (currentItems.length === targetItems.length && currentItems.every((item, index) => item === targetItems[index]))\n return\n\n // 把每个元素移动到目标序中下一个元素之前,而不是 appendChild 全部移到末尾。\n // appendChild 会把元素越过 content 中的框架注释锚点(v-if 锚点),导致锚点脱离\n // 模板位置,下次框架翻转时据此锚点插入新分支项就会落错位;insertBefore 只重排\n // 元素相对顺序,锚点保持在原模板位置。锚点复位由 _restoreFrameworkAnchors 独立完成。\n for (let index = targetItems.length - 1; index >= 0; index--) {\n const item = targetItems[index]\n const next = targetItems[index + 1]\n if (next) {\n if (item.nextElementSibling !== next) content.insertBefore(item, next)\n } else {\n content.appendChild(item)\n }\n }\n }\n\n private _returnItemsToSlot() {\n const menu = this._menu\n if (!menu) return\n\n this._closeSubmenusFrom(0, true)\n this._restoreClosingSubmenus()\n\n // 按 content 子节点序双向归还:元素回到 marker 位置,框架 v-if 注释锚点\n // 插到对应元素之前。Vue 仍持有这些锚点的 vnode.el 引用,随元素一起迁回宿主\n // 可确保框架下次 patch 以宿主为容器,不会因 parentNode === null 崩溃。\n const content = menu.content\n const pending: Comment[] = []\n for (const node of Array.from(content.childNodes)) {\n // instanceof 收窄:Comment 接口为空,nodeType 继承自 Node 的 number,无法用 === 收窄\n if (node instanceof Comment) {\n pending.push(node)\n continue\n }\n if (!(node instanceof HTMLElement)) continue\n const marker = this._menuItemAnchors.get(node)\n if (!marker) continue\n for (const c of pending) {\n if (marker.parentNode === this) this.insertBefore(c, marker)\n else this.appendChild(c)\n }\n pending.length = 0\n if (marker.parentNode === this) {\n this.insertBefore(node, marker)\n } else {\n this.appendChild(node)\n }\n marker.remove()\n this._menuItemAnchors.delete(node)\n }\n // 尾部框架锚点追加到宿主末尾\n for (const c of pending) this.appendChild(c)\n // 映射中剩余 = 已不在 content 的条目(如翻转中被框架卸载的),仅清理 marker\n this._menuItemAnchors.forEach(marker => marker.remove())\n this._menuItemAnchors.clear()\n }\n\n private async _closeMenuAfterPresence() {\n const menu = this._menu\n if (menu && !(await hideOverlayPresence(menu.panel))) return\n if (this._isOpen || !this.isConnected || this._menu !== menu) return\n\n this._returnItemsToSlot()\n menu?.panel.remove()\n this._menu = undefined\n this._restoreFocusTarget?.focus()\n this._restoreFocusTarget = undefined\n }\n\n private _hideMenuItems() {\n getMenuChildren(this).forEach(child => child.setAttribute('slot', 'context-menu-hidden'))\n hideNestedMenuChildren(this, 'context-menu-hidden')\n }\n\n private _openSubmenu(item: HTMLElement, isInstant = false) {\n if (!this._isOpen || !item.hasAttribute('submenu') || item.hasAttribute('disabled')) return\n\n const level = this._getItemLevel(item)\n if (level === -1 || this._activeSubmenuItems[level] === item) return\n\n this._closeSubmenusFrom(level)\n const closingSubmenu = this._closingSubmenus.get(item)\n if (closingSubmenu) {\n this._closingSubmenus.delete(item)\n this._activeSubmenus[level] = closingSubmenu\n this._activeSubmenuItems[level] = item\n item.setAttribute('active', '')\n this._positionSubmenu(item, closingSubmenu)\n showOverlayPresence(closingSubmenu.panel, { isInstant })\n this._bindLevelHovers()\n return\n }\n if (getMenuChildren(item).length === 0) return\n\n const submenu = createMenuPortalOverlay('context-submenu', this)\n submenu.panel.dataset.level = String(level)\n submenu.panel.setAttribute('role', 'menu')\n submenu.panel.setAttribute('aria-label', '子菜单')\n submenu.panel.style.visibility = 'hidden'\n submenu.panel.addEventListener('click', this._onMenuClick)\n moveMenuChildren(item, submenu.content)\n\n this._activeSubmenus[level] = submenu\n this._activeSubmenuItems[level] = item\n item.setAttribute('active', '')\n this._positionSubmenu(item, submenu)\n showOverlayPresence(submenu.panel, { isInstant })\n this._bindLevelHovers()\n }\n\n private _closeSubmenusFrom(level: number, isInstant = false) {\n for (let index = this._activeSubmenus.length - 1; index >= level; index--) {\n const submenu = this._activeSubmenus[index]\n const item = this._activeSubmenuItems[index]\n item?.removeAttribute('active')\n if (!item || isInstant) {\n this._restoreSubmenuItems(submenu, item)\n submenu.panel.remove()\n } else {\n this._closingSubmenus.set(item, submenu)\n void this._closeSubmenuAfterPresence(submenu, item)\n }\n }\n this._activeSubmenus.length = level\n this._activeSubmenuItems.length = level\n }\n\n private async _closeSubmenuAfterPresence(submenu: MenuPortalOverlay, item: HTMLElement) {\n if (!(await hideOverlayPresence(submenu.panel))) return\n if (this._closingSubmenus.get(item) !== submenu) return\n\n this._closingSubmenus.delete(item)\n this._restoreSubmenuItems(submenu, item)\n submenu.panel.remove()\n }\n\n private _restoreSubmenuItems(submenu: MenuPortalOverlay, item?: HTMLElement) {\n if (!item) return\n moveMenuChildren(submenu.content, item)\n // 子菜单打开期间宿主可能重建了嵌套子项,归还时补隐藏,避免可见叠加。\n hideNestedMenuChildren(item, 'context-menu-hidden')\n }\n\n private _restoreClosingSubmenus() {\n this._closingSubmenus.forEach((submenu, item) => {\n this._restoreSubmenuItems(submenu, item)\n submenu.panel.remove()\n })\n this._closingSubmenus.clear()\n }\n\n private _getItemLevel(item: HTMLElement): number {\n const menu = this._menu\n if (menu?.panel.contains(item)) return 0\n const parentLevel = this._activeSubmenus.findIndex(submenu => submenu.panel.contains(item))\n return parentLevel === -1 ? -1 : parentLevel + 1\n }\n\n private _positionSubmenu(item: HTMLElement, submenu: MenuPortalOverlay) {\n // 与主菜单同因:panel 进入 open native dialog 后处于 transformed containing\n // block,viewport 坐标的 left/top 会相对 dialog padding box 解析而整体偏移,\n // 需改走 Floating UI 换算为 dialog 相对坐标;开合方向仍按视口坐标度量预判。\n if (submenu.panel.parentElement instanceof HTMLDialogElement && submenu.panel.parentElement.open) {\n const itemRect = item.getBoundingClientRect()\n const padding = 8\n const canOpenRight = itemRect.right + submenu.panel.getBoundingClientRect().width + padding <= window.innerWidth\n // 同帧关闭→重开会复用同一 panel 产生两个 in-flight 定位 promise,完成序不保证\n // 后者胜出;按 panel 键控的代数 token 只允许最新一次调用写入,迟到旧 promise 丢弃。\n const epoch = (this._submenuPositionEpochs.get(submenu.panel) ?? 0) + 1\n this._submenuPositionEpochs.set(submenu.panel, epoch)\n void computePosition(item, submenu.panel, {\n strategy: 'fixed',\n placement: canOpenRight ? 'right-start' : 'left-start',\n middleware: [shift({ padding, crossAxis: true })]\n }).then(({ x, y, middlewareData }) => {\n if (!this._activeSubmenus.includes(submenu) || epoch !== this._submenuPositionEpochs.get(submenu.panel)) return\n // 与主菜单 dialog 路径一致:origin 由 shift 增量推导,视口预判只决定 placement。\n const shiftX = middlewareData.shift?.x ?? 0\n const shiftY = middlewareData.shift?.y ?? 0\n const horizontalOrigin = shiftX < 0 ? 'right' : 'left'\n const verticalOrigin = shiftY < 0 ? 'bottom' : 'top'\n submenu.panel.style.left = `${x}px`\n submenu.panel.style.top = `${y}px`\n submenu.panel.style.setProperty(\n '--wui-internal-overlay-transform-origin',\n `${verticalOrigin} ${horizontalOrigin}`\n )\n submenu.panel.style.visibility = ''\n })\n return\n }\n\n const itemRect = item.getBoundingClientRect()\n const submenuRect = submenu.panel.getBoundingClientRect()\n const padding = 8\n const canOpenRight = itemRect.right + submenuRect.width + padding <= window.innerWidth\n const left = canOpenRight ? itemRect.right : Math.max(padding, itemRect.left - submenuRect.width)\n const top = Math.min(Math.max(padding, itemRect.top), window.innerHeight - submenuRect.height - padding)\n\n submenu.panel.style.left = `${left}px`\n submenu.panel.style.top = `${top}px`\n submenu.panel.style.setProperty('--wui-internal-overlay-transform-origin', canOpenRight ? 'top left' : 'top right')\n submenu.panel.style.visibility = ''\n }\n\n private _dispatchChange(open: boolean) {\n this.dispatchEvent(\n new CustomEvent('open-change', {\n detail: { open },\n bubbles: true,\n composed: true\n })\n )\n }\n\n private _isInsideShadowRoot(e: MouseEvent): boolean {\n for (const node of e.composedPath()) {\n if (node === this || node === this.shadowRoot) return true\n if (node instanceof Node && node.getRootNode() === this.shadowRoot) return true\n if (\n node instanceof Node &&\n (this._menu?.panel.contains(node) || this._activeSubmenus.some(menu => menu.panel.contains(node)))\n ) {\n return true\n }\n }\n return false\n }\n\n private _onContextMenu = (e: MouseEvent) => {\n if (this.disabled) return\n e.preventDefault()\n this._restoreFocusTarget = e.target instanceof HTMLElement ? e.target : undefined\n if (this._openAt(e.clientX, e.clientY, false)) this._userOpenChange.mark()\n }\n\n private _onContextMenuOutside = (e: MouseEvent) => {\n if (this._isOpen && !this._isInsideShadowRoot(e)) {\n this._closeFromUser()\n }\n }\n\n private _onKeydown = (e: KeyboardEvent) => {\n if (this.disabled) return\n\n // 键盘 ContextMenu 键或 Shift+F10\n if (e.key === 'ContextMenu' || (e.shiftKey && e.key === 'F10')) {\n e.preventDefault()\n const focused = document.activeElement\n if (focused && focused !== document.body) {\n const rect = focused.getBoundingClientRect()\n if (this._openAt(rect.left, rect.bottom, true)) this._userOpenChange.mark()\n } else {\n if (this._openAt(window.innerWidth / 2, window.innerHeight / 2, true)) this._userOpenChange.mark()\n }\n return\n }\n }\n\n private _onClickOutside = (e: MouseEvent) => {\n if (!this._isOpen || this._ignoreOutsideClick) return\n if (this._isInsideShadowRoot(e)) return\n this._closeFromUser()\n }\n\n private _onMenuClick = (e: MouseEvent) => {\n const item = getMenuItemFromEvent(e)\n if (!item || item.hasAttribute('disabled')) return\n if (item.hasAttribute('submenu')) {\n this._openSubmenu(item)\n return\n }\n this._closeFromUser()\n }\n\n private _getLevelItems(level: number): HTMLElement[] {\n const container = level === 0 ? this._menu?.content : this._activeSubmenus[level - 1]?.content\n if (!container) return []\n return getMenuChildren(container)\n }\n\n private _bindLevelHovers() {\n this._hoverCleanupFns.forEach(cleanup => cleanup())\n this._hoverCleanupFns.length = 0\n\n for (let level = 0; level <= this._activeSubmenus.length; level++) {\n this._getLevelItems(level).forEach(item => {\n if (!item.matches('web-ui-dropdown-item') || item.hasAttribute('disabled')) return\n const handler = (event: PointerEvent) => {\n if (event.pointerType === 'touch') return\n clearTimeout(this._submenuTimer)\n if (item.hasAttribute('submenu')) {\n if (this._activeSubmenuItems[level] !== item) {\n this._submenuTimer = setTimeout(() => this._openSubmenu(item), 200)\n }\n } else if (this._activeSubmenus.length > level) {\n this._closeSubmenusFrom(level)\n this._bindLevelHovers()\n }\n }\n item.addEventListener('pointerenter', handler, { passive: true })\n this._hoverCleanupFns.push(() => item.removeEventListener('pointerenter', handler))\n })\n }\n }\n\n private _ignoreCurrentOutsideClick() {\n this._ignoreOutsideClick = true\n clearTimeout(this._ignoreOutsideClickTimer)\n this._ignoreOutsideClickTimer = setTimeout(() => {\n this._ignoreOutsideClick = false\n })\n }\n\n private _onWheel = (e: WheelEvent) => {\n this._preventBackgroundScroll(e)\n }\n\n private _onTouchMove = (e: TouchEvent) => {\n this._preventBackgroundScroll(e)\n }\n\n private _onDocumentKeydown = (e: KeyboardEvent) => {\n if (!this._isOpen) return\n // 鼠标右键打开的菜单焦点不在菜单内,e.target !== this 时也必须能 Escape 关闭;\n // 菜单为模态浮层,按 Escape 即关闭,无需限定焦点位置\n if (e.key === 'Escape') {\n this._closeLastSubmenuOrMenu()\n e.preventDefault()\n return\n }\n const level = this._getFocusedLevel()\n if (level === undefined) return\n const items = this._getEnabledLevelItems(level)\n const focused = this._getFocusedItem()\n const currentIndex = focused ? items.indexOf(focused) : -1\n\n switch (e.key) {\n case 'ArrowDown':\n this._focusMenuItem(items[(currentIndex + 1 + items.length) % items.length])\n break\n case 'ArrowUp':\n this._focusMenuItem(items[(currentIndex - 1 + items.length) % items.length])\n break\n case 'Home':\n this._focusMenuItem(items[0])\n break\n case 'End':\n this._focusMenuItem(items.at(-1))\n break\n case 'ArrowRight':\n if (focused?.hasAttribute('submenu')) {\n this._openSubmenu(focused, true)\n requestAnimationFrame(() => this._focusMenuItem(this._getEnabledLevelItems(level + 1)[0]))\n }\n break\n case 'ArrowLeft':\n if (level > 0) {\n const parent = this._activeSubmenuItems[level - 1]\n this._closeSubmenusFrom(level - 1)\n this._focusMenuItem(parent)\n this._bindLevelHovers()\n }\n break\n case 'Enter':\n case ' ':\n focused?.click()\n break\n case 'Escape':\n this._closeLastSubmenuOrMenu()\n break\n default:\n return\n }\n e.preventDefault()\n }\n\n private _closeLastSubmenuOrMenu() {\n if (this._activeSubmenus.length > 0) {\n const level = this._activeSubmenus.length - 1\n const parent = this._activeSubmenuItems[level]\n this._closeSubmenusFrom(level)\n this._focusMenuItem(parent)\n this._bindLevelHovers()\n } else {\n this._closeFromUser()\n }\n }\n\n private _preventBackgroundScroll(e: Event) {\n if (!this._isOpen || this.noScrollLock || this._isMenuPanelEvent(e)) return\n e.preventDefault()\n }\n\n private _syncScrollLock(isOpen = this._isOpen) {\n this._scrollLock.sync(isOpen && !this.noScrollLock)\n }\n\n private _getEnabledLevelItems(level: number) {\n const container = level === 0 ? this._menu?.content : this._activeSubmenus[level - 1]?.content\n return container ? getEnabledMenuItems(container) : []\n }\n\n private _getFocusedItem(): HTMLElement | undefined {\n return findFocusedMenuItem([this._menu?.content, ...this._activeSubmenus.map(submenu => submenu.content)])\n }\n\n private _getFocusedLevel(): number | undefined {\n const item = this._getFocusedItem()\n if (!item) return undefined\n if (this._menu?.panel.contains(item)) return 0\n const submenuIndex = this._activeSubmenus.findIndex(menu => menu.panel.contains(item))\n return submenuIndex < 0 ? undefined : submenuIndex + 1\n }\n\n private _focusMenuItem(item: HTMLElement | undefined) {\n focusMenuItem(item)\n }\n\n private _isMenuPanelEvent(e: Event): boolean {\n return e\n .composedPath()\n .some(\n node =>\n node instanceof HTMLElement &&\n (node.classList.contains('context-menu') || node.classList.contains('context-submenu'))\n )\n }\n\n override render() {\n return html`\n <div class=\"context-menu-anchor\">\n <slot @slotchange=${this._onSlotChange}></slot>\n </div>\n `\n }\n\n private _onSlotChange() {\n this._hideMenuItems()\n // 打开期间宿主重建顶层项时,新成员要移入 portal 才对用户可见;\n // 关闭状态下无需移动,等下次打开时由 _setupMenuItems 统一处理。\n if (this._isOpen) this._scheduleRefresh()\n }\n\n declare readonly $events: {\n 'open-change': CustomEvent<{ open: boolean }>\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-context-menu': WebUiContextMenu\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/context-menu/index.ts"],"sourcesContent":["import { computePosition, shift } from '@floating-ui/dom'\nimport { html, LitElement, unsafeCSS } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport '@/components/dropdown-divider'\nimport '@/components/dropdown-header'\nimport '@/components/dropdown-item'\nimport { UserChangeController } from '@/shared/events/user-change'\nimport { createMenuPortalOverlay, type MenuPortalOverlay } from '@/shared/menu-portal/menu-portal'\nimport {\n captureFrameworkAnchors,\n findFocusedMenuItem,\n focusMenuItem,\n getEnabledMenuItems,\n getMenuChildren,\n getMenuItemFromEvent,\n hideNestedMenuChildren,\n moveMenuChildren,\n orderManagedMenuItems,\n reconcileManagedMenuItems,\n restoreFrameworkAnchors,\n returnManagedMenuItemsToSlot\n} from '@/shared/menu-portal/menu-tree'\nimport { hideOverlayPresence, showOverlayPresence } from '@/shared/overlay/presence'\nimport { defineScrollLockLease } from '@/shared/scroll-lock/scroll-lock'\n\nimport style from './style.css?inline'\n\nconst MARKER_TEXT = 'wui-context-menu-item'\n\n@customElement('web-ui-context-menu')\nexport class WebUiContextMenu extends LitElement {\n static override styles = unsafeCSS(style)\n\n @property({ type: Boolean, reflect: true }) disabled = false\n @property({ type: Boolean, reflect: true, attribute: 'no-scroll-lock' }) noScrollLock = false\n\n @state() private _isOpen = false\n @state() private _x = 0\n @state() private _y = 0\n\n private _activeSubmenus: MenuPortalOverlay[] = []\n private _activeSubmenuItems: HTMLElement[] = []\n // 子菜单 dialog 定位的唯一写者代数(按 panel 键控):同帧关闭→重开复用同一 panel\n // 时只允许最新一次定位写入;不同层级子菜单各有 panel,互不作废。\n private readonly _submenuPositionEpochs = new WeakMap<HTMLElement, number>()\n private readonly _closingSubmenus = new Map<HTMLElement, MenuPortalOverlay>()\n private _submenuTimer?: ReturnType<typeof setTimeout>\n private _ignoreOutsideClick = false\n private _ignoreOutsideClickTimer?: ReturnType<typeof setTimeout>\n private _hoverCleanupFns: (() => void)[] = []\n private _menu?: MenuPortalOverlay\n private readonly _menuItemAnchors = new Map<HTMLElement, Comment>()\n private readonly _scrollLock = defineScrollLockLease().make()\n private readonly _userOpenChange = new UserChangeController()\n private _restoreFocusTarget?: HTMLElement\n private _shouldOpenInstantly = true\n private _refreshScheduled = false\n // 菜单打开期间宿主可能不经重定位直接改写子内容(网络推送、定时器等),\n // 新节点缺隐藏 slot 会可见叠加到菜单上;观察 portal 内容并在下一帧重新同步。\n private readonly _contentObserver = new MutationObserver(() => {\n if (this._isOpen) this._scheduleRefresh()\n })\n\n get isOpen(): boolean {\n return this._isOpen\n }\n\n override connectedCallback() {\n super.connectedCallback()\n this._hideMenuItems()\n this.addEventListener('contextmenu', this._onContextMenu)\n this.addEventListener('keydown', this._onKeydown)\n this.addEventListener('click', this._onMenuClick)\n document.addEventListener('click', this._onClickOutside)\n document.addEventListener('contextmenu', this._onContextMenuOutside)\n document.addEventListener('wheel', this._onWheel, { capture: true, passive: false })\n document.addEventListener('touchmove', this._onTouchMove, { capture: true, passive: false })\n document.addEventListener('keydown', this._onDocumentKeydown)\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback()\n this.removeEventListener('contextmenu', this._onContextMenu)\n this.removeEventListener('keydown', this._onKeydown)\n this.removeEventListener('click', this._onMenuClick)\n document.removeEventListener('click', this._onClickOutside)\n document.removeEventListener('contextmenu', this._onContextMenuOutside)\n document.removeEventListener('wheel', this._onWheel, true)\n document.removeEventListener('touchmove', this._onTouchMove, true)\n document.removeEventListener('keydown', this._onDocumentKeydown)\n this._contentObserver.disconnect()\n clearTimeout(this._submenuTimer)\n clearTimeout(this._ignoreOutsideClickTimer)\n this._hoverCleanupFns.forEach(cleanup => cleanup())\n this._scrollLock.release()\n this._returnItemsToSlot()\n this._menu?.panel.remove()\n this._menu = undefined\n this._closeSubmenusFrom(0, true)\n this._restoreClosingSubmenus()\n // 脱离文档即视为关闭:否则重连后 _isOpen 仍为 true 而 _menu 已清空,\n // 下次 openAt 会走已打开分支静默失败,菜单无法再打开。\n this._isOpen = false\n this._restoreFocusTarget = undefined\n }\n\n protected override updated(changed: Map<string, unknown>) {\n // Lit 的已排队更新可在卸载后执行,不能让已失效实例重新获取全局滚动锁。\n if (!this.isConnected) {\n this._syncScrollLock(false)\n return\n }\n\n if (changed.has('_isOpen')) {\n if (this._isOpen) {\n this._syncScrollLock()\n if (!this._menu) {\n this._menu = createMenuPortalOverlay('context-menu', this)\n this._menu.panel.setAttribute('role', 'menu')\n this._menu.panel.setAttribute('aria-label', '上下文菜单')\n this._menu.panel.addEventListener('click', this._onMenuClick)\n }\n // 父项始终留在 menu.content 内,观察它即可覆盖各级子菜单在打开期间的内容重建。\n this._contentObserver.observe(this._menu.content, { childList: true, subtree: true })\n requestAnimationFrame(() => {\n if (!this._isOpen || !this._menu) return\n this._refreshMenu()\n showOverlayPresence(this._menu.panel, { isInstant: this._shouldOpenInstantly })\n this._shouldOpenInstantly = true\n this._focusFirstItem()\n })\n } else {\n this._syncScrollLock(false)\n this._contentObserver.disconnect()\n this._refreshScheduled = false\n void this._closeMenuAfterPresence()\n }\n if (this._userOpenChange.consume()) this._dispatchChange(this._isOpen)\n }\n if (changed.has('noScrollLock')) this._syncScrollLock()\n }\n\n /**\n * 在指定视口坐标打开菜单。\n * @param x 水平坐标(px)。\n * @param y 垂直坐标(px)。\n * @returns 无返回值;命令式打开不派发 `open-change` 事件。\n */\n openAt(x: number, y: number) {\n this._openAt(x, y, true)\n }\n\n private _openAt(x: number, y: number, isInstant: boolean): boolean {\n if (this.disabled) return false\n this._x = x\n this._y = y\n this._shouldOpenInstantly = isInstant\n this._ignoreCurrentOutsideClick()\n if (this._isOpen) {\n this._closeSubmenusFrom(0, true)\n this._restoreClosingSubmenus()\n this._scheduleRefresh()\n return false\n }\n this._restoreFocusTarget ??= document.activeElement instanceof HTMLElement ? document.activeElement : undefined\n this._isOpen = true\n return true\n }\n\n /**\n * 关闭菜单。\n * @returns 无返回值;命令式关闭不派发 `open-change` 事件。\n */\n close() {\n if (!this._isOpen) return\n this._isOpen = false\n }\n\n private readonly _closeFromUser = () => {\n if (!this._isOpen) return\n this._userOpenChange.mark()\n this.close()\n }\n\n // 同一帧内多次内容变化只刷新一次,避免观察者与刷新自身 append 形成循环。\n private _scheduleRefresh() {\n if (this._refreshScheduled || !this._isOpen) return\n this._refreshScheduled = true\n requestAnimationFrame(() => {\n this._refreshScheduled = false\n if (!this._isOpen || !this._menu) return\n this._refreshMenu()\n })\n }\n\n // 重新同步 portal 内容并重定位;fresh-open、重定位与观察者触发的刷新共用。\n private _refreshMenu() {\n if (!this._menu) return\n this._setupMenuItems()\n this._positionMenu()\n this._bindLevelHovers()\n }\n\n private _positionMenu() {\n const panel = this._menu?.panel\n if (!panel) return\n\n panel.style.visibility = 'hidden'\n panel.style.display = ''\n\n // 普通 overlay root 不经过 transformed containing block,保留轻量的同步定位。\n // 只有 panel 已进入 open native dialog 时才需要 Floating UI 解析坐标。\n if (!(panel.parentElement instanceof HTMLDialogElement && panel.parentElement.open)) {\n this._positionMenuInViewport(panel)\n return\n }\n\n void computePosition({ getBoundingClientRect: () => new DOMRect(this._x, this._y, 0, 0) }, panel, {\n strategy: 'fixed',\n placement: 'bottom-start',\n // crossAxis 必须显式开启:bottom-start 的 sideAxis 为 y,默认只钳制 x,\n // 视口下缘打开时菜单底部会溢出且无法滚动进入视野。\n middleware: [shift({ padding: 8, crossAxis: true })]\n }).then(({ x, y, middlewareData }) => {\n if (!this._isOpen || this._menu?.panel !== panel) return\n // dialog 相对坐标不能与 viewport 的 _x/_y 比较推导 origin(原点非零时几乎恒判\n // right/bottom);shift 数据是该坐标系内的钳制位移增量,负值即被推向该轴起点侧。\n const shiftX = middlewareData.shift?.x ?? 0\n const shiftY = middlewareData.shift?.y ?? 0\n this._applyMenuPosition(panel, x, y, shiftX < 0 ? 'right' : 'left', shiftY < 0 ? 'bottom' : 'top')\n })\n }\n\n private _positionMenuInViewport(panel: HTMLElement) {\n const { width, height } = panel.getBoundingClientRect()\n const vw = window.innerWidth\n const vh = window.innerHeight\n\n let x = this._x\n let y = this._y\n\n if (x + width > vw) x = vw - width - 8\n if (y + height > vh) y = vh - height - 8\n if (x < 0) x = 8\n if (y < 0) y = 8\n\n this._applyMenuPosition(panel, x, y)\n }\n\n private _applyMenuPosition(\n panel: HTMLElement,\n x: number,\n y: number,\n horizontalOrigin?: 'left' | 'right',\n verticalOrigin?: 'top' | 'bottom'\n ) {\n panel.style.left = `${x}px`\n panel.style.top = `${y}px`\n // 非 dialog 路径保持 viewport 坐标比较;dialog 路径由调用方传入 shift 推导值。\n const horizontal = horizontalOrigin ?? (x < this._x ? 'right' : 'left')\n const vertical = verticalOrigin ?? (y < this._y ? 'bottom' : 'top')\n panel.style.setProperty('--wui-internal-overlay-transform-origin', `${vertical} ${horizontal}`)\n panel.style.visibility = ''\n }\n\n private _focusFirstItem() {\n const items = this._menu?.content.querySelectorAll<HTMLElement>('web-ui-dropdown-item:not([disabled])')\n const firstItem = items?.[0]\n if (firstItem) {\n this._focusMenuItem(firstItem)\n }\n }\n\n private _setupMenuItems() {\n const menu = this._menu\n if (!menu) return\n const content = menu.content\n\n reconcileManagedMenuItems(this, content, this._menuItemAnchors, MARKER_TEXT)\n this._hideMenuItems()\n // 宿主可能在菜单打开期间动态改写 slot 子内容(如切换上下文后重建嵌套子项),\n // 新节点没有隐藏 slot 会落入父项默认 slot 可见渲染并叠到一级菜单上;\n // 因此对已移入 content 的嵌套子项也要重新隐藏。\n hideNestedMenuChildren(content, 'context-menu-hidden')\n\n // 框架 v-if 注释锚点的复位独立于元素重排:无论元素序是否已变都要执行。\n // 若挂在「元素序已变才重排」之下,顺序恰好正确时会跳过复位,锚点漂移无法收敛,\n // 而 Vue 下次翻转正依赖锚点引导新分支项的插入点。\n const anchors = captureFrameworkAnchors(content, MARKER_TEXT)\n orderManagedMenuItems(this, content, this._menuItemAnchors, MARKER_TEXT)\n restoreFrameworkAnchors(content, anchors)\n }\n\n private _returnItemsToSlot() {\n const menu = this._menu\n if (!menu) return\n\n this._closeSubmenusFrom(0, true)\n this._restoreClosingSubmenus()\n\n returnManagedMenuItemsToSlot(this, menu.content, this._menuItemAnchors)\n }\n\n private async _closeMenuAfterPresence() {\n const menu = this._menu\n if (menu && !(await hideOverlayPresence(menu.panel))) return\n if (this._isOpen || !this.isConnected || this._menu !== menu) return\n\n this._returnItemsToSlot()\n menu?.panel.remove()\n this._menu = undefined\n this._restoreFocusTarget?.focus()\n this._restoreFocusTarget = undefined\n }\n\n private _hideMenuItems() {\n getMenuChildren(this).forEach(child => child.setAttribute('slot', 'context-menu-hidden'))\n hideNestedMenuChildren(this, 'context-menu-hidden')\n }\n\n private _openSubmenu(item: HTMLElement, isInstant = false) {\n if (!this._isOpen || !item.hasAttribute('submenu') || item.hasAttribute('disabled')) return\n\n const level = this._getItemLevel(item)\n if (level === -1 || this._activeSubmenuItems[level] === item) return\n\n this._closeSubmenusFrom(level)\n const closingSubmenu = this._closingSubmenus.get(item)\n if (closingSubmenu) {\n this._closingSubmenus.delete(item)\n this._activeSubmenus[level] = closingSubmenu\n this._activeSubmenuItems[level] = item\n item.setAttribute('active', '')\n this._positionSubmenu(item, closingSubmenu)\n showOverlayPresence(closingSubmenu.panel, { isInstant })\n this._bindLevelHovers()\n return\n }\n if (getMenuChildren(item).length === 0) return\n\n const submenu = createMenuPortalOverlay('context-submenu', this)\n submenu.panel.dataset.level = String(level)\n submenu.panel.setAttribute('role', 'menu')\n submenu.panel.setAttribute('aria-label', '子菜单')\n submenu.panel.style.visibility = 'hidden'\n submenu.panel.addEventListener('click', this._onMenuClick)\n moveMenuChildren(item, submenu.content)\n\n this._activeSubmenus[level] = submenu\n this._activeSubmenuItems[level] = item\n item.setAttribute('active', '')\n this._positionSubmenu(item, submenu)\n showOverlayPresence(submenu.panel, { isInstant })\n this._bindLevelHovers()\n }\n\n private _closeSubmenusFrom(level: number, isInstant = false) {\n for (let index = this._activeSubmenus.length - 1; index >= level; index--) {\n const submenu = this._activeSubmenus[index]\n const item = this._activeSubmenuItems[index]\n item?.removeAttribute('active')\n if (!item || isInstant) {\n this._restoreSubmenuItems(submenu, item)\n submenu.panel.remove()\n } else {\n this._closingSubmenus.set(item, submenu)\n void this._closeSubmenuAfterPresence(submenu, item)\n }\n }\n this._activeSubmenus.length = level\n this._activeSubmenuItems.length = level\n }\n\n private async _closeSubmenuAfterPresence(submenu: MenuPortalOverlay, item: HTMLElement) {\n if (!(await hideOverlayPresence(submenu.panel))) return\n if (this._closingSubmenus.get(item) !== submenu) return\n\n this._closingSubmenus.delete(item)\n this._restoreSubmenuItems(submenu, item)\n submenu.panel.remove()\n }\n\n private _restoreSubmenuItems(submenu: MenuPortalOverlay, item?: HTMLElement) {\n if (!item) return\n moveMenuChildren(submenu.content, item)\n // 子菜单打开期间宿主可能重建了嵌套子项,归还时补隐藏,避免可见叠加。\n hideNestedMenuChildren(item, 'context-menu-hidden')\n }\n\n private _restoreClosingSubmenus() {\n this._closingSubmenus.forEach((submenu, item) => {\n this._restoreSubmenuItems(submenu, item)\n submenu.panel.remove()\n })\n this._closingSubmenus.clear()\n }\n\n private _getItemLevel(item: HTMLElement): number {\n const menu = this._menu\n if (menu?.panel.contains(item)) return 0\n const parentLevel = this._activeSubmenus.findIndex(submenu => submenu.panel.contains(item))\n return parentLevel === -1 ? -1 : parentLevel + 1\n }\n\n private _positionSubmenu(item: HTMLElement, submenu: MenuPortalOverlay) {\n // 与主菜单同因:panel 进入 open native dialog 后处于 transformed containing\n // block,viewport 坐标的 left/top 会相对 dialog padding box 解析而整体偏移,\n // 需改走 Floating UI 换算为 dialog 相对坐标;开合方向仍按视口坐标度量预判。\n if (submenu.panel.parentElement instanceof HTMLDialogElement && submenu.panel.parentElement.open) {\n const itemRect = item.getBoundingClientRect()\n const padding = 8\n const canOpenRight = itemRect.right + submenu.panel.getBoundingClientRect().width + padding <= window.innerWidth\n // 同帧关闭→重开会复用同一 panel 产生两个 in-flight 定位 promise,完成序不保证\n // 后者胜出;按 panel 键控的代数 token 只允许最新一次调用写入,迟到旧 promise 丢弃。\n const epoch = (this._submenuPositionEpochs.get(submenu.panel) ?? 0) + 1\n this._submenuPositionEpochs.set(submenu.panel, epoch)\n void computePosition(item, submenu.panel, {\n strategy: 'fixed',\n placement: canOpenRight ? 'right-start' : 'left-start',\n middleware: [shift({ padding, crossAxis: true })]\n }).then(({ x, y, middlewareData }) => {\n if (!this._activeSubmenus.includes(submenu) || epoch !== this._submenuPositionEpochs.get(submenu.panel)) return\n // 与主菜单 dialog 路径一致:origin 由 shift 增量推导,视口预判只决定 placement。\n const shiftX = middlewareData.shift?.x ?? 0\n const shiftY = middlewareData.shift?.y ?? 0\n const horizontalOrigin = shiftX < 0 ? 'right' : 'left'\n const verticalOrigin = shiftY < 0 ? 'bottom' : 'top'\n submenu.panel.style.left = `${x}px`\n submenu.panel.style.top = `${y}px`\n submenu.panel.style.setProperty(\n '--wui-internal-overlay-transform-origin',\n `${verticalOrigin} ${horizontalOrigin}`\n )\n submenu.panel.style.visibility = ''\n })\n return\n }\n\n const itemRect = item.getBoundingClientRect()\n const submenuRect = submenu.panel.getBoundingClientRect()\n const padding = 8\n const canOpenRight = itemRect.right + submenuRect.width + padding <= window.innerWidth\n const left = canOpenRight ? itemRect.right : Math.max(padding, itemRect.left - submenuRect.width)\n const top = Math.min(Math.max(padding, itemRect.top), window.innerHeight - submenuRect.height - padding)\n\n submenu.panel.style.left = `${left}px`\n submenu.panel.style.top = `${top}px`\n submenu.panel.style.setProperty('--wui-internal-overlay-transform-origin', canOpenRight ? 'top left' : 'top right')\n submenu.panel.style.visibility = ''\n }\n\n private _dispatchChange(open: boolean) {\n this.dispatchEvent(\n new CustomEvent('open-change', {\n detail: { open },\n bubbles: true,\n composed: true\n })\n )\n }\n\n private _isInsideShadowRoot(e: MouseEvent): boolean {\n for (const node of e.composedPath()) {\n if (node === this || node === this.shadowRoot) return true\n if (node instanceof Node && node.getRootNode() === this.shadowRoot) return true\n if (\n node instanceof Node &&\n (this._menu?.panel.contains(node) || this._activeSubmenus.some(menu => menu.panel.contains(node)))\n ) {\n return true\n }\n }\n return false\n }\n\n private _onContextMenu = (e: MouseEvent) => {\n if (this.disabled) return\n e.preventDefault()\n this._restoreFocusTarget = e.target instanceof HTMLElement ? e.target : undefined\n if (this._openAt(e.clientX, e.clientY, false)) this._userOpenChange.mark()\n }\n\n private _onContextMenuOutside = (e: MouseEvent) => {\n if (this._isOpen && !this._isInsideShadowRoot(e)) {\n this._closeFromUser()\n }\n }\n\n private _onKeydown = (e: KeyboardEvent) => {\n if (this.disabled) return\n\n // 键盘 ContextMenu 键或 Shift+F10\n if (e.key === 'ContextMenu' || (e.shiftKey && e.key === 'F10')) {\n e.preventDefault()\n const focused = document.activeElement\n if (focused && focused !== document.body) {\n const rect = focused.getBoundingClientRect()\n if (this._openAt(rect.left, rect.bottom, true)) this._userOpenChange.mark()\n } else {\n if (this._openAt(window.innerWidth / 2, window.innerHeight / 2, true)) this._userOpenChange.mark()\n }\n return\n }\n }\n\n private _onClickOutside = (e: MouseEvent) => {\n if (!this._isOpen || this._ignoreOutsideClick) return\n if (this._isInsideShadowRoot(e)) return\n this._closeFromUser()\n }\n\n private _onMenuClick = (e: MouseEvent) => {\n const item = getMenuItemFromEvent(e)\n if (!item || item.hasAttribute('disabled')) return\n if (item.hasAttribute('submenu')) {\n this._openSubmenu(item)\n return\n }\n this._closeFromUser()\n }\n\n private _getLevelItems(level: number): HTMLElement[] {\n const container = level === 0 ? this._menu?.content : this._activeSubmenus[level - 1]?.content\n if (!container) return []\n return getMenuChildren(container)\n }\n\n private _bindLevelHovers() {\n this._hoverCleanupFns.forEach(cleanup => cleanup())\n this._hoverCleanupFns.length = 0\n\n for (let level = 0; level <= this._activeSubmenus.length; level++) {\n this._getLevelItems(level).forEach(item => {\n if (!item.matches('web-ui-dropdown-item') || item.hasAttribute('disabled')) return\n const handler = (event: PointerEvent) => {\n if (event.pointerType === 'touch') return\n clearTimeout(this._submenuTimer)\n if (item.hasAttribute('submenu')) {\n if (this._activeSubmenuItems[level] !== item) {\n this._submenuTimer = setTimeout(() => this._openSubmenu(item), 200)\n }\n } else if (this._activeSubmenus.length > level) {\n this._closeSubmenusFrom(level)\n this._bindLevelHovers()\n }\n }\n item.addEventListener('pointerenter', handler, { passive: true })\n this._hoverCleanupFns.push(() => item.removeEventListener('pointerenter', handler))\n })\n }\n }\n\n private _ignoreCurrentOutsideClick() {\n this._ignoreOutsideClick = true\n clearTimeout(this._ignoreOutsideClickTimer)\n this._ignoreOutsideClickTimer = setTimeout(() => {\n this._ignoreOutsideClick = false\n })\n }\n\n private _onWheel = (e: WheelEvent) => {\n this._preventBackgroundScroll(e)\n }\n\n private _onTouchMove = (e: TouchEvent) => {\n this._preventBackgroundScroll(e)\n }\n\n private _onDocumentKeydown = (e: KeyboardEvent) => {\n if (!this._isOpen) return\n // 鼠标右键打开的菜单焦点不在菜单内,e.target !== this 时也必须能 Escape 关闭;\n // 菜单为模态浮层,按 Escape 即关闭,无需限定焦点位置\n if (e.key === 'Escape') {\n this._closeLastSubmenuOrMenu()\n e.preventDefault()\n return\n }\n const level = this._getFocusedLevel()\n if (level === undefined) return\n const items = this._getEnabledLevelItems(level)\n const focused = this._getFocusedItem()\n const currentIndex = focused ? items.indexOf(focused) : -1\n\n switch (e.key) {\n case 'ArrowDown':\n this._focusMenuItem(items[(currentIndex + 1 + items.length) % items.length])\n break\n case 'ArrowUp':\n this._focusMenuItem(items[(currentIndex - 1 + items.length) % items.length])\n break\n case 'Home':\n this._focusMenuItem(items[0])\n break\n case 'End':\n this._focusMenuItem(items.at(-1))\n break\n case 'ArrowRight':\n if (focused?.hasAttribute('submenu')) {\n this._openSubmenu(focused, true)\n requestAnimationFrame(() => this._focusMenuItem(this._getEnabledLevelItems(level + 1)[0]))\n }\n break\n case 'ArrowLeft':\n if (level > 0) {\n const parent = this._activeSubmenuItems[level - 1]\n this._closeSubmenusFrom(level - 1)\n this._focusMenuItem(parent)\n this._bindLevelHovers()\n }\n break\n case 'Enter':\n case ' ':\n focused?.click()\n break\n case 'Escape':\n this._closeLastSubmenuOrMenu()\n break\n default:\n return\n }\n e.preventDefault()\n }\n\n private _closeLastSubmenuOrMenu() {\n if (this._activeSubmenus.length > 0) {\n const level = this._activeSubmenus.length - 1\n const parent = this._activeSubmenuItems[level]\n this._closeSubmenusFrom(level)\n this._focusMenuItem(parent)\n this._bindLevelHovers()\n } else {\n this._closeFromUser()\n }\n }\n\n private _preventBackgroundScroll(e: Event) {\n if (!this._isOpen || this.noScrollLock || this._isMenuPanelEvent(e)) return\n e.preventDefault()\n }\n\n private _syncScrollLock(isOpen = this._isOpen) {\n this._scrollLock.sync(isOpen && !this.noScrollLock)\n }\n\n private _getEnabledLevelItems(level: number) {\n const container = level === 0 ? this._menu?.content : this._activeSubmenus[level - 1]?.content\n return container ? getEnabledMenuItems(container) : []\n }\n\n private _getFocusedItem(): HTMLElement | undefined {\n return findFocusedMenuItem([this._menu?.content, ...this._activeSubmenus.map(submenu => submenu.content)])\n }\n\n private _getFocusedLevel(): number | undefined {\n const item = this._getFocusedItem()\n if (!item) return undefined\n if (this._menu?.panel.contains(item)) return 0\n const submenuIndex = this._activeSubmenus.findIndex(menu => menu.panel.contains(item))\n return submenuIndex < 0 ? undefined : submenuIndex + 1\n }\n\n private _focusMenuItem(item: HTMLElement | undefined) {\n focusMenuItem(item)\n }\n\n private _isMenuPanelEvent(e: Event): boolean {\n return e\n .composedPath()\n .some(\n node =>\n node instanceof HTMLElement &&\n (node.classList.contains('context-menu') || node.classList.contains('context-submenu'))\n )\n }\n\n override render() {\n return html`\n <div class=\"context-menu-anchor\">\n <slot @slotchange=${this._onSlotChange}></slot>\n </div>\n `\n }\n\n private _onSlotChange() {\n this._hideMenuItems()\n // 打开期间宿主重建顶层项时,新成员要移入 portal 才对用户可见;\n // 关闭状态下无需移动,等下次打开时由 _setupMenuItems 统一处理。\n if (this._isOpen) this._scheduleRefresh()\n }\n\n declare readonly $events: {\n 'open-change': CustomEvent<{ open: boolean }>\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-context-menu': WebUiContextMenu\n }\n}\n"],"mappings":"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"}
@@ -24,6 +24,9 @@ export declare class WebUiDropdown extends LitElement {
24
24
  private readonly _userOpenChange;
25
25
  private _restoreFocusTarget?;
26
26
  private _shouldOpenInstantly;
27
+ private _menuSyncScheduled;
28
+ private readonly _level0ItemAnchors;
29
+ private readonly _menuContentObserver;
27
30
  get isOpen(): boolean;
28
31
  connectedCallback(): void;
29
32
  firstUpdated(): void;
@@ -42,12 +45,15 @@ export declare class WebUiDropdown extends LitElement {
42
45
  private _syncActiveAttrs;
43
46
  private _getLevelItems;
44
47
  private _populateLevel0;
48
+ private _scheduleMenuSync;
49
+ private _syncOpenMenuContent;
45
50
  private _returnLevel0Items;
46
51
  private _populateOverlay;
47
52
  private _depopulateOverlay;
48
53
  private _ensureOverlay;
49
54
  private _buildOverlay;
50
55
  private _disposeOverlay;
56
+ private _rebindMenuContentObservers;
51
57
  private _disposeAll;
52
58
  private _closeSubmenuAfterPresence;
53
59
  private _disposeClosingSubmenuOverlays;
@@ -8,12 +8,12 @@ import "../dropdown-divider/index.js";
8
8
  import "../dropdown-header/index.js";
9
9
  import "../dropdown-item/index.js";
10
10
  import { createMenuPortalOverlay as c } from "../../shared/menu-portal/menu-portal.js";
11
- import { findFocusedMenuItem as l, focusMenuItem as u, getEnabledMenuItems as d, getMenuChildren as f, getMenuItemFromEvent as p, hideNestedMenuChildren as m, moveMenuChildren as h } from "../../shared/menu-portal/menu-tree.js";
12
- import g from "./style.js";
13
- import { LitElement as _, html as v, unsafeCSS as y } from "lit";
14
- import { customElement as b, property as x, state as S } from "lit/decorators.js";
11
+ import { captureFrameworkAnchors as l, findFocusedMenuItem as u, focusMenuItem as d, getEnabledMenuItems as f, getMenuChildren as p, getMenuItemFromEvent as m, hideNestedMenuChildren as h, moveMenuChildren as g, orderManagedMenuItems as _, reconcileManagedMenuItems as v, restoreFrameworkAnchors as y, returnManagedMenuItemsToSlot as b } from "../../shared/menu-portal/menu-tree.js";
12
+ import x from "./style.js";
13
+ import { LitElement as S, html as C, unsafeCSS as w } from "lit";
14
+ import { customElement as T, property as E, state as D } from "lit/decorators.js";
15
15
  //#region src/components/dropdown/index.ts
16
- var C = "web-ui-menu-level-", w = 0, T = [
16
+ var O = "web-ui-menu-level-", k = "wui-dropdown-menu-item", A = 0, j = [
17
17
  "top",
18
18
  "top-start",
19
19
  "top-end",
@@ -26,14 +26,16 @@ var C = "web-ui-menu-level-", w = 0, T = [
26
26
  "left",
27
27
  "left-start",
28
28
  "left-end"
29
- ], E = class extends _ {
29
+ ], M = class extends S {
30
30
  constructor(...e) {
31
- super(...e), this.open = !1, this.disabled = !1, this.matchWidth = !1, this.noScrollLock = !1, this._placement = "bottom-start", this._offset = 4, this._activePath = [], this._overlays = /* @__PURE__ */ new Map(), this._closingSubmenuOverlays = /* @__PURE__ */ new Map(), this._ignoreOutsideClick = !1, this._hoverCleanupFns = [], this._scrollLock = o().make(), this._userOpenChange = new s(), this._shouldOpenInstantly = !0, this._onTriggerClick = (e) => {
31
+ super(...e), this.open = !1, this.disabled = !1, this.matchWidth = !1, this.noScrollLock = !1, this._placement = "bottom-start", this._offset = 4, this._activePath = [], this._overlays = /* @__PURE__ */ new Map(), this._closingSubmenuOverlays = /* @__PURE__ */ new Map(), this._ignoreOutsideClick = !1, this._hoverCleanupFns = [], this._scrollLock = o().make(), this._userOpenChange = new s(), this._shouldOpenInstantly = !0, this._menuSyncScheduled = !1, this._level0ItemAnchors = /* @__PURE__ */ new Map(), this._menuContentObserver = new MutationObserver(() => {
32
+ this.open && this._scheduleMenuSync();
33
+ }), this._onTriggerClick = (e) => {
32
34
  this.disabled || (this.open ? this._closeAll(!0) : this._openMenu(e.detail === 0, !0));
33
35
  }, this._onClickOutside = (e) => {
34
36
  !this.isOpen || this._ignoreOutsideClick || this._isInsideShadowRoot(e) || this._closeAll(!0);
35
37
  }, this._onMenuClick = (e) => {
36
- let t = p(e);
38
+ let t = m(e);
37
39
  if (!t || t.hasAttribute("disabled") || !t.hasAttribute("submenu")) return;
38
40
  let n = this._getFocusedLevel(t);
39
41
  if (n === void 0) return;
@@ -74,22 +76,19 @@ var C = "web-ui-menu-level-", w = 0, T = [
74
76
  case " ":
75
77
  t?.click();
76
78
  break;
77
- case "Escape":
78
- this._activePath.length > 0 ? this._closeSubmenuFrom(this._activePath.length) : this._closeAll(!0);
79
- break;
80
79
  default: return;
81
80
  }
82
81
  e.preventDefault(), e.stopPropagation();
83
82
  };
84
83
  }
85
84
  static {
86
- this.styles = y(g);
85
+ this.styles = w(x);
87
86
  }
88
87
  get placement() {
89
88
  return this._placement;
90
89
  }
91
90
  set placement(e) {
92
- this._placement = t(e, T, "bottom-start");
91
+ this._placement = t(e, j, "bottom-start");
93
92
  }
94
93
  get offset() {
95
94
  return this._offset;
@@ -101,13 +100,16 @@ var C = "web-ui-menu-level-", w = 0, T = [
101
100
  return this.open || this._activePath.length > 0;
102
101
  }
103
102
  connectedCallback() {
104
- super.connectedCallback(), this.addEventListener("keydown", this._onKeydown), document.addEventListener("click", this._onClickOutside);
103
+ super.connectedCallback(), this.addEventListener("keydown", this._onKeydown), document.addEventListener("click", this._onClickOutside), this._menuContentObserver.observe(this, {
104
+ childList: !0,
105
+ subtree: !0
106
+ });
105
107
  }
106
108
  firstUpdated() {
107
109
  this.shadowRoot?.querySelector("slot[name=\"trigger\"]")?.addEventListener("slotchange", () => this.requestUpdate());
108
110
  }
109
111
  disconnectedCallback() {
110
- super.disconnectedCallback(), this.removeEventListener("keydown", this._onKeydown), document.removeEventListener("click", this._onClickOutside), clearTimeout(this._openTimer), clearTimeout(this._ignoreOutsideClickTimer), this._scrollLock.release(), this._cleanupClosedMenu(), this._hoverCleanupFns.forEach((e) => e());
112
+ super.disconnectedCallback(), this._menuContentObserver.disconnect(), this.removeEventListener("keydown", this._onKeydown), document.removeEventListener("click", this._onClickOutside), clearTimeout(this._openTimer), clearTimeout(this._ignoreOutsideClickTimer), this._scrollLock.release(), this._cleanupClosedMenu(), this._hoverCleanupFns.forEach((e) => e());
111
113
  }
112
114
  updated(e) {
113
115
  this._syncTriggerAria(), e.has("open") && (this.open ? (this._restoreFocusTarget ??= document.activeElement instanceof HTMLElement ? document.activeElement : void 0, this._ignoreCurrentOutsideClick(), this._syncScrollLock(), this._hideAllSubmenuChildren(), requestAnimationFrame(() => {
@@ -145,7 +147,7 @@ var C = "web-ui-menu-level-", w = 0, T = [
145
147
  e && !await i(e) || this.open || !this.isConnected || (this._cleanupClosedMenu(), this._restoreFocusTarget?.focus(), this._restoreFocusTarget = void 0);
146
148
  }
147
149
  _hideAllSubmenuChildren() {
148
- m(this, `${C}-hidden`);
150
+ h(this, `${O}-hidden`);
149
151
  }
150
152
  _toggleSubmenu(e, t, n = !1) {
151
153
  if (this._activePath[e] === t) return;
@@ -175,25 +177,44 @@ var C = "web-ui-menu-level-", w = 0, T = [
175
177
  }
176
178
  _getLevelItems(e) {
177
179
  let t = this._overlays.get(e)?.content;
178
- return t ? f(t) : [];
180
+ return t ? p(t) : [];
179
181
  }
180
182
  _populateLevel0() {
181
183
  let e = this._overlays.get(0)?.content;
182
- e && h(this, e);
184
+ e && (v(this, e, this._level0ItemAnchors, k), _(this, e, this._level0ItemAnchors, k));
185
+ }
186
+ _scheduleMenuSync() {
187
+ this._menuSyncScheduled || (this._menuSyncScheduled = !0, requestAnimationFrame(() => {
188
+ this._menuSyncScheduled = !1, this.open && this._syncOpenMenuContent();
189
+ }));
190
+ }
191
+ _syncOpenMenuContent() {
192
+ let e = this._overlays.get(0)?.content;
193
+ if (this._hideAllSubmenuChildren(), e) {
194
+ v(this, e, this._level0ItemAnchors, k), h(e, `${O}-hidden`);
195
+ let t = l(e, k);
196
+ _(this, e, this._level0ItemAnchors, k), y(e, t);
197
+ }
198
+ for (let [e, t] of this._overlays) {
199
+ if (e === 0) continue;
200
+ let n = this._getLevelItems(e - 1)[this._activePath[e - 1]];
201
+ n && g(n, t.content), h(t.content, `${O}-hidden`);
202
+ }
203
+ this._bindLevelHovers();
183
204
  }
184
205
  _returnLevel0Items() {
185
206
  let e = this._overlays.get(0)?.content;
186
- e && h(e, this);
207
+ e && b(this, e, this._level0ItemAnchors);
187
208
  }
188
209
  _populateOverlay(e, t) {
189
210
  let n = this._overlays.get(e)?.content;
190
- n && h(t, n);
211
+ n && g(t, n);
191
212
  }
192
213
  _depopulateOverlay(e, t) {
193
214
  let n = this._overlays.get(e)?.content;
194
215
  if (!n) return;
195
216
  let r = e - 1, i = t ?? this._getLevelItems(r)[this._activePath[r]];
196
- i && h(n, i);
217
+ i && g(n, i);
197
218
  }
198
219
  _ensureOverlay(e, t = !1, n) {
199
220
  let r = n ? this._closingSubmenuOverlays.get(n) : void 0;
@@ -203,7 +224,7 @@ var C = "web-ui-menu-level-", w = 0, T = [
203
224
  }
204
225
  _buildOverlay(e, t = !1) {
205
226
  let { panel: n, content: i } = c("dropdown-overlay", this);
206
- n.setAttribute("role", "menu"), e === 0 && (n.id = `wui-dropdown-menu-${++w}`), n.dataset.level = String(e), n.addEventListener("click", this._onMenuClick), n.addEventListener("keydown", this._onKeydown);
227
+ n.setAttribute("role", "menu"), e === 0 && (n.id = `wui-dropdown-menu-${++A}`), n.dataset.level = String(e), n.addEventListener("click", this._onMenuClick), n.addEventListener("keydown", this._onKeydown);
207
228
  let o = e === 0 ? this._queryTriggerAnchor() : this._getSubmenuTriggerAnchor(e - 1);
208
229
  if (o) {
209
230
  let s = r().make({
@@ -219,12 +240,25 @@ var C = "web-ui-menu-level-", w = 0, T = [
219
240
  api: s,
220
241
  overlay: n,
221
242
  content: i
222
- }), e === 0 && this._populateLevel0(), s.open(), a(n, { isInstant: t });
243
+ }), e === 0 && this._populateLevel0(), this._menuContentObserver.observe(i, {
244
+ childList: !0,
245
+ subtree: !0
246
+ }), s.open(), a(n, { isInstant: t });
223
247
  }
224
248
  }
225
249
  _disposeOverlay(e) {
226
- let t = this._overlays.get(e)?.overlay;
227
- t?.removeEventListener("click", this._onMenuClick), t?.removeEventListener("keydown", this._onKeydown), t?.remove(), this._overlays.get(e)?.api.dispose(), this._overlays.delete(e);
250
+ let t = this._overlays.get(e);
251
+ t?.overlay.removeEventListener("click", this._onMenuClick), t?.overlay.removeEventListener("keydown", this._onKeydown), t?.overlay.remove(), t?.api.dispose(), this._overlays.delete(e), this._rebindMenuContentObservers();
252
+ }
253
+ _rebindMenuContentObservers() {
254
+ this._menuContentObserver.disconnect(), this._menuContentObserver.observe(this, {
255
+ childList: !0,
256
+ subtree: !0
257
+ });
258
+ for (let e of this._overlays.values()) this._menuContentObserver.observe(e.content, {
259
+ childList: !0,
260
+ subtree: !0
261
+ });
228
262
  }
229
263
  _disposeAll() {
230
264
  for (let e of this._overlays.keys()) this._disposeOverlay(e);
@@ -238,7 +272,7 @@ var C = "web-ui-menu-level-", w = 0, T = [
238
272
  }), this._closingSubmenuOverlays.clear();
239
273
  }
240
274
  _restoreSubmenuItems(e, t) {
241
- h(e.content, t);
275
+ g(e.content, t);
242
276
  }
243
277
  _syncScrollLock(e = this.open) {
244
278
  this._scrollLock.sync(e && !this.noScrollLock);
@@ -262,16 +296,16 @@ var C = "web-ui-menu-level-", w = 0, T = [
262
296
  }
263
297
  _getEnabledLevelItems(e) {
264
298
  let t = this._overlays.get(e)?.content;
265
- return t ? d(t) : [];
299
+ return t ? f(t) : [];
266
300
  }
267
301
  _getFocusedItem(e) {
268
- return e?.composedPath().find((e) => e instanceof HTMLElement && e.matches("web-ui-dropdown-item")) || l([...this._overlays.values()].map(({ overlay: e }) => e));
302
+ return e?.composedPath().find((e) => e instanceof HTMLElement && e.matches("web-ui-dropdown-item")) || u([...this._overlays.values()].map(({ overlay: e }) => e));
269
303
  }
270
304
  _getFocusedLevel(e = this._getFocusedItem()) {
271
305
  if (e) return [...this._overlays.entries()].find(([, t]) => t.overlay.contains(e))?.[0];
272
306
  }
273
307
  _focusMenuItem(e) {
274
- u(e);
308
+ d(e);
275
309
  }
276
310
  _isInsideShadowRoot(e) {
277
311
  for (let t of e.composedPath()) if (t === this || t === this.shadowRoot || t instanceof Node && t.getRootNode() === this.shadowRoot || t instanceof Node && [...this._overlays.values()].some(({ overlay: e }) => e.contains(t))) return !0;
@@ -300,32 +334,32 @@ var C = "web-ui-menu-level-", w = 0, T = [
300
334
  requestAnimationFrame(() => this._bindLevelHovers());
301
335
  }
302
336
  render() {
303
- return v`
337
+ return C`
304
338
  <div class="dropdown-trigger" @click=${this._onTriggerClick}>
305
339
  <slot name="trigger"></slot>
306
340
  </div>
307
341
  `;
308
342
  }
309
343
  };
310
- e([x({
344
+ e([E({
311
345
  type: Boolean,
312
346
  reflect: !0
313
- })], E.prototype, "open", void 0), e([x({
347
+ })], M.prototype, "open", void 0), e([E({
314
348
  type: Boolean,
315
349
  reflect: !0
316
- })], E.prototype, "disabled", void 0), e([x({
350
+ })], M.prototype, "disabled", void 0), e([E({
317
351
  type: Boolean,
318
352
  reflect: !0,
319
353
  attribute: "match-width"
320
- })], E.prototype, "matchWidth", void 0), e([x({
354
+ })], M.prototype, "matchWidth", void 0), e([E({
321
355
  type: Boolean,
322
356
  reflect: !0,
323
357
  attribute: "no-scroll-lock"
324
- })], E.prototype, "noScrollLock", void 0), e([x({
358
+ })], M.prototype, "noScrollLock", void 0), e([E({
325
359
  type: String,
326
360
  reflect: !0
327
- })], E.prototype, "placement", null), e([x({ type: Number })], E.prototype, "offset", null), e([S()], E.prototype, "_activePath", void 0), E = e([b("web-ui-dropdown")], E);
361
+ })], M.prototype, "placement", null), e([E({ type: Number })], M.prototype, "offset", null), e([D()], M.prototype, "_activePath", void 0), M = e([T("web-ui-dropdown")], M);
328
362
  //#endregion
329
- export { E as WebUiDropdown };
363
+ export { M as WebUiDropdown };
330
364
 
331
365
  //# sourceMappingURL=index.js.map