@greypan/web-ui 6.0.0 → 6.1.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.
Files changed (129) hide show
  1. package/README.CN.md +89 -26
  2. package/README.md +80 -30
  3. package/dist/assets/glass.js +1 -1
  4. package/dist/assets/glass.js.map +1 -1
  5. package/dist/assets/menu-portal.js +1 -1
  6. package/dist/assets/menu-portal.js.map +1 -1
  7. package/dist/components/autocomplete/index.d.ts +367 -4
  8. package/dist/components/autocomplete/index.js +83 -77
  9. package/dist/components/autocomplete/index.js.map +1 -1
  10. package/dist/components/autocomplete/style.js +1 -1
  11. package/dist/components/autocomplete/style.js.map +1 -1
  12. package/dist/components/avatar/style.js +1 -1
  13. package/dist/components/avatar/style.js.map +1 -1
  14. package/dist/components/back-top/style.js +1 -1
  15. package/dist/components/back-top/style.js.map +1 -1
  16. package/dist/components/badge/style.js +1 -1
  17. package/dist/components/badge/style.js.map +1 -1
  18. package/dist/components/button/style.js +1 -1
  19. package/dist/components/button/style.js.map +1 -1
  20. package/dist/components/button-group/style.js +1 -1
  21. package/dist/components/button-group/style.js.map +1 -1
  22. package/dist/components/checkbox/index.d.ts +366 -5
  23. package/dist/components/checkbox/index.js +19 -31
  24. package/dist/components/checkbox/index.js.map +1 -1
  25. package/dist/components/checkbox/style.js +1 -1
  26. package/dist/components/checkbox/style.js.map +1 -1
  27. package/dist/components/checkbox-group/index.d.ts +366 -4
  28. package/dist/components/checkbox-group/index.js +16 -25
  29. package/dist/components/checkbox-group/index.js.map +1 -1
  30. package/dist/components/collapse/index.js +24 -27
  31. package/dist/components/collapse/index.js.map +1 -1
  32. package/dist/components/context-menu/index.d.ts +6 -14
  33. package/dist/components/context-menu/index.js +88 -143
  34. package/dist/components/context-menu/index.js.map +1 -1
  35. package/dist/components/dialog/index.js +20 -23
  36. package/dist/components/dialog/index.js.map +1 -1
  37. package/dist/components/dialog/style.js +1 -1
  38. package/dist/components/dialog/style.js.map +1 -1
  39. package/dist/components/drawer/index.js +54 -57
  40. package/dist/components/drawer/index.js.map +1 -1
  41. package/dist/components/drawer/style.js +1 -1
  42. package/dist/components/drawer/style.js.map +1 -1
  43. package/dist/components/dropdown/index.d.ts +5 -14
  44. package/dist/components/dropdown/index.js +94 -155
  45. package/dist/components/dropdown/index.js.map +1 -1
  46. package/dist/components/dropdown-header/style.js +1 -1
  47. package/dist/components/dropdown-header/style.js.map +1 -1
  48. package/dist/components/dropdown-item/style.js +1 -1
  49. package/dist/components/dropdown-item/style.js.map +1 -1
  50. package/dist/components/empty/style.js +1 -1
  51. package/dist/components/empty/style.js.map +1 -1
  52. package/dist/components/input/index.d.ts +366 -5
  53. package/dist/components/input/index.js +35 -47
  54. package/dist/components/input/index.js.map +1 -1
  55. package/dist/components/input/style.js +1 -1
  56. package/dist/components/input/style.js.map +1 -1
  57. package/dist/components/input-number/index.d.ts +366 -5
  58. package/dist/components/input-number/index.js +31 -44
  59. package/dist/components/input-number/index.js.map +1 -1
  60. package/dist/components/input-number/style.js +1 -1
  61. package/dist/components/input-number/style.js.map +1 -1
  62. package/dist/components/layout/style.js +1 -1
  63. package/dist/components/layout/style.js.map +1 -1
  64. package/dist/components/option/style.js +1 -1
  65. package/dist/components/option/style.js.map +1 -1
  66. package/dist/components/popover/index.js +27 -46
  67. package/dist/components/popover/index.js.map +1 -1
  68. package/dist/components/popover/style.js +1 -1
  69. package/dist/components/popover/style.js.map +1 -1
  70. package/dist/components/radio/index.d.ts +366 -5
  71. package/dist/components/radio/index.js +19 -31
  72. package/dist/components/radio/index.js.map +1 -1
  73. package/dist/components/radio/style.js +1 -1
  74. package/dist/components/radio/style.js.map +1 -1
  75. package/dist/components/radio-group/index.d.ts +366 -4
  76. package/dist/components/radio-group/index.js +16 -25
  77. package/dist/components/radio-group/index.js.map +1 -1
  78. package/dist/components/segmented/index.d.ts +366 -4
  79. package/dist/components/segmented/index.js +26 -35
  80. package/dist/components/segmented/index.js.map +1 -1
  81. package/dist/components/segmented/style.js +1 -1
  82. package/dist/components/segmented/style.js.map +1 -1
  83. package/dist/components/segmented-trigger/style.js +1 -1
  84. package/dist/components/segmented-trigger/style.js.map +1 -1
  85. package/dist/components/select/index.d.ts +368 -5
  86. package/dist/components/select/index.js +64 -66
  87. package/dist/components/select/index.js.map +1 -1
  88. package/dist/components/select/style.js +1 -1
  89. package/dist/components/select/style.js.map +1 -1
  90. package/dist/components/slider/index.d.ts +366 -5
  91. package/dist/components/slider/index.js +38 -50
  92. package/dist/components/slider/index.js.map +1 -1
  93. package/dist/components/slider/style.js +1 -1
  94. package/dist/components/slider/style.js.map +1 -1
  95. package/dist/components/spinner/style.js +1 -1
  96. package/dist/components/spinner/style.js.map +1 -1
  97. package/dist/components/switch/index.d.ts +366 -5
  98. package/dist/components/switch/index.js +26 -38
  99. package/dist/components/switch/index.js.map +1 -1
  100. package/dist/components/switch/style.js +1 -1
  101. package/dist/components/switch/style.js.map +1 -1
  102. package/dist/components/textarea/index.d.ts +366 -5
  103. package/dist/components/textarea/index.js +43 -57
  104. package/dist/components/textarea/index.js.map +1 -1
  105. package/dist/components/textarea/style.js +1 -1
  106. package/dist/components/textarea/style.js.map +1 -1
  107. package/dist/components/theme/style.js +1 -1
  108. package/dist/components/theme/style.js.map +1 -1
  109. package/dist/components/toast/style.js +1 -1
  110. package/dist/components/toast/style.js.map +1 -1
  111. package/dist/components/tooltip/index.js +29 -48
  112. package/dist/components/tooltip/index.js.map +1 -1
  113. package/dist/components/tooltip/style.js +1 -1
  114. package/dist/components/tooltip/style.js.map +1 -1
  115. package/dist/shared/form-association/index.js +31 -4
  116. package/dist/shared/form-association/index.js.map +1 -1
  117. package/dist/shared/menu-behavior/index.js +112 -0
  118. package/dist/shared/menu-behavior/index.js.map +1 -0
  119. package/dist/shared/open-state/index.js +12 -0
  120. package/dist/shared/open-state/index.js.map +1 -0
  121. package/dist/shared/option-portal/combobox-shell.js +41 -0
  122. package/dist/shared/option-portal/combobox-shell.js.map +1 -0
  123. package/dist/shared/option-portal/index.js +22 -21
  124. package/dist/shared/option-portal/index.js.map +1 -1
  125. package/dist/shared/overlay/placement-props.js +19 -0
  126. package/dist/shared/overlay/placement-props.js.map +1 -0
  127. package/dist/shared/overlay/portal.js +4 -2
  128. package/dist/shared/overlay/portal.js.map +1 -1
  129. package/package.json +3 -7
@@ -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 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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"}
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 {\n createClosingSubmenuStack,\n createMenuHoverBinder,\n createMenuOutsideClickGuard,\n getEnabledMenuLevelItems,\n getFocusedMenuItemFromPanels,\n handleMenuKeyboard,\n type MenuHoverDelegate,\n type MenuKeyboardDelegate\n} from '@/shared/menu-behavior'\nimport { createMenuPortalOverlay, type MenuPortalOverlay } from '@/shared/menu-portal/menu-portal'\nimport {\n captureFrameworkAnchors,\n focusMenuItem,\n getMenuChildren,\n getMenuItemFromEvent,\n hideNestedMenuChildren,\n moveMenuChildren,\n orderManagedMenuItems,\n reconcileManagedMenuItems,\n restoreFrameworkAnchors,\n returnManagedMenuItemsToSlot\n} from '@/shared/menu-portal/menu-tree'\nimport { dispatchOpenChangeEvent } from '@/shared/open-state'\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 _menu?: MenuPortalOverlay\n // 行为层(hover / outside-click / 键盘 / submenu 收尾)由 shared/menu-behavior 驱动\n private readonly _outsideClickGuard = createMenuOutsideClickGuard(\n this,\n node => this._menu?.panel.contains(node) === true || this._activeSubmenus.some(menu => menu.panel.contains(node))\n )\n private readonly _closingSubmenus = createClosingSubmenuStack<MenuPortalOverlay>({\n getPanel: container => container.panel,\n restoreItems: (container, parentItem) => this._restoreSubmenuItems(container, parentItem),\n dispose: container => container.panel.remove()\n })\n private readonly _hoverDelegate: MenuHoverDelegate = {\n getOpenDepth: () => this._activeSubmenus.length,\n getLevelItems: level => this._getLevelItems(level),\n getActiveItem: level => this._activeSubmenuItems[level],\n isSubmenuItem: item => item.hasAttribute('submenu'),\n openSubmenu: item => this._openSubmenu(item),\n closeFrom: level => {\n this._closeSubmenusFrom(level)\n this._hoverBinder.bind()\n }\n }\n private readonly _hoverBinder = createMenuHoverBinder(this._hoverDelegate, 'web-ui-dropdown-item')\n private readonly _keyboardDelegate: MenuKeyboardDelegate = {\n getFocusedItem: event =>\n getFocusedMenuItemFromPanels(\n event,\n [this._menu?.content, ...this._activeSubmenus.map(submenu => submenu.content)],\n 'web-ui-dropdown-item'\n ),\n getLevelOf: item => {\n const level = this._getItemLevel(item)\n return level === -1 ? undefined : level\n },\n getEnabledItems: level => getEnabledMenuLevelItems(this._getLevelContainer(level)?.content),\n isSubmenuItem: item => item.hasAttribute('submenu'),\n openSubmenuInstant: item => this._openSubmenu(item, true),\n closeToParent: level => {\n const parent = this._activeSubmenuItems[level - 1]\n this._closeSubmenusFrom(level - 1)\n this._hoverBinder.bind()\n return parent\n },\n closeDeepestOrAll: () => this._closeLastSubmenuOrMenu()\n }\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 this._outsideClickGuard.dispose()\n this._hoverBinder.dispose()\n this._scrollLock.release()\n this._returnItemsToSlot()\n this._menu?.panel.remove()\n this._menu = undefined\n this._closeSubmenusFrom(0, true)\n this._closingSubmenus.restoreAll()\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._outsideClickGuard.arm()\n if (this._isOpen) {\n this._closeSubmenusFrom(0, true)\n this._closingSubmenus.restoreAll()\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._hoverBinder.bind()\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 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._closingSubmenus.restoreAll()\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.take(item)\n if (closingSubmenu) {\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._hoverBinder.bind()\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._hoverBinder.bind()\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.closeAsync(item, submenu)\n }\n }\n this._activeSubmenus.length = level\n this._activeSubmenuItems.length = level\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 _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 dispatchOpenChangeEvent(this, open)\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._outsideClickGuard.isInside(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._outsideClickGuard.isArmed()) return\n if (this._outsideClickGuard.isInside(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 _getLevelContainer(level: number) {\n return level === 0 ? this._menu : this._activeSubmenus[level - 1]\n }\n\n private _getLevelItems(level: number): HTMLElement[] {\n const container = this._getLevelContainer(level)\n if (!container) return []\n return getMenuChildren(container.content)\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 handleMenuKeyboard(this._keyboardDelegate, e)\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 focusMenuItem(parent)\n this._hoverBinder.bind()\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 _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,16 +1,17 @@
1
1
  import e from "../../_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorate.js";
2
2
  import t from "../../assets/glass.js";
3
- import { defineScrollLockLease as n } from "../../shared/scroll-lock/scroll-lock.js";
3
+ import { dispatchOpenChangeEvent as n } from "../../shared/open-state/index.js";
4
+ import { defineScrollLockLease as r } from "../../shared/scroll-lock/scroll-lock.js";
4
5
  import "../button/index.js";
5
- import { UserChangeController as r } from "../../shared/events/user-change.js";
6
- import { defineNativeDialogPresence as i } from "../../shared/overlay/native-dialog-presence.js";
7
- import a from "./style.js";
8
- import { LitElement as o, html as s, unsafeCSS as c } from "lit";
9
- import { customElement as l, property as u, state as d } from "lit/decorators.js";
6
+ import { UserChangeController as i } from "../../shared/events/user-change.js";
7
+ import { defineNativeDialogPresence as a } from "../../shared/overlay/native-dialog-presence.js";
8
+ import o from "./style.js";
9
+ import { LitElement as s, html as c, unsafeCSS as l } from "lit";
10
+ import { customElement as u, property as d, state as f } from "lit/decorators.js";
10
11
  //#region src/components/dialog/index.ts
11
- var f = class extends o {
12
+ var p = class extends s {
12
13
  constructor(...e) {
13
- super(...e), this.open = !1, this.noScrollLock = !1, this.noBackdropClose = !1, this.noEscapeClose = !1, this.controlled = !1, this._hasBody = !1, this._userOpenChange = new r(), this._scrollLock = n().make(), this._presence = i().make({
14
+ super(...e), this.open = !1, this.noScrollLock = !1, this.noBackdropClose = !1, this.noEscapeClose = !1, this.controlled = !1, this._hasBody = !1, this._userOpenChange = new i(), this._scrollLock = r().make(), this._presence = a().make({
14
15
  getDialog: () => this.dialog,
15
16
  isConnected: () => this.isConnected,
16
17
  isOpen: () => this.open
@@ -27,7 +28,7 @@ var f = class extends o {
27
28
  };
28
29
  }
29
30
  static {
30
- this.styles = [c(t), c(a)];
31
+ this.styles = [l(t), l(o)];
31
32
  }
32
33
  get dialog() {
33
34
  return this.shadowRoot?.querySelector("dialog") ?? null;
@@ -63,11 +64,7 @@ var f = class extends o {
63
64
  }
64
65
  }
65
66
  emitOpenChange(e = this.open) {
66
- this.dispatchEvent(new CustomEvent("open-change", {
67
- detail: { open: e },
68
- bubbles: !0,
69
- composed: !0
70
- }));
67
+ n(this, e);
71
68
  }
72
69
  _syncScrollLock(e = this.open) {
73
70
  this._scrollLock.sync(e && !this.noScrollLock);
@@ -76,7 +73,7 @@ var f = class extends o {
76
73
  e.target instanceof HTMLSlotElement && (this._hasBody = e.target.assignedElements().length > 0);
77
74
  }
78
75
  render() {
79
- return s`
76
+ return c`
80
77
  <dialog
81
78
  @cancel=${this.handleCancel}
82
79
  @close=${this._onNativeClose}
@@ -84,7 +81,7 @@ var f = class extends o {
84
81
  @transitionend=${this._onTransitionEnd}
85
82
  >
86
83
  <div class="wui-dialog-body wui-glass">
87
- ${this._hasBody ? s`<slot name="body" @slotchange=${this._onBodySlotChange}></slot>` : s`
84
+ ${this._hasBody ? c`<slot name="body" @slotchange=${this._onBodySlotChange}></slot>` : c`
88
85
  <slot name="body" @slotchange=${this._onBodySlotChange} hidden></slot>
89
86
  <div class="title"><slot name="title"></slot></div>
90
87
  <div class="desc"><slot></slot></div>
@@ -95,26 +92,26 @@ var f = class extends o {
95
92
  `;
96
93
  }
97
94
  };
98
- e([u({
95
+ e([d({
99
96
  type: Boolean,
100
97
  reflect: !0
101
- })], f.prototype, "open", void 0), e([u({
98
+ })], p.prototype, "open", void 0), e([d({
102
99
  type: Boolean,
103
100
  reflect: !0,
104
101
  attribute: "no-scroll-lock"
105
- })], f.prototype, "noScrollLock", void 0), e([u({
102
+ })], p.prototype, "noScrollLock", void 0), e([d({
106
103
  type: Boolean,
107
104
  reflect: !0,
108
105
  attribute: "no-backdrop-close"
109
- })], f.prototype, "noBackdropClose", void 0), e([u({
106
+ })], p.prototype, "noBackdropClose", void 0), e([d({
110
107
  type: Boolean,
111
108
  reflect: !0,
112
109
  attribute: "no-escape-close"
113
- })], f.prototype, "noEscapeClose", void 0), e([u({
110
+ })], p.prototype, "noEscapeClose", void 0), e([d({
114
111
  type: Boolean,
115
112
  reflect: !0
116
- })], f.prototype, "controlled", void 0), e([d()], f.prototype, "_hasBody", void 0), f = e([l("web-ui-dialog")], f);
113
+ })], p.prototype, "controlled", void 0), e([f()], p.prototype, "_hasBody", void 0), p = e([u("web-ui-dialog")], p);
117
114
  //#endregion
118
- export { f as WebUiDialog };
115
+ export { p as WebUiDialog };
119
116
 
120
117
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/dialog/index.ts"],"sourcesContent":["import { html, LitElement, type PropertyValues, unsafeCSS } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport '@/components/button'\nimport glass from '@/assets/glass.css?inline'\nimport { UserChangeController } from '@/shared/events/user-change'\nimport { defineNativeDialogPresence } from '@/shared/overlay/native-dialog-presence'\nimport { defineScrollLockLease } from '@/shared/scroll-lock/scroll-lock'\n\nimport style from './style.css?inline'\n\n@customElement('web-ui-dialog')\nexport class WebUiDialog extends LitElement {\n static override styles = [unsafeCSS(glass), unsafeCSS(style)]\n\n @property({ type: Boolean, reflect: true }) open = false\n @property({ type: Boolean, reflect: true, attribute: 'no-scroll-lock' }) noScrollLock = false\n @property({ type: Boolean, reflect: true, attribute: 'no-backdrop-close' }) noBackdropClose = false\n @property({ type: Boolean, reflect: true, attribute: 'no-escape-close' }) noEscapeClose = false\n\n /**\n * Controlled 模式下,Escape 和遮罩点击只派发 `open-change` 请求,\n * 不会自行修改 `open`。Consumer 回写 `open` 后才执行关闭动画。\n * 程序化 API(showModal/close/直接赋值 open)不受此模式影响,始终直通。\n */\n @property({ type: Boolean, reflect: true }) controlled = false\n\n @state() private _hasBody = false\n private readonly _userOpenChange = new UserChangeController()\n\n private readonly _scrollLock = defineScrollLockLease().make()\n private readonly _presence = defineNativeDialogPresence().make({\n getDialog: () => this.dialog,\n isConnected: () => this.isConnected,\n isOpen: () => this.open\n })\n\n private get dialog() {\n return this.shadowRoot?.querySelector('dialog') ?? null\n }\n\n protected override updated(props: PropertyValues) {\n super.updated(props)\n if (!this.isConnected) return\n\n if (props.has('open')) {\n if (this._userOpenChange.consume()) this.emitOpenChange()\n this._presence.sync(this.open)\n }\n if (props.has('open') || props.has('noScrollLock')) this._syncScrollLock()\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback()\n this._presence.dispose()\n this._scrollLock.release()\n }\n\n // 以模态方式打开对话框(命令式)\n showModal() {\n if (this.open) return\n this.open = true\n }\n\n close() {\n this.open = false\n }\n\n private handleCancel(e: Event) {\n // 子控件(例如 file input)可能派发冒泡的 cancel;只让 native dialog 自身的 cancel 关闭。\n if (e.target !== e.currentTarget) return\n\n // 保留 top layer 直到视觉退场完成,避免原生关闭跳过退出动画。\n e.preventDefault()\n if (this.noEscapeClose) return\n if (this.controlled) {\n this.emitOpenChange(false)\n return\n }\n this._userOpenChange.mark()\n this.close()\n }\n\n private handleBackdropClick(e: MouseEvent) {\n if (e.target !== (e.currentTarget as HTMLDialogElement)) return\n if (this.noBackdropClose) return\n if (this.controlled) {\n this.emitOpenChange(false)\n return\n }\n this._userOpenChange.mark()\n this.close()\n }\n\n // controlled 下派发关闭请求:detail 固定 false(用户只能请求关闭,打开永远由 Consumer 写)。\n private emitOpenChange(open = this.open) {\n this.dispatchEvent(\n new CustomEvent('open-change', {\n detail: { open },\n bubbles: true,\n composed: true\n })\n )\n }\n\n private _onTransitionEnd = (event: TransitionEvent) => {\n this._presence.handleTransitionEnd(event)\n }\n\n private _onNativeClose = () => {\n if (!this.open) return\n\n // controlled 下原生关闭(如表单 method=\"dialog\")视为未经 Consumer 批准的状态丢失:\n // 恢复受控状态并派发关闭请求,由 Consumer 决定是否关闭。\n if (this.controlled) {\n this._presence.sync(true)\n this.emitOpenChange(false)\n return\n }\n\n this._presence.handleNativeClose()\n this._userOpenChange.mark()\n this.open = false\n }\n\n private _syncScrollLock(isOpen = this.open) {\n this._scrollLock.sync(isOpen && !this.noScrollLock)\n }\n\n private _onBodySlotChange(e: Event) {\n if (!(e.target instanceof HTMLSlotElement)) return\n this._hasBody = e.target.assignedElements().length > 0\n }\n\n override render() {\n return html`\n <dialog\n @cancel=${this.handleCancel}\n @close=${this._onNativeClose}\n @click=${this.handleBackdropClick}\n @transitionend=${this._onTransitionEnd}\n >\n <div class=\"wui-dialog-body wui-glass\">\n ${\n this._hasBody\n ? html`<slot name=\"body\" @slotchange=${this._onBodySlotChange}></slot>`\n : html`\n <slot name=\"body\" @slotchange=${this._onBodySlotChange} hidden></slot>\n <div class=\"title\"><slot name=\"title\"></slot></div>\n <div class=\"desc\"><slot></slot></div>\n <div class=\"wui-dialog-footer\"><slot name=\"footer\"></slot></div>\n `\n }\n </div>\n </dialog>\n `\n }\n\n declare readonly $events: {\n 'open-change': CustomEvent<{ open: boolean }>\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-dialog': WebUiDialog\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/dialog/index.ts"],"sourcesContent":["import { html, LitElement, type PropertyValues, unsafeCSS } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport '@/components/button'\nimport glass from '@/assets/glass.css?inline'\nimport { UserChangeController } from '@/shared/events/user-change'\nimport { dispatchOpenChangeEvent } from '@/shared/open-state'\nimport { defineNativeDialogPresence } from '@/shared/overlay/native-dialog-presence'\nimport { defineScrollLockLease } from '@/shared/scroll-lock/scroll-lock'\n\nimport style from './style.css?inline'\n\n@customElement('web-ui-dialog')\nexport class WebUiDialog extends LitElement {\n static override styles = [unsafeCSS(glass), unsafeCSS(style)]\n\n @property({ type: Boolean, reflect: true }) open = false\n @property({ type: Boolean, reflect: true, attribute: 'no-scroll-lock' }) noScrollLock = false\n @property({ type: Boolean, reflect: true, attribute: 'no-backdrop-close' }) noBackdropClose = false\n @property({ type: Boolean, reflect: true, attribute: 'no-escape-close' }) noEscapeClose = false\n\n /**\n * Controlled 模式下,Escape 和遮罩点击只派发 `open-change` 请求,\n * 不会自行修改 `open`。Consumer 回写 `open` 后才执行关闭动画。\n * 程序化 API(showModal/close/直接赋值 open)不受此模式影响,始终直通。\n */\n @property({ type: Boolean, reflect: true }) controlled = false\n\n @state() private _hasBody = false\n private readonly _userOpenChange = new UserChangeController()\n\n private readonly _scrollLock = defineScrollLockLease().make()\n private readonly _presence = defineNativeDialogPresence().make({\n getDialog: () => this.dialog,\n isConnected: () => this.isConnected,\n isOpen: () => this.open\n })\n\n private get dialog() {\n return this.shadowRoot?.querySelector('dialog') ?? null\n }\n\n protected override updated(props: PropertyValues) {\n super.updated(props)\n if (!this.isConnected) return\n\n if (props.has('open')) {\n if (this._userOpenChange.consume()) this.emitOpenChange()\n this._presence.sync(this.open)\n }\n if (props.has('open') || props.has('noScrollLock')) this._syncScrollLock()\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback()\n this._presence.dispose()\n this._scrollLock.release()\n }\n\n // 以模态方式打开对话框(命令式)\n showModal() {\n if (this.open) return\n this.open = true\n }\n\n close() {\n this.open = false\n }\n\n private handleCancel(e: Event) {\n // 子控件(例如 file input)可能派发冒泡的 cancel;只让 native dialog 自身的 cancel 关闭。\n if (e.target !== e.currentTarget) return\n\n // 保留 top layer 直到视觉退场完成,避免原生关闭跳过退出动画。\n e.preventDefault()\n if (this.noEscapeClose) return\n if (this.controlled) {\n this.emitOpenChange(false)\n return\n }\n this._userOpenChange.mark()\n this.close()\n }\n\n private handleBackdropClick(e: MouseEvent) {\n if (e.target !== (e.currentTarget as HTMLDialogElement)) return\n if (this.noBackdropClose) return\n if (this.controlled) {\n this.emitOpenChange(false)\n return\n }\n this._userOpenChange.mark()\n this.close()\n }\n\n // controlled 下派发关闭请求:detail 固定 false(用户只能请求关闭,打开永远由 Consumer 写)。\n private emitOpenChange(open = this.open) {\n dispatchOpenChangeEvent(this, open)\n }\n\n private _onTransitionEnd = (event: TransitionEvent) => {\n this._presence.handleTransitionEnd(event)\n }\n\n private _onNativeClose = () => {\n if (!this.open) return\n\n // controlled 下原生关闭(如表单 method=\"dialog\")视为未经 Consumer 批准的状态丢失:\n // 恢复受控状态并派发关闭请求,由 Consumer 决定是否关闭。\n if (this.controlled) {\n this._presence.sync(true)\n this.emitOpenChange(false)\n return\n }\n\n this._presence.handleNativeClose()\n this._userOpenChange.mark()\n this.open = false\n }\n\n private _syncScrollLock(isOpen = this.open) {\n this._scrollLock.sync(isOpen && !this.noScrollLock)\n }\n\n private _onBodySlotChange(e: Event) {\n if (!(e.target instanceof HTMLSlotElement)) return\n this._hasBody = e.target.assignedElements().length > 0\n }\n\n override render() {\n return html`\n <dialog\n @cancel=${this.handleCancel}\n @close=${this._onNativeClose}\n @click=${this.handleBackdropClick}\n @transitionend=${this._onTransitionEnd}\n >\n <div class=\"wui-dialog-body wui-glass\">\n ${\n this._hasBody\n ? html`<slot name=\"body\" @slotchange=${this._onBodySlotChange}></slot>`\n : html`\n <slot name=\"body\" @slotchange=${this._onBodySlotChange} hidden></slot>\n <div class=\"title\"><slot name=\"title\"></slot></div>\n <div class=\"desc\"><slot></slot></div>\n <div class=\"wui-dialog-footer\"><slot name=\"footer\"></slot></div>\n `\n }\n </div>\n </dialog>\n `\n }\n\n declare readonly $events: {\n 'open-change': CustomEvent<{ open: boolean }>\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-dialog': WebUiDialog\n }\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  //#region src/components/dialog/style.css?inline
2
- var e = "*,:before,:after{box-sizing:border-box}:host{display:contents}dialog{transform:scale(var(--wui-scale-enter,.97));box-sizing:border-box;max-width:min(90vw, var(--wui-dialog-max-width,360px));max-height:90vh;color:var(--wui-color-text,#1b1b1b);opacity:0;transition:opacity var(--wui-duration-overlay-enter,.18s) var(--wui-ease-enter,cubic-bezier(.23, 1, .32, 1)), transform var(--wui-duration-overlay-enter,.18s) var(--wui-ease-enter,cubic-bezier(.23, 1, .32, 1));background:0 0;border:none;padding:0;font-family:inherit;overflow:visible}dialog::backdrop{opacity:0;background:var(--wui-dialog-overlay-bg,var(--wui-color-backdrop,#0000001a));transition:opacity var(--wui-duration-overlay-enter,.18s) ease}dialog[open].is-visible{opacity:1;transform:scale(1)}dialog.is-closing{pointer-events:none;transition-duration:var(--wui-duration-overlay-exit,.14s)}dialog.is-closing::backdrop{transition-duration:var(--wui-duration-overlay-exit,.14s)}dialog[open].is-visible::backdrop{opacity:1}@media (prefers-reduced-motion:reduce){dialog,dialog[open].is-visible{transition-duration:var(--wui-duration-feedback,.1s);transform:none}}.wui-dialog-body{--wui-internal-glass-shadow:var(--wui-shadow-overlay,2px 16px 40px #0006);--wui-glass-corner-radius:32px;background-color:var(--wui-dialog-bg,var(--wui-color-surface-overlay,#f6f6f6e0));border-radius:32px;padding:20px 24px 24px}.title{color:var(--wui-color-text,#1b1b1b);margin:0 0 16px;padding:0;font-size:18px;font-weight:600;line-height:1.2}.desc{color:var(--wui-color-text,#1b1b1b);margin:0 0 24px;padding:0;font-size:14px;line-height:1.5}.wui-dialog-footer{flex-direction:column;gap:10px;display:flex}.wui-dialog-footer ::slotted(web-ui-button){width:100%;display:flex}:host([horizontal]) .wui-dialog-footer{flex-direction:row;justify-content:center;gap:12px}:host([horizontal]) .wui-dialog-footer ::slotted(web-ui-button:not([full])){width:auto}:host([horizontal]) .wui-dialog-footer ::slotted(web-ui-button[full]){flex:1;min-width:0}";
2
+ var e = "*,:before,:after{box-sizing:border-box}:host{display:contents}dialog{transform:scale(var(--wui-scale-enter,.97));box-sizing:border-box;max-width:min(90vw, var(--wui-dialog-max-width,360px));max-height:90vh;color:var(--wui-color-text,#1b1b1b);opacity:0;transition:opacity var(--wui-duration-overlay-enter,.18s) var(--wui-ease-enter,cubic-bezier(.23, 1, .32, 1)), transform var(--wui-duration-overlay-enter,.18s) var(--wui-ease-enter,cubic-bezier(.23, 1, .32, 1));background:0 0;border:none;padding:0;font-family:inherit;overflow:visible}dialog::backdrop{opacity:0;background:var(--wui-dialog-overlay-bg,var(--wui-color-backdrop,#0000001f));transition:opacity var(--wui-duration-overlay-enter,.18s) ease}dialog[open].is-visible{opacity:1;transform:scale(1)}dialog.is-closing{pointer-events:none;transition-duration:var(--wui-duration-overlay-exit,.14s)}dialog.is-closing::backdrop{transition-duration:var(--wui-duration-overlay-exit,.14s)}dialog[open].is-visible::backdrop{opacity:1}@media (prefers-reduced-motion:reduce){dialog,dialog[open].is-visible{transition-duration:var(--wui-duration-feedback,.1s);transform:none}}.wui-dialog-body{--wui-internal-glass-shadow:var(--wui-shadow-overlay,2px 16px 40px #0006);--wui-glass-corner-radius:var(--wui-radius-overlay,28px);border-radius:var(--wui-radius-overlay,28px);background-color:var(--wui-dialog-bg,var(--wui-color-surface-overlay,#f6f6f6d1));padding:20px 24px 24px}.title{color:var(--wui-color-text,#1b1b1b);margin:0 0 16px;padding:0;font-size:18px;font-weight:600;line-height:1.2}.desc{color:var(--wui-color-text,#1b1b1b);margin:0 0 24px;padding:0;font-size:14px;line-height:1.5}.wui-dialog-footer{flex-direction:column;gap:10px;display:flex}.wui-dialog-footer ::slotted(web-ui-button){width:100%;display:flex}:host([horizontal]) .wui-dialog-footer{flex-direction:row;justify-content:center;gap:12px}:host([horizontal]) .wui-dialog-footer ::slotted(web-ui-button:not([full])){width:auto}:host([horizontal]) .wui-dialog-footer ::slotted(web-ui-button[full]){flex:1;min-width:0}";
3
3
  //#endregion
4
4
  export { e as default };
5
5
 
@@ -1 +1 @@
1
- {"version":3,"file":"style.js","names":[],"sources":["../../../src/components/dialog/style.css?inline"],"sourcesContent":["*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n:host {\n display: contents;\n}\n\ndialog {\n transform: scale(var(--wui-scale-enter, 0.97));\n\n overflow: visible;\n\n box-sizing: border-box;\n max-width: min(90vw, var(--wui-dialog-max-width, 360px));\n max-height: 90vh;\n padding: 0;\n border: none;\n\n font-family: inherit;\n color: var(--wui-color-text, #1b1b1b);\n\n opacity: 0;\n background: transparent;\n\n transition:\n opacity var(--wui-duration-overlay-enter, 180ms) var(--wui-ease-enter, cubic-bezier(0.23, 1, 0.32, 1)),\n transform var(--wui-duration-overlay-enter, 180ms) var(--wui-ease-enter, cubic-bezier(0.23, 1, 0.32, 1));\n\n &::backdrop {\n opacity: 0;\n background: var(--wui-dialog-overlay-bg, var(--wui-color-backdrop, rgb(0 0 0 / 0.1)));\n transition: opacity var(--wui-duration-overlay-enter, 180ms) ease;\n }\n}\n\ndialog[open].is-visible {\n transform: scale(1);\n opacity: 1;\n}\n\ndialog.is-closing {\n pointer-events: none;\n transition-duration: var(--wui-duration-overlay-exit, 140ms);\n}\n\ndialog.is-closing::backdrop {\n transition-duration: var(--wui-duration-overlay-exit, 140ms);\n}\n\ndialog[open].is-visible::backdrop {\n opacity: 1;\n}\n\n@media (prefers-reduced-motion: reduce) {\n dialog,\n dialog[open].is-visible {\n transform: none;\n transition-duration: var(--wui-duration-feedback, 100ms);\n }\n}\n\n/* ===== 玻璃卡片主体(样式由 glass.css 提供) ===== */\n\n.wui-dialog-body {\n --wui-internal-glass-shadow: var(--wui-shadow-overlay, 2px 16px 40px rgb(0 0 0 / 0.4));\n --wui-glass-corner-radius: 32px;\n padding: 20px 24px 24px;\n border-radius: 32px;\n background-color: var(--wui-dialog-bg, var(--wui-color-surface-overlay, rgb(246 246 246 / 0.88)));\n}\n\n/* ===== 标题 ===== */\n\n.title {\n margin: 0 0 16px;\n padding: 0;\n\n font-size: 18px;\n font-weight: 600;\n line-height: 1.2;\n color: var(--wui-color-text, #1b1b1b);\n}\n\n/* ===== 描述 ===== */\n\n.desc {\n margin: 0 0 24px;\n padding: 0;\n\n font-size: 14px;\n line-height: 1.5;\n color: var(--wui-color-text, #1b1b1b);\n}\n\n/* ===== 按钮区域 ===== */\n\n.wui-dialog-footer {\n display: flex;\n flex-direction: column;\n gap: 10px;\n\n /* ===== 按钮全宽(默认垂直排列) ===== */\n\n & ::slotted(web-ui-button) {\n display: flex;\n width: 100%;\n }\n}\n\n/* ===== horizontal 模式 ===== */\n\n:host([horizontal]) .wui-dialog-footer {\n flex-direction: row;\n gap: 12px;\n justify-content: center;\n\n /* 非 full 按钮:居中,不拉伸 */\n\n & ::slotted(web-ui-button:not([full])) {\n width: auto;\n }\n\n /* full 按钮:平分剩余空间 */\n\n & ::slotted(web-ui-button[full]) {\n flex: 1;\n min-width: 0;\n }\n}\n"],"mappings":""}
1
+ {"version":3,"file":"style.js","names":[],"sources":["../../../src/components/dialog/style.css?inline"],"sourcesContent":["*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n:host {\n display: contents;\n}\n\ndialog {\n transform: scale(var(--wui-scale-enter, 0.97));\n\n overflow: visible;\n\n box-sizing: border-box;\n max-width: min(90vw, var(--wui-dialog-max-width, 360px));\n max-height: 90vh;\n padding: 0;\n border: none;\n\n font-family: inherit;\n color: var(--wui-color-text, #1b1b1b);\n\n opacity: 0;\n background: transparent;\n\n transition:\n opacity var(--wui-duration-overlay-enter, 180ms) var(--wui-ease-enter, cubic-bezier(0.23, 1, 0.32, 1)),\n transform var(--wui-duration-overlay-enter, 180ms) var(--wui-ease-enter, cubic-bezier(0.23, 1, 0.32, 1));\n\n &::backdrop {\n opacity: 0;\n background: var(--wui-dialog-overlay-bg, var(--wui-color-backdrop, rgb(0 0 0 / 0.12)));\n transition: opacity var(--wui-duration-overlay-enter, 180ms) ease;\n }\n}\n\ndialog[open].is-visible {\n transform: scale(1);\n opacity: 1;\n}\n\ndialog.is-closing {\n pointer-events: none;\n transition-duration: var(--wui-duration-overlay-exit, 140ms);\n}\n\ndialog.is-closing::backdrop {\n transition-duration: var(--wui-duration-overlay-exit, 140ms);\n}\n\ndialog[open].is-visible::backdrop {\n opacity: 1;\n}\n\n@media (prefers-reduced-motion: reduce) {\n dialog,\n dialog[open].is-visible {\n transform: none;\n transition-duration: var(--wui-duration-feedback, 100ms);\n }\n}\n\n/* ===== 玻璃卡片主体(样式由 glass.css 提供) ===== */\n\n.wui-dialog-body {\n --wui-internal-glass-shadow: var(--wui-shadow-overlay, 2px 16px 40px rgb(0 0 0 / 0.4));\n --wui-glass-corner-radius: var(--wui-radius-overlay, 28px);\n padding: 20px 24px 24px;\n border-radius: var(--wui-radius-overlay, 28px);\n background-color: var(--wui-dialog-bg, var(--wui-color-surface-overlay, rgb(246 246 246 / 0.82)));\n}\n\n/* ===== 标题 ===== */\n\n.title {\n margin: 0 0 16px;\n padding: 0;\n\n font-size: 18px;\n font-weight: 600;\n line-height: 1.2;\n color: var(--wui-color-text, #1b1b1b);\n}\n\n/* ===== 描述 ===== */\n\n.desc {\n margin: 0 0 24px;\n padding: 0;\n\n font-size: 14px;\n line-height: 1.5;\n color: var(--wui-color-text, #1b1b1b);\n}\n\n/* ===== 按钮区域 ===== */\n\n.wui-dialog-footer {\n display: flex;\n flex-direction: column;\n gap: 10px;\n\n /* ===== 按钮全宽(默认垂直排列) ===== */\n\n & ::slotted(web-ui-button) {\n display: flex;\n width: 100%;\n }\n}\n\n/* ===== horizontal 模式 ===== */\n\n:host([horizontal]) .wui-dialog-footer {\n flex-direction: row;\n gap: 12px;\n justify-content: center;\n\n /* 非 full 按钮:居中,不拉伸 */\n\n & ::slotted(web-ui-button:not([full])) {\n width: auto;\n }\n\n /* full 按钮:平分剩余空间 */\n\n & ::slotted(web-ui-button[full]) {\n flex: 1;\n min-width: 0;\n }\n}\n"],"mappings":""}
@@ -3,24 +3,25 @@ import "../icon/index.js";
3
3
  import t from "../../icons/generated/ooui-close.js";
4
4
  import n from "../../assets/glass.js";
5
5
  import { normalizeLiteral as r } from "../../shared/normalize/index.js";
6
- import { findNearestTheme as i } from "../../shared/theme/theme-scope.js";
7
- import { defineScrollLockLease as a } from "../../shared/scroll-lock/scroll-lock.js";
6
+ import { dispatchOpenChangeEvent as i } from "../../shared/open-state/index.js";
7
+ import { findNearestTheme as a } from "../../shared/theme/theme-scope.js";
8
+ import { defineScrollLockLease as o } from "../../shared/scroll-lock/scroll-lock.js";
8
9
  import "../button/index.js";
9
- import { UserChangeController as o } from "../../shared/events/user-change.js";
10
- import { defineNativeDialogPresence as s } from "../../shared/overlay/native-dialog-presence.js";
11
- import { attachDragGesture as c } from "../../shared/gesture/drag-gesture.js";
12
- import { SPRING_PRESETS as l, rubberband as u, springOffsets as d } from "../../shared/gesture/physics.js";
13
- import { defineNestedDrawerLayers as f } from "../../shared/overlay/nested-drawer-layers.js";
14
- import p from "./style.js";
15
- import { LitElement as m, html as h, nothing as g, unsafeCSS as _ } from "lit";
16
- import { customElement as v, property as y } from "lit/decorators.js";
10
+ import { UserChangeController as s } from "../../shared/events/user-change.js";
11
+ import { defineNativeDialogPresence as c } from "../../shared/overlay/native-dialog-presence.js";
12
+ import { attachDragGesture as l } from "../../shared/gesture/drag-gesture.js";
13
+ import { SPRING_PRESETS as u, rubberband as d, springOffsets as f } from "../../shared/gesture/physics.js";
14
+ import { defineNestedDrawerLayers as p } from "../../shared/overlay/nested-drawer-layers.js";
15
+ import m from "./style.js";
16
+ import { LitElement as h, html as g, nothing as _, unsafeCSS as v } from "lit";
17
+ import { customElement as y, property as b } from "lit/decorators.js";
17
18
  //#region src/components/drawer/index.ts
18
- var b = [
19
+ var x = [
19
20
  "right",
20
21
  "left",
21
22
  "top",
22
23
  "bottom"
23
- ], x = 1 / 3, S = 500, C = 120, w = 16;
24
+ ], S = 1 / 3, C = 500, w = 120, T = 16;
24
25
  if (typeof CSS < "u" && "registerProperty" in CSS) {
25
26
  try {
26
27
  CSS.registerProperty({
@@ -39,13 +40,13 @@ if (typeof CSS < "u" && "registerProperty" in CSS) {
39
40
  });
40
41
  } catch {}
41
42
  }
42
- var T = class extends m {
43
+ var E = class extends h {
43
44
  constructor(...e) {
44
- super(...e), this.open = !1, this.noScrollLock = !1, this.noBackdropClose = !1, this.controlled = !1, this._placement = "right", this.heading = "", this.closable = !1, this.headless = !1, this.dialogLabel = "", this.draggable = !1, this._hasHeaderSlot = !1, this._hasFooterSlot = !1, this._userOpenChange = new o(), this._scrollLock = a().make(), this._presence = s().make({
45
+ super(...e), this.open = !1, this.noScrollLock = !1, this.noBackdropClose = !1, this.controlled = !1, this._placement = "right", this.heading = "", this.closable = !1, this.headless = !1, this.dialogLabel = "", this.draggable = !1, this._hasHeaderSlot = !1, this._hasFooterSlot = !1, this._userOpenChange = new s(), this._scrollLock = o().make(), this._presence = c().make({
45
46
  getDialog: () => this.dialog,
46
47
  isConnected: () => this.isConnected,
47
48
  isOpen: () => this.open
48
- }), this._nestedLayers = f().make({
49
+ }), this._nestedLayers = p().make({
49
50
  getDialog: () => this.dialog,
50
51
  getPlacement: () => this._placement
51
52
  }), this._dragGesture = null, this._dragInitialOffset = 0, this._dragOffset = 0, this._dragAnimation = null, this._dragAwaitWriteback = !1, this._handleDragWritebackTimeout = () => {
@@ -67,14 +68,14 @@ var T = class extends m {
67
68
  };
68
69
  }
69
70
  static {
70
- this.styles = [_(n), _(p)];
71
+ this.styles = [v(n), v(m)];
71
72
  }
72
73
  get placement() {
73
74
  return this._placement;
74
75
  }
75
76
  set placement(e) {
76
77
  let t = this._placement;
77
- this._placement = r(e, b, "right"), this.requestUpdate("placement", t);
78
+ this._placement = r(e, x, "right"), this.requestUpdate("placement", t);
78
79
  }
79
80
  get dialog() {
80
81
  return this.shadowRoot?.querySelector("dialog") ?? null;
@@ -102,7 +103,7 @@ var T = class extends m {
102
103
  return this._measureDragSize() + this._readDrawerInset(e);
103
104
  }
104
105
  _isReducedMotion() {
105
- let e = i(this);
106
+ let e = a(this);
106
107
  if (e) return e.isReducedMotion();
107
108
  try {
108
109
  return typeof window.matchMedia == "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
@@ -121,18 +122,18 @@ var T = class extends m {
121
122
  let t = this.dialog;
122
123
  if (!t || !t.classList.contains("is-visible")) return;
123
124
  let n = getComputedStyle(t).transform, r = n && n !== "none" && typeof DOMMatrixReadOnly == "function" ? new DOMMatrixReadOnly(n)[this._dragAxis === "x" ? "m41" : "m42"] : 0;
124
- this._dragAnimation?.cancel(), this._dragAnimation = null, this._cancelDragAwait(), this._dragInitialOffset = r * this._dragCloseSign, this._dragOffset = this._dragInitialOffset, t.classList.add("is-dragging"), this._dragGesture = c(e, {
125
+ this._dragAnimation?.cancel(), this._dragAnimation = null, this._cancelDragAwait(), this._dragInitialOffset = r * this._dragCloseSign, this._dragOffset = this._dragInitialOffset, t.classList.add("is-dragging"), this._dragGesture = l(e, {
125
126
  axis: this._dragAxis,
126
127
  onMove: (e) => {
127
128
  let n = this._dragAxis === "x" ? e.deltaX : e.deltaY, r = this._dragInitialOffset + n * this._dragCloseSign;
128
- this._dragOffset = u(r, this._measureDragSize() * .1, .15);
129
+ this._dragOffset = d(r, this._measureDragSize() * .1, .15);
129
130
  let i = this._measureDragSize();
130
- t.classList.toggle("is-drag-close", i > 0 && this._dragOffset > i * x), this._applyDragOffset(t, this._dragOffset);
131
+ t.classList.toggle("is-drag-close", i > 0 && this._dragOffset > i * S), this._applyDragOffset(t, this._dragOffset);
131
132
  },
132
133
  onEnd: (e) => {
133
134
  this._dragGesture = null, t.classList.remove("is-dragging", "is-drag-close");
134
135
  let n = this._measureDragSize(), r = (this._dragAxis === "x" ? e.velocityX : e.velocityY) * this._dragCloseSign;
135
- this._dragOffset > n * x || this._dragOffset > 8 && r > S ? this._springToClose(t, r) : this._springRebound(t, r, this._dragOffset);
136
+ this._dragOffset > n * S || this._dragOffset > 8 && r > C ? this._springToClose(t, r) : this._springRebound(t, r, this._dragOffset);
136
137
  },
137
138
  onCancel: () => {
138
139
  this._dragGesture = null, t.classList.remove("is-dragging", "is-drag-close"), this._springRebound(t, 0, this._dragOffset);
@@ -168,16 +169,16 @@ var T = class extends m {
168
169
  o();
169
170
  return;
170
171
  }
171
- let s = d(n, i, t, l.close), c = s.map((e) => ({
172
+ let s = f(n, i, t, u.close), c = s.map((e) => ({
172
173
  transform: this._dragAxis === "x" ? `translateX(${e * r}px)` : `translateY(${e * r}px)`,
173
174
  "--wui-internal-drag-backdrop-opacity": String(Math.max(0, 1 - e / a))
174
- })), u = (s.length - 1) * w, f = e.animate(c, {
175
- duration: u,
175
+ })), l = (s.length - 1) * T, d = e.animate(c, {
176
+ duration: l,
176
177
  easing: "linear",
177
178
  fill: "both"
178
179
  });
179
- f.onfinish = () => {
180
- f.cancel(), o();
180
+ d.onfinish = () => {
181
+ d.cancel(), o();
181
182
  };
182
183
  }
183
184
  _springRebound(e, t, n) {
@@ -189,22 +190,22 @@ var T = class extends m {
189
190
  r();
190
191
  return;
191
192
  }
192
- let i = this._dragCloseSign, a = this._measureDragSize(), o = d(n, 0, t, l.rebound), s = o.map((e) => ({
193
+ let i = this._dragCloseSign, a = this._measureDragSize(), o = f(n, 0, t, u.rebound), s = o.map((e) => ({
193
194
  transform: this._dragAxis === "x" ? `translateX(${e * i}px)` : `translateY(${e * i}px)`,
194
195
  "--wui-internal-drag-backdrop-opacity": String(Math.max(0, 1 - e / a))
195
196
  }));
196
197
  e.classList.add("is-dragging");
197
- let c = (o.length - 1) * w, u = e.animate(s, {
198
+ let c = (o.length - 1) * T, l = e.animate(s, {
198
199
  duration: c,
199
200
  easing: "linear",
200
201
  fill: "both"
201
202
  });
202
- u.onfinish = () => {
203
- u.cancel(), r();
204
- }, this._dragAnimation = u;
203
+ l.onfinish = () => {
204
+ l.cancel(), r();
205
+ }, this._dragAnimation = l;
205
206
  }
206
207
  _closeFromDrag() {
207
- this._closeFromUser(), this.controlled && (this._dragAwaitWriteback = !0, this._dragRequestTimer = setTimeout(this._handleDragWritebackTimeout, C));
208
+ this._closeFromUser(), this.controlled && (this._dragAwaitWriteback = !0, this._dragRequestTimer = setTimeout(this._handleDragWritebackTimeout, w));
208
209
  }
209
210
  connectedCallback() {
210
211
  super.connectedCallback(), this._hasHeaderSlot = Array.from(this.children).some((e) => e.getAttribute?.("slot") === "header"), this._hasFooterSlot = Array.from(this.children).some((e) => e.getAttribute?.("slot") === "footer");
@@ -282,24 +283,20 @@ var T = class extends m {
282
283
  e.target === e.currentTarget && (this.noBackdropClose || this._isDragging() || this._closeFromUser());
283
284
  }
284
285
  emitOpenChange(e = this.open) {
285
- this.dispatchEvent(new CustomEvent("open-change", {
286
- detail: { open: e },
287
- bubbles: !0,
288
- composed: !0
289
- }));
286
+ i(this, e);
290
287
  }
291
288
  _syncScrollLock(e = this.open) {
292
289
  this._scrollLock.sync(e && !this.noScrollLock);
293
290
  }
294
291
  render() {
295
- let e = this._hasHeaderSlot || !!this.heading, n = this.dialogLabel.trim(), r = !n && !this.headless && e ? "wui-drawer-heading" : g, i = this.draggable ? h`
292
+ let e = this._hasHeaderSlot || !!this.heading, n = this.dialogLabel.trim(), r = !n && !this.headless && e ? "wui-drawer-heading" : _, i = this.draggable ? g`
296
293
  <div class="wui-drawer-drag-zone" @pointerdown=${this._handleDragPointerDown}>
297
294
  <div class="wui-drawer-drag-bar"></div>
298
295
  </div>
299
- ` : g;
300
- return h`
296
+ ` : _;
297
+ return g`
301
298
  <dialog
302
- aria-label=${n || g}
299
+ aria-label=${n || _}
303
300
  aria-labelledby=${r}
304
301
  @cancel=${this.handleCancel}
305
302
  @close=${this.handleNativeClose}
@@ -307,11 +304,11 @@ var T = class extends m {
307
304
  @keydown=${this.handleKeydown}
308
305
  @transitionend=${this.handleTransitionEnd}
309
306
  >
310
- ${this.headless ? h`<slot></slot>${i}` : h`
307
+ ${this.headless ? g`<slot></slot>${i}` : g`
311
308
  <div class="wui-drawer-body wui-glass">
312
309
  <div class="wui-drawer-header" id="wui-drawer-heading" ?hidden=${!e}>
313
310
  <slot name="header" @slotchange=${this.handleHeaderSlotChange}>
314
- ${this.heading ? h`<span class="wui-drawer-heading">${this.heading}</span>` : g}
311
+ ${this.heading ? g`<span class="wui-drawer-heading">${this.heading}</span>` : _}
315
312
  </slot>
316
313
  </div>
317
314
  <div class="wui-drawer-content">
@@ -321,7 +318,7 @@ var T = class extends m {
321
318
  <slot name="footer" @slotchange=${this.handleFooterSlotChange}></slot>
322
319
  </div>
323
320
  </div>
324
- ${this.closable ? h`
321
+ ${this.closable ? g`
325
322
  <web-ui-button
326
323
  class="wui-drawer-close"
327
324
  @click=${this._closeFromUser}
@@ -332,44 +329,44 @@ var T = class extends m {
332
329
  >
333
330
  <web-ui-icon size="14" .icon=${t}></web-ui-icon>
334
331
  </web-ui-button>
335
- ` : g}
332
+ ` : _}
336
333
  ${i}
337
334
  `}
338
335
  </dialog>
339
336
  `;
340
337
  }
341
338
  };
342
- e([y({
339
+ e([b({
343
340
  type: Boolean,
344
341
  reflect: !0
345
- })], T.prototype, "open", void 0), e([y({
342
+ })], E.prototype, "open", void 0), e([b({
346
343
  type: Boolean,
347
344
  reflect: !0,
348
345
  attribute: "no-scroll-lock"
349
- })], T.prototype, "noScrollLock", void 0), e([y({
346
+ })], E.prototype, "noScrollLock", void 0), e([b({
350
347
  type: Boolean,
351
348
  reflect: !0,
352
349
  attribute: "no-backdrop-close"
353
- })], T.prototype, "noBackdropClose", void 0), e([y({
350
+ })], E.prototype, "noBackdropClose", void 0), e([b({
354
351
  type: Boolean,
355
352
  reflect: !0
356
- })], T.prototype, "controlled", void 0), e([y({
353
+ })], E.prototype, "controlled", void 0), e([b({
357
354
  type: String,
358
355
  reflect: !0
359
- })], T.prototype, "placement", null), e([y({ type: String })], T.prototype, "heading", void 0), e([y({
356
+ })], E.prototype, "placement", null), e([b({ type: String })], E.prototype, "heading", void 0), e([b({
360
357
  type: Boolean,
361
358
  reflect: !0
362
- })], T.prototype, "closable", void 0), e([y({
359
+ })], E.prototype, "closable", void 0), e([b({
363
360
  type: Boolean,
364
361
  reflect: !0
365
- })], T.prototype, "headless", void 0), e([y({
362
+ })], E.prototype, "headless", void 0), e([b({
366
363
  type: String,
367
364
  attribute: "dialog-label"
368
- })], T.prototype, "dialogLabel", void 0), e([y({
365
+ })], E.prototype, "dialogLabel", void 0), e([b({
369
366
  type: Boolean,
370
367
  reflect: !0
371
- })], T.prototype, "draggable", void 0), T = e([v("web-ui-drawer")], T);
368
+ })], E.prototype, "draggable", void 0), E = e([y("web-ui-drawer")], E);
372
369
  //#endregion
373
- export { T as WebUiDrawer };
370
+ export { E as WebUiDrawer };
374
371
 
375
372
  //# sourceMappingURL=index.js.map