@greypan/web-ui 3.0.0 → 4.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.
Files changed (220) hide show
  1. package/README.CN.md +297 -34
  2. package/README.md +304 -39
  3. package/dist/_virtual/{_@oxc-project_runtime@0.140.0 → _@oxc-project_runtime@0.146.0}/helpers/esm/decorate.js +1 -1
  4. package/dist/assets/glass.js +1 -1
  5. package/dist/assets/glass.js.map +1 -1
  6. package/dist/assets/menu-portal.js +1 -1
  7. package/dist/assets/menu-portal.js.map +1 -1
  8. package/dist/assets/overlay-motion.js +1 -1
  9. package/dist/assets/overlay-motion.js.map +1 -1
  10. package/dist/assets/toast-containers.js +1 -1
  11. package/dist/assets/toast-containers.js.map +1 -1
  12. package/dist/components/autocomplete/index.d.ts +4 -12
  13. package/dist/components/autocomplete/index.js +95 -102
  14. package/dist/components/autocomplete/index.js.map +1 -1
  15. package/dist/components/autocomplete/style.js +1 -1
  16. package/dist/components/autocomplete/style.js.map +1 -1
  17. package/dist/components/avatar/index.d.ts +0 -1
  18. package/dist/components/avatar/index.js +8 -13
  19. package/dist/components/avatar/index.js.map +1 -1
  20. package/dist/components/back-top/index.js +1 -1
  21. package/dist/components/back-top/style.js +1 -1
  22. package/dist/components/back-top/style.js.map +1 -1
  23. package/dist/components/badge/index.d.ts +1 -0
  24. package/dist/components/badge/index.js +12 -14
  25. package/dist/components/badge/index.js.map +1 -1
  26. package/dist/components/button/index.d.ts +2 -0
  27. package/dist/components/button/index.js +51 -38
  28. package/dist/components/button/index.js.map +1 -1
  29. package/dist/components/button/style.js +1 -1
  30. package/dist/components/button/style.js.map +1 -1
  31. package/dist/components/button-group/index.d.ts +1 -2
  32. package/dist/components/button-group/index.js +19 -23
  33. package/dist/components/button-group/index.js.map +1 -1
  34. package/dist/components/checkbox/index.d.ts +4 -7
  35. package/dist/components/checkbox/index.js +48 -37
  36. package/dist/components/checkbox/index.js.map +1 -1
  37. package/dist/components/checkbox/style.js +1 -1
  38. package/dist/components/checkbox/style.js.map +1 -1
  39. package/dist/components/checkbox-group/index.d.ts +4 -10
  40. package/dist/components/checkbox-group/index.js +74 -60
  41. package/dist/components/checkbox-group/index.js.map +1 -1
  42. package/dist/components/context-menu/index.js +7 -8
  43. package/dist/components/context-menu/index.js.map +1 -1
  44. package/dist/components/dialog/index.d.ts +1 -0
  45. package/dist/components/dialog/index.js +13 -9
  46. package/dist/components/dialog/index.js.map +1 -1
  47. package/dist/components/dialog/style.js +1 -1
  48. package/dist/components/dialog/style.js.map +1 -1
  49. package/dist/components/drawer/index.d.ts +15 -0
  50. package/dist/components/drawer/index.js +58 -34
  51. package/dist/components/drawer/index.js.map +1 -1
  52. package/dist/components/drawer/style.js +1 -1
  53. package/dist/components/drawer/style.js.map +1 -1
  54. package/dist/components/dropdown/index.js +1 -1
  55. package/dist/components/dropdown-divider/index.js +1 -1
  56. package/dist/components/dropdown-header/index.js +1 -1
  57. package/dist/components/dropdown-header/style.js +1 -1
  58. package/dist/components/dropdown-header/style.js.map +1 -1
  59. package/dist/components/dropdown-item/index.js +3 -2
  60. package/dist/components/dropdown-item/index.js.map +1 -1
  61. package/dist/components/empty/index.js +1 -1
  62. package/dist/components/empty/style.js +1 -1
  63. package/dist/components/empty/style.js.map +1 -1
  64. package/dist/components/icon/index.js +1 -1
  65. package/dist/components/index.d.ts +1 -1
  66. package/dist/components/index.js +9 -9
  67. package/dist/components/input/index.d.ts +2 -3
  68. package/dist/components/input/index.js +62 -52
  69. package/dist/components/input/index.js.map +1 -1
  70. package/dist/components/input/style.js +1 -1
  71. package/dist/components/input/style.js.map +1 -1
  72. package/dist/components/input-number/index.d.ts +2 -3
  73. package/dist/components/input-number/index.js +58 -38
  74. package/dist/components/input-number/index.js.map +1 -1
  75. package/dist/components/input-number/style.js +1 -1
  76. package/dist/components/input-number/style.js.map +1 -1
  77. package/dist/components/layout/index.d.ts +53 -3
  78. package/dist/components/layout/index.js +146 -39
  79. package/dist/components/layout/index.js.map +1 -1
  80. package/dist/components/layout/style.js +1 -1
  81. package/dist/components/layout/style.js.map +1 -1
  82. package/dist/components/option/index.js +1 -1
  83. package/dist/components/option/style.js +1 -1
  84. package/dist/components/option/style.js.map +1 -1
  85. package/dist/components/popover/index.js +7 -5
  86. package/dist/components/popover/index.js.map +1 -1
  87. package/dist/components/popover/style.js +1 -1
  88. package/dist/components/popover/style.js.map +1 -1
  89. package/dist/components/radio/index.d.ts +4 -7
  90. package/dist/components/radio/index.js +48 -37
  91. package/dist/components/radio/index.js.map +1 -1
  92. package/dist/components/radio/style.js +1 -1
  93. package/dist/components/radio/style.js.map +1 -1
  94. package/dist/components/radio-group/index.d.ts +4 -10
  95. package/dist/components/radio-group/index.js +70 -52
  96. package/dist/components/radio-group/index.js.map +1 -1
  97. package/dist/components/segmented/index.d.ts +6 -11
  98. package/dist/components/segmented/index.js +67 -55
  99. package/dist/components/segmented/index.js.map +1 -1
  100. package/dist/components/segmented/style.js +1 -1
  101. package/dist/components/segmented/style.js.map +1 -1
  102. package/dist/components/segmented-trigger/index.d.ts +1 -2
  103. package/dist/components/segmented-trigger/index.js +18 -19
  104. package/dist/components/segmented-trigger/index.js.map +1 -1
  105. package/dist/components/segmented-trigger/style.js +1 -1
  106. package/dist/components/segmented-trigger/style.js.map +1 -1
  107. package/dist/components/select/index.d.ts +13 -8
  108. package/dist/components/select/index.js +110 -75
  109. package/dist/components/select/index.js.map +1 -1
  110. package/dist/components/select/style.js +1 -1
  111. package/dist/components/select/style.js.map +1 -1
  112. package/dist/components/slider/index.d.ts +3 -5
  113. package/dist/components/slider/index.js +59 -42
  114. package/dist/components/slider/index.js.map +1 -1
  115. package/dist/components/slider/style.js +1 -1
  116. package/dist/components/slider/style.js.map +1 -1
  117. package/dist/components/spinner/index.d.ts +0 -2
  118. package/dist/components/spinner/index.js +14 -13
  119. package/dist/components/spinner/index.js.map +1 -1
  120. package/dist/components/spinner/style.js +1 -1
  121. package/dist/components/spinner/style.js.map +1 -1
  122. package/dist/components/svg-draw-lines/index.js +1 -1
  123. package/dist/components/switch/index.d.ts +5 -6
  124. package/dist/components/switch/index.js +51 -37
  125. package/dist/components/switch/index.js.map +1 -1
  126. package/dist/components/switch/style.js +1 -1
  127. package/dist/components/switch/style.js.map +1 -1
  128. package/dist/components/textarea/index.d.ts +5 -6
  129. package/dist/components/textarea/index.js +73 -63
  130. package/dist/components/textarea/index.js.map +1 -1
  131. package/dist/components/textarea/style.js +1 -1
  132. package/dist/components/textarea/style.js.map +1 -1
  133. package/dist/components/theme/index.js +1 -1
  134. package/dist/components/theme/style.js +1 -1
  135. package/dist/components/theme/style.js.map +1 -1
  136. package/dist/components/toast/style.js +1 -1
  137. package/dist/components/toast/style.js.map +1 -1
  138. package/dist/components/toast/toast.js +12 -12
  139. package/dist/components/toast/toast.js.map +1 -1
  140. package/dist/components/tooltip/index.js +1 -1
  141. package/dist/components/tooltip/style.js +1 -1
  142. package/dist/components/tooltip/style.js.map +1 -1
  143. package/dist/icons/generated/lucide-clapperboard.d.ts +6 -0
  144. package/dist/icons/generated/lucide-clapperboard.js +10 -0
  145. package/dist/icons/generated/lucide-clapperboard.js.map +1 -0
  146. package/dist/icons/generated/lucide-external-link.d.ts +6 -0
  147. package/dist/icons/generated/lucide-external-link.js +10 -0
  148. package/dist/icons/generated/lucide-external-link.js.map +1 -0
  149. package/dist/icons/generated/lucide-eye.d.ts +6 -0
  150. package/dist/icons/generated/lucide-eye.js +10 -0
  151. package/dist/icons/generated/lucide-eye.js.map +1 -0
  152. package/dist/icons/generated/lucide-file-text.d.ts +6 -0
  153. package/dist/icons/generated/lucide-file-text.js +10 -0
  154. package/dist/icons/generated/lucide-file-text.js.map +1 -0
  155. package/dist/icons/generated/lucide-film.d.ts +6 -0
  156. package/dist/icons/generated/lucide-film.js +10 -0
  157. package/dist/icons/generated/lucide-film.js.map +1 -0
  158. package/dist/icons/generated/lucide-folder-open.d.ts +6 -0
  159. package/dist/icons/generated/lucide-folder-open.js +10 -0
  160. package/dist/icons/generated/lucide-folder-open.js.map +1 -0
  161. package/dist/icons/generated/lucide-headphones.d.ts +6 -0
  162. package/dist/icons/generated/lucide-headphones.js +10 -0
  163. package/dist/icons/generated/lucide-headphones.js.map +1 -0
  164. package/dist/icons/generated/lucide-layout-grid.d.ts +6 -0
  165. package/dist/icons/generated/lucide-layout-grid.js +10 -0
  166. package/dist/icons/generated/lucide-layout-grid.js.map +1 -0
  167. package/dist/icons/generated/lucide-link.d.ts +6 -0
  168. package/dist/icons/generated/lucide-link.js +10 -0
  169. package/dist/icons/generated/lucide-link.js.map +1 -0
  170. package/dist/icons/generated/lucide-music.d.ts +6 -0
  171. package/dist/icons/generated/lucide-music.js +10 -0
  172. package/dist/icons/generated/lucide-music.js.map +1 -0
  173. package/dist/icons/generated/lucide-package.d.ts +6 -0
  174. package/dist/icons/generated/lucide-package.js +10 -0
  175. package/dist/icons/generated/lucide-package.js.map +1 -0
  176. package/dist/icons/generated/lucide-pen-line.d.ts +6 -0
  177. package/dist/icons/generated/lucide-pen-line.js +10 -0
  178. package/dist/icons/generated/lucide-pen-line.js.map +1 -0
  179. package/dist/icons/generated/lucide-pencil.d.ts +6 -0
  180. package/dist/icons/generated/lucide-pencil.js +10 -0
  181. package/dist/icons/generated/lucide-pencil.js.map +1 -0
  182. package/dist/icons/generated/lucide-play.d.ts +6 -0
  183. package/dist/icons/generated/lucide-play.js +10 -0
  184. package/dist/icons/generated/lucide-play.js.map +1 -0
  185. package/dist/icons/generated/lucide-refresh-cw.d.ts +6 -0
  186. package/dist/icons/generated/lucide-refresh-cw.js +10 -0
  187. package/dist/icons/generated/lucide-refresh-cw.js.map +1 -0
  188. package/dist/icons/generated/lucide-sliders-horizontal.d.ts +6 -0
  189. package/dist/icons/generated/lucide-sliders-horizontal.js +10 -0
  190. package/dist/icons/generated/lucide-sliders-horizontal.js.map +1 -0
  191. package/dist/icons/generated/lucide-tags.d.ts +6 -0
  192. package/dist/icons/generated/lucide-tags.js +10 -0
  193. package/dist/icons/generated/lucide-tags.js.map +1 -0
  194. package/dist/icons/generated/lucide-wrench.d.ts +6 -0
  195. package/dist/icons/generated/lucide-wrench.js +10 -0
  196. package/dist/icons/generated/lucide-wrench.js.map +1 -0
  197. package/dist/icons/index.d.ts +18 -0
  198. package/dist/icons/index.js +19 -1
  199. package/dist/node_modules/.pnpm/@floating-ui_core@1.8.0/node_modules/@floating-ui/core/dist/floating-ui.core.js +1 -3
  200. package/dist/node_modules/.pnpm/@floating-ui_core@1.8.0/node_modules/@floating-ui/core/dist/floating-ui.core.js.map +1 -1
  201. package/dist/node_modules/.pnpm/@floating-ui_utils@0.2.12/node_modules/@floating-ui/utils/dist/floating-ui.utils.dom.js +2 -1
  202. package/dist/node_modules/.pnpm/@floating-ui_utils@0.2.12/node_modules/@floating-ui/utils/dist/floating-ui.utils.dom.js.map +1 -1
  203. package/dist/shared/events/user-change.js.map +1 -1
  204. package/dist/shared/form-association/index.js +41 -0
  205. package/dist/shared/form-association/index.js.map +1 -0
  206. package/dist/shared/group-management/index.js +109 -0
  207. package/dist/shared/group-management/index.js.map +1 -0
  208. package/dist/shared/menu-portal/menu-tree.js +20 -7
  209. package/dist/shared/menu-portal/menu-tree.js.map +1 -1
  210. package/dist/shared/option-portal/index.js +65 -0
  211. package/dist/shared/option-portal/index.js.map +1 -0
  212. package/dist/shared/overlay/overlay.js +1 -1
  213. package/dist/shared/overlay/overlay.js.map +1 -1
  214. package/dist/shared/overlay/portal.js +9 -6
  215. package/dist/shared/overlay/portal.js.map +1 -1
  216. package/dist/shared/overlay/presence.js.map +1 -1
  217. package/dist/shared/visible-area/index.js +69 -0
  218. package/dist/shared/visible-area/index.js.map +1 -0
  219. package/dist/types/utils.d.ts +6 -1
  220. package/package.json +7 -5
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/context-menu/index.ts"],"sourcesContent":["import { 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 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\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 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 _scrollLock = defineScrollLockLease().make()\n private readonly _userOpenChange = new UserChangeController()\n private _restoreFocusTarget?: HTMLElement\n private _shouldOpenInstantly = true\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 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 }\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 requestAnimationFrame(() => {\n if (!this._isOpen || !this._menu) return\n this._setupMenuItems()\n this._positionMenu()\n showOverlayPresence(this._menu.panel, { isInstant: this._shouldOpenInstantly })\n this._shouldOpenInstantly = true\n this._focusFirstItem()\n this._bindLevelHovers()\n })\n } else {\n this._syncScrollLock(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 requestAnimationFrame(() => this._positionMenu())\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 private _positionMenu() {\n const panel = this._menu?.panel\n if (!panel) return\n\n panel.style.visibility = 'hidden'\n panel.style.display = ''\n\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 panel.style.left = `${x}px`\n panel.style.top = `${y}px`\n const horizontalOrigin = x < this._x ? 'right' : 'left'\n const verticalOrigin = y < this._y ? 'bottom' : 'top'\n panel.style.setProperty('--wui-overlay-transform-origin', `${verticalOrigin} ${horizontalOrigin}`)\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\n this._hideMenuItems()\n moveMenuChildren(this, menu.content)\n }\n\n private _returnItemsToSlot() {\n const menu = this._menu\n if (!menu) return\n\n this._closeSubmenusFrom(0, true)\n this._restoreClosingSubmenus()\n moveMenuChildren(menu.content, this)\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 const children = getMenuChildren(item)\n if (children.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 submenu.content.append(...children)\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) moveMenuChildren(submenu.content, item)\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 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-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._hideMenuItems}></slot>\n </div>\n `\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 { 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 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\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 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 _scrollLock = defineScrollLockLease().make()\n private readonly _userOpenChange = new UserChangeController()\n private _restoreFocusTarget?: HTMLElement\n private _shouldOpenInstantly = true\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 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 }\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 requestAnimationFrame(() => {\n if (!this._isOpen || !this._menu) return\n this._setupMenuItems()\n this._positionMenu()\n showOverlayPresence(this._menu.panel, { isInstant: this._shouldOpenInstantly })\n this._shouldOpenInstantly = true\n this._focusFirstItem()\n this._bindLevelHovers()\n })\n } else {\n this._syncScrollLock(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 requestAnimationFrame(() => this._positionMenu())\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 private _positionMenu() {\n const panel = this._menu?.panel\n if (!panel) return\n\n panel.style.visibility = 'hidden'\n panel.style.display = ''\n\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 panel.style.left = `${x}px`\n panel.style.top = `${y}px`\n const horizontalOrigin = x < this._x ? 'right' : 'left'\n const verticalOrigin = y < this._y ? 'bottom' : 'top'\n panel.style.setProperty('--wui-internal-overlay-transform-origin', `${verticalOrigin} ${horizontalOrigin}`)\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\n this._hideMenuItems()\n moveMenuChildren(this, menu.content)\n }\n\n private _returnItemsToSlot() {\n const menu = this._menu\n if (!menu) return\n\n this._closeSubmenusFrom(0, true)\n this._restoreClosingSubmenus()\n moveMenuChildren(menu.content, this)\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) moveMenuChildren(submenu.content, item)\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 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._hideMenuItems}></slot>\n </div>\n `\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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"}
@@ -4,6 +4,7 @@ export declare class WebUiDialog extends LitElement {
4
4
  open: boolean;
5
5
  noScrollLock: boolean;
6
6
  noBackdropClose: boolean;
7
+ noEscapeClose: boolean;
7
8
  private _hasBody;
8
9
  private readonly _userOpenChange;
9
10
  private readonly _scrollLock;
@@ -1,4 +1,4 @@
1
- import e from "../../_virtual/_@oxc-project_runtime@0.140.0/helpers/esm/decorate.js";
1
+ import e from "../../_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorate.js";
2
2
  import t from "../../assets/glass.js";
3
3
  import { defineScrollLockLease as n } from "../../shared/scroll-lock/scroll-lock.js";
4
4
  import "../button/index.js";
@@ -10,7 +10,7 @@ import { customElement as l, property as u, state as d } from "lit/decorators.js
10
10
  //#region src/components/dialog/index.ts
11
11
  var f = class extends o {
12
12
  constructor(...e) {
13
- super(...e), this.open = !1, this.noScrollLock = !1, this.noBackdropClose = !1, this._hasBody = !1, this._userOpenChange = new r(), this._scrollLock = n().make(), this._presence = i().make({
13
+ super(...e), this.open = !1, this.noScrollLock = !1, this.noBackdropClose = !1, this.noEscapeClose = !1, this._hasBody = !1, this._userOpenChange = new r(), this._scrollLock = n().make(), this._presence = i().make({
14
14
  getDialog: () => this.dialog,
15
15
  isConnected: () => this.isConnected,
16
16
  isOpen: () => this.open
@@ -39,7 +39,7 @@ var f = class extends o {
39
39
  this.open = !1;
40
40
  }
41
41
  handleCancel(e) {
42
- e.preventDefault(), this._userOpenChange.mark(), this.close();
42
+ e.preventDefault(), !this.noEscapeClose && (this._userOpenChange.mark(), this.close());
43
43
  }
44
44
  handleBackdropClick(e) {
45
45
  e.target === e.currentTarget && (this.noBackdropClose || (this._userOpenChange.mark(), this.close()));
@@ -67,11 +67,11 @@ var f = class extends o {
67
67
  >
68
68
  <div class="wui-dialog-body wui-glass">
69
69
  ${this._hasBody ? s`<slot name="body" @slotchange=${this._onBodySlotChange}></slot>` : s`
70
- <slot name="body" @slotchange=${this._onBodySlotChange} hidden></slot>
71
- <div class="title"><slot name="title"></slot></div>
72
- <div class="desc"><slot></slot></div>
73
- <div class="wui-dialog-footer"><slot name="footer"></slot></div>
74
- `}
70
+ <slot name="body" @slotchange=${this._onBodySlotChange} hidden></slot>
71
+ <div class="title"><slot name="title"></slot></div>
72
+ <div class="desc"><slot></slot></div>
73
+ <div class="wui-dialog-footer"><slot name="footer"></slot></div>
74
+ `}
75
75
  </div>
76
76
  </dialog>
77
77
  `;
@@ -88,7 +88,11 @@ e([u({
88
88
  type: Boolean,
89
89
  reflect: !0,
90
90
  attribute: "no-backdrop-close"
91
- })], f.prototype, "noBackdropClose", void 0), e([d()], f.prototype, "_hasBody", void 0), f = e([l("web-ui-dialog")], f);
91
+ })], f.prototype, "noBackdropClose", void 0), e([u({
92
+ type: Boolean,
93
+ reflect: !0,
94
+ attribute: "no-escape-close"
95
+ })], f.prototype, "noEscapeClose", void 0), e([d()], f.prototype, "_hasBody", void 0), f = e([l("web-ui-dialog")], f);
92
96
  //#endregion
93
97
  export { f as WebUiDialog };
94
98
 
@@ -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\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 // 保留 top layer 直到视觉退场完成,避免原生关闭跳过退出动画。\n e.preventDefault()\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 this._userOpenChange.mark()\n this.close()\n }\n\n private emitOpenChange() {\n this.dispatchEvent(\n new CustomEvent('open-change', {\n detail: { open: this.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 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 ${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 </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":";;;;;;;;;;AAYO,IAAM,IAAN,cAA0B,EAAW;;EA4EX,aAzEoB,KAAA,OAAA,IACqC,KAAA,eAAA,IACM,KAAA,kBAAA,IAElE,KAAA,WAAA,IACO,KAAA,kBAAA,IAAI,EAAqB,GAE7B,KAAA,cAAA,EAAsB,CAAC,CAAC,KAAK,GAC/B,KAAA,YAAA,EAA2B,CAAC,CAAC,KAAK;GAC7D,iBAAiB,KAAK;GACtB,mBAAmB,KAAK;GACxB,cAAc,KAAK;EACrB,CAAC,GAyD2B,KAAA,oBAAA,MAA2B;GACrD,KAAK,UAAU,oBAAoB,CAAK;EAC1C,GAE+B,KAAA,uBAAA;GACxB,AAGL,KAAK,UAFL,KAAK,UAAU,kBAAkB,GACjC,KAAK,gBAAgB,KAAK,GACd;EACd;;;EAhFyB,KAAA,SAAA,CAAC,EAAU,CAAK,GAAG,EAAU,CAAK,CAAC;;CAgB5D,IAAY,SAAS;EACnB,OAAO,KAAK,YAAY,cAAc,QAAQ,KAAK;CACrD;CAEA,QAA2B,GAAuB;EAChD,MAAM,QAAQ,CAAK,GACd,KAAK,gBAEN,EAAM,IAAI,MAAM,MACd,KAAK,gBAAgB,QAAQ,KAAG,KAAK,eAAe,GACxD,KAAK,UAAU,KAAK,KAAK,IAAI,KAE3B,EAAM,IAAI,MAAM,KAAK,EAAM,IAAI,cAAc,MAAG,KAAK,gBAAgB;CAC3E;CAEA,uBAAgC;EAG9B,AAFA,MAAM,qBAAqB,GAC3B,KAAK,UAAU,QAAQ,GACvB,KAAK,YAAY,QAAQ;CAC3B;CAGA,YAAY;EACN,AACJ,KAAK,SAAO;CACd;CAEA,QAAQ;EACN,KAAK,OAAO;CACd;CAEA,aAAqB,GAAU;EAI7B,AAFA,EAAE,eAAe,GACjB,KAAK,gBAAgB,KAAK,GAC1B,KAAK,MAAM;CACb;CAEA,oBAA4B,GAAe;EACrC,EAAE,WAAY,EAAE,kBAChB,KAAK,oBACT,KAAK,gBAAgB,KAAK,GAC1B,KAAK,MAAM;CACb;CAEA,iBAAyB;EACvB,KAAK,cACH,IAAI,YAAY,eAAe;GAC7B,QAAQ,EAAE,MAAM,KAAK,KAAK;GAC1B,SAAS;GACT,UAAU;EACZ,CAAC,CACH;CACF;CAaA,gBAAwB,IAAS,KAAK,MAAM;EAC1C,KAAK,YAAY,KAAK,KAAU,CAAC,KAAK,YAAY;CACpD;CAEA,kBAA0B,GAAU;EAC5B,EAAE,kBAAkB,oBAC1B,KAAK,WAAW,EAAE,OAAO,iBAAiB,CAAC,CAAC,SAAS;CACvD;CAEA,SAAkB;EAChB,OAAO,CAAI;;kBAEG,KAAK,aAAa;iBACnB,KAAK,eAAe;iBACpB,KAAK,oBAAoB;yBACjB,KAAK,iBAAiB;;;YAGnC,KAAK,WACH,CAAI,iCAAiC,KAAK,kBAAkB,YAC5D,CAAI;gDAC8B,KAAK,kBAAkB;;;;gBAIvD;;;;CAId;AAKF;AAlHG,EAAA,CAAA,EAAS;CAAE,MAAM;CAAS,SAAS;AAAK,CAAC,CAAA,GAAA,EAAA,WAAA,QAAA,KAAA,CAAA,GACzC,EAAA,CAAA,EAAS;CAAE,MAAM;CAAS,SAAS;CAAM,WAAW;AAAiB,CAAC,CAAA,GAAA,EAAA,WAAA,gBAAA,KAAA,CAAA,GACtE,EAAA,CAAA,EAAS;CAAE,MAAM;CAAS,SAAS;CAAM,WAAW;AAAoB,CAAC,CAAA,GAAA,EAAA,WAAA,mBAAA,KAAA,CAAA,GAEzE,EAAA,CAAA,EAAM,CAAA,GAAA,EAAA,WAAA,YAAA,KAAA,CAAA,GARR,IAAA,EAAA,CAAA,EAAc,eAAe,CAAA,GAAA,CAAA"}
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 @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 // 保留 top layer 直到视觉退场完成,避免原生关闭跳过退出动画。\n e.preventDefault()\n if (this.noEscapeClose) return\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 this._userOpenChange.mark()\n this.close()\n }\n\n private emitOpenChange() {\n this.dispatchEvent(\n new CustomEvent('open-change', {\n detail: { open: this.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 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-glass-shadow:var(--wui-shadow-overlay,2px 16px 40px #0006);background-color:var(--wui-color-surface-overlay,#f6f6f6e0);border-radius:32px;padding:32px 24px 24px}.title{color:var(--wui-color-text,#1b1b1b);margin:0 0 16px;padding:0;font-size:18px;font-weight:600;line-height:1.4}.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,#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);background-color:var(--wui-color-surface-overlay,#f6f6f6d1);border-radius:32px;padding:32px 24px 24px}.title{color:var(--wui-color-text,#1b1b1b);margin:0 0 16px;padding:0;font-size:18px;font-weight:600;line-height:1.4}.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-glass-shadow: var(--wui-shadow-overlay, 2px 16px 40px rgb(0 0 0 / 0.4));\n padding: 32px 24px 24px;\n border-radius: 32px;\n background-color: 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.4;\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.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 padding: 32px 24px 24px;\n border-radius: 32px;\n background-color: 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.4;\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":""}
@@ -6,11 +6,26 @@ export declare class WebUiDrawer extends LitElement {
6
6
  open: boolean;
7
7
  noScrollLock: boolean;
8
8
  noBackdropClose: boolean;
9
+ /**
10
+ * request-only 模式下,Escape、遮罩和关闭按钮只派发 `open-change` 请求,
11
+ * 不会自行修改 `open`。Consumer 回写 `open` 后才执行关闭动画。
12
+ */
13
+ requestOnly: boolean;
9
14
  get placement(): DrawerPlacement;
10
15
  set placement(v: string);
11
16
  private _placement;
12
17
  heading: string;
13
18
  closable: boolean;
19
+ /**
20
+ * Headless 模式:只保留 overlay 基础设施(backdrop、动画、scroll lock、dialog 语义),
21
+ * 移除内置 UI(glass 样式、header、close 按钮、footer)。Consumer 自定义内容样式。
22
+ */
23
+ headless: boolean;
24
+ /**
25
+ * 内部原生 dialog 的 accessible name。headless 模式必须由 Consumer 提供,
26
+ * 因为该模式不会渲染可自动关联的内置 header。
27
+ */
28
+ dialogLabel: string;
14
29
  private get dialog();
15
30
  private _hasHeaderSlot;
16
31
  private _hasFooterSlot;
@@ -1,4 +1,4 @@
1
- import e from "../../_virtual/_@oxc-project_runtime@0.140.0/helpers/esm/decorate.js";
1
+ import e from "../../_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorate.js";
2
2
  import "../icon/index.js";
3
3
  import t from "../../icons/generated/ooui-close.js";
4
4
  import n from "../../assets/glass.js";
@@ -18,12 +18,18 @@ var m = [
18
18
  "bottom"
19
19
  ], h = class extends c {
20
20
  constructor(...e) {
21
- super(...e), this.open = !1, this.noScrollLock = !1, this.noBackdropClose = !1, this._placement = "right", this.heading = "", this.closable = !1, this._hasHeaderSlot = !1, this._hasFooterSlot = !1, this._userOpenChange = new a(), this._scrollLock = i().make(), this._presence = o().make({
21
+ super(...e), this.open = !1, this.noScrollLock = !1, this.noBackdropClose = !1, this.requestOnly = !1, this._placement = "right", this.heading = "", this.closable = !1, this.headless = !1, this.dialogLabel = "", this._hasHeaderSlot = !1, this._hasFooterSlot = !1, this._userOpenChange = new a(), this._scrollLock = i().make(), this._presence = o().make({
22
22
  getDialog: () => this.dialog,
23
23
  isConnected: () => this.isConnected,
24
24
  isOpen: () => this.open
25
25
  }), this._closeFromUser = () => {
26
- this.open && (this._userOpenChange.mark(), this.close());
26
+ if (this.open) {
27
+ if (this.requestOnly) {
28
+ this.emitOpenChange(!1);
29
+ return;
30
+ }
31
+ this._userOpenChange.mark(), this.close();
32
+ }
27
33
  };
28
34
  }
29
35
  static {
@@ -81,14 +87,20 @@ var m = [
81
87
  e.preventDefault(), this._closeFromUser();
82
88
  }
83
89
  handleNativeClose() {
84
- this.open &&= (this._presence.handleNativeClose(), this._userOpenChange.mark(), !1);
90
+ if (this.open) {
91
+ if (this.requestOnly) {
92
+ this._presence.sync(!0), this.emitOpenChange(!1);
93
+ return;
94
+ }
95
+ this._presence.handleNativeClose(), this._userOpenChange.mark(), this.open = !1;
96
+ }
85
97
  }
86
98
  handleBackdropClick(e) {
87
99
  e.target === e.currentTarget && (this.noBackdropClose || this._closeFromUser());
88
100
  }
89
- emitOpenChange() {
101
+ emitOpenChange(e = this.open) {
90
102
  this.dispatchEvent(new CustomEvent("open-change", {
91
- detail: { open: this.open },
103
+ detail: { open: e },
92
104
  bubbles: !0,
93
105
  composed: !0
94
106
  }));
@@ -97,42 +109,44 @@ var m = [
97
109
  this._scrollLock.sync(e && !this.noScrollLock);
98
110
  }
99
111
  render() {
100
- let e = this._hasHeaderSlot || !!this.heading;
112
+ let e = this._hasHeaderSlot || !!this.heading, n = this.dialogLabel.trim(), r = !n && !this.headless && e ? "wui-drawer-heading" : u;
101
113
  return l`
102
114
  <dialog
115
+ aria-label=${n || u}
116
+ aria-labelledby=${r}
103
117
  @cancel=${this.handleCancel}
104
118
  @close=${this.handleNativeClose}
105
119
  @click=${this.handleBackdropClick}
106
120
  @keydown=${this.handleKeydown}
107
121
  @transitionend=${this.handleTransitionEnd}
108
122
  >
109
- <div class="wui-drawer-body wui-glass">
110
- ${e ? l`
111
- <div class="wui-drawer-header">
112
- <slot name="header" @slotchange=${this.handleHeaderSlotChange}>
113
- ${this.heading ? l`<span class="wui-drawer-heading">${this.heading}</span>` : u}
114
- </slot>
123
+ ${this.headless ? l`<slot></slot>` : l`
124
+ <div class="wui-drawer-body wui-glass">
125
+ <div class="wui-drawer-header" id="wui-drawer-heading" ?hidden=${!e}>
126
+ <slot name="header" @slotchange=${this.handleHeaderSlotChange}>
127
+ ${this.heading ? l`<span class="wui-drawer-heading">${this.heading}</span>` : u}
128
+ </slot>
129
+ </div>
130
+ <div class="wui-drawer-content">
131
+ <slot></slot>
132
+ </div>
133
+ <div class="wui-drawer-footer" ?hidden=${!this._hasFooterSlot}>
134
+ <slot name="footer" @slotchange=${this.handleFooterSlotChange}></slot>
135
+ </div>
115
136
  </div>
116
- ` : u}
117
- <div class="wui-drawer-content">
118
- <slot></slot>
119
- </div>
120
- <div class="wui-drawer-footer" ?hidden=${!this._hasFooterSlot}>
121
- <slot name="footer" @slotchange=${this.handleFooterSlotChange}></slot>
122
- </div>
123
- </div>
124
- ${this.closable ? l`
125
- <web-ui-button
126
- class="wui-drawer-close"
127
- @click=${this._closeFromUser}
128
- aria-label="关闭"
129
- variant="secondary"
130
- icon
131
- size="26"
132
- >
133
- <web-ui-icon .icon=${t} size="16"></web-ui-icon>
134
- </web-ui-button>
135
- ` : u}
137
+ ${this.closable ? l`
138
+ <web-ui-button
139
+ class="wui-drawer-close"
140
+ @click=${this._closeFromUser}
141
+ aria-label="关闭"
142
+ variant="secondary"
143
+ icon
144
+ size="26"
145
+ >
146
+ <web-ui-icon .icon=${t}></web-ui-icon>
147
+ </web-ui-button>
148
+ ` : u}
149
+ `}
136
150
  </dialog>
137
151
  `;
138
152
  }
@@ -149,12 +163,22 @@ e([p({
149
163
  reflect: !0,
150
164
  attribute: "no-backdrop-close"
151
165
  })], h.prototype, "noBackdropClose", void 0), e([p({
166
+ type: Boolean,
167
+ reflect: !0,
168
+ attribute: "request-only"
169
+ })], h.prototype, "requestOnly", void 0), e([p({
152
170
  type: String,
153
171
  reflect: !0
154
172
  })], h.prototype, "placement", null), e([p({ type: String })], h.prototype, "heading", void 0), e([p({
155
173
  type: Boolean,
156
174
  reflect: !0
157
- })], h.prototype, "closable", void 0), h = e([f("web-ui-drawer")], h);
175
+ })], h.prototype, "closable", void 0), e([p({
176
+ type: Boolean,
177
+ reflect: !0
178
+ })], h.prototype, "headless", void 0), e([p({
179
+ type: String,
180
+ attribute: "dialog-label"
181
+ })], h.prototype, "dialogLabel", void 0), h = e([f("web-ui-drawer")], h);
158
182
  //#endregion
159
183
  export { h as WebUiDrawer };
160
184
 
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/drawer/index.ts"],"sourcesContent":["import { html, LitElement, nothing, type PropertyValues, unsafeCSS } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\n\nimport '@/components/icon'\nimport '@/components/button'\nimport glass from '@/assets/glass.css?inline'\nimport { oouiClose } from '@/icons'\nimport { UserChangeController } from '@/shared/events/user-change'\nimport { normalizeLiteral } from '@/shared/normalize'\nimport { defineNativeDialogPresence } from '@/shared/overlay/native-dialog-presence'\nimport { defineScrollLockLease } from '@/shared/scroll-lock/scroll-lock'\n\nimport style from './style.css?inline'\n\nconst ALLOWED_PLACEMENTS = ['right', 'left', 'top', 'bottom'] as const\n\nexport type DrawerPlacement = (typeof ALLOWED_PLACEMENTS)[number]\n\n@customElement('web-ui-drawer')\nexport class WebUiDrawer 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: String, reflect: true })\n get placement(): DrawerPlacement {\n return this._placement\n }\n set placement(v: string) {\n const old = this._placement\n this._placement = normalizeLiteral(v, ALLOWED_PLACEMENTS, 'right')\n this.requestUpdate('placement', old)\n }\n private _placement: DrawerPlacement = 'right'\n\n // 标题文字(未传 header slot 时显示默认 header)\n @property({ type: String }) heading = ''\n\n @property({ type: Boolean, reflect: true }) closable = false\n\n private get dialog() {\n return this.shadowRoot?.querySelector('dialog') ?? null\n }\n\n private _hasHeaderSlot = false\n private _hasFooterSlot = false\n private readonly _userOpenChange = new UserChangeController()\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 override connectedCallback() {\n super.connectedCallback()\n this._hasHeaderSlot = Array.from(this.children).some(child => child.getAttribute?.('slot') === 'header')\n this._hasFooterSlot = Array.from(this.children).some(child => child.getAttribute?.('slot') === 'footer')\n }\n\n override firstUpdated() {\n this._checkSlotContent('footer')\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback()\n this._presence.dispose()\n this._scrollLock.release()\n }\n\n private _checkSlotContent(name: string) {\n const slot = this.shadowRoot?.querySelector(`slot[name=\"${name}\"]`) as HTMLSlotElement | null\n if (!slot) return\n const has = slot.assignedNodes().length > 0\n if (name === 'footer' && has !== this._hasFooterSlot) {\n this._hasFooterSlot = has\n this.requestUpdate()\n }\n }\n\n private handleHeaderSlotChange(e: Event) {\n const has = (e.target as HTMLSlotElement).assignedNodes().length > 0\n if (has !== this._hasHeaderSlot) {\n this._hasHeaderSlot = has\n this.requestUpdate()\n }\n }\n\n private handleFooterSlotChange(e: Event) {\n const has = (e.target as HTMLSlotElement).assignedNodes().length > 0\n if (has !== this._hasFooterSlot) {\n this._hasFooterSlot = has\n this.requestUpdate()\n }\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 private handleTransitionEnd(e: TransitionEvent) {\n this._presence.handleTransitionEnd(e)\n }\n\n private handleKeydown(e: KeyboardEvent) {\n if (e.key !== 'Escape') return\n e.preventDefault()\n this._closeFromUser()\n }\n\n // 打开抽屉(命令式)\n show() {\n if (this.open) return\n this.open = true\n }\n\n // 关闭抽屉(带动画)\n close() {\n if (!this.open) return\n this.open = false\n }\n\n private readonly _closeFromUser = () => {\n if (!this.open) return\n this._userOpenChange.mark()\n this.close()\n }\n\n private handleCancel(e: Event) {\n // 保留 top layer 直到 CSS 过渡结束,避免原生关闭跳过退出动画。\n e.preventDefault()\n this._closeFromUser()\n }\n\n private handleNativeClose() {\n if (!this.open) return\n\n // 原生关闭可绕过 cancel;同步受控状态,避免 show() 误判为已打开。\n this._presence.handleNativeClose()\n this._userOpenChange.mark()\n this.open = false\n }\n\n private handleBackdropClick(e: MouseEvent) {\n if (e.target !== (e.currentTarget as HTMLDialogElement)) return\n if (this.noBackdropClose) return\n this._closeFromUser()\n }\n\n private emitOpenChange() {\n this.dispatchEvent(\n new CustomEvent('open-change', {\n detail: { open: this.open },\n bubbles: true,\n composed: true\n })\n )\n }\n\n private _syncScrollLock(isOpen = this.open) {\n this._scrollLock.sync(isOpen && !this.noScrollLock)\n }\n\n override render() {\n const showHeader = this._hasHeaderSlot || !!this.heading\n\n return html`\n <dialog\n @cancel=${this.handleCancel}\n @close=${this.handleNativeClose}\n @click=${this.handleBackdropClick}\n @keydown=${this.handleKeydown}\n @transitionend=${this.handleTransitionEnd}\n >\n <div class=\"wui-drawer-body wui-glass\">\n ${showHeader\n ? html`\n <div class=\"wui-drawer-header\">\n <slot name=\"header\" @slotchange=${this.handleHeaderSlotChange}>\n ${this.heading ? html`<span class=\"wui-drawer-heading\">${this.heading}</span>` : nothing}\n </slot>\n </div>\n `\n : nothing}\n <div class=\"wui-drawer-content\">\n <slot></slot>\n </div>\n <div class=\"wui-drawer-footer\" ?hidden=${!this._hasFooterSlot}>\n <slot name=\"footer\" @slotchange=${this.handleFooterSlotChange}></slot>\n </div>\n </div>\n ${this.closable\n ? html`\n <web-ui-button\n class=\"wui-drawer-close\"\n @click=${this._closeFromUser}\n aria-label=\"关闭\"\n variant=\"secondary\"\n icon\n size=\"26\"\n >\n <web-ui-icon .icon=${oouiClose} size=\"16\"></web-ui-icon>\n </web-ui-button>\n `\n : nothing}\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-drawer': WebUiDrawer\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/drawer/index.ts"],"sourcesContent":["import { html, LitElement, nothing, type PropertyValues, unsafeCSS } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\n\nimport '@/components/icon'\nimport '@/components/button'\nimport glass from '@/assets/glass.css?inline'\nimport { oouiClose } from '@/icons'\nimport { UserChangeController } from '@/shared/events/user-change'\nimport { normalizeLiteral } from '@/shared/normalize'\nimport { defineNativeDialogPresence } from '@/shared/overlay/native-dialog-presence'\nimport { defineScrollLockLease } from '@/shared/scroll-lock/scroll-lock'\n\nimport style from './style.css?inline'\n\nconst ALLOWED_PLACEMENTS = ['right', 'left', 'top', 'bottom'] as const\n\nexport type DrawerPlacement = (typeof ALLOWED_PLACEMENTS)[number]\n\n@customElement('web-ui-drawer')\nexport class WebUiDrawer 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\n /**\n * request-only 模式下,Escape、遮罩和关闭按钮只派发 `open-change` 请求,\n * 不会自行修改 `open`。Consumer 回写 `open` 后才执行关闭动画。\n */\n @property({ type: Boolean, reflect: true, attribute: 'request-only' }) requestOnly = false\n\n @property({ type: String, reflect: true })\n get placement(): DrawerPlacement {\n return this._placement\n }\n set placement(v: string) {\n const old = this._placement\n this._placement = normalizeLiteral(v, ALLOWED_PLACEMENTS, 'right')\n this.requestUpdate('placement', old)\n }\n private _placement: DrawerPlacement = 'right'\n\n // 标题文字(未传 header slot 时显示默认 header)\n @property({ type: String }) heading = ''\n\n @property({ type: Boolean, reflect: true }) closable = false\n\n /**\n * Headless 模式:只保留 overlay 基础设施(backdrop、动画、scroll lock、dialog 语义),\n * 移除内置 UI(glass 样式、header、close 按钮、footer)。Consumer 自定义内容样式。\n */\n @property({ type: Boolean, reflect: true }) headless = false\n\n /**\n * 内部原生 dialog 的 accessible name。headless 模式必须由 Consumer 提供,\n * 因为该模式不会渲染可自动关联的内置 header。\n */\n @property({ type: String, attribute: 'dialog-label' }) dialogLabel = ''\n\n private get dialog() {\n return this.shadowRoot?.querySelector('dialog') ?? null\n }\n\n private _hasHeaderSlot = false\n private _hasFooterSlot = false\n private readonly _userOpenChange = new UserChangeController()\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 override connectedCallback() {\n super.connectedCallback()\n this._hasHeaderSlot = Array.from(this.children).some(child => child.getAttribute?.('slot') === 'header')\n this._hasFooterSlot = Array.from(this.children).some(child => child.getAttribute?.('slot') === 'footer')\n }\n\n override firstUpdated() {\n this._checkSlotContent('footer')\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback()\n this._presence.dispose()\n this._scrollLock.release()\n }\n\n private _checkSlotContent(name: string) {\n const slot = this.shadowRoot?.querySelector(`slot[name=\"${name}\"]`) as HTMLSlotElement | null\n if (!slot) return\n const has = slot.assignedNodes().length > 0\n if (name === 'footer' && has !== this._hasFooterSlot) {\n this._hasFooterSlot = has\n this.requestUpdate()\n }\n }\n\n private handleHeaderSlotChange(e: Event) {\n const has = (e.target as HTMLSlotElement).assignedNodes().length > 0\n if (has !== this._hasHeaderSlot) {\n this._hasHeaderSlot = has\n this.requestUpdate()\n }\n }\n\n private handleFooterSlotChange(e: Event) {\n const has = (e.target as HTMLSlotElement).assignedNodes().length > 0\n if (has !== this._hasFooterSlot) {\n this._hasFooterSlot = has\n this.requestUpdate()\n }\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 private handleTransitionEnd(e: TransitionEvent) {\n this._presence.handleTransitionEnd(e)\n }\n\n private handleKeydown(e: KeyboardEvent) {\n if (e.key !== 'Escape') return\n e.preventDefault()\n this._closeFromUser()\n }\n\n // 打开抽屉(命令式)\n show() {\n if (this.open) return\n this.open = true\n }\n\n // 关闭抽屉(带动画)\n close() {\n if (!this.open) return\n this.open = false\n }\n\n private readonly _closeFromUser = () => {\n if (!this.open) return\n if (this.requestOnly) {\n this.emitOpenChange(false)\n return\n }\n\n this._userOpenChange.mark()\n this.close()\n }\n\n private handleCancel(e: Event) {\n // 保留 top layer 直到 CSS 过渡结束,避免原生关闭跳过退出动画。\n e.preventDefault()\n this._closeFromUser()\n }\n\n private handleNativeClose() {\n if (!this.open) return\n\n // 原生关闭可绕过 cancel;request-only 时恢复受控状态,由 Consumer 决定是否关闭。\n if (this.requestOnly) {\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 handleBackdropClick(e: MouseEvent) {\n if (e.target !== (e.currentTarget as HTMLDialogElement)) return\n if (this.noBackdropClose) return\n this._closeFromUser()\n }\n\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 _syncScrollLock(isOpen = this.open) {\n this._scrollLock.sync(isOpen && !this.noScrollLock)\n }\n\n override render() {\n const showHeader = this._hasHeaderSlot || !!this.heading\n const dialogLabel = this.dialogLabel.trim()\n const dialogLabelledBy = !dialogLabel && !this.headless && showHeader ? 'wui-drawer-heading' : nothing\n\n // 保持同一个 dialog 实例,避免打开期间切换 headless 时脱离 top layer。\n return html`\n <dialog\n aria-label=${dialogLabel || nothing}\n aria-labelledby=${dialogLabelledBy}\n @cancel=${this.handleCancel}\n @close=${this.handleNativeClose}\n @click=${this.handleBackdropClick}\n @keydown=${this.handleKeydown}\n @transitionend=${this.handleTransitionEnd}\n >\n ${\n this.headless\n ? html`<slot></slot>`\n : html`\n <div class=\"wui-drawer-body wui-glass\">\n <div class=\"wui-drawer-header\" id=\"wui-drawer-heading\" ?hidden=${!showHeader}>\n <slot name=\"header\" @slotchange=${this.handleHeaderSlotChange}>\n ${this.heading ? html`<span class=\"wui-drawer-heading\">${this.heading}</span>` : nothing}\n </slot>\n </div>\n <div class=\"wui-drawer-content\">\n <slot></slot>\n </div>\n <div class=\"wui-drawer-footer\" ?hidden=${!this._hasFooterSlot}>\n <slot name=\"footer\" @slotchange=${this.handleFooterSlotChange}></slot>\n </div>\n </div>\n ${\n this.closable\n ? html`\n <web-ui-button\n class=\"wui-drawer-close\"\n @click=${this._closeFromUser}\n aria-label=\"关闭\"\n variant=\"secondary\"\n icon\n size=\"26\"\n >\n <web-ui-icon .icon=${oouiClose}></web-ui-icon>\n </web-ui-button>\n `\n : nothing\n }\n `\n }\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-drawer': WebUiDrawer\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAcA,IAAM,IAAqB;CAAC;CAAS;CAAQ;CAAO;AAAQ,GAK/C,IAAN,cAA0B,EAAW;;EAkIF,aA/HW,KAAA,OAAA,IACqC,KAAA,eAAA,IACM,KAAA,kBAAA,IAMT,KAAA,cAAA,IAW/C,KAAA,aAAA,SAGA,KAAA,UAAA,IAEiB,KAAA,WAAA,IAMA,KAAA,WAAA,IAMc,KAAA,cAAA,IAM5C,KAAA,iBAAA,IACA,KAAA,iBAAA,IACU,KAAA,kBAAA,IAAI,EAAqB,GAC7B,KAAA,cAAA,EAAsB,CAAC,CAAC,KAAK,GAC/B,KAAA,YAAA,EAA2B,CAAC,CAAC,KAAK;GAC7D,iBAAiB,KAAK;GACtB,mBAAmB,KAAK;GACxB,cAAc,KAAK;EACrB,CAAC,GA6EuC,KAAA,uBAAA;GACjC,SAAK,MACV;QAAI,KAAK,aAAa;KACpB,KAAK,eAAe,EAAK;KACzB;IACF;IAGA,AADA,KAAK,gBAAgB,KAAK,GAC1B,KAAK,MAAM;GAHX;EAIF;;;EA1IyB,KAAA,SAAA,CAAC,EAAU,CAAK,GAAG,EAAU,CAAK,CAAC;;CAY5D,IACI,YAA6B;EAC/B,OAAO,KAAK;CACd;CACA,IAAI,UAAU,GAAW;EACvB,IAAM,IAAM,KAAK;EAEjB,AADA,KAAK,aAAa,EAAiB,GAAG,GAAoB,OAAO,GACjE,KAAK,cAAc,aAAa,CAAG;CACrC;CAoBA,IAAY,SAAS;EACnB,OAAO,KAAK,YAAY,cAAc,QAAQ,KAAK;CACrD;CAYA,oBAA6B;EAG3B,AAFA,MAAM,kBAAkB,GACxB,KAAK,iBAAiB,MAAM,KAAK,KAAK,QAAQ,CAAC,CAAC,MAAK,MAAS,EAAM,eAAe,MAAM,MAAM,QAAQ,GACvG,KAAK,iBAAiB,MAAM,KAAK,KAAK,QAAQ,CAAC,CAAC,MAAK,MAAS,EAAM,eAAe,MAAM,MAAM,QAAQ;CACzG;CAEA,eAAwB;EACtB,KAAK,kBAAkB,QAAQ;CACjC;CAEA,uBAAgC;EAG9B,AAFA,MAAM,qBAAqB,GAC3B,KAAK,UAAU,QAAQ,GACvB,KAAK,YAAY,QAAQ;CAC3B;CAEA,kBAA0B,GAAc;EACtC,IAAM,IAAO,KAAK,YAAY,cAAc,cAAc,EAAK,GAAG;EAClE,IAAI,CAAC,GAAM;EACX,IAAM,IAAM,EAAK,cAAc,CAAC,CAAC,SAAS;EAC1C,AAAI,MAAS,YAAY,MAAQ,KAAK,mBACpC,KAAK,iBAAiB,GACtB,KAAK,cAAc;CAEvB;CAEA,uBAA+B,GAAU;EACvC,IAAM,IAAO,EAAE,OAA2B,cAAc,CAAC,CAAC,SAAS;EACnE,AAAI,MAAQ,KAAK,mBACf,KAAK,iBAAiB,GACtB,KAAK,cAAc;CAEvB;CAEA,uBAA+B,GAAU;EACvC,IAAM,IAAO,EAAE,OAA2B,cAAc,CAAC,CAAC,SAAS;EACnE,AAAI,MAAQ,KAAK,mBACf,KAAK,iBAAiB,GACtB,KAAK,cAAc;CAEvB;CAEA,QAA2B,GAAuB;EAChD,MAAM,QAAQ,CAAK,GACd,KAAK,gBAEN,EAAM,IAAI,MAAM,MACd,KAAK,gBAAgB,QAAQ,KAAG,KAAK,eAAe,GACxD,KAAK,UAAU,KAAK,KAAK,IAAI,KAE3B,EAAM,IAAI,MAAM,KAAK,EAAM,IAAI,cAAc,MAAG,KAAK,gBAAgB;CAC3E;CAEA,oBAA4B,GAAoB;EAC9C,KAAK,UAAU,oBAAoB,CAAC;CACtC;CAEA,cAAsB,GAAkB;EAClC,EAAE,QAAQ,aACd,EAAE,eAAe,GACjB,KAAK,eAAe;CACtB;CAGA,OAAO;EACD,AACJ,KAAK,SAAO;CACd;CAGA,QAAQ;EACD,AACL,KAAK,SAAO;CACd;CAaA,aAAqB,GAAU;EAG7B,AADA,EAAE,eAAe,GACjB,KAAK,eAAe;CACtB;CAEA,oBAA4B;EACrB,SAAK,MAGV;OAAI,KAAK,aAAa;IAEpB,AADA,KAAK,UAAU,KAAK,EAAI,GACxB,KAAK,eAAe,EAAK;IACzB;GACF;GAIA,AAFA,KAAK,UAAU,kBAAkB,GACjC,KAAK,gBAAgB,KAAK,GAC1B,KAAK,OAAO;EAJZ;CAKF;CAEA,oBAA4B,GAAe;EACrC,EAAE,WAAY,EAAE,kBAChB,KAAK,mBACT,KAAK,eAAe;CACtB;CAEA,eAAuB,IAAO,KAAK,MAAM;EACvC,KAAK,cACH,IAAI,YAAY,eAAe;GAC7B,QAAQ,EAAE,QAAK;GACf,SAAS;GACT,UAAU;EACZ,CAAC,CACH;CACF;CAEA,gBAAwB,IAAS,KAAK,MAAM;EAC1C,KAAK,YAAY,KAAK,KAAU,CAAC,KAAK,YAAY;CACpD;CAEA,SAAkB;EAChB,IAAM,IAAa,KAAK,kBAAkB,CAAC,CAAC,KAAK,SAC3C,IAAc,KAAK,YAAY,KAAK,GACpC,IAAmB,CAAC,KAAe,CAAC,KAAK,YAAY,IAAa,uBAAuB;EAG/F,OAAO,CAAI;;qBAEM,KAAe,EAAQ;0BAClB,EAAiB;kBACzB,KAAK,aAAa;iBACnB,KAAK,kBAAkB;iBACvB,KAAK,oBAAoB;mBACvB,KAAK,cAAc;yBACb,KAAK,oBAAoB;;UAGxC,KAAK,WACD,CAAI,kBACJ,CAAI;;mFAEiE,CAAC,EAAW;sDACzC,KAAK,uBAAuB;wBAC1D,KAAK,UAAU,CAAI,oCAAoC,KAAK,QAAQ,WAAW,EAAQ;;;;;;2DAMpD,CAAC,KAAK,eAAe;sDAC1B,KAAK,uBAAuB;;;kBAIhE,KAAK,WACD,CAAI;;;mCAGS,KAAK,eAAe;;;;;;+CAMR,EAAU;;0BAGnC,EACL;gBAER;;;CAGP;AAKF;AA7OG,EAAA,CAAA,EAAS;CAAE,MAAM;CAAS,SAAS;AAAK,CAAC,CAAA,GAAA,EAAA,WAAA,QAAA,KAAA,CAAA,GACzC,EAAA,CAAA,EAAS;CAAE,MAAM;CAAS,SAAS;CAAM,WAAW;AAAiB,CAAC,CAAA,GAAA,EAAA,WAAA,gBAAA,KAAA,CAAA,GACtE,EAAA,CAAA,EAAS;CAAE,MAAM;CAAS,SAAS;CAAM,WAAW;AAAoB,CAAC,CAAA,GAAA,EAAA,WAAA,mBAAA,KAAA,CAAA,GAMzE,EAAA,CAAA,EAAS;CAAE,MAAM;CAAS,SAAS;CAAM,WAAW;AAAe,CAAC,CAAA,GAAA,EAAA,WAAA,eAAA,KAAA,CAAA,GAEpE,EAAA,CAAA,EAAS;CAAE,MAAM;CAAQ,SAAS;AAAK,CAAC,CAAA,GAAA,EAAA,WAAA,aAAA,IAAA,GAYxC,EAAA,CAAA,EAAS,EAAE,MAAM,OAAO,CAAC,CAAA,GAAA,EAAA,WAAA,WAAA,KAAA,CAAA,GAEzB,EAAA,CAAA,EAAS;CAAE,MAAM;CAAS,SAAS;AAAK,CAAC,CAAA,GAAA,EAAA,WAAA,YAAA,KAAA,CAAA,GAMzC,EAAA,CAAA,EAAS;CAAE,MAAM;CAAS,SAAS;AAAK,CAAC,CAAA,GAAA,EAAA,WAAA,YAAA,KAAA,CAAA,GAMzC,EAAA,CAAA,EAAS;CAAE,MAAM;CAAQ,WAAW;AAAe,CAAC,CAAA,GAAA,EAAA,WAAA,eAAA,KAAA,CAAA,GAxCtD,IAAA,EAAA,CAAA,EAAc,eAAe,CAAA,GAAA,CAAA"}
@@ -1,5 +1,5 @@
1
1
  //#region src/components/drawer/style.css?inline
2
- var e = "*,:before,:after{box-sizing:border-box}:host{display:contents}dialog{box-sizing:border-box;width:var(--wui-drawer-width,320px);max-width:90vw;height:100dvh;max-height:100dvh;color:var(--wui-color-text,#1b1b1b);transition:transform var(--wui-duration-drawer-enter,.28s) var(--wui-ease-enter,cubic-bezier(.23, 1, .32, 1));background:0 0;border:none;margin:0;padding:0;font-family:inherit;inset:0 0 0 auto;overflow:visible;transform:translate(100%)}dialog::backdrop{opacity:0;background:var(--wui-drawer-overlay-bg,#0000001f);transition:opacity var(--wui-duration-drawer-enter,.28s) ease}dialog[open].is-visible,:host([placement=left]) dialog[open].is-visible,:host([placement=top]) dialog[open].is-visible,:host([placement=bottom]) dialog[open].is-visible{transform:translate(0)}dialog.is-closing{pointer-events:none;transition:transform var(--wui-duration-drawer-exit,.24s) var(--wui-ease-enter,cubic-bezier(.23, 1, .32, 1))}dialog.is-closing::backdrop{transition:opacity var(--wui-duration-drawer-exit,.24s) ease}dialog[open].is-visible::backdrop{opacity:1}:host([placement=left]) dialog{left:0;right:auto;transform:translate(-100%)}:host([placement=top]) dialog,:host([placement=bottom]) dialog{width:100vw;max-width:100vw;height:var(--wui-drawer-height,300px);max-height:90dvh}:host([placement=top]) dialog{inset:0 0 auto;transform:translateY(-100%)}:host([placement=bottom]) dialog{inset:auto 0 0;transform:translateY(100%)}.wui-drawer-body{--wui-glass-shadow:var(--wui-shadow-overlay,2px 16px 40px #0006);background-color:var(--wui-color-surface-overlay,#f6f6f6e0);flex-direction:column;height:100%;display:flex}.wui-drawer-header{border-bottom:1px solid #0000000f;justify-content:space-between;align-items:center;padding:16px 20px;font-size:18px;font-weight:600;line-height:1.4;display:flex}.wui-drawer-content{overscroll-behavior:contain;font-size:var(--wui-font-size,14px);color:var(--wui-color-text,#1b1b1b);flex:1;padding:20px;line-height:1.5;overflow-y:auto}.wui-drawer-footer{border-top:1px solid #0000000f;flex-shrink:0;padding:16px 20px}@media (prefers-reduced-motion:reduce){dialog{transition:opacity var(--wui-duration-feedback,.1s) ease}dialog::backdrop{transition-duration:var(--wui-duration-feedback,.1s)}dialog[open].is-visible{transform:none}}.wui-drawer-footer[hidden]{display:none}.wui-drawer-close{z-index:3;position:absolute;top:16px;right:16px}";
2
+ var e = "*,:before,:after{box-sizing:border-box}:host{display:contents}dialog{box-sizing:border-box;width:var(--wui-drawer-width,320px);max-width:90vw;height:100dvh;max-height:100dvh;color:var(--wui-color-text,#1b1b1b);transition:transform var(--wui-duration-drawer-enter,.28s) var(--wui-ease-enter,cubic-bezier(.23, 1, .32, 1));background:0 0;border:none;margin:0;padding:0;font-family:inherit;inset:0 0 0 auto;overflow:visible;transform:translate(100%)}dialog::backdrop{opacity:0;background:var(--wui-drawer-overlay-bg,var(--wui-color-backdrop,#0000001f));transition:opacity var(--wui-duration-drawer-enter,.28s) var(--wui-ease-enter,cubic-bezier(.23, 1, .32, 1))}dialog[open].is-visible,:host([placement=left]) dialog[open].is-visible,:host([placement=top]) dialog[open].is-visible,:host([placement=bottom]) dialog[open].is-visible{transform:translate(0)}dialog.is-closing{pointer-events:none;transition:transform var(--wui-duration-drawer-exit,.24s) var(--wui-ease-enter,cubic-bezier(.23, 1, .32, 1))}dialog.is-closing::backdrop{transition:opacity var(--wui-duration-drawer-exit,.24s) var(--wui-ease-enter,cubic-bezier(.23, 1, .32, 1))}dialog[open].is-visible::backdrop{opacity:1}:host([placement=left]) dialog{left:0;right:auto;transform:translate(-100%)}:host([placement=top]) dialog,:host([placement=bottom]) dialog{width:100vw;max-width:100vw;height:var(--wui-drawer-height,300px);max-height:90dvh}:host([placement=top]) dialog{inset:0 0 auto;transform:translateY(-100%)}:host([placement=bottom]) dialog{inset:auto 0 0;transform:translateY(100%)}.wui-drawer-body{--wui-internal-glass-shadow:var(--wui-shadow-overlay,2px 16px 40px #0006);background-color:var(--wui-drawer-bg,var(--wui-color-surface-overlay,#f6f6f6d1));flex-direction:column;height:100%;display:flex}.wui-drawer-header{border-bottom:1px solid #0000000f;justify-content:space-between;align-items:center;padding:16px 20px;font-size:18px;font-weight:600;line-height:1.4;display:flex}.wui-drawer-header[hidden]{display:none}.wui-drawer-content{overscroll-behavior:contain;font-size:var(--wui-font-size,14px);color:var(--wui-color-text,#1b1b1b);flex:1;padding:20px;line-height:1.5;overflow-y:auto}.wui-drawer-footer{border-top:1px solid #0000000f;flex-shrink:0;padding:16px 20px}@media (prefers-reduced-motion:reduce){dialog{transition:opacity var(--wui-duration-feedback,.1s) ease}dialog::backdrop{transition-duration:var(--wui-duration-feedback,.1s)}dialog[open].is-visible{transform:none}}.wui-drawer-footer[hidden]{display:none}.wui-drawer-close{z-index:3;position:absolute;top:16px;right:16px}:host([headless]) dialog{width:auto;max-width:none}";
3
3
  //#endregion
4
4
  export { e as default };
5
5
 
@@ -1 +1 @@
1
- {"version":3,"file":"style.js","names":[],"sources":["../../../src/components/drawer/style.css?inline"],"sourcesContent":["*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n:host {\n display: contents;\n}\n\n/* ===== Dialog 容器 ===== */\ndialog {\n /* 默认:右侧滑出 — 需显式 left:auto 覆盖 UA dialog:modal 的 left:0 */\n top: 0;\n right: 0;\n bottom: 0;\n left: auto;\n transform: translateX(100%);\n\n /* 阴影属于抽屉主体,不能由同尺寸的原生 dialog 裁剪。内容滚动由 .wui-drawer-content 管理。 */\n overflow: visible;\n\n box-sizing: border-box;\n width: var(--wui-drawer-width, 320px);\n max-width: 90vw;\n height: 100vh;\n height: 100dvh;\n max-height: 100vh;\n max-height: 100dvh;\n margin: 0;\n padding: 0;\n border: none;\n\n font-family: inherit;\n color: var(--wui-color-text, #1b1b1b);\n\n background: transparent;\n\n transition: transform var(--wui-duration-drawer-enter, 280ms) var(--wui-ease-enter, cubic-bezier(0.23, 1, 0.32, 1));\n\n &::backdrop {\n opacity: 0;\n background: var(--wui-drawer-overlay-bg, rgb(0 0 0 / 0.12));\n transition: opacity var(--wui-duration-drawer-enter, 280ms) ease;\n }\n}\n\ndialog[open].is-visible {\n transform: translate(0, 0);\n}\n\n:host([placement='left']) dialog[open].is-visible,\n:host([placement='top']) dialog[open].is-visible,\n:host([placement='bottom']) dialog[open].is-visible {\n transform: translate(0, 0);\n}\n\ndialog.is-closing {\n pointer-events: none;\n transition: transform var(--wui-duration-drawer-exit, 240ms) var(--wui-ease-enter, cubic-bezier(0.23, 1, 0.32, 1));\n}\n\ndialog.is-closing::backdrop {\n transition: opacity var(--wui-duration-drawer-exit, 240ms) ease;\n}\n\ndialog[open].is-visible::backdrop {\n opacity: 1;\n}\n\n/* ===== Placement 变体 ===== */\n\n:host([placement='left']) dialog {\n right: auto;\n left: 0;\n transform: translateX(-100%);\n}\n\n:host([placement='top']) dialog,\n:host([placement='bottom']) dialog {\n width: 100vw;\n max-width: 100vw;\n height: var(--wui-drawer-height, 300px);\n max-height: 90dvh;\n}\n\n:host([placement='top']) dialog {\n top: 0;\n right: 0;\n bottom: auto;\n left: 0;\n transform: translateY(-100%);\n}\n\n:host([placement='bottom']) dialog {\n top: auto;\n right: 0;\n bottom: 0;\n left: 0;\n transform: translateY(100%);\n}\n\n/* ===== 抽屉主体 ===== */\n\n.wui-drawer-body {\n --wui-glass-shadow: var(--wui-shadow-overlay, 2px 16px 40px rgb(0 0 0 / 0.4));\n display: flex;\n flex-direction: column;\n height: 100%;\n background-color: var(--wui-color-surface-overlay, rgb(246 246 246 / 0.88));\n}\n\n/* ===== Header ===== */\n\n.wui-drawer-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n\n padding: 16px 20px;\n border-bottom: 1px solid rgb(0 0 0 / 0.06);\n\n font-size: 18px;\n font-weight: 600;\n line-height: 1.4;\n}\n\n/* ===== 内容区 ===== */\n\n.wui-drawer-content {\n overflow-y: auto;\n overscroll-behavior: contain;\n flex: 1;\n\n padding: 20px;\n\n font-size: var(--wui-font-size, 14px);\n line-height: 1.5;\n color: var(--wui-color-text, #1b1b1b);\n}\n\n/* ===== Footer ===== */\n\n.wui-drawer-footer {\n flex-shrink: 0;\n padding: 16px 20px;\n border-top: 1px solid rgb(0 0 0 / 0.06);\n}\n\n@media (prefers-reduced-motion: reduce) {\n dialog {\n transition: opacity var(--wui-duration-feedback, 100ms) ease;\n }\n\n dialog::backdrop {\n transition-duration: var(--wui-duration-feedback, 100ms);\n }\n\n dialog[open].is-visible {\n transform: none;\n }\n}\n\n.wui-drawer-footer[hidden] {\n display: none;\n}\n\n/* ===== 关闭按钮(独立定位,与 header 解耦) ===== */\n\n.wui-drawer-close {\n position: absolute;\n z-index: 3;\n top: 16px;\n right: 16px;\n}\n"],"mappings":""}
1
+ {"version":3,"file":"style.js","names":[],"sources":["../../../src/components/drawer/style.css?inline"],"sourcesContent":["*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n:host {\n display: contents;\n}\n\n/* ===== Dialog 容器 ===== */\ndialog {\n /* 默认:右侧滑出 — 需显式 left:auto 覆盖 UA dialog:modal 的 left:0 */\n top: 0;\n right: 0;\n bottom: 0;\n left: auto;\n transform: translateX(100%);\n\n /* 阴影属于抽屉主体,不能由同尺寸的原生 dialog 裁剪。内容滚动由 .wui-drawer-content 管理。 */\n overflow: visible;\n\n box-sizing: border-box;\n width: var(--wui-drawer-width, 320px);\n max-width: 90vw;\n height: 100vh;\n height: 100dvh;\n max-height: 100vh;\n max-height: 100dvh;\n margin: 0;\n padding: 0;\n border: none;\n\n font-family: inherit;\n color: var(--wui-color-text, #1b1b1b);\n\n background: transparent;\n\n transition: transform var(--wui-duration-drawer-enter, 280ms) var(--wui-ease-enter, cubic-bezier(0.23, 1, 0.32, 1));\n\n &::backdrop {\n opacity: 0;\n background: var(--wui-drawer-overlay-bg, var(--wui-color-backdrop, rgb(0 0 0 / 0.12)));\n transition: opacity var(--wui-duration-drawer-enter, 280ms) var(--wui-ease-enter, cubic-bezier(0.23, 1, 0.32, 1));\n }\n}\n\ndialog[open].is-visible {\n transform: translate(0, 0);\n}\n\n:host([placement='left']) dialog[open].is-visible,\n:host([placement='top']) dialog[open].is-visible,\n:host([placement='bottom']) dialog[open].is-visible {\n transform: translate(0, 0);\n}\n\ndialog.is-closing {\n pointer-events: none;\n transition: transform var(--wui-duration-drawer-exit, 240ms) var(--wui-ease-enter, cubic-bezier(0.23, 1, 0.32, 1));\n}\n\ndialog.is-closing::backdrop {\n transition: opacity var(--wui-duration-drawer-exit, 240ms) var(--wui-ease-enter, cubic-bezier(0.23, 1, 0.32, 1));\n}\n\ndialog[open].is-visible::backdrop {\n opacity: 1;\n}\n\n/* ===== Placement 变体 ===== */\n\n:host([placement='left']) dialog {\n right: auto;\n left: 0;\n transform: translateX(-100%);\n}\n\n:host([placement='top']) dialog,\n:host([placement='bottom']) dialog {\n width: 100vw;\n max-width: 100vw;\n height: var(--wui-drawer-height, 300px);\n max-height: 90dvh;\n}\n\n:host([placement='top']) dialog {\n top: 0;\n right: 0;\n bottom: auto;\n left: 0;\n transform: translateY(-100%);\n}\n\n:host([placement='bottom']) dialog {\n top: auto;\n right: 0;\n bottom: 0;\n left: 0;\n transform: translateY(100%);\n}\n\n/* ===== 抽屉主体 ===== */\n\n.wui-drawer-body {\n --wui-internal-glass-shadow: var(--wui-shadow-overlay, 2px 16px 40px rgb(0 0 0 / 0.4));\n display: flex;\n flex-direction: column;\n height: 100%;\n background-color: var(--wui-drawer-bg, var(--wui-color-surface-overlay, rgb(246 246 246 / 0.82)));\n}\n\n/* ===== Header ===== */\n\n.wui-drawer-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n\n padding: 16px 20px;\n border-bottom: 1px solid rgb(0 0 0 / 0.06);\n\n font-size: 18px;\n font-weight: 600;\n line-height: 1.4;\n}\n\n/* ===== 隐藏态 ===== */\n\n.wui-drawer-header[hidden] {\n display: none;\n}\n\n/* ===== 内容区 ===== */\n\n.wui-drawer-content {\n overflow-y: auto;\n overscroll-behavior: contain;\n flex: 1;\n\n padding: 20px;\n\n font-size: var(--wui-font-size, 14px);\n line-height: 1.5;\n color: var(--wui-color-text, #1b1b1b);\n}\n\n/* ===== Footer ===== */\n\n.wui-drawer-footer {\n flex-shrink: 0;\n padding: 16px 20px;\n border-top: 1px solid rgb(0 0 0 / 0.06);\n}\n\n@media (prefers-reduced-motion: reduce) {\n dialog {\n transition: opacity var(--wui-duration-feedback, 100ms) ease;\n }\n\n dialog::backdrop {\n transition-duration: var(--wui-duration-feedback, 100ms);\n }\n\n dialog[open].is-visible {\n transform: none;\n }\n}\n\n.wui-drawer-footer[hidden] {\n display: none;\n}\n\n/* ===== 关闭按钮(独立定位,与 header 解耦) ===== */\n\n.wui-drawer-close {\n position: absolute;\n z-index: 3;\n top: 16px;\n right: 16px;\n}\n\n/* ===== Headless 模式 ===== */\n\n/* Headless 模式下,dialog 只作为 overlay 容器,不渲染内置 UI */\n:host([headless]) dialog {\n /* 保持 overlay 基础设施,移除内置样式 */\n width: auto;\n max-width: none;\n}\n"],"mappings":""}
@@ -1,4 +1,4 @@
1
- import e from "../../_virtual/_@oxc-project_runtime@0.140.0/helpers/esm/decorate.js";
1
+ import e from "../../_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorate.js";
2
2
  import { normalizeLiteral as t, normalizeNumber as n } from "../../shared/normalize/index.js";
3
3
  import { defineOverlay as r } from "../../shared/overlay/overlay.js";
4
4
  import { hideOverlayPresence as i, showOverlayPresence as a } from "../../shared/overlay/presence.js";
@@ -1,4 +1,4 @@
1
- import e from "../../_virtual/_@oxc-project_runtime@0.140.0/helpers/esm/decorate.js";
1
+ import e from "../../_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorate.js";
2
2
  import t from "./style.js";
3
3
  import { LitElement as n, html as r, unsafeCSS as i } from "lit";
4
4
  import { customElement as a } from "lit/decorators.js";
@@ -1,4 +1,4 @@
1
- import e from "../../_virtual/_@oxc-project_runtime@0.140.0/helpers/esm/decorate.js";
1
+ import e from "../../_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorate.js";
2
2
  import t from "./style.js";
3
3
  import { LitElement as n, html as r, unsafeCSS as i } from "lit";
4
4
  import { customElement as a } from "lit/decorators.js";
@@ -1,5 +1,5 @@
1
1
  //#region src/components/dropdown-header/style.css?inline
2
- var e = "*,:before,:after{box-sizing:border-box}:host{display:block}.header{-webkit-user-select:none;user-select:none;color:#0006;padding:8px 12px 4px;font-size:11px;font-weight:600}";
2
+ var e = "*,:before,:after{box-sizing:border-box}:host{display:block}.header{-webkit-user-select:none;user-select:none;color:var(--wui-color-text-tertiary,#1b1b1b59);padding:8px 8px 4px;font-size:12px;font-weight:600}";
3
3
  //#endregion
4
4
  export { e as default };
5
5
 
@@ -1 +1 @@
1
- {"version":3,"file":"style.js","names":[],"sources":["../../../src/components/dropdown-header/style.css?inline"],"sourcesContent":["*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n:host {\n display: block;\n}\n\n.header {\n user-select: none;\n\n padding: 8px 12px 4px;\n\n font-size: 11px;\n font-weight: 600;\n color: rgb(0 0 0 / 0.4);\n}\n"],"mappings":""}
1
+ {"version":3,"file":"style.js","names":[],"sources":["../../../src/components/dropdown-header/style.css?inline"],"sourcesContent":["*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n:host {\n display: block;\n}\n\n.header {\n user-select: none;\n\n padding: 8px 8px 4px;\n\n font-size: 12px;\n font-weight: 600;\n color: var(--wui-color-text-tertiary, rgb(27 27 27 / 0.35));\n}\n"],"mappings":""}