@greypan/web-ui 6.3.0 → 6.4.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 (107) hide show
  1. package/README.CN.md +13 -4
  2. package/README.md +13 -4
  3. package/dist/_virtual/{_@oxc-project_runtime@0.146.0 → _@oxc-project_runtime@0.149.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/components/autocomplete/index.js +3 -3
  11. package/dist/components/autocomplete/index.js.map +1 -1
  12. package/dist/components/autocomplete/style.js +1 -1
  13. package/dist/components/autocomplete/style.js.map +1 -1
  14. package/dist/components/avatar/index.js +1 -1
  15. package/dist/components/back-top/index.js +1 -1
  16. package/dist/components/badge/index.js +1 -1
  17. package/dist/components/button/index.js +1 -1
  18. package/dist/components/button-group/index.js +1 -1
  19. package/dist/components/checkbox/index.js +2 -2
  20. package/dist/components/checkbox/index.js.map +1 -1
  21. package/dist/components/checkbox/style.js +1 -1
  22. package/dist/components/checkbox/style.js.map +1 -1
  23. package/dist/components/checkbox-group/index.js +2 -2
  24. package/dist/components/checkbox-group/index.js.map +1 -1
  25. package/dist/components/collapse/index.js +3 -3
  26. package/dist/components/collapse/index.js.map +1 -1
  27. package/dist/components/context-menu/index.js +7 -7
  28. package/dist/components/context-menu/index.js.map +1 -1
  29. package/dist/components/dialog/index.js +1 -1
  30. package/dist/components/dialog/style.js +1 -1
  31. package/dist/components/dialog/style.js.map +1 -1
  32. package/dist/components/drawer/index.js +1 -1
  33. package/dist/components/drawer/index.js.map +1 -1
  34. package/dist/components/drawer/style.js +1 -1
  35. package/dist/components/dropdown/index.js +5 -5
  36. package/dist/components/dropdown/index.js.map +1 -1
  37. package/dist/components/dropdown-divider/index.js +1 -1
  38. package/dist/components/dropdown-header/index.js +1 -1
  39. package/dist/components/dropdown-item/index.js +1 -1
  40. package/dist/components/empty/index.js +1 -1
  41. package/dist/components/icon/index.js +1 -1
  42. package/dist/components/icon/style.js +1 -1
  43. package/dist/components/icon/style.js.map +1 -1
  44. package/dist/components/image-preview/index.js +5 -5
  45. package/dist/components/image-preview/index.js.map +1 -1
  46. package/dist/components/image-preview/style.js +1 -1
  47. package/dist/components/image-preview/style.js.map +1 -1
  48. package/dist/components/input/index.js +2 -2
  49. package/dist/components/input/index.js.map +1 -1
  50. package/dist/components/input/style.js.map +1 -1
  51. package/dist/components/input-number/index.js +2 -2
  52. package/dist/components/input-number/index.js.map +1 -1
  53. package/dist/components/layout/index.js +1 -1
  54. package/dist/components/option/index.js +1 -1
  55. package/dist/components/popover/index.js +1 -1
  56. package/dist/components/radio/index.js +2 -2
  57. package/dist/components/radio/index.js.map +1 -1
  58. package/dist/components/radio/style.js +1 -1
  59. package/dist/components/radio/style.js.map +1 -1
  60. package/dist/components/radio-group/index.js +2 -2
  61. package/dist/components/radio-group/index.js.map +1 -1
  62. package/dist/components/segmented/index.js +1 -1
  63. package/dist/components/segmented/style.js +1 -1
  64. package/dist/components/segmented/style.js.map +1 -1
  65. package/dist/components/segmented-trigger/index.js +1 -1
  66. package/dist/components/select/index.js +3 -3
  67. package/dist/components/select/index.js.map +1 -1
  68. package/dist/components/select/style.js +1 -1
  69. package/dist/components/select/style.js.map +1 -1
  70. package/dist/components/slider/index.js +1 -1
  71. package/dist/components/slider/style.js +1 -1
  72. package/dist/components/slider/style.js.map +1 -1
  73. package/dist/components/spinner/index.js +2 -2
  74. package/dist/components/spinner/index.js.map +1 -1
  75. package/dist/components/spinner/style.js +1 -1
  76. package/dist/components/spinner/style.js.map +1 -1
  77. package/dist/components/svg-draw-lines/index.js +1 -1
  78. package/dist/components/svg-draw-lines/style.js +1 -1
  79. package/dist/components/svg-draw-lines/style.js.map +1 -1
  80. package/dist/components/switch/index.js +2 -2
  81. package/dist/components/switch/index.js.map +1 -1
  82. package/dist/components/switch/style.js +1 -1
  83. package/dist/components/switch/style.js.map +1 -1
  84. package/dist/components/textarea/index.js +3 -3
  85. package/dist/components/textarea/index.js.map +1 -1
  86. package/dist/components/textarea/style.js.map +1 -1
  87. package/dist/components/theme/index.js +1 -1
  88. package/dist/components/theme/style.js +1 -1
  89. package/dist/components/theme/style.js.map +1 -1
  90. package/dist/components/toast/style.js +1 -1
  91. package/dist/components/toast/style.js.map +1 -1
  92. package/dist/components/toast/toast.js +1 -1
  93. package/dist/components/tooltip/index.js +6 -3
  94. package/dist/components/tooltip/index.js.map +1 -1
  95. package/dist/shared/form-association/index.js +1 -1
  96. package/dist/shared/form-association/index.js.map +1 -1
  97. package/dist/shared/menu-portal/menu-tree.js +1 -1
  98. package/dist/shared/menu-portal/menu-tree.js.map +1 -1
  99. package/dist/shared/overlay/native-dialog-presence.js +2 -2
  100. package/dist/shared/overlay/native-dialog-presence.js.map +1 -1
  101. package/dist/shared/overlay/overlay.js +1 -1
  102. package/dist/shared/overlay/overlay.js.map +1 -1
  103. package/dist/shared/overlay/portal.js +1 -1
  104. package/dist/shared/overlay/portal.js.map +1 -1
  105. package/dist/shared/visible-area/index.js +1 -1
  106. package/dist/shared/visible-area/index.js.map +1 -1
  107. package/package.json +3 -3
@@ -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 { attachDragGesture, type DragGestureHandle, rubberband, SPRING_PRESETS, springOffsets } from '@/shared/gesture'\nimport { normalizeLiteral } from '@/shared/normalize'\nimport { dispatchOpenChangeEvent } from '@/shared/open-state'\nimport { defineNativeDialogPresence } from '@/shared/overlay/native-dialog-presence'\nimport { defineNestedDrawerLayers } from '@/shared/overlay/nested-drawer-layers'\nimport { findNearestTheme } from '@/shared/overlay/theme-overlay-scope'\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// 拖拽关闭判定:位移超过抽屉尺寸 1/3,或闭合方向甩动速度超过 500px/s 视为关闭意图。\nconst DRAG_CLOSE_RATIO = 1 / 3\nconst DRAG_FLICK_VELOCITY = 500\nconst DRAG_REQUEST_WINDOW_MS = 120\nconst SPRING_SAMPLE_MS = 16\n\n/*\n * 拖拽期间由 JS 写入的遮罩透明度变量参与 WAAPI 关键帧。未注册的自定义属性在\n * 关键帧之间是离散插值(半帧跳变),注册为 <number> 后获得线性插值。\n * inherits 必须为 true:::backdrop 只从 originating element 继承「可继承属性」,\n * 注册为不可继承会让变量到不了遮罩,跟手淡出整体失效(Chrome 151 实证)。\n * 注册表是全局的,模块重复执行(HMR)时捕获已注册错误并忽略。\n */\nif (typeof CSS !== 'undefined' && 'registerProperty' in CSS) {\n try {\n CSS.registerProperty({\n name: '--wui-internal-drag-backdrop-opacity',\n syntax: '<number>',\n inherits: true,\n initialValue: '1'\n })\n } catch {\n // 已注册(如 HMR 重复执行)时忽略\n }\n /*\n * 公开 token 注册为 <length>:Consumer 传 unitless 0 时计算值被归一为 0px,\n * 避免 calc(100% + 0) 因 number/percentage 不兼容而使闭合位移失效。\n */\n try {\n CSS.registerProperty({\n name: '--wui-drawer-inset',\n syntax: '<length>',\n inherits: true,\n initialValue: '8px'\n })\n } catch {\n // 已注册(如 HMR 重复执行)时忽略\n }\n}\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 * Controlled 模式下,Escape、遮罩、关闭按钮和拖拽关闭只派发 `open-change` 请求,\n * 不会自行修改 `open`。Consumer 回写 `open` 后才执行关闭动画。\n * 程序化 API(show/close/直接赋值 open)不受此模式影响,始终直通。\n */\n @property({ type: Boolean, reflect: true }) controlled = 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 /**\n * 启用拖拽关闭:打开态在抽屉内缘渲染 drag bar(灰色胶囊 + 加宽热区),\n * 指针拖拽实时跟手,松手按位移与甩动速度判定关闭或弹回。\n * 关闭态基于原生 dialog 无可见渲染物,因此不支持从关闭态拖拽打开。\n */\n @property({ type: Boolean, reflect: true }) override draggable = 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 // nested 层序:打开后纳入全局栈,上层打开/关闭时本层缩放平移(对齐 Base UI)。\n private readonly _nestedLayers = defineNestedDrawerLayers().make({\n getDialog: () => this.dialog,\n getPlacement: () => this._placement\n })\n\n // ===== 拖拽关闭手势状态 =====\n private _dragGesture: DragGestureHandle | null = null\n // pointerdown 时刻已存在的闭合方向位移(从弹回动画中抓取时非 0)。\n private _dragInitialOffset = 0\n private _dragOffset = 0\n private _dragAnimation: Animation | null = null\n // controlled:弹簧到闭合位后等待 Consumer 回写 open;超时未回写则弹回。\n private _dragAwaitWriteback = false\n private _dragRequestTimer: ReturnType<typeof setTimeout> | undefined\n\n // placement 的闭合轴向:right/left 沿 X 轴,top/bottom 沿 Y 轴。\n private get _dragAxis(): 'x' | 'y' {\n return this._placement === 'left' || this._placement === 'right' ? 'x' : 'y'\n }\n\n // 位移正方向 = 抽屉的闭合方向(拖出屏幕为正)。\n private get _dragCloseSign(): number {\n switch (this._placement) {\n case 'right':\n return 1\n case 'left':\n return -1\n case 'top':\n return -1\n case 'bottom':\n return 1\n }\n }\n\n private _measureDragSize(): number {\n const dialog = this.dialog\n if (!dialog) return 0\n return this._dragAxis === 'x' ? dialog.offsetWidth : dialog.offsetHeight\n }\n\n // 浮动卡片(非 headless)的四周留边;闭合位移需越过它才能完全滑出视口。\n // headless 在 :host([headless]) dialog 上显式归零(防嵌套继承),解析失败回退 0。\n private _readDrawerInset(dialog: HTMLDialogElement): number {\n const raw = getComputedStyle(dialog).getPropertyValue('--wui-internal-drawer-inset')\n const parsed = Number.parseFloat(raw)\n return Number.isFinite(parsed) ? parsed : 0\n }\n\n // 闭合方向上的完全出屏距离:抽屉尺寸 + 浮动留边(headless 下即尺寸本身)。\n // CSS 闭合态 transform、controlled 悬停终态与弹簧终点共用同一数学,避免衔接跳变。\n private _dragCloseDistance(dialog: HTMLDialogElement): number {\n return this._measureDragSize() + this._readDrawerInset(dialog)\n }\n\n private _isReducedMotion(): boolean {\n // 优先尊重所在 web-ui-theme 的 motion 设置(与 svg-draw-lines 一致);\n // 无主题范围时回退到系统 prefers-reduced-motion。jsdom 等环境无 matchMedia,视为完整动效。\n const theme = findNearestTheme(this)\n if (theme) return theme.isReducedMotion()\n try {\n return typeof window.matchMedia === 'function' && window.matchMedia('(prefers-reduced-motion: reduce)').matches\n } catch {\n return false\n }\n }\n\n private _isDragging(): boolean {\n return this._dragGesture?.isDragging() ?? false\n }\n\n private _cancelDragAwait() {\n if (this._dragRequestTimer !== undefined) {\n clearTimeout(this._dragRequestTimer)\n this._dragRequestTimer = undefined\n }\n this._dragAwaitWriteback = false\n }\n\n private _handleDragPointerDown(e: PointerEvent) {\n if (!this.open || !this.draggable || this._isDragging()) return\n // 多点触控的副指针(isPrimary 明确为 false)不参与手势。\n if (e.isPrimary === false) return\n\n const dialog = this.dialog\n if (!dialog) return\n // is-visible 由 presence 在 showModal 后一帧加上;就位前(打开极早期)忽略。\n // 就位后即使 enter 过渡仍在进行也允许抓取,起始位移从当前计算值续接。\n if (!dialog.classList.contains('is-visible')) return\n\n // 先读取动画中的当前位移再取消弹回动画,避免取消后回跳到内联样式值。\n const currentTransform = getComputedStyle(dialog).transform\n const axisValue =\n currentTransform && currentTransform !== 'none' && typeof DOMMatrixReadOnly === 'function'\n ? new DOMMatrixReadOnly(currentTransform)[this._dragAxis === 'x' ? 'm41' : 'm42']\n : 0\n this._dragAnimation?.cancel()\n this._dragAnimation = null\n this._cancelDragAwait()\n\n this._dragInitialOffset = axisValue * this._dragCloseSign\n this._dragOffset = this._dragInitialOffset\n dialog.classList.add('is-dragging')\n\n this._dragGesture = attachDragGesture(e, {\n axis: this._dragAxis,\n onMove: info => {\n const pointerDelta = this._dragAxis === 'x' ? info.deltaX : info.deltaY\n // 闭合方向全额跟随;开启方向施加阻尼(橡皮筋),最多回弹 10% 抽屉尺寸。\n const raw = this._dragInitialOffset + pointerDelta * this._dragCloseSign\n this._dragOffset = rubberband(raw, this._measureDragSize() * 0.1, 0.15)\n\n // 达到关闭阈值时胶囊变 accent 色作视觉确认(ADR-0027)。\n const dragSize = this._measureDragSize()\n dialog.classList.toggle('is-drag-close', dragSize > 0 && this._dragOffset > dragSize * DRAG_CLOSE_RATIO)\n this._applyDragOffset(dialog, this._dragOffset)\n },\n onEnd: info => {\n this._dragGesture = null\n dialog.classList.remove('is-dragging', 'is-drag-close')\n const size = this._measureDragSize()\n const velocity = (this._dragAxis === 'x' ? info.velocityX : info.velocityY) * this._dragCloseSign\n const shouldClose =\n this._dragOffset > size * DRAG_CLOSE_RATIO || (this._dragOffset > 8 && velocity > DRAG_FLICK_VELOCITY)\n\n if (shouldClose) this._springToClose(dialog, velocity)\n else this._springRebound(dialog, velocity, this._dragOffset)\n },\n onCancel: () => {\n this._dragGesture = null\n dialog.classList.remove('is-dragging', 'is-drag-close')\n this._springRebound(dialog, 0, this._dragOffset)\n }\n })\n }\n\n // 受控状态写入等外部原因强制终结拖拽:清手势状态与拖拽样式,不弹回,\n // 后续管线(关闭/打开)由调用方继续执行。\n private _cancelActiveDrag() {\n if (!this._isDragging()) return\n this._dragGesture?.destroy()\n this._dragGesture = null\n const dialog = this.dialog\n if (!dialog) return\n dialog.classList.remove('is-dragging', 'is-drag-close')\n this._clearDragStyles(dialog)\n }\n\n // 将闭合方向位移映射为 dialog transform,并同步遮罩透明度保持跟手反馈。\n private _applyDragOffset(dialog: HTMLDialogElement, offset: number) {\n const sign = this._dragCloseSign\n const value = offset * sign\n dialog.style.transform = this._dragAxis === 'x' ? `translateX(${value}px)` : `translateY(${value}px)`\n const size = this._measureDragSize()\n const progress = size > 0 ? Math.min(1, Math.max(0, offset / size)) : 0\n dialog.style.setProperty('--wui-internal-drag-backdrop-opacity', String(1 - progress))\n }\n\n private _clearDragStyles(dialog: HTMLDialogElement) {\n dialog.style.removeProperty('transform')\n dialog.style.removeProperty('--wui-internal-drag-backdrop-opacity')\n }\n\n // 弹簧到完全闭合;controlled 下保持闭合位等待回写,其余走常规关闭管线。\n private _springToClose(dialog: HTMLDialogElement, velocity: number) {\n const from = this._dragOffset\n this._dragOffset = 0\n const sign = this._dragCloseSign\n // 弹簧终点 = 完全出屏距离(含浮动留边),与 CSS 闭合态/悬停终态一致,\n // 否则 onfinish 后会有一个留边宽度的瞬移。\n const to = this._dragCloseDistance(dialog)\n const size = this._measureDragSize()\n\n const finishClose = () => {\n if (this.controlled) {\n // 保持在闭合位(is-visible 未移除,状态仍 open),等待 Consumer 回写或超时弹回。\n const distance = to\n dialog.style.transform =\n this._dragAxis === 'x' ? `translateX(${distance * sign}px)` : `translateY(${distance * sign}px)`\n dialog.style.setProperty('--wui-internal-drag-backdrop-opacity', '0')\n } else {\n // 移除 is-visible 后基础 transform 即闭合位,清内联样式不产生跳变;\n // presence.sync(false) 检测不到 is-visible 会立即完成关闭,不重播退出动画。\n dialog.classList.remove('is-visible')\n this._clearDragStyles(dialog)\n }\n this._closeFromDrag()\n }\n\n if (this._isReducedMotion() || typeof dialog.animate !== 'function') {\n finishClose()\n return\n }\n\n const samples = springOffsets(from, to, velocity, SPRING_PRESETS.close)\n const keyframes = samples.map(o => ({\n transform: this._dragAxis === 'x' ? `translateX(${o * sign}px)` : `translateY(${o * sign}px)`,\n '--wui-internal-drag-backdrop-opacity': String(Math.max(0, 1 - o / size))\n }))\n // 采样帧含首值与附加终点,实际时长为帧间隔数 × 采样周期。\n const duration = (samples.length - 1) * SPRING_SAMPLE_MS\n // fill:'both' 与 _springRebound 对称:堵住 finish→onfinish 清理窗口,\n // 防止该窗口内绘制的帧暴露内联位移、随清理触发跳变。\n const animation = dialog.animate(keyframes, { duration, easing: 'linear', fill: 'both' })\n // 审计(Safari 不采信 fill 覆盖的 before-change style):受控分支 finishClose\n // 把内联写入闭合位,cancel 时内联 == fill 终态 == 生效闭合位,三值一致;非受控\n // 分支 finishClose 在 fill 覆盖期间完成 is-visible 切换并清内联,cancel 后\n // computed 落到 CSS 闭合位且 == fill 终态 to,同样无中间旧值可被采样。因此\n // 「先应用终态再 cancel」对关闭路径已满足,无需像弹回那样保留内联终值。\n animation.onfinish = () => {\n finishClose()\n animation.cancel()\n }\n }\n\n // 弹回打开:从 from 位移弹回 0;结束后把内联样式交还打开态/CSS transition 管辖。\n private _springRebound(dialog: HTMLDialogElement, velocity: number, from: number) {\n this._dragOffset = 0\n\n // 弹回结束把内联样式覆写为打开态终值而非删除:Safari 计算 CSS transition 的\n // before-change style 时不采信 WAAPI fill 动画的覆盖值,删除内联会让它采样到\n // 拖拽残留位移(如 -64px)作为旧值,与打开态 0 之间触发一次额外的 transform\n // 过渡 —— 即弹回结束后再次多弹一次(Chrome 采信 fill 故不复现)。\n // translateX(0px)/translateY(0px) 与打开态 CSS transform translate(0,0) 数学等价,\n // backdrop opacity 1 与 --wui-internal-drag-backdrop-opacity 的默认值一致;内联\n // 保留到关闭管线:关闭时 _springToClose 的 animate 会覆盖它,其 finishClose 清理\n // 内联时 dialog 已进入关闭流程(is-visible 移除/闭合位写入),无 before-change\n // 采样窗口。\n const applyOpenInlineState = () => {\n dialog.style.transform = this._dragAxis === 'x' ? 'translateX(0px)' : 'translateY(0px)'\n dialog.style.setProperty('--wui-internal-drag-backdrop-opacity', '1')\n }\n\n // 同步收尾,顺序不可调换:\n // 1. 把内联覆写为打开态终值(保留 is-dragging,transition:none 仍在抑制);\n // 2. void dialog.offsetWidth 强制一次同步样式重算:Safari 的 before-change\n // style 不采信 WAAPI fill 覆盖,此刻计算样式已真实归 0,重算把 0「烘焙」为\n // 它内部 transition 参考值。rAF 不够:rAF 回调与 onfinish 常落在同一渲染帧,\n // Safari 在抑制解除时还没把 0 写进 transition 状态,会按旧值 -offset 补触发\n // (MutationObserver 实测:onfinish 与 is-dragging 移除同毫秒、transitionstart\n // 延后 16ms 仍以 -9.6 为 from)。\n // 3. 同步移除 is-dragging:重算后解除抑制是 0→0,无 before-change/after 差异,\n // 无过渡可触发。顺序若反(先移除类再重算),解除抑制的瞬间 Safari 仍以旧值\n // 为参考,过渡照旧触发。\n // 防御:dialog 已关闭/断连时跳过样式收尾(is-dragging 随元素销毁自然消失)。\n // clearInline:无动画路径(tap、from≈0、reduced-motion、无 WAAPI)没有需要「烘焙」\n // 的拖拽残留,直接移除内联拖拽样式回到 CSS 打开态即可。不能像动画路径那样把内联\n // 写成 translateX(0px) 长期保留:0px 内联会盖住闭合态 CSS transform,用户之后用\n // 按钮/遮罩/Esc(不走 _springToClose 的 animate)关闭时计算值恒为 0、开与关都\n // 不再触发过渡 —— 快速连点后「后续开关丢失过渡动画」的根因。\n const finishRebound = (clearInline: boolean) => {\n this._dragAnimation = null\n if (!dialog.isConnected) return\n if (clearInline) this._clearDragStyles(dialog)\n else applyOpenInlineState()\n void dialog.offsetWidth\n dialog.classList.remove('is-dragging')\n }\n\n if (this._isReducedMotion() || typeof dialog.animate !== 'function' || Math.abs(from) < 1) {\n // 无动画路径同样先加 is-dragging 抑制过渡,再走同一收尾;\n // 保证所有 _springRebound 出口(onfinish/onCancel/受控回写弹回)行为一致。\n dialog.classList.add('is-dragging')\n finishRebound(true)\n return\n }\n\n const sign = this._dragCloseSign\n const size = this._measureDragSize()\n const samples = springOffsets(from, 0, velocity, SPRING_PRESETS.rebound)\n const keyframes = samples.map(o => ({\n transform: this._dragAxis === 'x' ? `translateX(${o * sign}px)` : `translateY(${o * sign}px)`,\n '--wui-internal-drag-backdrop-opacity': String(Math.max(0, 1 - o / size))\n }))\n // 弹回期间抑制 transform/backdrop 的 CSS transition,避免与弹簧动画叠加。\n dialog.classList.add('is-dragging')\n const duration = (samples.length - 1) * SPRING_SAMPLE_MS\n // fill:'both' 堵住 finish→onfinish 清理之间的竞态窗口:无 fill 时动画结束即\n // 失效,窗口内被绘制的帧会暴露内联拖拽位移,随后的清理再触发 CSS 过渡滑回\n // 打开位 —— 表现为回弹后又多弹一次。\n const animation = dialog.animate(keyframes, { duration, easing: 'linear', fill: 'both' })\n // onfinish 时保持 is-dragging(transition 仍被抑制)走 finishRebound:\n // 覆写内联 → 强制重算烘焙 0 → 移除类,Safari 无旧值可采信、无过渡可触发。\n animation.onfinish = () => {\n finishRebound(false)\n animation.cancel()\n }\n this._dragAnimation = animation\n }\n\n private _closeFromDrag() {\n // 先派发关闭请求(_closeFromUser),再进入悬停等待:等待态中 _closeFromUser\n // 会被 L2 去重守卫短路,顺序颠倒会吞掉首次请求。\n this._closeFromUser()\n if (this.controlled) {\n this._dragAwaitWriteback = true\n this._dragRequestTimer = setTimeout(this._handleDragWritebackTimeout, DRAG_REQUEST_WINDOW_MS)\n }\n }\n\n // controlled 回写窗口超时:Consumer 拒绝关闭,从闭合位弹回打开位。\n private readonly _handleDragWritebackTimeout = () => {\n this._dragRequestTimer = undefined\n if (!this._dragAwaitWriteback || !this.open || this._isDragging()) {\n this._dragAwaitWriteback = false\n return\n }\n this._dragAwaitWriteback = false\n\n const dialog = this.dialog\n if (!dialog) return\n dialog.classList.add('is-visible')\n // 悬停终态位于完全出屏位(含留边),弹回也从该真实位置起步,避免首帧内跳。\n this._springRebound(dialog, 0, this._dragCloseDistance(dialog))\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._nestedLayers.dispose()\n this._dragGesture?.destroy()\n this._dragGesture = null\n this._presence.dispose()\n this._scrollLock.release()\n this._dragAnimation?.cancel()\n this._dragAnimation = null\n this._cancelDragAwait()\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 // Consumer 在拖拽进行中写入受控 open:受控状态优先,立即终结手势(等同\n // pointercancel 的清理但不弹回),交由下方 presence 走标准关闭/打开管线。\n if (this._isDragging() && !this.open) this._cancelActiveDrag()\n if (this._userOpenChange.consume()) this.emitOpenChange()\n // controlled 拖拽关闭的回写结果:确认关闭则清除闭合位悬停状态走正常关闭;\n // 拒绝关闭(回写 open=true)则从闭合位(含留边)弹回。\n if (this._dragAwaitWriteback) {\n this._cancelDragAwait()\n const dialog = this.dialog\n if (dialog && !this.open) {\n dialog.classList.remove('is-visible')\n this._clearDragStyles(dialog)\n } else if (dialog && this.open) {\n this._springRebound(dialog, 0, this._dragCloseDistance(dialog))\n }\n }\n // 关闭且无进行中的拖拽动画时,清理可能残留的内联拖拽样式(如弹回路径按\n // Safari 双回弹修复保留的 translateX(0px))。若不清理,0px 内联会盖住闭合态\n // CSS transform,后续开/关都不再触发过渡。拖拽动画进行中(_dragAnimation 非空)\n // 由各自的 onfinish 收尾,这里不干预。\n if (!this.open && this._dragAnimation === null && !this._isDragging()) {\n const dialog = this.dialog\n if (dialog) this._clearDragStyles(dialog)\n }\n this._presence.sync(this.open)\n if (this.open) {\n // presence.sync 已同步发起 showModal:此刻 dialog.open 为真,计数出正确\n // 的层序 depth 并驱动下层缩放。直接同步 register,不等待 is-visible\n //(那要再等一帧,且打开过渡期间上层关系已应确立)。\n this._nestedLayers.register()\n } else {\n this._nestedLayers.unregister()\n }\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 /*\n * nested 场景防连锁:子 drawer 的 dialog 经 slot 投影在本层 shadow 内,其\n * keydown composed 冒泡路径会再次经过本层 dialog(事件路径含 slot 宿主链)。\n * 若事件传播路径在本层 dialog 之前已存在其他 <dialog>,说明该事件来自子层,本层忽略。\n */\n const path = e.composedPath()\n const ownDialogIndex = path.indexOf(e.currentTarget as Node)\n if (ownDialogIndex > 0) {\n const hasChildDialog = path.slice(0, ownDialogIndex).some(n => n instanceof HTMLDialogElement)\n if (hasChildDialog) return\n }\n // 拖拽进行中忽略 ESC,避免手势与关闭管线竞争。\n if (this._isDragging()) {\n e.preventDefault()\n return\n }\n e.preventDefault()\n this._closeFromUser()\n }\n\n show() {\n if (this.open) return\n this.open = true\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 // controlled 悬停等待回写期间,关闭意图已在途(open-change 已派发):\n // Escape/遮罩/关闭按钮的重复触发不再派发第二次请求,也不与超时弹回竞争。\n if (this._dragAwaitWriteback) return\n if (this.controlled) {\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 // 关闭态下的 close 事件只可能是我们自己 finishClosing 排队的异步事件(或冗余的\n // 外部关闭):交给 presence 消费 self-close 标志,避免标志泄漏到下一次真实关闭。\n if (!this.open) {\n this._presence.handleNativeClose()\n return\n }\n\n // 我们自己 dialog.close() 排队的 close 事件是异步任务:正常时序在本关闭会话内\n // 到达,快速「关闭→重开」时它在重新打开之后才到达(过期事件)。两种情况都已在\n // finishClosing 完成全部清理,由 presence 消费并返回 true,不能当成外部关闭把\n // 刚重开的 dialog 关掉——否则重开即被误关,表现为连续开关丢失过渡动画。\n if (this._presence.handleNativeClose()) return\n\n // 悬停等待期间的原生关闭(如表单 method=\"dialog\"):视为回写窗口内的重复\n // 关闭意图,取消等待不补发请求;已原生关闭的 dialog 不再被超时弹回拉起。\n if (this._dragAwaitWriteback) {\n this._cancelDragAwait()\n return\n }\n\n // 原生关闭可绕过 cancel;controlled 时恢复受控状态,由 Consumer 决定是否关闭。\n if (this.controlled) {\n this._presence.sync(true)\n this.emitOpenChange(false)\n return\n }\n\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 // 拖拽进行中 pointer capture 使 click 落在 dialog 上,忽略以避免与手势竞争。\n if (this._isDragging()) return\n this._closeFromUser()\n }\n\n private emitOpenChange(open = this.open) {\n dispatchOpenChangeEvent(this, open)\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 // 拖拽热区:仅在打开且 draggable 时渲染;胶囊 + 加宽命中条贴在抽屉内缘。\n const dragBar = this.draggable\n ? html`\n <div class=\"wui-drawer-drag-zone\" @pointerdown=${this._handleDragPointerDown}>\n <div class=\"wui-drawer-drag-bar\"></div>\n </div>\n `\n : 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>${dragBar}`\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 size=\"14\" .icon=${oouiClose}></web-ui-icon>\n </web-ui-button>\n `\n : nothing\n }\n ${dragBar}\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":"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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 { attachDragGesture, type DragGestureHandle, rubberband, SPRING_PRESETS, springOffsets } from '@/shared/gesture'\nimport { normalizeLiteral } from '@/shared/normalize'\nimport { dispatchOpenChangeEvent } from '@/shared/open-state'\nimport { defineNativeDialogPresence } from '@/shared/overlay/native-dialog-presence'\nimport { defineNestedDrawerLayers } from '@/shared/overlay/nested-drawer-layers'\nimport { findNearestTheme } from '@/shared/overlay/theme-overlay-scope'\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// 拖拽关闭判定:位移超过抽屉尺寸 1/3,或闭合方向甩动速度超过 500px/s 视为关闭意图。\nconst DRAG_CLOSE_RATIO = 1 / 3\nconst DRAG_FLICK_VELOCITY = 500\nconst DRAG_REQUEST_WINDOW_MS = 120\nconst SPRING_SAMPLE_MS = 16\n\n/*\n * 拖拽期间由 JS 写入的遮罩透明度变量参与 WAAPI 关键帧。未注册的自定义属性在\n * 关键帧之间是离散插值(半帧跳变),注册为 <number> 后获得线性插值。\n * inherits 必须为 true:::backdrop 只从 originating element 继承「可继承属性」,\n * 注册为不可继承会让变量到不了遮罩,跟手淡出整体失效(Chrome 151 实证)。\n * 注册表是全局的,模块重复执行(HMR)时捕获已注册错误并忽略。\n */\nif (typeof CSS !== 'undefined' && 'registerProperty' in CSS) {\n try {\n CSS.registerProperty({\n name: '--wui-internal-drag-backdrop-opacity',\n syntax: '<number>',\n inherits: true,\n initialValue: '1'\n })\n } catch {\n // 已注册(如 HMR 重复执行)时忽略\n }\n /*\n * 公开 token 注册为 <length>:Consumer 传 unitless 0 时计算值被归一为 0px,\n * 避免 calc(100% + 0) 因 number/percentage 不兼容而使闭合位移失效。\n */\n try {\n CSS.registerProperty({\n name: '--wui-drawer-inset',\n syntax: '<length>',\n inherits: true,\n initialValue: '8px'\n })\n } catch {\n // 已注册(如 HMR 重复执行)时忽略\n }\n}\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 * Controlled 模式下,Escape、遮罩、关闭按钮和拖拽关闭只派发 `open-change` 请求,\n * 不会自行修改 `open`。Consumer 回写 `open` 后才执行关闭动画。\n * 程序化 API(show/close/直接赋值 open)不受此模式影响,始终直通。\n */\n @property({ type: Boolean, reflect: true }) controlled = 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 /**\n * 启用拖拽关闭:打开态在抽屉内缘渲染 drag bar(灰色胶囊 + 加宽热区),\n * 指针拖拽实时跟手,松手按位移与甩动速度判定关闭或弹回。\n * 关闭态基于原生 dialog 无可见渲染物,因此不支持从关闭态拖拽打开。\n */\n @property({ type: Boolean, reflect: true }) override draggable = 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 // nested 层序:打开后纳入全局栈,上层打开/关闭时本层缩放平移(对齐 Base UI)。\n private readonly _nestedLayers = defineNestedDrawerLayers().make({\n getDialog: () => this.dialog,\n getPlacement: () => this._placement\n })\n\n // ===== 拖拽关闭手势状态 =====\n private _dragGesture: DragGestureHandle | null = null\n // pointerdown 时刻已存在的闭合方向位移(从弹回动画中抓取时非 0)。\n private _dragInitialOffset = 0\n private _dragOffset = 0\n private _dragAnimation: Animation | null = null\n // controlled:弹簧到闭合位后等待 Consumer 回写 open;超时未回写则弹回。\n private _dragAwaitWriteback = false\n private _dragRequestTimer: ReturnType<typeof setTimeout> | undefined\n\n // placement 的闭合轴向:right/left 沿 X 轴,top/bottom 沿 Y 轴。\n private get _dragAxis(): 'x' | 'y' {\n return this._placement === 'left' || this._placement === 'right' ? 'x' : 'y'\n }\n\n // 位移正方向 = 抽屉的闭合方向(拖出屏幕为正)。\n private get _dragCloseSign(): number {\n switch (this._placement) {\n case 'right':\n return 1\n case 'left':\n return -1\n case 'top':\n return -1\n case 'bottom':\n return 1\n }\n }\n\n private _measureDragSize(): number {\n const dialog = this.dialog\n if (!dialog) return 0\n return this._dragAxis === 'x' ? dialog.offsetWidth : dialog.offsetHeight\n }\n\n // 浮动卡片(非 headless)的四周留边;闭合位移需越过它才能完全滑出视口。\n // headless 在 :host([headless]) dialog 上显式归零(防嵌套继承),解析失败回退 0。\n private _readDrawerInset(dialog: HTMLDialogElement): number {\n const raw = getComputedStyle(dialog).getPropertyValue('--wui-internal-drawer-inset')\n const parsed = Number.parseFloat(raw)\n return Number.isFinite(parsed) ? parsed : 0\n }\n\n // 闭合方向上的完全出屏距离:抽屉尺寸 + 浮动留边(headless 下即尺寸本身)。\n // CSS 闭合态 transform、controlled 悬停终态与弹簧终点共用同一数学,避免衔接跳变。\n private _dragCloseDistance(dialog: HTMLDialogElement): number {\n return this._measureDragSize() + this._readDrawerInset(dialog)\n }\n\n private _isReducedMotion(): boolean {\n // 优先尊重所在 web-ui-theme 的 motion 设置(与 svg-draw-lines 一致);\n // 无主题范围时回退到系统 prefers-reduced-motion。jsdom 等环境无 matchMedia,视为完整动效。\n const theme = findNearestTheme(this)\n if (theme) return theme.isReducedMotion()\n try {\n return typeof window.matchMedia === 'function' && window.matchMedia('(prefers-reduced-motion: reduce)').matches\n } catch {\n return false\n }\n }\n\n private _isDragging(): boolean {\n return this._dragGesture?.isDragging() ?? false\n }\n\n private _cancelDragAwait() {\n if (this._dragRequestTimer !== undefined) {\n clearTimeout(this._dragRequestTimer)\n this._dragRequestTimer = undefined\n }\n this._dragAwaitWriteback = false\n }\n\n private _handleDragPointerDown(e: PointerEvent) {\n if (!this.open || !this.draggable || this._isDragging()) return\n // 多点触控的副指针(isPrimary 明确为 false)不参与手势。\n if (e.isPrimary === false) return\n\n const dialog = this.dialog\n if (!dialog) return\n // is-visible 由 presence 在 showModal 后一帧加上;就位前(打开极早期)忽略。\n // 就位后即使 enter 过渡仍在进行也允许抓取,起始位移从当前计算值续接。\n if (!dialog.classList.contains('is-visible')) return\n\n // 先读取动画中的当前位移再取消弹回动画,避免取消后回跳到内联样式值。\n const currentTransform = getComputedStyle(dialog).transform\n const axisValue =\n currentTransform && currentTransform !== 'none' && typeof DOMMatrixReadOnly === 'function'\n ? new DOMMatrixReadOnly(currentTransform)[this._dragAxis === 'x' ? 'm41' : 'm42']\n : 0\n this._dragAnimation?.cancel()\n this._dragAnimation = null\n this._cancelDragAwait()\n\n this._dragInitialOffset = axisValue * this._dragCloseSign\n this._dragOffset = this._dragInitialOffset\n dialog.classList.add('is-dragging')\n\n this._dragGesture = attachDragGesture(e, {\n axis: this._dragAxis,\n onMove: info => {\n const pointerDelta = this._dragAxis === 'x' ? info.deltaX : info.deltaY\n // 闭合方向全额跟随;开启方向施加阻尼(橡皮筋),最多回弹 10% 抽屉尺寸。\n const raw = this._dragInitialOffset + pointerDelta * this._dragCloseSign\n this._dragOffset = rubberband(raw, this._measureDragSize() * 0.1, 0.15)\n\n // 达到关闭阈值时胶囊变 accent 色作视觉确认(ADR-0027)。\n const dragSize = this._measureDragSize()\n dialog.classList.toggle('is-drag-close', dragSize > 0 && this._dragOffset > dragSize * DRAG_CLOSE_RATIO)\n this._applyDragOffset(dialog, this._dragOffset)\n },\n onEnd: info => {\n this._dragGesture = null\n dialog.classList.remove('is-dragging', 'is-drag-close')\n const size = this._measureDragSize()\n const velocity = (this._dragAxis === 'x' ? info.velocityX : info.velocityY) * this._dragCloseSign\n const shouldClose =\n this._dragOffset > size * DRAG_CLOSE_RATIO || (this._dragOffset > 8 && velocity > DRAG_FLICK_VELOCITY)\n\n if (shouldClose) this._springToClose(dialog, velocity)\n else this._springRebound(dialog, velocity, this._dragOffset)\n },\n onCancel: () => {\n this._dragGesture = null\n dialog.classList.remove('is-dragging', 'is-drag-close')\n this._springRebound(dialog, 0, this._dragOffset)\n }\n })\n }\n\n // 受控状态写入等外部原因强制终结拖拽:清手势状态与拖拽样式,不弹回,\n // 后续管线(关闭/打开)由调用方继续执行。\n private _cancelActiveDrag() {\n if (!this._isDragging()) return\n this._dragGesture?.destroy()\n this._dragGesture = null\n const dialog = this.dialog\n if (!dialog) return\n dialog.classList.remove('is-dragging', 'is-drag-close')\n this._clearDragStyles(dialog)\n }\n\n // 将闭合方向位移映射为 dialog transform,并同步遮罩透明度保持跟手反馈。\n private _applyDragOffset(dialog: HTMLDialogElement, offset: number) {\n const sign = this._dragCloseSign\n const value = offset * sign\n dialog.style.transform = this._dragAxis === 'x' ? `translateX(${value}px)` : `translateY(${value}px)`\n const size = this._measureDragSize()\n const progress = size > 0 ? Math.min(1, Math.max(0, offset / size)) : 0\n dialog.style.setProperty('--wui-internal-drag-backdrop-opacity', String(1 - progress))\n }\n\n private _clearDragStyles(dialog: HTMLDialogElement) {\n dialog.style.removeProperty('transform')\n dialog.style.removeProperty('--wui-internal-drag-backdrop-opacity')\n }\n\n // 弹簧到完全闭合;controlled 下保持闭合位等待回写,其余走常规关闭管线。\n private _springToClose(dialog: HTMLDialogElement, velocity: number) {\n const from = this._dragOffset\n this._dragOffset = 0\n const sign = this._dragCloseSign\n // 弹簧终点 = 完全出屏距离(含浮动留边),与 CSS 闭合态/悬停终态一致,\n // 否则 onfinish 后会有一个留边宽度的瞬移。\n const to = this._dragCloseDistance(dialog)\n const size = this._measureDragSize()\n\n const finishClose = () => {\n if (this.controlled) {\n // 保持在闭合位(is-visible 未移除,状态仍 open),等待 Consumer 回写或超时弹回。\n const distance = to\n dialog.style.transform =\n this._dragAxis === 'x' ? `translateX(${distance * sign}px)` : `translateY(${distance * sign}px)`\n dialog.style.setProperty('--wui-internal-drag-backdrop-opacity', '0')\n } else {\n // 移除 is-visible 后基础 transform 即闭合位,清内联样式不产生跳变;\n // presence.sync(false) 检测不到 is-visible 会立即完成关闭,不重播退出动画。\n dialog.classList.remove('is-visible')\n this._clearDragStyles(dialog)\n }\n this._closeFromDrag()\n }\n\n if (this._isReducedMotion() || typeof dialog.animate !== 'function') {\n finishClose()\n return\n }\n\n const samples = springOffsets(from, to, velocity, SPRING_PRESETS.close)\n const keyframes = samples.map(o => ({\n transform: this._dragAxis === 'x' ? `translateX(${o * sign}px)` : `translateY(${o * sign}px)`,\n '--wui-internal-drag-backdrop-opacity': String(Math.max(0, 1 - o / size))\n }))\n // 采样帧含首值与附加终点,实际时长为帧间隔数 × 采样周期。\n const duration = (samples.length - 1) * SPRING_SAMPLE_MS\n // fill:'both' 与 _springRebound 对称:堵住 finish→onfinish 清理窗口,\n // 防止该窗口内绘制的帧暴露内联位移、随清理触发跳变。\n const animation = dialog.animate(keyframes, { duration, easing: 'linear', fill: 'both' })\n // 审计(Safari 不采信 fill 覆盖的 before-change style):受控分支 finishClose\n // 把内联写入闭合位,cancel 时内联 == fill 终态 == 生效闭合位,三值一致;非受控\n // 分支 finishClose 在 fill 覆盖期间完成 is-visible 切换并清内联,cancel 后\n // computed 落到 CSS 闭合位且 == fill 终态 to,同样无中间旧值可被采样。因此\n // 「先应用终态再 cancel」对关闭路径已满足,无需像弹回那样保留内联终值。\n animation.onfinish = () => {\n finishClose()\n animation.cancel()\n }\n }\n\n // 弹回打开:从 from 位移弹回 0;结束后把内联样式交还打开态/CSS transition 管辖。\n private _springRebound(dialog: HTMLDialogElement, velocity: number, from: number) {\n this._dragOffset = 0\n\n // 弹回结束把内联样式覆写为打开态终值而非删除:Safari 计算 CSS transition 的\n // before-change style 时不采信 WAAPI fill 动画的覆盖值,删除内联会让它采样到\n // 拖拽残留位移(如 -64px)作为旧值,与打开态 0 之间触发一次额外的 transform\n // 过渡 —— 即弹回结束后再次多弹一次(Chrome 采信 fill 故不复现)。\n // translateX(0px)/translateY(0px) 与打开态 CSS transform translate(0,0) 数学等价,\n // backdrop opacity 1 与 --wui-internal-drag-backdrop-opacity 的默认值一致;内联\n // 保留到关闭管线:关闭时 _springToClose 的 animate 会覆盖它,其 finishClose 清理\n // 内联时 dialog 已进入关闭流程(is-visible 移除/闭合位写入),无 before-change\n // 采样窗口。\n const applyOpenInlineState = () => {\n dialog.style.transform = this._dragAxis === 'x' ? 'translateX(0px)' : 'translateY(0px)'\n dialog.style.setProperty('--wui-internal-drag-backdrop-opacity', '1')\n }\n\n // 同步收尾,顺序不可调换:\n // 1. 把内联覆写为打开态终值(保留 is-dragging,transition:none 仍在抑制);\n // 2. void dialog.offsetWidth 强制一次同步样式重算:Safari 的 before-change\n // style 不采信 WAAPI fill 覆盖,此刻计算样式已真实归 0,重算把 0「烘焙」为\n // 它内部 transition 参考值。rAF 不够:rAF 回调与 onfinish 常落在同一渲染帧,\n // Safari 在抑制解除时还没把 0 写进 transition 状态,会按旧值 -offset 补触发\n // (MutationObserver 实测:onfinish 与 is-dragging 移除同毫秒、transitionstart\n // 延后 16ms 仍以 -9.6 为 from)。\n // 3. 同步移除 is-dragging:重算后解除抑制是 0→0,无 before-change/after 差异,\n // 无过渡可触发。顺序若反(先移除类再重算),解除抑制的瞬间 Safari 仍以旧值\n // 为参考,过渡照旧触发。\n // 防御:dialog 已关闭/断连时跳过样式收尾(is-dragging 随元素销毁自然消失)。\n // clearInline:无动画路径(tap、from≈0、reduced-motion、无 WAAPI)没有需要「烘焙」\n // 的拖拽残留,直接移除内联拖拽样式回到 CSS 打开态即可。不能像动画路径那样把内联\n // 写成 translateX(0px) 长期保留:0px 内联会盖住闭合态 CSS transform,用户之后用\n // 按钮/遮罩/Esc(不走 _springToClose 的 animate)关闭时计算值恒为 0、开与关都\n // 不再触发过渡 —— 快速连点后「后续开关丢失过渡动画」的根因。\n const finishRebound = (clearInline: boolean) => {\n this._dragAnimation = null\n if (!dialog.isConnected) return\n if (clearInline) this._clearDragStyles(dialog)\n else applyOpenInlineState()\n void dialog.offsetWidth\n dialog.classList.remove('is-dragging')\n }\n\n if (this._isReducedMotion() || typeof dialog.animate !== 'function' || Math.abs(from) < 1) {\n // 无动画路径同样先加 is-dragging 抑制过渡,再走同一收尾;\n // 保证所有 _springRebound 出口(onfinish/onCancel/受控回写弹回)行为一致。\n dialog.classList.add('is-dragging')\n finishRebound(true)\n return\n }\n\n const sign = this._dragCloseSign\n const size = this._measureDragSize()\n const samples = springOffsets(from, 0, velocity, SPRING_PRESETS.rebound)\n const keyframes = samples.map(o => ({\n transform: this._dragAxis === 'x' ? `translateX(${o * sign}px)` : `translateY(${o * sign}px)`,\n '--wui-internal-drag-backdrop-opacity': String(Math.max(0, 1 - o / size))\n }))\n // 弹回期间抑制 transform/backdrop 的 CSS transition,避免与弹簧动画叠加。\n dialog.classList.add('is-dragging')\n const duration = (samples.length - 1) * SPRING_SAMPLE_MS\n // fill:'both' 堵住 finish→onfinish 清理之间的竞态窗口:无 fill 时动画结束即\n // 失效,窗口内被绘制的帧会暴露内联拖拽位移,随后的清理再触发 CSS 过渡滑回\n // 打开位 —— 表现为回弹后又多弹一次。\n const animation = dialog.animate(keyframes, { duration, easing: 'linear', fill: 'both' })\n // onfinish 时保持 is-dragging(transition 仍被抑制)走 finishRebound:\n // 覆写内联 → 强制重算烘焙 0 → 移除类,Safari 无旧值可采信、无过渡可触发。\n animation.onfinish = () => {\n finishRebound(false)\n animation.cancel()\n }\n this._dragAnimation = animation\n }\n\n private _closeFromDrag() {\n // 先派发关闭请求(_closeFromUser),再进入悬停等待:等待态中 _closeFromUser\n // 会被 L2 去重守卫短路,顺序颠倒会吞掉首次请求。\n this._closeFromUser()\n if (this.controlled) {\n this._dragAwaitWriteback = true\n this._dragRequestTimer = setTimeout(this._handleDragWritebackTimeout, DRAG_REQUEST_WINDOW_MS)\n }\n }\n\n // controlled 回写窗口超时:Consumer 拒绝关闭,从闭合位弹回打开位。\n private readonly _handleDragWritebackTimeout = () => {\n this._dragRequestTimer = undefined\n if (!this._dragAwaitWriteback || !this.open || this._isDragging()) {\n this._dragAwaitWriteback = false\n return\n }\n this._dragAwaitWriteback = false\n\n const dialog = this.dialog\n if (!dialog) return\n dialog.classList.add('is-visible')\n // 悬停终态位于完全出屏位(含留边),弹回也从该真实位置起步,避免首帧内跳。\n this._springRebound(dialog, 0, this._dragCloseDistance(dialog))\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._nestedLayers.dispose()\n this._dragGesture?.destroy()\n this._dragGesture = null\n this._presence.dispose()\n this._scrollLock.release()\n this._dragAnimation?.cancel()\n this._dragAnimation = null\n this._cancelDragAwait()\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 // Consumer 在拖拽进行中写入受控 open:受控状态优先,立即终结手势(等同\n // pointercancel 的清理但不弹回),交由下方 presence 走标准关闭/打开管线。\n if (this._isDragging() && !this.open) this._cancelActiveDrag()\n if (this._userOpenChange.consume()) this.emitOpenChange()\n // controlled 拖拽关闭的回写结果:确认关闭则清除闭合位悬停状态走正常关闭;\n // 拒绝关闭(回写 open=true)则从闭合位(含留边)弹回。\n if (this._dragAwaitWriteback) {\n this._cancelDragAwait()\n const dialog = this.dialog\n if (dialog && !this.open) {\n dialog.classList.remove('is-visible')\n this._clearDragStyles(dialog)\n } else if (dialog && this.open) {\n this._springRebound(dialog, 0, this._dragCloseDistance(dialog))\n }\n }\n // 关闭且无进行中的拖拽动画时,清理可能残留的内联拖拽样式(如弹回路径按\n // Safari 双回弹修复保留的 translateX(0px))。若不清理,0px 内联会盖住闭合态\n // CSS transform,后续开/关都不再触发过渡。拖拽动画进行中(_dragAnimation 非空)\n // 由各自的 onfinish 收尾,这里不干预。\n if (!this.open && this._dragAnimation === null && !this._isDragging()) {\n const dialog = this.dialog\n if (dialog) this._clearDragStyles(dialog)\n }\n this._presence.sync(this.open)\n if (this.open) {\n // presence.sync 已同步发起 showModal:此刻 dialog.open 为真,计数出正确\n // 的层序 depth 并驱动下层缩放。直接同步 register,不等待 is-visible\n //(那要再等一帧,且打开过渡期间上层关系已应确立)。\n this._nestedLayers.register()\n } else {\n this._nestedLayers.unregister()\n }\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 /*\n * nested 场景防连锁:子 drawer 的 dialog 经 slot 投影在本层 shadow 内,其\n * keydown composed 冒泡路径会再次经过本层 dialog(事件路径含 slot 宿主链)。\n * 若事件传播路径在本层 dialog 之前已存在其他 <dialog>,说明该事件来自子层,本层忽略。\n */\n const path = e.composedPath()\n const ownDialogIndex = path.indexOf(e.currentTarget as Node)\n if (ownDialogIndex > 0) {\n const hasChildDialog = path.slice(0, ownDialogIndex).some(n => n instanceof HTMLDialogElement)\n if (hasChildDialog) return\n }\n // 拖拽进行中忽略 ESC,避免手势与关闭管线竞争。\n if (this._isDragging()) {\n e.preventDefault()\n return\n }\n e.preventDefault()\n this._closeFromUser()\n }\n\n show() {\n if (this.open) return\n this.open = true\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 // controlled 悬停等待回写期间,关闭意图已在途(open-change 已派发):\n // Escape/遮罩/关闭按钮的重复触发不再派发第二次请求,也不与超时弹回竞争。\n if (this._dragAwaitWriteback) return\n if (this.controlled) {\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 // 关闭态下的 close 事件只可能是我们自己 finishClosing 排队的异步事件(或冗余的\n // 外部关闭):交给 presence 消费 self-close 标志,避免标志泄漏到下一次真实关闭。\n if (!this.open) {\n this._presence.handleNativeClose()\n return\n }\n\n // 我们自己 dialog.close() 排队的 close 事件是异步任务:正常时序在本关闭会话内\n // 到达,快速「关闭→重开」时它在重新打开之后才到达(过期事件)。两种情况都已在\n // finishClosing 完成全部清理,由 presence 消费并返回 true,不能当成外部关闭把\n // 刚重开的 dialog 关掉——否则重开即被误关,表现为连续开关丢失过渡动画。\n if (this._presence.handleNativeClose()) return\n\n // 悬停等待期间的原生关闭(如表单 method=\"dialog\"):视为回写窗口内的重复\n // 关闭意图,取消等待不补发请求;已原生关闭的 dialog 不再被超时弹回拉起。\n if (this._dragAwaitWriteback) {\n this._cancelDragAwait()\n return\n }\n\n // 原生关闭可绕过 cancel;controlled 时恢复受控状态,由 Consumer 决定是否关闭。\n if (this.controlled) {\n this._presence.sync(true)\n this.emitOpenChange(false)\n return\n }\n\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 // 拖拽进行中 pointer capture 使 click 落在 dialog 上,忽略以避免与手势竞争。\n if (this._isDragging()) return\n this._closeFromUser()\n }\n\n private emitOpenChange(open = this.open) {\n dispatchOpenChangeEvent(this, open)\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 // 拖拽热区:仅在打开且 draggable 时渲染;胶囊 + 加宽命中条贴在抽屉内缘。\n const dragBar = this.draggable\n ? html`\n <div class=\"wui-drawer-drag-zone\" @pointerdown=${this._handleDragPointerDown}>\n <div class=\"wui-drawer-drag-bar\"></div>\n </div>\n `\n : 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>${dragBar}`\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 size=\"14\" .icon=${oouiClose}></web-ui-icon>\n </web-ui-button>\n `\n : nothing\n }\n ${dragBar}\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":";;;;;;;;;;;;;;;;;;AAkBA,IAAM,IAAqB;CAAC;CAAS;CAAQ;CAAO;AAAQ,GAKtD,IAAmB,IAAI,GACvB,IAAsB,KACtB,IAAyB,KACzB,IAAmB;AASzB,IAAI,OAAO,MAAQ,OAAe,sBAAsB,KAAK;CAC3D,IAAI;EACF,IAAI,iBAAiB;GACnB,MAAM;GACN,QAAQ;GACR,UAAU;GACV,cAAc;EAChB,CAAC;CACH,QAAQ,CAER;CAKA,IAAI;EACF,IAAI,iBAAiB;GACnB,MAAM;GACN,QAAQ;GACR,UAAU;GACV,cAAc;EAChB,CAAC;CACH,QAAQ,CAER;AACF;AAGO,IAAM,IAAN,cAA0B,EAAW;;EAwfF,aArfW,KAAA,OAAA,IACqC,KAAA,eAAA,IACM,KAAA,kBAAA,IAOrC,KAAA,aAAA,IAWnB,KAAA,aAAA,SAGA,KAAA,UAAA,IAEiB,KAAA,WAAA,IAMA,KAAA,WAAA,IAMc,KAAA,cAAA,IAOJ,KAAA,YAAA,IAMxC,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,GAEgC,KAAA,gBAAA,EAAyB,CAAC,CAAC,KAAK;GAC/D,iBAAiB,KAAK;GACtB,oBAAoB,KAAK;EAC3B,CAAC,GAGgD,KAAA,eAAA,MAEpB,KAAA,qBAAA,GACP,KAAA,cAAA,GACqB,KAAA,iBAAA,MAEb,KAAA,sBAAA,IAgSuB,KAAA,oCAAA;GAEnD,IADA,KAAK,oBAAoB,KAAA,GACrB,CAAC,KAAK,uBAAuB,CAAC,KAAK,QAAQ,KAAK,YAAY,GAAG;IACjE,KAAK,sBAAsB;IAC3B;GACF;GACA,KAAK,sBAAsB;GAE3B,IAAM,IAAS,KAAK;GACf,MACL,EAAO,UAAU,IAAI,YAAY,GAEjC,KAAK,eAAe,GAAQ,GAAG,KAAK,mBAAmB,CAAM,CAAC;EAChE,GAgIwC,KAAA,uBAAA;GACjC,SAAK,QAGN,MAAK,qBACT;QAAI,KAAK,YAAY;KACnB,KAAK,eAAe,EAAK;KACzB;IACF;IAGA,AADA,KAAK,gBAAgB,KAAK,GAC1B,KAAK,MAAM;GAHX;EAIF;;;EAngByB,KAAA,SAAA,CAAC,EAAU,CAAK,GAAG,EAAU,CAAK,CAAC;;CAa5D,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;CA2BA,IAAY,SAAS;EACnB,OAAO,KAAK,YAAY,cAAc,QAAQ,KAAK;CACrD;CA4BA,IAAY,YAAuB;EACjC,OAAO,KAAK,eAAe,UAAU,KAAK,eAAe,UAAU,MAAM;CAC3E;CAGA,IAAY,iBAAyB;EACnC,QAAQ,KAAK,YAAb;GACE,KAAK,SACH,OAAO;GACT,KAAK,QACH,OAAO;GACT,KAAK,OACH,OAAO;GACT,KAAK,UACH,OAAO;EACX;CACF;CAEA,mBAAmC;EACjC,IAAM,IAAS,KAAK;EAEpB,OADK,IACE,KAAK,cAAc,MAAM,EAAO,cAAc,EAAO,eADxC;CAEtB;CAIA,iBAAyB,GAAmC;EAC1D,IAAM,IAAM,iBAAiB,CAAM,CAAC,CAAC,iBAAiB,6BAA6B,GAC7E,IAAS,OAAO,WAAW,CAAG;EACpC,OAAO,OAAO,SAAS,CAAM,IAAI,IAAS;CAC5C;CAIA,mBAA2B,GAAmC;EAC5D,OAAO,KAAK,iBAAiB,IAAI,KAAK,iBAAiB,CAAM;CAC/D;CAEA,mBAAoC;EAGlC,IAAM,IAAQ,EAAiB,IAAI;EACnC,IAAI,GAAO,OAAO,EAAM,gBAAgB;EACxC,IAAI;GACF,OAAO,OAAO,OAAO,cAAe,cAAc,OAAO,WAAW,kCAAkC,CAAC,CAAC;EAC1G,QAAQ;GACN,OAAO;EACT;CACF;CAEA,cAA+B;EAC7B,OAAO,KAAK,cAAc,WAAW,KAAK;CAC5C;CAEA,mBAA2B;EAKzB,AAJI,KAAK,sBAAsB,KAAA,MAC7B,aAAa,KAAK,iBAAiB,GACnC,KAAK,oBAAoB,KAAA,IAE3B,KAAK,sBAAsB;CAC7B;CAEA,uBAA+B,GAAiB;EAG9C,IAFI,CAAC,KAAK,QAAQ,CAAC,KAAK,aAAa,KAAK,YAAY,KAElD,EAAE,cAAc,IAAO;EAE3B,IAAM,IAAS,KAAK;EAIpB,IAHI,CAAC,KAGD,CAAC,EAAO,UAAU,SAAS,YAAY,GAAG;EAG9C,IAAM,IAAmB,iBAAiB,CAAM,CAAC,CAAC,WAC5C,IACJ,KAAoB,MAAqB,UAAU,OAAO,qBAAsB,aAC5E,IAAI,kBAAkB,CAAgB,CAAC,CAAC,KAAK,cAAc,MAAM,QAAQ,SACzE;EASN,AARA,KAAK,gBAAgB,OAAO,GAC5B,KAAK,iBAAiB,MACtB,KAAK,iBAAiB,GAEtB,KAAK,qBAAqB,IAAY,KAAK,gBAC3C,KAAK,cAAc,KAAK,oBACxB,EAAO,UAAU,IAAI,aAAa,GAElC,KAAK,eAAe,EAAkB,GAAG;GACvC,MAAM,KAAK;GACX,SAAQ,MAAQ;IACd,IAAM,IAAe,KAAK,cAAc,MAAM,EAAK,SAAS,EAAK,QAE3D,IAAM,KAAK,qBAAqB,IAAe,KAAK;IAC1D,KAAK,cAAc,EAAW,GAAK,KAAK,iBAAiB,IAAI,IAAK,GAAI;IAGtE,IAAM,IAAW,KAAK,iBAAiB;IAEvC,AADA,EAAO,UAAU,OAAO,iBAAiB,IAAW,KAAK,KAAK,cAAc,IAAW,CAAgB,GACvG,KAAK,iBAAiB,GAAQ,KAAK,WAAW;GAChD;GACA,QAAO,MAAQ;IAEb,AADA,KAAK,eAAe,MACpB,EAAO,UAAU,OAAO,eAAe,eAAe;IACtD,IAAM,IAAO,KAAK,iBAAiB,GAC7B,KAAY,KAAK,cAAc,MAAM,EAAK,YAAY,EAAK,aAAa,KAAK;IAInF,AAFE,KAAK,cAAc,IAAO,KAAqB,KAAK,cAAc,KAAK,IAAW,IAEnE,KAAK,eAAe,GAAQ,CAAQ,IAChD,KAAK,eAAe,GAAQ,GAAU,KAAK,WAAW;GAC7D;GACA,gBAAgB;IAGd,AAFA,KAAK,eAAe,MACpB,EAAO,UAAU,OAAO,eAAe,eAAe,GACtD,KAAK,eAAe,GAAQ,GAAG,KAAK,WAAW;GACjD;EACF,CAAC;CACH;CAIA,oBAA4B;EAC1B,IAAI,CAAC,KAAK,YAAY,GAAG;EAEzB,AADA,KAAK,cAAc,QAAQ,GAC3B,KAAK,eAAe;EACpB,IAAM,IAAS,KAAK;EACf,MACL,EAAO,UAAU,OAAO,eAAe,eAAe,GACtD,KAAK,iBAAiB,CAAM;CAC9B;CAGA,iBAAyB,GAA2B,GAAgB;EAElE,IAAM,IAAQ,IADD,KAAK;EAElB,EAAO,MAAM,YAAY,KAAK,cAAc,MAAM,cAAc,EAAM,OAAO,cAAc,EAAM;EACjG,IAAM,IAAO,KAAK,iBAAiB,GAC7B,IAAW,IAAO,IAAI,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,IAAS,CAAI,CAAC,IAAI;EACtE,EAAO,MAAM,YAAY,wCAAwC,OAAO,IAAI,CAAQ,CAAC;CACvF;CAEA,iBAAyB,GAA2B;EAElD,AADA,EAAO,MAAM,eAAe,WAAW,GACvC,EAAO,MAAM,eAAe,sCAAsC;CACpE;CAGA,eAAuB,GAA2B,GAAkB;EAClE,IAAM,IAAO,KAAK;EAClB,KAAK,cAAc;EACnB,IAAM,IAAO,KAAK,gBAGZ,IAAK,KAAK,mBAAmB,CAAM,GACnC,IAAO,KAAK,iBAAiB,GAE7B,UAAoB;GACxB,IAAI,KAAK,YAAY;IAEnB,IAAM,IAAW;IAGjB,AAFA,EAAO,MAAM,YACX,KAAK,cAAc,MAAM,cAAc,IAAW,EAAK,OAAO,cAAc,IAAW,EAAK,MAC9F,EAAO,MAAM,YAAY,wCAAwC,GAAG;GACtE,OAIE,AADA,EAAO,UAAU,OAAO,YAAY,GACpC,KAAK,iBAAiB,CAAM;GAE9B,KAAK,eAAe;EACtB;EAEA,IAAI,KAAK,iBAAiB,KAAK,OAAO,EAAO,WAAY,YAAY;GACnE,EAAY;GACZ;EACF;EAEA,IAAM,IAAU,EAAc,GAAM,GAAI,GAAU,EAAe,KAAK,GAChE,IAAY,EAAQ,KAAI,OAAM;GAClC,WAAW,KAAK,cAAc,MAAM,cAAc,IAAI,EAAK,OAAO,cAAc,IAAI,EAAK;GACzF,wCAAwC,OAAO,KAAK,IAAI,GAAG,IAAI,IAAI,CAAI,CAAC;EAC1E,EAAE,GAEI,KAAY,EAAQ,SAAS,KAAK,GAGlC,IAAY,EAAO,QAAQ,GAAW;GAAE;GAAU,QAAQ;GAAU,MAAM;EAAO,CAAC;EAMxF,EAAU,iBAAiB;GAEzB,AADA,EAAY,GACZ,EAAU,OAAO;EACnB;CACF;CAGA,eAAuB,GAA2B,GAAkB,GAAc;EAChF,KAAK,cAAc;EAWnB,IAAM,UAA6B;GAEjC,AADA,EAAO,MAAM,YAAY,KAAK,cAAc,MAAM,oBAAoB,mBACtE,EAAO,MAAM,YAAY,wCAAwC,GAAG;EACtE,GAmBM,KAAiB,MAAyB;GAC9C,KAAK,iBAAiB,MACjB,EAAO,gBACR,IAAa,KAAK,iBAAiB,CAAM,IACxC,EAAqB,GAC1B,EAAY,aACZ,EAAO,UAAU,OAAO,aAAa;EACvC;EAEA,IAAI,KAAK,iBAAiB,KAAK,OAAO,EAAO,WAAY,cAAc,KAAK,IAAI,CAAI,IAAI,GAAG;GAIzF,AADA,EAAO,UAAU,IAAI,aAAa,GAClC,EAAc,EAAI;GAClB;EACF;EAEA,IAAM,IAAO,KAAK,gBACZ,IAAO,KAAK,iBAAiB,GAC7B,IAAU,EAAc,GAAM,GAAG,GAAU,EAAe,OAAO,GACjE,IAAY,EAAQ,KAAI,OAAM;GAClC,WAAW,KAAK,cAAc,MAAM,cAAc,IAAI,EAAK,OAAO,cAAc,IAAI,EAAK;GACzF,wCAAwC,OAAO,KAAK,IAAI,GAAG,IAAI,IAAI,CAAI,CAAC;EAC1E,EAAE;EAEF,EAAO,UAAU,IAAI,aAAa;EAClC,IAAM,KAAY,EAAQ,SAAS,KAAK,GAIlC,IAAY,EAAO,QAAQ,GAAW;GAAE;GAAU,QAAQ;GAAU,MAAM;EAAO,CAAC;EAOxF,AAJA,EAAU,iBAAiB;GAEzB,AADA,EAAc,EAAK,GACnB,EAAU,OAAO;EACnB,GACA,KAAK,iBAAiB;CACxB;CAEA,iBAAyB;EAIvB,AADA,KAAK,eAAe,GAChB,KAAK,eACP,KAAK,sBAAsB,IAC3B,KAAK,oBAAoB,WAAW,KAAK,6BAA6B,CAAsB;CAEhG;CAkBA,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;EAS9B,AARA,MAAM,qBAAqB,GAC3B,KAAK,cAAc,QAAQ,GAC3B,KAAK,cAAc,QAAQ,GAC3B,KAAK,eAAe,MACpB,KAAK,UAAU,QAAQ,GACvB,KAAK,YAAY,QAAQ,GACzB,KAAK,gBAAgB,OAAO,GAC5B,KAAK,iBAAiB,MACtB,KAAK,iBAAiB;CACxB;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,UAAM,QAAQ,CAAK,GACd,KAAK,aAEV;OAAI,EAAM,IAAI,MAAM,GAAG;IAOrB,IAJI,KAAK,YAAY,KAAK,CAAC,KAAK,QAAM,KAAK,kBAAkB,GACzD,KAAK,gBAAgB,QAAQ,KAAG,KAAK,eAAe,GAGpD,KAAK,qBAAqB;KAC5B,KAAK,iBAAiB;KACtB,IAAM,IAAS,KAAK;KACpB,AAAI,KAAU,CAAC,KAAK,QAClB,EAAO,UAAU,OAAO,YAAY,GACpC,KAAK,iBAAiB,CAAM,KACnB,KAAU,KAAK,QACxB,KAAK,eAAe,GAAQ,GAAG,KAAK,mBAAmB,CAAM,CAAC;IAElE;IAKA,IAAI,CAAC,KAAK,QAAQ,KAAK,mBAAmB,QAAQ,CAAC,KAAK,YAAY,GAAG;KACrE,IAAM,IAAS,KAAK;KACpB,AAAI,KAAQ,KAAK,iBAAiB,CAAM;IAC1C;IAEA,AADA,KAAK,UAAU,KAAK,KAAK,IAAI,GACzB,KAAK,OAIP,KAAK,cAAc,SAAS,IAE5B,KAAK,cAAc,WAAW;GAElC;GACA,CAAI,EAAM,IAAI,MAAM,KAAK,EAAM,IAAI,cAAc,MAAG,KAAK,gBAAgB;EADzE;CAEF;CAEA,oBAA4B,GAAoB;EAC9C,KAAK,UAAU,oBAAoB,CAAC;CACtC;CAEA,cAAsB,GAAkB;EACtC,IAAI,EAAE,QAAQ,UAAU;EAMxB,IAAM,IAAO,EAAE,aAAa,GACtB,IAAiB,EAAK,QAAQ,EAAE,aAAqB;EAElC,MADrB,IAAiB,KACI,EAAK,MAAM,GAAG,CAAc,CAAC,CAAC,MAAK,MAAK,aAAa,iBACxE,IAGN;OAAI,KAAK,YAAY,GAAG;IACtB,EAAE,eAAe;IACjB;GACF;GAEA,AADA,EAAE,eAAe,GACjB,KAAK,eAAe;EAFpB;CAGF;CAEA,OAAO;EACD,AACJ,KAAK,SAAO;CACd;CAEA,QAAQ;EACD,AACL,KAAK,SAAO;CACd;CAgBA,aAAqB,GAAU;EAG7B,AADA,EAAE,eAAe,GACjB,KAAK,eAAe;CACtB;CAEA,oBAA4B;EAG1B,IAAI,CAAC,KAAK,MAAM;GACd,KAAK,UAAU,kBAAkB;GACjC;EACF;EAMI,UAAK,UAAU,kBAAkB,GAIrC;OAAI,KAAK,qBAAqB;IAC5B,KAAK,iBAAiB;IACtB;GACF;GAGA,IAAI,KAAK,YAAY;IAEnB,AADA,KAAK,UAAU,KAAK,EAAI,GACxB,KAAK,eAAe,EAAK;IACzB;GACF;GAGA,AADA,KAAK,gBAAgB,KAAK,GAC1B,KAAK,OAAO;EAVZ;CAWF;CAEA,oBAA4B,GAAe;EACrC,EAAE,WAAY,EAAE,kBAChB,KAAK,mBAEL,KAAK,YAAY,KACrB,KAAK,eAAe;CACtB;CAEA,eAAuB,IAAO,KAAK,MAAM;EACvC,EAAwB,MAAM,CAAI;CACpC;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,GAGzF,IAAU,KAAK,YACjB,CAAI;2DAC+C,KAAK,uBAAuB;;;YAI/E;EAGJ,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,gBAAgB,MACpB,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;;;;;;yDAME,EAAU;;0BAG7C,EACL;kBACC,EAAQ;gBAEjB;;;CAGP;AAKF;AA7nBG,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,GAOzE,EAAA,CAAA,EAAS;CAAE,MAAM;CAAS,SAAS;AAAK,CAAC,CAAA,GAAA,EAAA,WAAA,cAAA,KAAA,CAAA,GAEzC,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,GAOpD,EAAA,CAAA,EAAS;CAAE,MAAM;CAAS,SAAS;AAAK,CAAC,CAAA,GAAA,EAAA,WAAA,aAAA,KAAA,CAAA,GAhD3C,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;position:fixed;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:focus,dialog:focus-visible{outline:none}dialog[open].is-visible{transform:translate(0)}:host(:not([headless])) dialog.is-dragging[open].is-visible,dialog.is-dragging,:is(:host(:not([headless])) dialog.is-dragging[open].is-visible,dialog.is-dragging)::backdrop{transition:none}: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:var(--wui-internal-drag-backdrop-opacity,1)}.wui-drawer-drag-zone{--wui-internal-drag-bar-center:calc(var(--wui-drawer-content-padding,20px) / 2);touch-action:none;cursor:grab;z-index:4;transition:opacity var(--wui-duration-feedback,.1s) ease;position:absolute}dialog.is-nested-lower .wui-drawer-drag-zone{pointer-events:none;opacity:0}:host([placement=right]) .wui-drawer-drag-zone{width:var(--wui-drawer-drag-zone-size,20px);height:100%;top:0;left:0}:host([placement=left]) .wui-drawer-drag-zone{width:var(--wui-drawer-drag-zone-size,20px);height:100%;top:0;right:0}:host([placement=top]) .wui-drawer-drag-zone,:host([placement=bottom]) .wui-drawer-drag-zone{width:100%;height:var(--wui-drawer-drag-zone-size,20px);left:0;right:0}:host([placement=top]) .wui-drawer-drag-zone{bottom:0}:host([placement=bottom]) .wui-drawer-drag-zone{top:0}.wui-drawer-drag-bar{width:var(--wui-drawer-drag-bar-thickness,4px);height:var(--wui-drawer-drag-bar-length,56px);border-radius:var(--wui-radius-control,calc(infinity * 1px));background:color-mix(in srgb, var(--wui-color-text,#1b1b1b) 24%, transparent);position:absolute}:host([placement=right]) .wui-drawer-drag-bar{top:calc(50% - var(--wui-drawer-drag-bar-length,56px) / 2);left:calc(var(--wui-internal-drag-bar-center) - var(--wui-drawer-drag-bar-thickness,4px) / 2)}:host([placement=left]) .wui-drawer-drag-bar{top:calc(50% - var(--wui-drawer-drag-bar-length,56px) / 2);right:calc(var(--wui-internal-drag-bar-center) - var(--wui-drawer-drag-bar-thickness,4px) / 2)}:host([placement=top]) .wui-drawer-drag-bar,:host([placement=bottom]) .wui-drawer-drag-bar{width:var(--wui-drawer-drag-bar-length,56px);height:var(--wui-drawer-drag-bar-thickness,4px)}:host([placement=top]) .wui-drawer-drag-bar{bottom:calc(var(--wui-internal-drag-bar-center) - var(--wui-drawer-drag-bar-thickness,4px) / 2);left:calc(50% - var(--wui-drawer-drag-bar-length,56px) / 2)}:host([placement=bottom]) .wui-drawer-drag-bar{top:calc(var(--wui-internal-drag-bar-center) - var(--wui-drawer-drag-bar-thickness,4px) / 2);left:calc(50% - var(--wui-drawer-drag-bar-length,56px) / 2)}.wui-drawer-drag-zone:hover .wui-drawer-drag-bar,.wui-drawer-drag-zone:active .wui-drawer-drag-bar{background:color-mix(in srgb, var(--wui-color-text,#1b1b1b) 40%, transparent)}dialog.is-drag-close .wui-drawer-drag-zone .wui-drawer-drag-bar{background:var(--wui-color-accent,#08f)}: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%)}dialog.is-dragging .wui-drawer-drag-zone{cursor:grabbing}.wui-drawer-body{--wui-internal-glass-shadow:var(--wui-shadow-overlay,2px 16px 40px #0006);--wui-glass-corner-radius:var(--wui-drawer-radius,var(--wui-radius-overlay,28px));background-color:var(--wui-drawer-bg,var(--wui-color-surface-overlay,#f6f6f6d1));flex-direction:column;height:100%;display:flex}.wui-drawer-header{padding:var(--wui-drawer-header-padding,16px 20px);border-bottom:1px solid #0000000f;justify-content:space-between;align-items:center;font-size:18px;font-weight:600;line-height:1.4;display:flex}.wui-drawer-header[hidden]{display:none}.wui-drawer-content{overscroll-behavior:contain;padding:var(--wui-drawer-content-padding,20px);font-size:var(--wui-font-size,14px);color:var(--wui-color-text,#1b1b1b);flex:1;line-height:1.5;overflow-y:auto}.wui-drawer-footer{padding:var(--wui-drawer-footer-padding,16px 20px);border-top:1px solid #0000000f;flex-shrink:0}.wui-drawer-footer[hidden]{display:none}.wui-drawer-close{z-index:3;top:var(--wui-drawer-close-top,16px);right:var(--wui-drawer-close-right,20px);position:absolute;bottom:auto;left:auto}:host([headless]) dialog{--wui-internal-drawer-inset:0px;width:auto;max-width:none}:host(:not([headless])) dialog{--wui-internal-drawer-inset:var(--wui-drawer-inset,8px);--wui-internal-drawer-nested-depth:0;--wui-internal-drawer-nested-scale:1;--wui-internal-drawer-nested-shift:0px;top:var(--wui-internal-drawer-inset);right:var(--wui-internal-drawer-inset);bottom:var(--wui-internal-drawer-inset);transform:translateX(calc(100% + var(--wui-internal-drawer-inset)));border-radius:var(--wui-drawer-radius,var(--wui-radius-overlay,28px));height:auto;left:auto}:host(:not([headless])) .wui-drawer-body{border-radius:inherit}:host(:not([headless])[placement=left]) dialog{top:var(--wui-internal-drawer-inset);right:auto;bottom:var(--wui-internal-drawer-inset);left:var(--wui-internal-drawer-inset);transform:translateX(calc(-100% - var(--wui-internal-drawer-inset)));height:auto}:host(:not([headless])[placement=top]) dialog{top:var(--wui-internal-drawer-inset);right:var(--wui-internal-drawer-inset);bottom:auto;left:var(--wui-internal-drawer-inset);transform:translateY(calc(-100% - var(--wui-internal-drawer-inset)));width:auto;max-width:none}:host(:not([headless])[placement=bottom]) dialog{top:auto;right:var(--wui-internal-drawer-inset);bottom:var(--wui-internal-drawer-inset);left:var(--wui-internal-drawer-inset);transform:translateY(calc(100% + var(--wui-internal-drawer-inset)));width:auto;max-width:none}:host(:not([headless])) dialog[open].is-visible{transform:translate(0);translate:calc(var(--wui-internal-drawer-nested-shift) * -1) 0;scale:var(--wui-internal-drawer-nested-scale);transition:transform var(--wui-duration-drawer-enter,.28s) var(--wui-ease-enter,cubic-bezier(.23, 1, .32, 1)), translate var(--wui-duration-drawer-nested,.45s) cubic-bezier(.22, 1, .36, 1), scale var(--wui-duration-drawer-nested,.45s) cubic-bezier(.22, 1, .36, 1)}:host(:not([headless])[placement=left]) dialog[open].is-visible{transform:translate(0);translate:var(--wui-internal-drawer-nested-shift) 0}:host(:not([headless])[placement=top]) dialog[open].is-visible{transform:translate(0);translate:0 var(--wui-internal-drawer-nested-shift)}:host(:not([headless])[placement=bottom]) dialog[open].is-visible{transform:translate(0);translate:0 calc(var(--wui-internal-drawer-nested-shift) * -1)}@media (prefers-reduced-motion:reduce){dialog:not(.is-dragging){transition:opacity var(--wui-duration-feedback,.1s) ease!important}dialog:not(.is-dragging)::backdrop{transition-duration:var(--wui-duration-feedback,.1s)!important}dialog[open].is-visible:not(.is-dragging){transform:translate(0)scale(1)!important}}";
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;position:fixed;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:focus,dialog:focus-visible{outline:none}dialog[open].is-visible{transform:translate(0)}:host(:not([headless])) dialog.is-dragging[open].is-visible,dialog.is-dragging,:is(:host(:not([headless])) dialog.is-dragging[open].is-visible,dialog.is-dragging)::backdrop{transition:none}: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:var(--wui-internal-drag-backdrop-opacity,1)}.wui-drawer-drag-zone{--wui-internal-drag-bar-center:calc(var(--wui-drawer-content-padding,20px) / 2);touch-action:none;cursor:grab;z-index:4;transition:opacity var(--wui-duration-feedback,.1s) ease;position:absolute}dialog.is-nested-lower .wui-drawer-drag-zone{pointer-events:none;opacity:0}:host([placement=right]) .wui-drawer-drag-zone{width:var(--wui-drawer-drag-zone-size,20px);height:100%;top:0;left:0}:host([placement=left]) .wui-drawer-drag-zone{width:var(--wui-drawer-drag-zone-size,20px);height:100%;top:0;right:0}:host([placement=top]) .wui-drawer-drag-zone,:host([placement=bottom]) .wui-drawer-drag-zone{width:100%;height:var(--wui-drawer-drag-zone-size,20px);left:0;right:0}:host([placement=top]) .wui-drawer-drag-zone{bottom:0}:host([placement=bottom]) .wui-drawer-drag-zone{top:0}.wui-drawer-drag-bar{width:var(--wui-drawer-drag-bar-thickness,4px);height:var(--wui-drawer-drag-bar-length,56px);border-radius:var(--wui-radius-control,calc(infinity * 1px));background:color-mix(in srgb, var(--wui-color-text,#1b1b1b) 24%, transparent);position:absolute}:host([placement=right]) .wui-drawer-drag-bar{top:calc(50% - var(--wui-drawer-drag-bar-length,56px) / 2);left:calc(var(--wui-internal-drag-bar-center) - var(--wui-drawer-drag-bar-thickness,4px) / 2)}:host([placement=left]) .wui-drawer-drag-bar{top:calc(50% - var(--wui-drawer-drag-bar-length,56px) / 2);right:calc(var(--wui-internal-drag-bar-center) - var(--wui-drawer-drag-bar-thickness,4px) / 2)}:host([placement=top]) .wui-drawer-drag-bar,:host([placement=bottom]) .wui-drawer-drag-bar{width:var(--wui-drawer-drag-bar-length,56px);height:var(--wui-drawer-drag-bar-thickness,4px)}:host([placement=top]) .wui-drawer-drag-bar{bottom:calc(var(--wui-internal-drag-bar-center) - var(--wui-drawer-drag-bar-thickness,4px) / 2);left:calc(50% - var(--wui-drawer-drag-bar-length,56px) / 2)}:host([placement=bottom]) .wui-drawer-drag-bar{top:calc(var(--wui-internal-drag-bar-center) - var(--wui-drawer-drag-bar-thickness,4px) / 2);left:calc(50% - var(--wui-drawer-drag-bar-length,56px) / 2)}.wui-drawer-drag-zone:hover .wui-drawer-drag-bar,.wui-drawer-drag-zone:active .wui-drawer-drag-bar{background:color-mix(in srgb, var(--wui-color-text,#1b1b1b) 40%, transparent)}dialog.is-drag-close .wui-drawer-drag-zone .wui-drawer-drag-bar{background:var(--wui-color-accent,#08f)}: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%)}dialog.is-dragging .wui-drawer-drag-zone{cursor:grabbing}.wui-drawer-body{--wui-internal-glass-shadow:var(--wui-shadow-overlay,2px 16px 40px #0006);--wui-glass-corner-radius:var(--wui-drawer-radius,var(--wui-radius-overlay,28px));background-color:var(--wui-drawer-bg,var(--wui-color-surface-overlay,#f6f6f6d1));flex-direction:column;height:100%;display:flex}.wui-drawer-header{padding:var(--wui-drawer-header-padding,16px 20px);border-bottom:1px solid #0000000f;justify-content:space-between;align-items:center;font-size:18px;font-weight:600;line-height:1.4;display:flex}.wui-drawer-header[hidden]{display:none}.wui-drawer-content{overscroll-behavior:contain;padding:var(--wui-drawer-content-padding,20px);font-size:var(--wui-font-size,14px);color:var(--wui-color-text,#1b1b1b);flex:1;line-height:1.5;overflow-y:auto}.wui-drawer-footer{padding:var(--wui-drawer-footer-padding,16px 20px);border-top:1px solid #0000000f;flex-shrink:0}.wui-drawer-footer[hidden]{display:none}.wui-drawer-close{z-index:3;top:var(--wui-drawer-close-top,16px);right:var(--wui-drawer-close-right,20px);position:absolute;bottom:auto;left:auto}:host([headless]) dialog{--wui-internal-drawer-inset:0px;width:auto;max-width:none}:host(:not([headless])) dialog{--wui-internal-drawer-inset:var(--wui-drawer-inset,8px);--wui-internal-drawer-nested-depth:0;--wui-internal-drawer-nested-scale:1;--wui-internal-drawer-nested-shift:0px;top:var(--wui-internal-drawer-inset);right:var(--wui-internal-drawer-inset);bottom:var(--wui-internal-drawer-inset);transform:translateX(calc(100% + var(--wui-internal-drawer-inset)));border-radius:var(--wui-drawer-radius,var(--wui-radius-overlay,28px));height:auto;left:auto}:host(:not([headless])) .wui-drawer-body{border-radius:inherit}:host(:not([headless])[placement=left]) dialog{top:var(--wui-internal-drawer-inset);right:auto;bottom:var(--wui-internal-drawer-inset);left:var(--wui-internal-drawer-inset);transform:translateX(calc(-100% - var(--wui-internal-drawer-inset)));height:auto}:host(:not([headless])[placement=top]) dialog{top:var(--wui-internal-drawer-inset);right:var(--wui-internal-drawer-inset);bottom:auto;left:var(--wui-internal-drawer-inset);transform:translateY(calc(-100% - var(--wui-internal-drawer-inset)));width:auto;max-width:none}:host(:not([headless])[placement=bottom]) dialog{top:auto;right:var(--wui-internal-drawer-inset);bottom:var(--wui-internal-drawer-inset);left:var(--wui-internal-drawer-inset);transform:translateY(calc(100% + var(--wui-internal-drawer-inset)));width:auto;max-width:none}:host(:not([headless])) dialog[open].is-visible{transform:translate(0);translate:calc(var(--wui-internal-drawer-nested-shift) * -1) 0;scale:var(--wui-internal-drawer-nested-scale);transition:transform var(--wui-duration-drawer-enter,.28s) var(--wui-ease-enter,cubic-bezier(.23, 1, .32, 1)), translate var(--wui-duration-drawer-nested,.45s) cubic-bezier(.22, 1, .36, 1), scale var(--wui-duration-drawer-nested,.45s) cubic-bezier(.22, 1, .36, 1)}:host(:not([headless])[placement=left]) dialog[open].is-visible{transform:translate(0);translate:var(--wui-internal-drawer-nested-shift) 0}:host(:not([headless])[placement=top]) dialog[open].is-visible{transform:translate(0);translate:0 var(--wui-internal-drawer-nested-shift)}:host(:not([headless])[placement=bottom]) dialog[open].is-visible{transform:translate(0);translate:0 calc(var(--wui-internal-drawer-nested-shift) * -1)}@media (prefers-reduced-motion:reduce){dialog:not(.is-dragging){transition:opacity var(--wui-duration-feedback,.1s) ease!important}dialog:not(.is-dragging)::backdrop{transition-duration:var(--wui-duration-feedback,.1s)!important}dialog[open].is-visible:not(.is-dragging){transform:translate(0,0)scale(1)!important}}";
3
3
  //#endregion
4
4
  export { e as default };
5
5
 
@@ -1,4 +1,4 @@
1
- import e from "../../_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorate.js";
1
+ import e from "../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorate.js";
2
2
  import { normalizeLiteral as t, normalizeNumber as n } from "../../shared/normalize/index.js";
3
3
  import { dispatchOpenChangeEvent as r } from "../../shared/open-state/index.js";
4
4
  import { overlayComposition as i } from "../../shared/overlay/composition.js";
@@ -61,7 +61,7 @@ var I = "web-ui-menu-level-", L = "wui-dropdown-menu-item", R = 0, z = class ext
61
61
  }), this._onTriggerClick = (e) => {
62
62
  this.disabled || (this.open ? this._closeAll(!0) : this._openMenu(e.detail === 0, !0));
63
63
  }, this._onClickOutside = (e) => {
64
- !this.isOpen || this._outsideClickGuard.isArmed() || this._outsideClickGuard.isInside(e) || this._closeAll(!0);
64
+ this.isOpen && !this._outsideClickGuard.isArmed() && (this._outsideClickGuard.isInside(e) || this._closeAll(!0));
65
65
  }, this._onMenuClick = (e) => {
66
66
  let t = p(e);
67
67
  if (!t || t.hasAttribute("disabled") || !t.hasAttribute("submenu")) return;
@@ -70,7 +70,7 @@ var I = "web-ui-menu-level-", L = "wui-dropdown-menu-item", R = 0, z = class ext
70
70
  let r = this._getLevelItems(n).indexOf(t);
71
71
  r >= 0 && this._toggleSubmenu(n, r, e.detail === 0);
72
72
  }, this._onKeydown = (e) => {
73
- this.disabled || !this.open || T(this._keyboardDelegate, e);
73
+ !this.disabled && this.open && T(this._keyboardDelegate, e);
74
74
  };
75
75
  }
76
76
  static {
@@ -132,7 +132,7 @@ var I = "web-ui-menu-level-", L = "wui-dropdown-menu-item", R = 0, z = class ext
132
132
  }
133
133
  async _closeRootAfterPresence() {
134
134
  let e = this._overlays.get(0)?.overlay;
135
- e && !await o(e) || this.open || !this.isConnected || (this._cleanupClosedMenu(), this._restoreFocusTarget?.focus(), this._restoreFocusTarget = void 0);
135
+ (!e || await o(e)) && !this.open && this.isConnected && (this._cleanupClosedMenu(), this._restoreFocusTarget?.focus(), this._restoreFocusTarget = void 0);
136
136
  }
137
137
  _hideAllSubmenuChildren() {
138
138
  m(this, `${I}-hidden`);
@@ -142,7 +142,7 @@ var I = "web-ui-menu-level-", L = "wui-dropdown-menu-item", R = 0, z = class ext
142
142
  this._closeSubmenuFrom(e + 1), this._activePath = [...this._activePath.slice(0, e), t], this._syncActiveAttrs();
143
143
  let r = this._getLevelItems(e)[t];
144
144
  r?.hasAttribute("submenu") && this._lifecycle.scheduleFrame(() => {
145
- !this.open || this._activePath[e] !== t || this._ensureOverlay(e + 1, n, r) || this._populateOverlay(e + 1, r);
145
+ this.open && this._activePath[e] === t && (this._ensureOverlay(e + 1, n, r) || this._populateOverlay(e + 1, r));
146
146
  }), this._bindHoversAfterUpdate();
147
147
  }
148
148
  _closeSubmenuFrom(e, t = !1) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/dropdown/index.ts"],"sourcesContent":["import type { Placement } from '@floating-ui/dom'\nimport { html, LitElement, unsafeCSS } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport '@/components/dropdown-divider'\nimport '@/components/dropdown-header'\nimport '@/components/dropdown-item'\nimport { UserChangeController } from '@/shared/events/user-change'\nimport {\n createClosingSubmenuStack,\n createMenuHoverBinder,\n createMenuOutsideClickGuard,\n getEnabledMenuLevelItems,\n getFocusedMenuItemFromPanels,\n handleMenuKeyboard,\n type MenuHoverDelegate,\n type MenuKeyboardDelegate\n} from '@/shared/menu-behavior'\nimport { createMenuPortalOverlay } from '@/shared/menu-portal/menu-portal'\nimport {\n captureFrameworkAnchors,\n focusMenuItem,\n getMenuChildren,\n getMenuItemFromEvent,\n hideNestedMenuChildren,\n moveMenuChildren,\n orderManagedMenuItems,\n reconcileManagedMenuItems,\n restoreFrameworkAnchors,\n returnManagedMenuItemsToSlot,\n type MenuItemAnchors\n} from '@/shared/menu-portal/menu-tree'\nimport { normalizeLiteral, normalizeNumber } from '@/shared/normalize'\nimport { dispatchOpenChangeEvent } from '@/shared/open-state'\nimport { overlayComposition } from '@/shared/overlay/composition'\nimport { defineOverlayLifecycle } from '@/shared/overlay/lifecycle'\nimport { defineOverlay } from '@/shared/overlay/overlay'\nimport type { OverlayApi } from '@/shared/overlay/overlay'\nimport { FLOATING_PLACEMENTS } from '@/shared/overlay/placement-props'\nimport { hideOverlayPresence, showOverlayPresence } from '@/shared/overlay/presence'\nimport { defineScrollLockLease } from '@/shared/scroll-lock/scroll-lock'\n\nimport style from './style.css?inline'\n\nexport type { Placement }\n\nconst SLOT_PREFIX = 'web-ui-menu-level-'\n// 一级菜单项的模板位 marker(机制与 context-menu 共用,见 menu-tree 的托管项锚说明)。\nconst MENU_ITEM_MARKER = 'wui-dropdown-menu-item'\n\nlet dropdownIdCounter = 0\n\ninterface MenuOverlay {\n api: OverlayApi\n overlay: HTMLElement\n content: HTMLElement\n}\n\n@customElement('web-ui-dropdown')\nexport class WebUiDropdown extends LitElement {\n static override styles = unsafeCSS(style)\n\n @property({ type: Boolean, reflect: true }) open = false\n @property({ type: Boolean, reflect: true }) disabled = false\n @property({ type: Boolean, reflect: true, attribute: 'match-width' }) matchWidth = false\n @property({ type: Boolean, reflect: true, attribute: 'no-scroll-lock' }) noScrollLock = false\n\n private _placement: Placement = 'bottom-start'\n\n @property({ type: String, reflect: true })\n get placement(): Placement {\n return this._placement\n }\n set placement(v: string) {\n this._placement = normalizeLiteral(v, FLOATING_PLACEMENTS, 'bottom-start')\n }\n\n private _offset = 4\n\n @property({ type: Number })\n get offset(): number {\n return this._offset\n }\n set offset(v: number) {\n this._offset = normalizeNumber(v, 0, 100, 4)\n }\n\n @state() private _activePath: number[] = []\n\n private readonly _overlays = new Map<number, MenuOverlay>()\n private readonly _lifecycle = defineOverlayLifecycle().make({\n isConnected: () => this.isConnected,\n isOpen: () => this.open\n })\n private readonly _scrollLock = defineScrollLockLease().make()\n // 行为层(hover / outside-click / 键盘 / submenu 收尾)由 shared/menu-behavior 驱动\n private readonly _outsideClickGuard = createMenuOutsideClickGuard(this, node =>\n [...this._overlays.values()].some(({ overlay }) => overlayComposition.contains(overlay, node))\n )\n private readonly _closingSubmenus = createClosingSubmenuStack<MenuOverlay>({\n getPanel: container => container.overlay,\n restoreItems: (container, parentItem) => moveMenuChildren(container.content, parentItem),\n dispose: container => {\n overlayComposition.unregisterPanel(container.overlay)\n container.overlay.remove()\n container.api.dispose()\n }\n })\n private readonly _hoverDelegate: MenuHoverDelegate = {\n getOpenDepth: () => this._activePath.length,\n getLevelItems: level => this._getLevelItems(level),\n getActiveItem: level => this._getLevelItems(level)[this._activePath[level]],\n isSubmenuItem: item => item.hasAttribute('submenu'),\n openSubmenu: item => {\n const level = this._getFocusedLevel(item)\n if (level === undefined) return\n const itemIndex = this._getLevelItems(level).indexOf(item)\n if (itemIndex >= 0) this._toggleSubmenu(level, itemIndex)\n },\n closeFrom: level => this._closeSubmenuFrom(level + 1)\n }\n private readonly _hoverBinder = createMenuHoverBinder(this._hoverDelegate, 'web-ui-dropdown-item')\n private readonly _keyboardDelegate: MenuKeyboardDelegate = {\n getFocusedItem: event =>\n getFocusedMenuItemFromPanels(\n event,\n [...this._overlays.values()].map(({ overlay }) => overlay),\n 'web-ui-dropdown-item'\n ),\n getLevelOf: item => this._getFocusedLevel(item),\n getEnabledItems: level => getEnabledMenuLevelItems(this._overlays.get(level)?.content),\n isSubmenuItem: item => item.hasAttribute('submenu'),\n openSubmenuInstant: item => {\n const level = this._getFocusedLevel(item)\n if (level === undefined) return\n const itemIndex = this._getLevelItems(level).indexOf(item)\n if (itemIndex >= 0) this._toggleSubmenu(level, itemIndex, true)\n },\n closeToParent: level => {\n this._closeSubmenuFrom(level)\n return this._getLevelItems(level - 1)[this._activePath[level - 1]]\n },\n closeDeepestOrAll: () => {\n if (this._activePath.length > 0) this._closeSubmenuFrom(this._activePath.length)\n else this._closeAll(true)\n }\n }\n private readonly _userOpenChange = new UserChangeController()\n private _restoreFocusTarget?: HTMLElement\n private _shouldOpenInstantly = true\n private _menuSyncScheduled = false\n private readonly _level0ItemAnchors: MenuItemAnchors = new Map()\n // 打开期实时渲染:框架(Vue/React 条件渲染)可能在菜单打开期间持续插入菜单项。\n // 观察宿主子树(新增项可能被框架 wrapper 包裹)与各层级面板 content 的子树\n // (打开中的 submenu 父项已被迁入 content,其新增子项只在这里可见),下一帧统一\n // 把新子树迁入对应层级面板;面板尺寸变化由 defineOverlay 的 autoUpdate 跟进重定位。\n private readonly _menuContentObserver = new MutationObserver(() => {\n if (this.open) this._scheduleMenuSync()\n })\n\n get isOpen(): boolean {\n return this.open || this._activePath.length > 0\n }\n\n override connectedCallback() {\n super.connectedCallback()\n this._lifecycle.resume()\n this.addEventListener('keydown', this._onKeydown)\n document.addEventListener('click', this._onClickOutside)\n this._menuContentObserver.observe(this, { childList: true, subtree: true })\n }\n\n override firstUpdated() {\n // trigger slot 内容增删不触发宿主响应式更新:显式请求一轮渲染,\n // 由 updated() 的 ARIA 回写覆盖晚到的 trigger 元素。\n this.shadowRoot\n ?.querySelector<HTMLSlotElement>('slot[name=\"trigger\"]')\n ?.addEventListener('slotchange', () => this.requestUpdate())\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback()\n this._menuContentObserver.disconnect()\n this.removeEventListener('keydown', this._onKeydown)\n document.removeEventListener('click', this._onClickOutside)\n this._outsideClickGuard.dispose()\n this._hoverBinder.dispose()\n this._lifecycle.dispose()\n this._scrollLock.release()\n this._cleanupClosedMenu()\n }\n\n protected override updated(changed: Map<string, unknown>) {\n // ARIA 回写不依赖 open 分支,任何渲染后都保持与宿主状态同步。\n this._syncTriggerAria()\n\n if (changed.has('open')) {\n if (this.open) {\n this._restoreFocusTarget ??= document.activeElement instanceof HTMLElement ? document.activeElement : undefined\n this._outsideClickGuard.arm()\n this._syncScrollLock()\n this._hideAllSubmenuChildren()\n this._lifecycle.invalidate()\n this._lifecycle.scheduleFrame(() => {\n this._ensureOverlay(0, this._shouldOpenInstantly)\n this._shouldOpenInstantly = true\n focusMenuItem(getEnabledMenuLevelItems(this._overlays.get(0)?.content)[0])\n // 面板 id 在 overlay 构建后才有:回写 aria-controls 指向。\n this._syncTriggerAria()\n })\n this._bindHoversAfterUpdate()\n } else {\n this._lifecycle.invalidate()\n this._syncScrollLock()\n void this._closeRootAfterPresence()\n }\n if (this._userOpenChange.consume()) {\n dispatchOpenChangeEvent(this, this.open)\n }\n }\n if (changed.has('noScrollLock')) this._syncScrollLock()\n if (changed.has('placement') || changed.has('offset') || changed.has('matchWidth')) {\n this._overlays.get(0)?.api.update({\n placement: this.placement,\n offset: this.offset,\n matchWidth: this.matchWidth\n })\n }\n this._hoverBinder.bind()\n }\n\n // 命令式打开,不派发 open-change。\n openMenu() {\n this._openMenu(true, false)\n }\n\n private _openMenu(isInstant: boolean, fromUser: boolean) {\n if (this.disabled || this.open) return\n this._outsideClickGuard.arm()\n this._shouldOpenInstantly = isInstant\n if (fromUser) this._userOpenChange.mark()\n this.open = true\n }\n\n // 命令式关闭全部层级,不派发 open-change。\n closeAll() {\n this._closeAll(false)\n }\n\n private _closeAll(fromUser: boolean) {\n if (!this.open) return\n if (fromUser) this._userOpenChange.mark()\n this.open = false\n }\n\n // 不影响 open prop。\n private _closeAllSubmenus() {\n this._closeSubmenuFrom(1, true)\n this._closingSubmenus.restoreAll()\n this._syncActiveAttrs()\n }\n\n private _cleanupClosedMenu() {\n this._closeAllSubmenus()\n this._returnLevel0Items()\n this._disposeAll()\n }\n\n private async _closeRootAfterPresence() {\n const overlay = this._overlays.get(0)?.overlay\n if (overlay && !(await hideOverlayPresence(overlay))) return\n if (this.open || !this.isConnected) return\n\n this._cleanupClosedMenu()\n this._restoreFocusTarget?.focus()\n this._restoreFocusTarget = undefined\n }\n\n private _hideAllSubmenuChildren() {\n hideNestedMenuChildren(this, `${SLOT_PREFIX}-hidden`)\n }\n\n private _toggleSubmenu(level: number, itemIndex: number, isInstant = false) {\n if (this._activePath[level] === itemIndex) {\n return\n }\n\n this._closeSubmenuFrom(level + 1)\n\n this._activePath = [...this._activePath.slice(0, level), itemIndex]\n this._syncActiveAttrs()\n\n const item = this._getLevelItems(level)[itemIndex]\n if (item?.hasAttribute('submenu')) {\n this._lifecycle.scheduleFrame(() => {\n if (!this.open || this._activePath[level] !== itemIndex) return\n const restored = this._ensureOverlay(level + 1, isInstant, item)\n if (!restored) this._populateOverlay(level + 1, item)\n })\n }\n\n this._bindHoversAfterUpdate()\n }\n\n private _closeSubmenuFrom(level: number, isInstant = false) {\n for (let lv = this._activePath.length; lv >= level; lv--) {\n const overlay = this._overlays.get(lv)\n const parentItem = this._getLevelItems(lv - 1)[this._activePath[lv - 1]]\n if (!overlay) continue\n\n if (!parentItem || isInstant) {\n this._depopulateOverlay(lv, parentItem)\n this._disposeOverlay(lv)\n } else {\n this._overlays.delete(lv)\n this._closingSubmenus.closeAsync(parentItem, overlay)\n }\n }\n const nextActivePath = this._activePath.slice(0, level - 1)\n if (nextActivePath.length !== this._activePath.length) {\n this._activePath = nextActivePath\n }\n this._syncActiveAttrs()\n }\n\n private _syncActiveAttrs() {\n this.querySelectorAll('web-ui-dropdown-item').forEach(item => {\n item.removeAttribute('active')\n })\n this._overlays.forEach((_, level) => {\n this._getLevelItems(level).forEach(item => item.removeAttribute('active'))\n })\n this._activePath.forEach((itemIndex, level) => {\n const item = this._getLevelItems(level)[itemIndex]\n if (item?.matches('web-ui-dropdown-item')) {\n item.setAttribute('active', '')\n }\n })\n }\n\n private _getLevelItems(level: number): HTMLElement[] {\n const content = this._overlays.get(level)?.content\n return content ? getMenuChildren(content) : []\n }\n\n private _populateLevel0() {\n const content = this._overlays.get(0)?.content\n if (!content) return\n // 初次打开即建立 marker 锚:宿主此后始终持有模板序骨架,打开期框架插入与\n // 关闭归还都以它为基准(机制与 context-menu 共用,见 menu-tree)。\n reconcileManagedMenuItems(this, content, this._level0ItemAnchors, MENU_ITEM_MARKER)\n orderManagedMenuItems(this, content, this._level0ItemAnchors, MENU_ITEM_MARKER)\n }\n\n private _scheduleMenuSync() {\n if (this._menuSyncScheduled) return\n this._menuSyncScheduled = true\n requestAnimationFrame(() => {\n this._menuSyncScheduled = false\n if (!this.open) return\n this._syncOpenMenuContent()\n })\n }\n\n // 打开期的统一内容同步:把打开期间框架新增的菜单子树迁入对应层级面板。\n // 一级项走 marker 锚 reconcile(保序 + 关闭按模板位归还);面板尺寸变化由\n // defineOverlay 的 autoUpdate 跟进重定位。\n private _syncOpenMenuContent() {\n const level0Content = this._overlays.get(0)?.content\n // 迁移前隐藏宿主中新项的嵌套子项(hidden slot 属性随元素一起迁移)。\n this._hideAllSubmenuChildren()\n if (level0Content) {\n reconcileManagedMenuItems(this, level0Content, this._level0ItemAnchors, MENU_ITEM_MARKER)\n // 新迁入项的嵌套子项未打 hidden slot,会经 dropdown-item 默认 slot 渲染进\n // 父项 label 叠到菜单上;面板项在面板内渲染,宿主查询够不到,须对 content 补隐藏。\n hideNestedMenuChildren(level0Content, `${SLOT_PREFIX}-hidden`)\n // 框架注释锚点复位独立于元素重排:锚点漂移不收敛会破坏后续翻转的插入点(同 context-menu)。\n const frameworkAnchors = captureFrameworkAnchors(level0Content, MENU_ITEM_MARKER)\n orderManagedMenuItems(this, level0Content, this._level0ItemAnchors, MENU_ITEM_MARKER)\n restoreFrameworkAnchors(level0Content, frameworkAnchors)\n }\n for (const [level, overlay] of this._overlays) {\n if (level === 0) continue\n const parentItem = this._getLevelItems(level - 1)[this._activePath[level - 1]]\n if (parentItem) moveMenuChildren(parentItem, overlay.content)\n hideNestedMenuChildren(overlay.content, `${SLOT_PREFIX}-hidden`)\n }\n this._hoverBinder.bind()\n }\n\n private _returnLevel0Items() {\n const content = this._overlays.get(0)?.content\n if (content) returnManagedMenuItemsToSlot(this, content, this._level0ItemAnchors)\n }\n\n private _populateOverlay(level: number, submenuItem: HTMLElement) {\n const content = this._overlays.get(level)?.content\n if (content) moveMenuChildren(submenuItem, content)\n }\n\n private _depopulateOverlay(level: number, parentItem?: HTMLElement) {\n const content = this._overlays.get(level)?.content\n if (!content) return\n const targetLevel = level - 1\n const submenuItem = parentItem ?? this._getLevelItems(targetLevel)[this._activePath[targetLevel]]\n if (submenuItem) moveMenuChildren(content, submenuItem)\n }\n\n private _ensureOverlay(level: number, isInstant = false, submenuItem?: HTMLElement): boolean {\n const closingOverlay = submenuItem ? this._closingSubmenus.take(submenuItem) : undefined\n if (closingOverlay) {\n this._overlays.set(level, closingOverlay)\n closingOverlay.api.open()\n showOverlayPresence(closingOverlay.overlay, { isInstant })\n return true\n }\n const existing = this._overlays.get(level)\n if (existing) {\n existing.api.open()\n showOverlayPresence(existing.overlay, { isInstant })\n return false\n }\n this._buildOverlay(level, isInstant)\n return false\n }\n\n private _buildOverlay(level: number, isInstant = false) {\n const { panel: overlay, content } = createMenuPortalOverlay('dropdown-overlay', this)\n overlay.setAttribute('role', 'menu')\n // 根层级面板带 id:trigger 的 aria-controls 指向它(Q9a)。\n if (level === 0) overlay.id = `wui-dropdown-menu-${++dropdownIdCounter}`\n overlay.dataset.level = String(level)\n overlay.addEventListener('click', this._onMenuClick)\n overlay.addEventListener('keydown', this._onKeydown)\n\n const anchor = level === 0 ? this._queryTriggerAnchor() : this._getSubmenuTriggerAnchor(level - 1)\n\n if (anchor) {\n const ctrl = defineOverlay().make({\n anchor,\n overlay,\n placement: level === 0 ? this.placement : 'right-start',\n offset: level === 0 ? this.offset : 0,\n // 菜单至少与 trigger/父项同宽,内容可更宽(floor 来自 --wui-overlay-min-width)\n minAnchorWidth: true,\n matchWidth: level === 0 ? this.matchWidth : false,\n strategy: 'fixed'\n })\n this._overlays.set(level, { api: ctrl, overlay, content })\n overlayComposition.registerPanel(overlay, this._overlays.get(level - 1)?.overlay)\n if (level === 0) this._populateLevel0()\n // submenu 父项在面板 content 内,其新增子项只有观察 content 子树才能看到\n this._menuContentObserver.observe(content, { childList: true, subtree: true })\n ctrl.open()\n showOverlayPresence(overlay, { isInstant })\n }\n }\n\n private _disposeOverlay(level: number) {\n const overlay = this._overlays.get(level)\n if (overlay) overlayComposition.unregisterPanel(overlay.overlay)\n overlay?.overlay.removeEventListener('click', this._onMenuClick)\n overlay?.overlay.removeEventListener('keydown', this._onKeydown)\n overlay?.overlay.remove()\n overlay?.api.dispose()\n this._overlays.delete(level)\n this._rebindMenuContentObservers()\n }\n\n // jsdom 的 MutationObserver 不实现 unobserve;统一 disconnect 后重绑剩余目标。\n private _rebindMenuContentObservers() {\n this._menuContentObserver.disconnect()\n this._menuContentObserver.observe(this, { childList: true, subtree: true })\n for (const overlay of this._overlays.values()) {\n this._menuContentObserver.observe(overlay.content, { childList: true, subtree: true })\n }\n }\n\n private _disposeAll() {\n for (const level of this._overlays.keys()) {\n this._disposeOverlay(level)\n }\n }\n\n private _syncScrollLock(isOpen = this.open) {\n this._scrollLock.sync(isOpen && !this.noScrollLock)\n }\n\n private _queryTriggerAnchor(): HTMLElement | null {\n const slot = this.shadowRoot?.querySelector<HTMLSlotElement>('slot[name=\"trigger\"]')\n const el = slot?.assignedElements()[0]\n return el instanceof HTMLElement ? el : null\n }\n\n /*\n * trigger 语义回写(Q9a):slot 进来的元素是真正的可聚焦交互元素,\n * aria-haspopup / aria-expanded / aria-controls 必须设在它身上;\n * 组件没有 trigger 包装 div,此前完全没有 ARIA 接线。\n */\n private _syncTriggerAria() {\n const trigger = this._queryTriggerAnchor()\n if (!trigger) return\n trigger.setAttribute('aria-haspopup', 'menu')\n trigger.setAttribute('aria-expanded', String(this.open))\n const rootOverlay = this._overlays.get(0)?.overlay\n if (rootOverlay?.id) trigger.setAttribute('aria-controls', rootOverlay.id)\n else trigger.removeAttribute('aria-controls')\n }\n\n private _getSubmenuTriggerAnchor(level: number): HTMLElement | null {\n const idx = this._activePath[level]\n if (idx === undefined) return null\n const items = this._getLevelItems(level)\n const item = items[idx]\n if (!item) return null\n return item.shadowRoot?.querySelector('.item-inner') ?? item\n }\n\n private _onTriggerClick = (event: MouseEvent) => {\n if (this.disabled) return\n if (this.open) {\n this._closeAll(true)\n } else {\n this._openMenu(event.detail === 0, true)\n }\n }\n\n private _onClickOutside = (e: MouseEvent) => {\n if (!this.isOpen || this._outsideClickGuard.isArmed()) return\n if (this._outsideClickGuard.isInside(e)) return\n this._closeAll(true)\n }\n\n private _onMenuClick = (e: MouseEvent) => {\n const item = getMenuItemFromEvent(e)\n if (!item || item.hasAttribute('disabled') || !item.hasAttribute('submenu')) return\n\n const level = this._getFocusedLevel(item)\n if (level === undefined) return\n const itemIndex = this._getLevelItems(level).indexOf(item)\n if (itemIndex >= 0) this._toggleSubmenu(level, itemIndex, e.detail === 0)\n }\n\n private _onKeydown = (e: KeyboardEvent) => {\n if (this.disabled || !this.open) return\n\n // overlay 上键盘事件 target 是菜单项而非组件自身,不能限定 e.target === this,\n // 否则鼠标打开、焦点在菜单项时 Escape 无法关闭\n handleMenuKeyboard(this._keyboardDelegate, e)\n }\n\n private _getFocusedLevel(item?: HTMLElement): number | undefined {\n if (!item) return undefined\n const overlay = [...this._overlays.entries()].find(([, value]) => value.overlay.contains(item))\n return overlay?.[0]\n }\n\n private _bindHoversAfterUpdate() {\n requestAnimationFrame(() => this._hoverBinder.bind())\n }\n\n override render() {\n return html`\n <div class=\"dropdown-trigger\" @click=${this._onTriggerClick}>\n <slot name=\"trigger\"></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-dropdown': WebUiDropdown\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/dropdown/index.ts"],"sourcesContent":["import type { Placement } from '@floating-ui/dom'\nimport { html, LitElement, unsafeCSS } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport '@/components/dropdown-divider'\nimport '@/components/dropdown-header'\nimport '@/components/dropdown-item'\nimport { UserChangeController } from '@/shared/events/user-change'\nimport {\n createClosingSubmenuStack,\n createMenuHoverBinder,\n createMenuOutsideClickGuard,\n getEnabledMenuLevelItems,\n getFocusedMenuItemFromPanels,\n handleMenuKeyboard,\n type MenuHoverDelegate,\n type MenuKeyboardDelegate\n} from '@/shared/menu-behavior'\nimport { createMenuPortalOverlay } from '@/shared/menu-portal/menu-portal'\nimport {\n captureFrameworkAnchors,\n focusMenuItem,\n getMenuChildren,\n getMenuItemFromEvent,\n hideNestedMenuChildren,\n moveMenuChildren,\n orderManagedMenuItems,\n reconcileManagedMenuItems,\n restoreFrameworkAnchors,\n returnManagedMenuItemsToSlot,\n type MenuItemAnchors\n} from '@/shared/menu-portal/menu-tree'\nimport { normalizeLiteral, normalizeNumber } from '@/shared/normalize'\nimport { dispatchOpenChangeEvent } from '@/shared/open-state'\nimport { overlayComposition } from '@/shared/overlay/composition'\nimport { defineOverlayLifecycle } from '@/shared/overlay/lifecycle'\nimport { defineOverlay } from '@/shared/overlay/overlay'\nimport type { OverlayApi } from '@/shared/overlay/overlay'\nimport { FLOATING_PLACEMENTS } from '@/shared/overlay/placement-props'\nimport { hideOverlayPresence, showOverlayPresence } from '@/shared/overlay/presence'\nimport { defineScrollLockLease } from '@/shared/scroll-lock/scroll-lock'\n\nimport style from './style.css?inline'\n\nexport type { Placement }\n\nconst SLOT_PREFIX = 'web-ui-menu-level-'\n// 一级菜单项的模板位 marker(机制与 context-menu 共用,见 menu-tree 的托管项锚说明)。\nconst MENU_ITEM_MARKER = 'wui-dropdown-menu-item'\n\nlet dropdownIdCounter = 0\n\ninterface MenuOverlay {\n api: OverlayApi\n overlay: HTMLElement\n content: HTMLElement\n}\n\n@customElement('web-ui-dropdown')\nexport class WebUiDropdown extends LitElement {\n static override styles = unsafeCSS(style)\n\n @property({ type: Boolean, reflect: true }) open = false\n @property({ type: Boolean, reflect: true }) disabled = false\n @property({ type: Boolean, reflect: true, attribute: 'match-width' }) matchWidth = false\n @property({ type: Boolean, reflect: true, attribute: 'no-scroll-lock' }) noScrollLock = false\n\n private _placement: Placement = 'bottom-start'\n\n @property({ type: String, reflect: true })\n get placement(): Placement {\n return this._placement\n }\n set placement(v: string) {\n this._placement = normalizeLiteral(v, FLOATING_PLACEMENTS, 'bottom-start')\n }\n\n private _offset = 4\n\n @property({ type: Number })\n get offset(): number {\n return this._offset\n }\n set offset(v: number) {\n this._offset = normalizeNumber(v, 0, 100, 4)\n }\n\n @state() private _activePath: number[] = []\n\n private readonly _overlays = new Map<number, MenuOverlay>()\n private readonly _lifecycle = defineOverlayLifecycle().make({\n isConnected: () => this.isConnected,\n isOpen: () => this.open\n })\n private readonly _scrollLock = defineScrollLockLease().make()\n // 行为层(hover / outside-click / 键盘 / submenu 收尾)由 shared/menu-behavior 驱动\n private readonly _outsideClickGuard = createMenuOutsideClickGuard(this, node =>\n [...this._overlays.values()].some(({ overlay }) => overlayComposition.contains(overlay, node))\n )\n private readonly _closingSubmenus = createClosingSubmenuStack<MenuOverlay>({\n getPanel: container => container.overlay,\n restoreItems: (container, parentItem) => moveMenuChildren(container.content, parentItem),\n dispose: container => {\n overlayComposition.unregisterPanel(container.overlay)\n container.overlay.remove()\n container.api.dispose()\n }\n })\n private readonly _hoverDelegate: MenuHoverDelegate = {\n getOpenDepth: () => this._activePath.length,\n getLevelItems: level => this._getLevelItems(level),\n getActiveItem: level => this._getLevelItems(level)[this._activePath[level]],\n isSubmenuItem: item => item.hasAttribute('submenu'),\n openSubmenu: item => {\n const level = this._getFocusedLevel(item)\n if (level === undefined) return\n const itemIndex = this._getLevelItems(level).indexOf(item)\n if (itemIndex >= 0) this._toggleSubmenu(level, itemIndex)\n },\n closeFrom: level => this._closeSubmenuFrom(level + 1)\n }\n private readonly _hoverBinder = createMenuHoverBinder(this._hoverDelegate, 'web-ui-dropdown-item')\n private readonly _keyboardDelegate: MenuKeyboardDelegate = {\n getFocusedItem: event =>\n getFocusedMenuItemFromPanels(\n event,\n [...this._overlays.values()].map(({ overlay }) => overlay),\n 'web-ui-dropdown-item'\n ),\n getLevelOf: item => this._getFocusedLevel(item),\n getEnabledItems: level => getEnabledMenuLevelItems(this._overlays.get(level)?.content),\n isSubmenuItem: item => item.hasAttribute('submenu'),\n openSubmenuInstant: item => {\n const level = this._getFocusedLevel(item)\n if (level === undefined) return\n const itemIndex = this._getLevelItems(level).indexOf(item)\n if (itemIndex >= 0) this._toggleSubmenu(level, itemIndex, true)\n },\n closeToParent: level => {\n this._closeSubmenuFrom(level)\n return this._getLevelItems(level - 1)[this._activePath[level - 1]]\n },\n closeDeepestOrAll: () => {\n if (this._activePath.length > 0) this._closeSubmenuFrom(this._activePath.length)\n else this._closeAll(true)\n }\n }\n private readonly _userOpenChange = new UserChangeController()\n private _restoreFocusTarget?: HTMLElement\n private _shouldOpenInstantly = true\n private _menuSyncScheduled = false\n private readonly _level0ItemAnchors: MenuItemAnchors = new Map()\n // 打开期实时渲染:框架(Vue/React 条件渲染)可能在菜单打开期间持续插入菜单项。\n // 观察宿主子树(新增项可能被框架 wrapper 包裹)与各层级面板 content 的子树\n // (打开中的 submenu 父项已被迁入 content,其新增子项只在这里可见),下一帧统一\n // 把新子树迁入对应层级面板;面板尺寸变化由 defineOverlay 的 autoUpdate 跟进重定位。\n private readonly _menuContentObserver = new MutationObserver(() => {\n if (this.open) this._scheduleMenuSync()\n })\n\n get isOpen(): boolean {\n return this.open || this._activePath.length > 0\n }\n\n override connectedCallback() {\n super.connectedCallback()\n this._lifecycle.resume()\n this.addEventListener('keydown', this._onKeydown)\n document.addEventListener('click', this._onClickOutside)\n this._menuContentObserver.observe(this, { childList: true, subtree: true })\n }\n\n override firstUpdated() {\n // trigger slot 内容增删不触发宿主响应式更新:显式请求一轮渲染,\n // 由 updated() 的 ARIA 回写覆盖晚到的 trigger 元素。\n this.shadowRoot\n ?.querySelector<HTMLSlotElement>('slot[name=\"trigger\"]')\n ?.addEventListener('slotchange', () => this.requestUpdate())\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback()\n this._menuContentObserver.disconnect()\n this.removeEventListener('keydown', this._onKeydown)\n document.removeEventListener('click', this._onClickOutside)\n this._outsideClickGuard.dispose()\n this._hoverBinder.dispose()\n this._lifecycle.dispose()\n this._scrollLock.release()\n this._cleanupClosedMenu()\n }\n\n protected override updated(changed: Map<string, unknown>) {\n // ARIA 回写不依赖 open 分支,任何渲染后都保持与宿主状态同步。\n this._syncTriggerAria()\n\n if (changed.has('open')) {\n if (this.open) {\n this._restoreFocusTarget ??= document.activeElement instanceof HTMLElement ? document.activeElement : undefined\n this._outsideClickGuard.arm()\n this._syncScrollLock()\n this._hideAllSubmenuChildren()\n this._lifecycle.invalidate()\n this._lifecycle.scheduleFrame(() => {\n this._ensureOverlay(0, this._shouldOpenInstantly)\n this._shouldOpenInstantly = true\n focusMenuItem(getEnabledMenuLevelItems(this._overlays.get(0)?.content)[0])\n // 面板 id 在 overlay 构建后才有:回写 aria-controls 指向。\n this._syncTriggerAria()\n })\n this._bindHoversAfterUpdate()\n } else {\n this._lifecycle.invalidate()\n this._syncScrollLock()\n void this._closeRootAfterPresence()\n }\n if (this._userOpenChange.consume()) {\n dispatchOpenChangeEvent(this, this.open)\n }\n }\n if (changed.has('noScrollLock')) this._syncScrollLock()\n if (changed.has('placement') || changed.has('offset') || changed.has('matchWidth')) {\n this._overlays.get(0)?.api.update({\n placement: this.placement,\n offset: this.offset,\n matchWidth: this.matchWidth\n })\n }\n this._hoverBinder.bind()\n }\n\n // 命令式打开,不派发 open-change。\n openMenu() {\n this._openMenu(true, false)\n }\n\n private _openMenu(isInstant: boolean, fromUser: boolean) {\n if (this.disabled || this.open) return\n this._outsideClickGuard.arm()\n this._shouldOpenInstantly = isInstant\n if (fromUser) this._userOpenChange.mark()\n this.open = true\n }\n\n // 命令式关闭全部层级,不派发 open-change。\n closeAll() {\n this._closeAll(false)\n }\n\n private _closeAll(fromUser: boolean) {\n if (!this.open) return\n if (fromUser) this._userOpenChange.mark()\n this.open = false\n }\n\n // 不影响 open prop。\n private _closeAllSubmenus() {\n this._closeSubmenuFrom(1, true)\n this._closingSubmenus.restoreAll()\n this._syncActiveAttrs()\n }\n\n private _cleanupClosedMenu() {\n this._closeAllSubmenus()\n this._returnLevel0Items()\n this._disposeAll()\n }\n\n private async _closeRootAfterPresence() {\n const overlay = this._overlays.get(0)?.overlay\n if (overlay && !(await hideOverlayPresence(overlay))) return\n if (this.open || !this.isConnected) return\n\n this._cleanupClosedMenu()\n this._restoreFocusTarget?.focus()\n this._restoreFocusTarget = undefined\n }\n\n private _hideAllSubmenuChildren() {\n hideNestedMenuChildren(this, `${SLOT_PREFIX}-hidden`)\n }\n\n private _toggleSubmenu(level: number, itemIndex: number, isInstant = false) {\n if (this._activePath[level] === itemIndex) {\n return\n }\n\n this._closeSubmenuFrom(level + 1)\n\n this._activePath = [...this._activePath.slice(0, level), itemIndex]\n this._syncActiveAttrs()\n\n const item = this._getLevelItems(level)[itemIndex]\n if (item?.hasAttribute('submenu')) {\n this._lifecycle.scheduleFrame(() => {\n if (!this.open || this._activePath[level] !== itemIndex) return\n const restored = this._ensureOverlay(level + 1, isInstant, item)\n if (!restored) this._populateOverlay(level + 1, item)\n })\n }\n\n this._bindHoversAfterUpdate()\n }\n\n private _closeSubmenuFrom(level: number, isInstant = false) {\n for (let lv = this._activePath.length; lv >= level; lv--) {\n const overlay = this._overlays.get(lv)\n const parentItem = this._getLevelItems(lv - 1)[this._activePath[lv - 1]]\n if (!overlay) continue\n\n if (!parentItem || isInstant) {\n this._depopulateOverlay(lv, parentItem)\n this._disposeOverlay(lv)\n } else {\n this._overlays.delete(lv)\n this._closingSubmenus.closeAsync(parentItem, overlay)\n }\n }\n const nextActivePath = this._activePath.slice(0, level - 1)\n if (nextActivePath.length !== this._activePath.length) {\n this._activePath = nextActivePath\n }\n this._syncActiveAttrs()\n }\n\n private _syncActiveAttrs() {\n this.querySelectorAll('web-ui-dropdown-item').forEach(item => {\n item.removeAttribute('active')\n })\n this._overlays.forEach((_, level) => {\n this._getLevelItems(level).forEach(item => item.removeAttribute('active'))\n })\n this._activePath.forEach((itemIndex, level) => {\n const item = this._getLevelItems(level)[itemIndex]\n if (item?.matches('web-ui-dropdown-item')) {\n item.setAttribute('active', '')\n }\n })\n }\n\n private _getLevelItems(level: number): HTMLElement[] {\n const content = this._overlays.get(level)?.content\n return content ? getMenuChildren(content) : []\n }\n\n private _populateLevel0() {\n const content = this._overlays.get(0)?.content\n if (!content) return\n // 初次打开即建立 marker 锚:宿主此后始终持有模板序骨架,打开期框架插入与\n // 关闭归还都以它为基准(机制与 context-menu 共用,见 menu-tree)。\n reconcileManagedMenuItems(this, content, this._level0ItemAnchors, MENU_ITEM_MARKER)\n orderManagedMenuItems(this, content, this._level0ItemAnchors, MENU_ITEM_MARKER)\n }\n\n private _scheduleMenuSync() {\n if (this._menuSyncScheduled) return\n this._menuSyncScheduled = true\n requestAnimationFrame(() => {\n this._menuSyncScheduled = false\n if (!this.open) return\n this._syncOpenMenuContent()\n })\n }\n\n // 打开期的统一内容同步:把打开期间框架新增的菜单子树迁入对应层级面板。\n // 一级项走 marker 锚 reconcile(保序 + 关闭按模板位归还);面板尺寸变化由\n // defineOverlay 的 autoUpdate 跟进重定位。\n private _syncOpenMenuContent() {\n const level0Content = this._overlays.get(0)?.content\n // 迁移前隐藏宿主中新项的嵌套子项(hidden slot 属性随元素一起迁移)。\n this._hideAllSubmenuChildren()\n if (level0Content) {\n reconcileManagedMenuItems(this, level0Content, this._level0ItemAnchors, MENU_ITEM_MARKER)\n // 新迁入项的嵌套子项未打 hidden slot,会经 dropdown-item 默认 slot 渲染进\n // 父项 label 叠到菜单上;面板项在面板内渲染,宿主查询够不到,须对 content 补隐藏。\n hideNestedMenuChildren(level0Content, `${SLOT_PREFIX}-hidden`)\n // 框架注释锚点复位独立于元素重排:锚点漂移不收敛会破坏后续翻转的插入点(同 context-menu)。\n const frameworkAnchors = captureFrameworkAnchors(level0Content, MENU_ITEM_MARKER)\n orderManagedMenuItems(this, level0Content, this._level0ItemAnchors, MENU_ITEM_MARKER)\n restoreFrameworkAnchors(level0Content, frameworkAnchors)\n }\n for (const [level, overlay] of this._overlays) {\n if (level === 0) continue\n const parentItem = this._getLevelItems(level - 1)[this._activePath[level - 1]]\n if (parentItem) moveMenuChildren(parentItem, overlay.content)\n hideNestedMenuChildren(overlay.content, `${SLOT_PREFIX}-hidden`)\n }\n this._hoverBinder.bind()\n }\n\n private _returnLevel0Items() {\n const content = this._overlays.get(0)?.content\n if (content) returnManagedMenuItemsToSlot(this, content, this._level0ItemAnchors)\n }\n\n private _populateOverlay(level: number, submenuItem: HTMLElement) {\n const content = this._overlays.get(level)?.content\n if (content) moveMenuChildren(submenuItem, content)\n }\n\n private _depopulateOverlay(level: number, parentItem?: HTMLElement) {\n const content = this._overlays.get(level)?.content\n if (!content) return\n const targetLevel = level - 1\n const submenuItem = parentItem ?? this._getLevelItems(targetLevel)[this._activePath[targetLevel]]\n if (submenuItem) moveMenuChildren(content, submenuItem)\n }\n\n private _ensureOverlay(level: number, isInstant = false, submenuItem?: HTMLElement): boolean {\n const closingOverlay = submenuItem ? this._closingSubmenus.take(submenuItem) : undefined\n if (closingOverlay) {\n this._overlays.set(level, closingOverlay)\n closingOverlay.api.open()\n showOverlayPresence(closingOverlay.overlay, { isInstant })\n return true\n }\n const existing = this._overlays.get(level)\n if (existing) {\n existing.api.open()\n showOverlayPresence(existing.overlay, { isInstant })\n return false\n }\n this._buildOverlay(level, isInstant)\n return false\n }\n\n private _buildOverlay(level: number, isInstant = false) {\n const { panel: overlay, content } = createMenuPortalOverlay('dropdown-overlay', this)\n overlay.setAttribute('role', 'menu')\n // 根层级面板带 id:trigger 的 aria-controls 指向它(Q9a)。\n if (level === 0) overlay.id = `wui-dropdown-menu-${++dropdownIdCounter}`\n overlay.dataset.level = String(level)\n overlay.addEventListener('click', this._onMenuClick)\n overlay.addEventListener('keydown', this._onKeydown)\n\n const anchor = level === 0 ? this._queryTriggerAnchor() : this._getSubmenuTriggerAnchor(level - 1)\n\n if (anchor) {\n const ctrl = defineOverlay().make({\n anchor,\n overlay,\n placement: level === 0 ? this.placement : 'right-start',\n offset: level === 0 ? this.offset : 0,\n // 菜单至少与 trigger/父项同宽,内容可更宽(floor 来自 --wui-overlay-min-width)\n minAnchorWidth: true,\n matchWidth: level === 0 ? this.matchWidth : false,\n strategy: 'fixed'\n })\n this._overlays.set(level, { api: ctrl, overlay, content })\n overlayComposition.registerPanel(overlay, this._overlays.get(level - 1)?.overlay)\n if (level === 0) this._populateLevel0()\n // submenu 父项在面板 content 内,其新增子项只有观察 content 子树才能看到\n this._menuContentObserver.observe(content, { childList: true, subtree: true })\n ctrl.open()\n showOverlayPresence(overlay, { isInstant })\n }\n }\n\n private _disposeOverlay(level: number) {\n const overlay = this._overlays.get(level)\n if (overlay) overlayComposition.unregisterPanel(overlay.overlay)\n overlay?.overlay.removeEventListener('click', this._onMenuClick)\n overlay?.overlay.removeEventListener('keydown', this._onKeydown)\n overlay?.overlay.remove()\n overlay?.api.dispose()\n this._overlays.delete(level)\n this._rebindMenuContentObservers()\n }\n\n // jsdom 的 MutationObserver 不实现 unobserve;统一 disconnect 后重绑剩余目标。\n private _rebindMenuContentObservers() {\n this._menuContentObserver.disconnect()\n this._menuContentObserver.observe(this, { childList: true, subtree: true })\n for (const overlay of this._overlays.values()) {\n this._menuContentObserver.observe(overlay.content, { childList: true, subtree: true })\n }\n }\n\n private _disposeAll() {\n for (const level of this._overlays.keys()) {\n this._disposeOverlay(level)\n }\n }\n\n private _syncScrollLock(isOpen = this.open) {\n this._scrollLock.sync(isOpen && !this.noScrollLock)\n }\n\n private _queryTriggerAnchor(): HTMLElement | null {\n const slot = this.shadowRoot?.querySelector<HTMLSlotElement>('slot[name=\"trigger\"]')\n const el = slot?.assignedElements()[0]\n return el instanceof HTMLElement ? el : null\n }\n\n /*\n * trigger 语义回写(Q9a):slot 进来的元素是真正的可聚焦交互元素,\n * aria-haspopup / aria-expanded / aria-controls 必须设在它身上;\n * 组件没有 trigger 包装 div,此前完全没有 ARIA 接线。\n */\n private _syncTriggerAria() {\n const trigger = this._queryTriggerAnchor()\n if (!trigger) return\n trigger.setAttribute('aria-haspopup', 'menu')\n trigger.setAttribute('aria-expanded', String(this.open))\n const rootOverlay = this._overlays.get(0)?.overlay\n if (rootOverlay?.id) trigger.setAttribute('aria-controls', rootOverlay.id)\n else trigger.removeAttribute('aria-controls')\n }\n\n private _getSubmenuTriggerAnchor(level: number): HTMLElement | null {\n const idx = this._activePath[level]\n if (idx === undefined) return null\n const items = this._getLevelItems(level)\n const item = items[idx]\n if (!item) return null\n return item.shadowRoot?.querySelector('.item-inner') ?? item\n }\n\n private _onTriggerClick = (event: MouseEvent) => {\n if (this.disabled) return\n if (this.open) {\n this._closeAll(true)\n } else {\n this._openMenu(event.detail === 0, true)\n }\n }\n\n private _onClickOutside = (e: MouseEvent) => {\n if (!this.isOpen || this._outsideClickGuard.isArmed()) return\n if (this._outsideClickGuard.isInside(e)) return\n this._closeAll(true)\n }\n\n private _onMenuClick = (e: MouseEvent) => {\n const item = getMenuItemFromEvent(e)\n if (!item || item.hasAttribute('disabled') || !item.hasAttribute('submenu')) return\n\n const level = this._getFocusedLevel(item)\n if (level === undefined) return\n const itemIndex = this._getLevelItems(level).indexOf(item)\n if (itemIndex >= 0) this._toggleSubmenu(level, itemIndex, e.detail === 0)\n }\n\n private _onKeydown = (e: KeyboardEvent) => {\n if (this.disabled || !this.open) return\n\n // overlay 上键盘事件 target 是菜单项而非组件自身,不能限定 e.target === this,\n // 否则鼠标打开、焦点在菜单项时 Escape 无法关闭\n handleMenuKeyboard(this._keyboardDelegate, e)\n }\n\n private _getFocusedLevel(item?: HTMLElement): number | undefined {\n if (!item) return undefined\n const overlay = [...this._overlays.entries()].find(([, value]) => value.overlay.contains(item))\n return overlay?.[0]\n }\n\n private _bindHoversAfterUpdate() {\n requestAnimationFrame(() => this._hoverBinder.bind())\n }\n\n override render() {\n return html`\n <div class=\"dropdown-trigger\" @click=${this._onTriggerClick}>\n <slot name=\"trigger\"></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-dropdown': WebUiDropdown\n }\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import e from "../../_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorate.js";
1
+ import e from "../../_virtual/_@oxc-project_runtime@0.149.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.146.0/helpers/esm/decorate.js";
1
+ import e from "../../_virtual/_@oxc-project_runtime@0.149.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.146.0/helpers/esm/decorate.js";
1
+ import e from "../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorate.js";
2
2
  import "../icon/index.js";
3
3
  import t from "../../icons/generated/lucide-chevron-right.js";
4
4
  import n from "./style.js";
@@ -1,4 +1,4 @@
1
- import e from "../../_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorate.js";
1
+ import e from "../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorate.js";
2
2
  import "../icon/index.js";
3
3
  import t from "../../icons/generated/lucide-inbox.js";
4
4
  import { normalizeLiteral as n } from "../../shared/normalize/index.js";
@@ -1,5 +1,5 @@
1
1
  import e from "./style.js";
2
- import t from "../../_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorate.js";
2
+ import t from "../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorate.js";
3
3
  import { LitElement as n, html as r, nothing as i, unsafeCSS as a } from "lit";
4
4
  import { customElement as o, property as s } from "lit/decorators.js";
5
5
  import { classMap as c } from "lit/directives/class-map.js";
@@ -1,5 +1,5 @@
1
1
  //#region src/components/icon/style.css?inline
2
- var e = "*,:before,:after{box-sizing:border-box}:host{-webkit-user-select:none;user-select:none;display:inline-flex}svg{color:var(--wui-icon-color,inherit);display:block}svg.spin{animation:.6s linear infinite wui-icon-spin}@keyframes wui-icon-spin{to{transform:rotate(360deg)}}";
2
+ var e = "*,:before,:after{box-sizing:border-box}:host{-webkit-user-select:none;user-select:none;display:inline-flex}svg{color:var(--wui-icon-color,inherit);display:block}svg.spin{animation:wui-icon-spin var(--wui-duration-spin,.6s) linear infinite}@keyframes wui-icon-spin{to{transform:rotate(360deg)}}";
3
3
  //#endregion
4
4
  export { e as default };
5
5
 
@@ -1 +1 @@
1
- {"version":3,"file":"style.js","names":[],"sources":["../../../src/components/icon/style.css?inline"],"sourcesContent":["*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n:host {\n user-select: none;\n display: inline-flex;\n}\n\nsvg {\n display: block;\n color: var(--wui-icon-color, inherit);\n}\n\nsvg.spin {\n animation: wui-icon-spin 0.6s linear infinite;\n}\n\n@keyframes wui-icon-spin {\n to {\n transform: rotate(360deg);\n }\n}\n"],"mappings":""}
1
+ {"version":3,"file":"style.js","names":[],"sources":["../../../src/components/icon/style.css?inline"],"sourcesContent":["*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n:host {\n user-select: none;\n display: inline-flex;\n}\n\nsvg {\n display: block;\n color: var(--wui-icon-color, inherit);\n}\n\n/* 周期走主题 token:组件级 @media 块看不到 motion 属性,只有 token 能同时响应\n 显式 motion=\"reduced\" 与系统 prefers-reduced-motion。 */\nsvg.spin {\n animation: wui-icon-spin var(--wui-duration-spin, 600ms) linear infinite;\n}\n\n@keyframes wui-icon-spin {\n to {\n transform: rotate(360deg);\n }\n}\n"],"mappings":""}
@@ -1,4 +1,4 @@
1
- import e from "../../_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorate.js";
1
+ import e from "../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorate.js";
2
2
  import "../icon/index.js";
3
3
  import t from "../../icons/generated/lucide-chevron-left.js";
4
4
  import n from "../../icons/generated/lucide-chevron-right.js";
@@ -37,7 +37,7 @@ var E = 1, D = 4, O = .5, k = .002, A = 3, j = .15, M = 320, N = 16, P = 160, F
37
37
  }, this._handleTransitionEnd = (e) => {
38
38
  this._presence.handleTransitionEnd(e);
39
39
  }, this._handleKeydown = (e) => {
40
- if (!(!this._open || e.defaultPrevented) && !(e.altKey || e.ctrlKey || e.metaKey)) switch (e.key) {
40
+ if (this._open && !e.defaultPrevented && !(e.altKey || e.ctrlKey || e.metaKey)) switch (e.key) {
41
41
  case "ArrowLeft":
42
42
  e.preventDefault(), this.prev();
43
43
  break;
@@ -70,7 +70,7 @@ var E = 1, D = 4, O = .5, k = .002, A = 3, j = .15, M = 320, N = 16, P = 160, F
70
70
  this.noBackdropClose || this.close();
71
71
  }
72
72
  }, this._handleDoubleClick = (e) => {
73
- !this._open || e.target !== this._image || (this._scale > E ? this.resetZoom() : this._setScale(D / 2));
73
+ this._open && e.target === this._image && (this._scale > E ? this.resetZoom() : this._setScale(D / 2));
74
74
  }, this._handlePinchStart = (e) => {
75
75
  if (!this._open) return;
76
76
  this._endGesture(), this._dragging = !0, this._dragged = !0;
@@ -85,12 +85,12 @@ var E = 1, D = 4, O = .5, k = .002, A = 3, j = .15, M = 320, N = 16, P = 160, F
85
85
  }, this._handlePinchCancel = () => {
86
86
  this._dragging = !1;
87
87
  }, this._handleSwipeSettleEnd = (e) => {
88
- !this._swipeSettling || e.propertyName !== "transform" || this._finishSwipeSettle();
88
+ this._swipeSettling && e.propertyName === "transform" && this._finishSwipeSettle();
89
89
  }, this._handlePointerDown = (e) => {
90
90
  if (!this._open || (this._cancelSwipeSettle(), this._swipeDir = 0, this._swipeOffset = 0, e.button !== 0 || e.isPrimary === !1)) return;
91
91
  this._blankPointerDown = this._isBlank(e.target), this._dragged = !1;
92
92
  let t = this._stage;
93
- !t || e.target !== t && !t.contains(e.target) || (this._swipeGesture = this._scale === E && this.swipe && this.images.length > 1, this._swipeStartTime = e.timeStamp, this._panOriginX = this._offsetX, this._panOriginY = this._offsetY, this._gesture?.destroy(), this._gesture = p(e, {
93
+ t && (e.target === t || t.contains(e.target)) && (this._swipeGesture = this._scale === E && this.swipe && this.images.length > 1, this._swipeStartTime = e.timeStamp, this._panOriginX = this._offsetX, this._panOriginY = this._offsetY, this._gesture?.destroy(), this._gesture = p(e, {
94
94
  axis: "both",
95
95
  threshold: A,
96
96
  onMove: (e) => {