@greypan/web-ui 10.1.0 → 10.1.2

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 (31) hide show
  1. package/README.CN.md +10 -1
  2. package/README.md +12 -1
  3. package/dist/components/autocomplete/index.js +62 -60
  4. package/dist/components/autocomplete/index.js.map +1 -1
  5. package/dist/components/collapse/index.js +1 -1
  6. package/dist/components/collapse/index.js.map +1 -1
  7. package/dist/components/collapse/style.js +1 -1
  8. package/dist/components/collapse/style.js.map +1 -1
  9. package/dist/components/context-menu/index.d.ts +39 -0
  10. package/dist/components/context-menu/index.js +21 -10
  11. package/dist/components/context-menu/index.js.map +1 -1
  12. package/dist/components/dialog/index.js +39 -37
  13. package/dist/components/dialog/index.js.map +1 -1
  14. package/dist/components/drawer/index.js +64 -62
  15. package/dist/components/drawer/index.js.map +1 -1
  16. package/dist/components/empty/style.js +1 -1
  17. package/dist/components/empty/style.js.map +1 -1
  18. package/dist/components/segmented/index.js +17 -7
  19. package/dist/components/segmented/index.js.map +1 -1
  20. package/dist/components/select/index.js +45 -43
  21. package/dist/components/select/index.js.map +1 -1
  22. package/dist/components/toast/manager.js +1 -1
  23. package/dist/components/toast/manager.js.map +1 -1
  24. package/dist/scrollbars/internal.js +6 -0
  25. package/dist/scrollbars/internal.js.map +1 -0
  26. package/dist/scrollbars/style.css +1 -1
  27. package/dist/shared/menu-portal/menu-portal.js +16 -14
  28. package/dist/shared/menu-portal/menu-portal.js.map +1 -1
  29. package/dist/shared/overlay/overlay-root.js +13 -12
  30. package/dist/shared/overlay/overlay-root.js.map +1 -1
  31. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/dialog/index.ts"],"sourcesContent":["import '@/components/button'\nimport '@/components/icon'\n\nimport { html, LitElement, nothing, type PropertyValues, unsafeCSS } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport glass from '@/assets/glass.css?inline'\nimport modalSurface from '@/assets/modal-surface.css?inline'\nimport { oouiClose } from '@/icons'\nimport { UserChangeController } from '@/shared/events/user-change'\nimport { dispatchOpenChangeEvent } from '@/shared/open-state'\nimport { defineNativeDialogPresence } from '@/shared/overlay/native-dialog-presence'\nimport { defineOpenOverlay, type OpenOverlayHandle } from '@/shared/overlay/open-overlay'\nimport { defineScrollLockLease } from '@/shared/scroll-lock/scroll-lock'\n\nimport style from './style.css?inline'\n\n@customElement('web-ui-dialog')\nexport class WebUiDialog extends LitElement {\n static override styles = [unsafeCSS(glass), unsafeCSS(modalSurface), unsafeCSS(style)]\n\n @property({ type: Boolean, reflect: true }) open = false\n @property({ type: Boolean, reflect: true }) closable = false\n @property({ type: Boolean, reflect: true, attribute: 'no-scroll-lock' }) noScrollLock = false\n @property({ type: Boolean, reflect: true, attribute: 'no-backdrop-close' }) noBackdropClose = false\n @property({ type: Boolean, reflect: true, attribute: 'no-escape-close' }) noEscapeClose = false\n\n /**\n * Controlled 模式下,Escape 和遮罩点击只派发 `open-change` 请求,\n * 不会自行修改 `open`。Consumer 回写 `open` 后才执行关闭动画。\n * 程序化 API(showModal/close/直接赋值 open)不受此模式影响,始终直通。\n */\n @property({ type: Boolean, reflect: true }) controlled = false\n\n /**\n * `header` 槽为空时的标题文案。槽优先:一旦有元素分配到 `header`,槽内容整条接管,\n * 属性被忽略。默认空串且槽也为空时不渲染 chrome 带。`headless` 模式不渲染 chrome 带,\n * `heading` 一并失效。\n */\n @property({ type: String }) heading = ''\n\n /**\n * Headless 模式:只保留 overlay 基础设施(backdrop、动画、scroll lock、`<dialog>` 语义),\n * 移除内置 UI(chrome 带、正文区、footer),默认槽的内容渲染进卡片内的滚动层\n * `.wui-dialog-content`,视觉交给 Consumer。形态与 `<web-ui-drawer>` 的 `headless` 一致。\n *\n * 该模式没有可自动关联的内置标题,内部原生 dialog 的 accessible name 必须由 Consumer\n * 用 `dialog-label`(或自绘标题 + 自己的 `aria-labelledby`)提供。\n */\n @property({ type: Boolean, reflect: true }) headless = false\n\n /**\n * 内部原生 dialog 的 accessible name。`headless` 模式必须由 Consumer 提供,\n * 因为该模式不会渲染可自动关联的内置 chrome 带。\n */\n @property({ type: String, attribute: 'dialog-label' }) dialogLabel = ''\n\n @state() private _hasHeader = false\n private readonly _userOpenChange = new UserChangeController()\n\n /*\n * 开启态浮层(issue #120 Block 1)。dialog 的 Escape 原本只走原生 cancel 事件:仲裁者\n * 会在 keydown 上 preventDefault 压掉原生关闭请求并 stopPropagation,因此这里必须\n * 自己表达关闭语义(controlled 只派发请求)。\n */\n private readonly _overlay = defineOpenOverlay().make({\n requestClose: () => this._closeFromUser(),\n isConnected: () => this.isConnected\n })\n /** 当前开启会话的句柄;未开启时为 null。查询与惰性同步走它。 */\n private _handle: OpenOverlayHandle | null = null\n private readonly _scrollLock = defineScrollLockLease().make()\n private readonly _presence = defineNativeDialogPresence().make({\n getDialog: () => this.dialog,\n isConnected: () => this.isConnected,\n isOpen: () => this.open\n })\n\n private get dialog() {\n return this.shadowRoot?.querySelector('dialog') ?? null\n }\n\n protected override updated(props: PropertyValues) {\n super.updated(props)\n if (!this.isConnected) return\n\n if (props.has('open')) {\n if (this._userOpenChange.consume()) this.emitOpenChange()\n this._presence.sync(this.open)\n if (this.open) {\n // presence.sync 内的 showModal() 把 dialog 提升进 top layer。双击手势本身\n // 留下的活选区会被浏览器拿去和提升后的新布局重新解析,结果选区落到刚挂载的\n // dialog 正文上。脆弱的是「手势来源的活选区」,不是 dblclick 的判定窗口:把\n // 打开推迟 250ms(早过双击判定)仍然复现。选区由浏览器在 showModal() 调用\n // 内部生成:脚本没有任何 Selection API 调用,在 dblclick 上 preventDefault\n // 也拦不住。\n //\n // 实测(Chromium)showModal() 不会动打开前已经存在的选区,所以这一次清理\n // 同时也覆盖「先选中页面文本、再打开」的情况——modal 打开后被选中的内容挡在\n // 遮罩后面本来也已不可用。这里只清文档选区,不动 user-select,浮层内正文\n // 仍可正常拖选。\n window.getSelection()?.removeAllRanges()\n }\n // 原生 dialog 登记为开启态浮层:挂在它上面的 portal 面板成为本层后代,\n // Escape 仲裁据此判出最内层(issue #120 Block 1)。\n const dialog = this.dialog\n if (dialog) {\n if (this.open) {\n // 同一面板重新 claim = 新的一次开启:旧会话(含其子层)整体作废。\n this._handle?.release()\n this._handle = this._overlay.claim(dialog, { ancestryFrom: this })\n } else {\n this._handle?.release()\n this._handle = null\n }\n }\n }\n if (props.has('open') || props.has('noScrollLock')) this._syncScrollLock()\n this._syncOverlayInert()\n }\n\n override connectedCallback() {\n super.connectedCallback()\n // 重挂载对账:断连时 presence、滚动锁与登记都已被撤销,而 `open` 未变化时\n // `updated()` 不会补跑任何 sync 分支。首次连接时 shadow 尚未渲染、`this.dialog`\n // 为 null,三种情况都直接跳过;打开态的首次进入仍由 updated() 的\n // `props.has('open')` 分支处理。\n this._presence.reconcile()\n this._syncScrollLock()\n this._reclaimIfOpen()\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback()\n this._presence.dispose()\n this._scrollLock.release()\n // 断连即撤销登记:层不留在全局注册表里,重连后由 _reclaimIfOpen 显式重新声明。\n this._handle?.release()\n this._handle = null\n }\n\n /*\n * 「重挂载恢复」的调用方一半:模块刻意不观察 DOM 连接状态,所以断连撤销登记之后,\n * 重连时仍有开启态就必须由组件重新声明。\n */\n private _reclaimIfOpen() {\n if (!this.open || this._handle) return\n const dialog = this.dialog\n if (!dialog) return\n this._handle = this._overlay.claim(dialog, { ancestryFrom: this })\n this._syncOverlayInert()\n }\n\n /*\n * `no-escape-close` 时 dialog 仍是候选(Escape 被吞掉、原生 cancel 被压掉),只是\n * 不走关闭入口。与旧的「跳过候选」语义不同:旧语义放任事件落到下层浮层,把外层\n * 一起关掉。该属性可在开启期间改写,所以每次渲染后读它,而不是在 claim 时固定。\n */\n private _syncOverlayInert() {\n this._handle?.setInert(this.noEscapeClose)\n }\n\n showModal() {\n if (this.open) return\n this.open = true\n }\n\n close() {\n this.open = false\n }\n\n /*\n * 仲裁者在 capture 阶段 preventDefault 了 Escape 的 keydown,UA 因此不再派发原生\n * cancel,但本 handler 仍有两个仲裁者不负责的职责,**不可删**:\n * ① 任何到达 dialog 的 cancel 都要 preventDefault,把 top layer 保留到视觉退场结束\n * (否则原生关闭会跳过退出动画);\n * ② 忽略子控件(例如 file input)冒泡上来的 cancel。\n * 实测(overlay-open-owner-260918,2026-09-18)摘掉 `@cancel` 后 dialog.browser.spec.ts\n * 的「no-escape-close 存在时 Escape/cancel 不关闭对话框」与 remount-reconcile 的\n * 「打开态被移出文档再接回」两例立刻转红,断言正是 `dispatchEvent(cancel) === false`。\n */\n private handleCancel(e: Event) {\n if (e.target !== e.currentTarget) return\n\n e.preventDefault()\n if (this.noEscapeClose) return\n this._closeFromUser()\n }\n\n private handleBackdropClick(e: MouseEvent) {\n if (e.target !== (e.currentTarget as HTMLDialogElement)) return\n if (this.noBackdropClose) return\n this._closeFromUser()\n }\n\n /** 用户发起的关闭入口(Escape、遮罩点击、关闭按钮)共用一条路径。 */\n private readonly _closeFromUser = () => {\n if (this.controlled) {\n this.emitOpenChange(false)\n return\n }\n this._userOpenChange.mark()\n this.close()\n }\n\n // controlled 下派发关闭请求:detail 固定 false(用户只能请求关闭,打开永远由 Consumer 写)。\n private emitOpenChange(open = this.open) {\n dispatchOpenChangeEvent(this, open)\n }\n\n private _onTransitionEnd = (event: TransitionEvent) => {\n this._presence.handleTransitionEnd(event)\n }\n\n private _onNativeClose = () => {\n // 关闭态下的 close 事件只可能是我们自己 finishClosing 排队的异步事件(或冗余的\n // 外部关闭):交给 presence 消费 self-close 标志,避免标志泄漏到下一次真实关闭。\n if (!this.open) {\n this._presence.handleNativeClose()\n return\n }\n\n // 我们自己 dialog.close() 排队的 close 事件是异步任务:正常时序在本关闭会话内\n // 到达,快速「关闭→重开」时它在重新打开之后才到达(过期事件)。两种情况都已\n // 完成清理,由 presence 消费并返回 true,不能当成外部关闭把刚重开的 dialog 关掉。\n if (this._presence.handleNativeClose()) return\n\n // controlled 下原生关闭(如表单 method=\"dialog\")视为未经 Consumer 批准的状态丢失:\n // 恢复受控状态并派发关闭请求,由 Consumer 决定是否关闭。\n if (this.controlled) {\n this._presence.sync(true)\n this.emitOpenChange(false)\n return\n }\n\n this._userOpenChange.mark()\n this.open = false\n }\n\n private _syncScrollLock(isOpen = this.open) {\n this._scrollLock.sync(isOpen && !this.noScrollLock)\n }\n\n /**\n * 只在「有/无」翻转时写 state:slotchange 在每次分配变化时都会触发,逐次赋值会让\n * Lit 无谓重渲染。`heading` 与槽共同决定 chrome 带的存在,见 `_showHeader`。\n */\n private _onHeaderSlotChange(e: Event) {\n if (!(e.target instanceof HTMLSlotElement)) return\n const has = e.target.assignedElements().length > 0\n if (has !== this._hasHeader) this._hasHeader = has\n }\n\n private get _showHeader(): boolean {\n return this._hasHeader || this.heading !== ''\n }\n\n /**\n * 默认模式与 `headless` 模式共用同一枚关闭按钮:都绝对定位到卡片右上角,偏移由\n * `--wui-dialog-close-top` / `--wui-dialog-close-right` 控制(见 style.css)。\n * 它在两种模式下都是卡片的直接子元素,定位参照是卡片本身(.wui-dialog-body)。\n */\n private _renderCloseButton() {\n return html`\n <web-ui-button\n class=\"wui-dialog-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 }\n\n override render() {\n const showHeader = this._showHeader\n /*\n * accessible name:显式 `dialog-label` 优先;否则(非 headless 且带可见时)把原生 dialog\n * 的 `aria-labelledby` 指向 chrome 带——与 drawer 同形。headless 模式没有可自动关联的\n * 内置标题,名字留给 Consumer。\n */\n const dialogLabel = this.dialogLabel.trim()\n const dialogLabelledBy = !dialogLabel && !this.headless && showHeader ? 'wui-dialog-heading' : nothing\n\n return html`\n <dialog\n aria-label=${dialogLabel || nothing}\n aria-labelledby=${dialogLabelledBy}\n @cancel=${this.handleCancel}\n @close=${this._onNativeClose}\n @click=${this.handleBackdropClick}\n @transitionend=${this._onTransitionEnd}\n >\n <div class=\"wui-dialog-body wui-glass\">\n ${\n this.headless\n ? html`\n <div class=\"wui-dialog-content\">\n <slot></slot>\n </div>\n ${this.closable ? this._renderCloseButton() : nothing}\n `\n : html`\n <div class=\"header\" id=\"wui-dialog-heading\" ?hidden=${!showHeader}>\n <slot name=\"header\" @slotchange=${this._onHeaderSlotChange}>\n ${this.heading ? html`<span class=\"wui-dialog-heading\">${this.heading}</span>` : nothing}\n </slot>\n </div>\n <div class=\"desc\"><slot></slot></div>\n <div class=\"wui-dialog-footer\"><slot name=\"footer\"></slot></div>\n ${this.closable ? this._renderCloseButton() : nothing}\n `\n }\n </div>\n </dialog>\n `\n }\n\n declare readonly $events: {\n 'open-change': CustomEvent<{ open: boolean }>\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-dialog': WebUiDialog\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/dialog/index.ts"],"sourcesContent":["import '@/components/button'\nimport '@/components/icon'\n\nimport { html, LitElement, nothing, type PropertyValues, unsafeCSS } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport glass from '@/assets/glass.css?inline'\nimport modalSurface from '@/assets/modal-surface.css?inline'\nimport { oouiClose } from '@/icons'\nimport internalScrollbars from '@/scrollbars/internal.css?inline'\nimport { UserChangeController } from '@/shared/events/user-change'\nimport { dispatchOpenChangeEvent } from '@/shared/open-state'\nimport { defineNativeDialogPresence } from '@/shared/overlay/native-dialog-presence'\nimport { defineOpenOverlay, type OpenOverlayHandle } from '@/shared/overlay/open-overlay'\nimport { defineScrollLockLease } from '@/shared/scroll-lock/scroll-lock'\n\nimport style from './style.css?inline'\n\n@customElement('web-ui-dialog')\nexport class WebUiDialog extends LitElement {\n static override styles = [unsafeCSS(glass), unsafeCSS(modalSurface), unsafeCSS(internalScrollbars), unsafeCSS(style)]\n\n @property({ type: Boolean, reflect: true }) open = false\n @property({ type: Boolean, reflect: true }) closable = false\n @property({ type: Boolean, reflect: true, attribute: 'no-scroll-lock' }) noScrollLock = false\n @property({ type: Boolean, reflect: true, attribute: 'no-backdrop-close' }) noBackdropClose = false\n @property({ type: Boolean, reflect: true, attribute: 'no-escape-close' }) noEscapeClose = false\n\n /**\n * Controlled 模式下,Escape 和遮罩点击只派发 `open-change` 请求,\n * 不会自行修改 `open`。Consumer 回写 `open` 后才执行关闭动画。\n * 程序化 API(showModal/close/直接赋值 open)不受此模式影响,始终直通。\n */\n @property({ type: Boolean, reflect: true }) controlled = false\n\n /**\n * `header` 槽为空时的标题文案。槽优先:一旦有元素分配到 `header`,槽内容整条接管,\n * 属性被忽略。默认空串且槽也为空时不渲染 chrome 带。`headless` 模式不渲染 chrome 带,\n * `heading` 一并失效。\n */\n @property({ type: String }) heading = ''\n\n /**\n * Headless 模式:只保留 overlay 基础设施(backdrop、动画、scroll lock、`<dialog>` 语义),\n * 移除内置 UI(chrome 带、正文区、footer),默认槽的内容渲染进卡片内的滚动层\n * `.wui-dialog-content`,视觉交给 Consumer。形态与 `<web-ui-drawer>` 的 `headless` 一致。\n *\n * 该模式没有可自动关联的内置标题,内部原生 dialog 的 accessible name 必须由 Consumer\n * 用 `dialog-label`(或自绘标题 + 自己的 `aria-labelledby`)提供。\n */\n @property({ type: Boolean, reflect: true }) headless = false\n\n /**\n * 内部原生 dialog 的 accessible name。`headless` 模式必须由 Consumer 提供,\n * 因为该模式不会渲染可自动关联的内置 chrome 带。\n */\n @property({ type: String, attribute: 'dialog-label' }) dialogLabel = ''\n\n @state() private _hasHeader = false\n private readonly _userOpenChange = new UserChangeController()\n\n /*\n * 开启态浮层(issue #120 Block 1)。dialog 的 Escape 原本只走原生 cancel 事件:仲裁者\n * 会在 keydown 上 preventDefault 压掉原生关闭请求并 stopPropagation,因此这里必须\n * 自己表达关闭语义(controlled 只派发请求)。\n */\n private readonly _overlay = defineOpenOverlay().make({\n requestClose: () => this._closeFromUser(),\n isConnected: () => this.isConnected\n })\n /** 当前开启会话的句柄;未开启时为 null。查询与惰性同步走它。 */\n private _handle: OpenOverlayHandle | null = null\n private readonly _scrollLock = defineScrollLockLease().make()\n private readonly _presence = defineNativeDialogPresence().make({\n getDialog: () => this.dialog,\n isConnected: () => this.isConnected,\n isOpen: () => this.open\n })\n\n private get dialog() {\n return this.shadowRoot?.querySelector('dialog') ?? null\n }\n\n protected override updated(props: PropertyValues) {\n super.updated(props)\n if (!this.isConnected) return\n\n if (props.has('open')) {\n if (this._userOpenChange.consume()) this.emitOpenChange()\n this._presence.sync(this.open)\n if (this.open) {\n // presence.sync 内的 showModal() 把 dialog 提升进 top layer。双击手势本身\n // 留下的活选区会被浏览器拿去和提升后的新布局重新解析,结果选区落到刚挂载的\n // dialog 正文上。脆弱的是「手势来源的活选区」,不是 dblclick 的判定窗口:把\n // 打开推迟 250ms(早过双击判定)仍然复现。选区由浏览器在 showModal() 调用\n // 内部生成:脚本没有任何 Selection API 调用,在 dblclick 上 preventDefault\n // 也拦不住。\n //\n // 实测(Chromium)showModal() 不会动打开前已经存在的选区,所以这一次清理\n // 同时也覆盖「先选中页面文本、再打开」的情况——modal 打开后被选中的内容挡在\n // 遮罩后面本来也已不可用。这里只清文档选区,不动 user-select,浮层内正文\n // 仍可正常拖选。\n window.getSelection()?.removeAllRanges()\n }\n // 原生 dialog 登记为开启态浮层:挂在它上面的 portal 面板成为本层后代,\n // Escape 仲裁据此判出最内层(issue #120 Block 1)。\n const dialog = this.dialog\n if (dialog) {\n if (this.open) {\n // 同一面板重新 claim = 新的一次开启:旧会话(含其子层)整体作废。\n this._handle?.release()\n this._handle = this._overlay.claim(dialog, { ancestryFrom: this })\n } else {\n this._handle?.release()\n this._handle = null\n }\n }\n }\n if (props.has('open') || props.has('noScrollLock')) this._syncScrollLock()\n this._syncOverlayInert()\n }\n\n override connectedCallback() {\n super.connectedCallback()\n // 重挂载对账:断连时 presence、滚动锁与登记都已被撤销,而 `open` 未变化时\n // `updated()` 不会补跑任何 sync 分支。首次连接时 shadow 尚未渲染、`this.dialog`\n // 为 null,三种情况都直接跳过;打开态的首次进入仍由 updated() 的\n // `props.has('open')` 分支处理。\n this._presence.reconcile()\n this._syncScrollLock()\n this._reclaimIfOpen()\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback()\n this._presence.dispose()\n this._scrollLock.release()\n // 断连即撤销登记:层不留在全局注册表里,重连后由 _reclaimIfOpen 显式重新声明。\n this._handle?.release()\n this._handle = null\n }\n\n /*\n * 「重挂载恢复」的调用方一半:模块刻意不观察 DOM 连接状态,所以断连撤销登记之后,\n * 重连时仍有开启态就必须由组件重新声明。\n */\n private _reclaimIfOpen() {\n if (!this.open || this._handle) return\n const dialog = this.dialog\n if (!dialog) return\n this._handle = this._overlay.claim(dialog, { ancestryFrom: this })\n this._syncOverlayInert()\n }\n\n /*\n * `no-escape-close` 时 dialog 仍是候选(Escape 被吞掉、原生 cancel 被压掉),只是\n * 不走关闭入口。与旧的「跳过候选」语义不同:旧语义放任事件落到下层浮层,把外层\n * 一起关掉。该属性可在开启期间改写,所以每次渲染后读它,而不是在 claim 时固定。\n */\n private _syncOverlayInert() {\n this._handle?.setInert(this.noEscapeClose)\n }\n\n showModal() {\n if (this.open) return\n this.open = true\n }\n\n close() {\n this.open = false\n }\n\n /*\n * 仲裁者在 capture 阶段 preventDefault 了 Escape 的 keydown,UA 因此不再派发原生\n * cancel,但本 handler 仍有两个仲裁者不负责的职责,**不可删**:\n * ① 任何到达 dialog 的 cancel 都要 preventDefault,把 top layer 保留到视觉退场结束\n * (否则原生关闭会跳过退出动画);\n * ② 忽略子控件(例如 file input)冒泡上来的 cancel。\n * 实测(overlay-open-owner-260918,2026-09-18)摘掉 `@cancel` 后 dialog.browser.spec.ts\n * 的「no-escape-close 存在时 Escape/cancel 不关闭对话框」与 remount-reconcile 的\n * 「打开态被移出文档再接回」两例立刻转红,断言正是 `dispatchEvent(cancel) === false`。\n */\n private handleCancel(e: Event) {\n if (e.target !== e.currentTarget) return\n\n e.preventDefault()\n if (this.noEscapeClose) return\n this._closeFromUser()\n }\n\n private handleBackdropClick(e: MouseEvent) {\n if (e.target !== (e.currentTarget as HTMLDialogElement)) return\n if (this.noBackdropClose) return\n this._closeFromUser()\n }\n\n /** 用户发起的关闭入口(Escape、遮罩点击、关闭按钮)共用一条路径。 */\n private readonly _closeFromUser = () => {\n if (this.controlled) {\n this.emitOpenChange(false)\n return\n }\n this._userOpenChange.mark()\n this.close()\n }\n\n // controlled 下派发关闭请求:detail 固定 false(用户只能请求关闭,打开永远由 Consumer 写)。\n private emitOpenChange(open = this.open) {\n dispatchOpenChangeEvent(this, open)\n }\n\n private _onTransitionEnd = (event: TransitionEvent) => {\n this._presence.handleTransitionEnd(event)\n }\n\n private _onNativeClose = () => {\n // 关闭态下的 close 事件只可能是我们自己 finishClosing 排队的异步事件(或冗余的\n // 外部关闭):交给 presence 消费 self-close 标志,避免标志泄漏到下一次真实关闭。\n if (!this.open) {\n this._presence.handleNativeClose()\n return\n }\n\n // 我们自己 dialog.close() 排队的 close 事件是异步任务:正常时序在本关闭会话内\n // 到达,快速「关闭→重开」时它在重新打开之后才到达(过期事件)。两种情况都已\n // 完成清理,由 presence 消费并返回 true,不能当成外部关闭把刚重开的 dialog 关掉。\n if (this._presence.handleNativeClose()) return\n\n // controlled 下原生关闭(如表单 method=\"dialog\")视为未经 Consumer 批准的状态丢失:\n // 恢复受控状态并派发关闭请求,由 Consumer 决定是否关闭。\n if (this.controlled) {\n this._presence.sync(true)\n this.emitOpenChange(false)\n return\n }\n\n this._userOpenChange.mark()\n this.open = false\n }\n\n private _syncScrollLock(isOpen = this.open) {\n this._scrollLock.sync(isOpen && !this.noScrollLock)\n }\n\n /**\n * 只在「有/无」翻转时写 state:slotchange 在每次分配变化时都会触发,逐次赋值会让\n * Lit 无谓重渲染。`heading` 与槽共同决定 chrome 带的存在,见 `_showHeader`。\n */\n private _onHeaderSlotChange(e: Event) {\n if (!(e.target instanceof HTMLSlotElement)) return\n const has = e.target.assignedElements().length > 0\n if (has !== this._hasHeader) this._hasHeader = has\n }\n\n private get _showHeader(): boolean {\n return this._hasHeader || this.heading !== ''\n }\n\n /**\n * 默认模式与 `headless` 模式共用同一枚关闭按钮:都绝对定位到卡片右上角,偏移由\n * `--wui-dialog-close-top` / `--wui-dialog-close-right` 控制(见 style.css)。\n * 它在两种模式下都是卡片的直接子元素,定位参照是卡片本身(.wui-dialog-body)。\n */\n private _renderCloseButton() {\n return html`\n <web-ui-button\n class=\"wui-dialog-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 }\n\n override render() {\n const showHeader = this._showHeader\n /*\n * accessible name:显式 `dialog-label` 优先;否则(非 headless 且带可见时)把原生 dialog\n * 的 `aria-labelledby` 指向 chrome 带——与 drawer 同形。headless 模式没有可自动关联的\n * 内置标题,名字留给 Consumer。\n */\n const dialogLabel = this.dialogLabel.trim()\n const dialogLabelledBy = !dialogLabel && !this.headless && showHeader ? 'wui-dialog-heading' : nothing\n\n return html`\n <dialog\n aria-label=${dialogLabel || nothing}\n aria-labelledby=${dialogLabelledBy}\n @cancel=${this.handleCancel}\n @close=${this._onNativeClose}\n @click=${this.handleBackdropClick}\n @transitionend=${this._onTransitionEnd}\n >\n <div class=\"wui-dialog-body wui-glass\">\n ${\n this.headless\n ? html`\n <div class=\"wui-dialog-content wui-scroll\">\n <slot></slot>\n </div>\n ${this.closable ? this._renderCloseButton() : nothing}\n `\n : html`\n <div class=\"header\" id=\"wui-dialog-heading\" ?hidden=${!showHeader}>\n <slot name=\"header\" @slotchange=${this._onHeaderSlotChange}>\n ${this.heading ? html`<span class=\"wui-dialog-heading\">${this.heading}</span>` : nothing}\n </slot>\n </div>\n <div class=\"desc wui-scroll\"><slot></slot></div>\n <div class=\"wui-dialog-footer\"><slot name=\"footer\"></slot></div>\n ${this.closable ? this._renderCloseButton() : nothing}\n `\n }\n </div>\n </dialog>\n `\n }\n\n declare readonly $events: {\n 'open-change': CustomEvent<{ open: boolean }>\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-dialog': WebUiDialog\n }\n}\n"],"mappings":"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"}
@@ -4,27 +4,28 @@ import t from "../../assets/glass.js";
4
4
  import n from "../../icons/generated/ooui-close.js";
5
5
  import { normalizeLiteral as r } from "../../shared/normalize/index.js";
6
6
  import "../button/index.js";
7
- import { dispatchOpenChangeEvent as i } from "../../shared/open-state/index.js";
8
- import { defineOpenOverlay as a } from "../../shared/overlay/open-overlay.js";
9
- import { defineScrollLockLease as o } from "../../shared/scroll-lock/scroll-lock.js";
10
- import { prefersReducedMotion as s } from "../../shared/theme/reduced-motion.js";
11
- import { UserChangeController as c } from "../../shared/events/user-change.js";
12
- import l from "../../assets/modal-surface.js";
13
- import { defineNativeDialogPresence as u } from "../../shared/overlay/native-dialog-presence.js";
14
- import { ElementHeightController as d } from "../../shared/element-height/index.js";
15
- import { attachDragGesture as f } from "../../shared/gesture/drag-gesture.js";
16
- import { dampOverscroll as p } from "../../shared/gesture/physics.js";
17
- import { defineNestedDrawerLayers as m } from "../../shared/overlay/nested-drawer-layers.js";
18
- import h from "./style.js";
19
- import { LitElement as g, html as _, nothing as v, unsafeCSS as y } from "lit";
20
- import { customElement as b, property as x } from "lit/decorators.js";
7
+ import i from "../../scrollbars/internal.js";
8
+ import { dispatchOpenChangeEvent as a } from "../../shared/open-state/index.js";
9
+ import { defineOpenOverlay as o } from "../../shared/overlay/open-overlay.js";
10
+ import { defineScrollLockLease as s } from "../../shared/scroll-lock/scroll-lock.js";
11
+ import { prefersReducedMotion as c } from "../../shared/theme/reduced-motion.js";
12
+ import { UserChangeController as l } from "../../shared/events/user-change.js";
13
+ import u from "../../assets/modal-surface.js";
14
+ import { defineNativeDialogPresence as d } from "../../shared/overlay/native-dialog-presence.js";
15
+ import { ElementHeightController as f } from "../../shared/element-height/index.js";
16
+ import { attachDragGesture as p } from "../../shared/gesture/drag-gesture.js";
17
+ import { dampOverscroll as m } from "../../shared/gesture/physics.js";
18
+ import { defineNestedDrawerLayers as h } from "../../shared/overlay/nested-drawer-layers.js";
19
+ import g from "./style.js";
20
+ import { LitElement as _, html as v, nothing as y, unsafeCSS as b } from "lit";
21
+ import { customElement as x, property as S } from "lit/decorators.js";
21
22
  //#region src/components/drawer/index.ts
22
- var S = [
23
+ var C = [
23
24
  "right",
24
25
  "left",
25
26
  "top",
26
27
  "bottom"
27
- ], C = .5, w = 10, T = 500, E = 50, D = 10, O = 120, k = 10, A = [
28
+ ], w = .5, T = 10, E = 500, D = 50, O = 10, k = 120, A = 10, j = [
28
29
  "button",
29
30
  "input",
30
31
  "select",
@@ -50,7 +51,7 @@ var S = [
50
51
  "[role=\"combobox\"]",
51
52
  "[role=\"slider\"]",
52
53
  "[role=\"spinbutton\"]"
53
- ].join(","), j = 180, M = 420, N = "cubic-bezier(0.32, 0.72, 0, 1)", P = "cubic-bezier(0.22, 1.3, 0.36, 1)", F = 1, I = 80;
54
+ ].join(","), M = 180, N = 420, P = "cubic-bezier(0.32, 0.72, 0, 1)", F = "cubic-bezier(0.22, 1.3, 0.36, 1)", I = 1, L = 80;
54
55
  if (typeof CSS < "u" && "registerProperty" in CSS) {
55
56
  try {
56
57
  CSS.registerProperty({
@@ -85,16 +86,16 @@ if (typeof CSS < "u" && "registerProperty" in CSS) {
85
86
  });
86
87
  } catch {}
87
88
  }
88
- var L = class extends g {
89
+ var R = class extends _ {
89
90
  constructor(...e) {
90
- super(...e), this.open = !1, this.noScrollLock = !1, this.noBackdropClose = !1, this.controlled = !1, this._placement = "right", this.heading = "", this.closable = !1, this.headless = !1, this.dialogLabel = "", this.draggable = !1, this._hasHeaderSlot = !1, this._hasFooterSlot = !1, this._userOpenChange = new c(), this._headerHeight = new d(this, ".wui-drawer-header", (e) => this._setAvoidance("--wui-internal-drawer-header-inset", e)), this._footerHeight = new d(this, ".wui-drawer-footer", (e) => this._setAvoidance("--wui-internal-drawer-footer-inset", e)), this._scrollLock = o().make(), this._presence = u().make({
91
+ super(...e), this.open = !1, this.noScrollLock = !1, this.noBackdropClose = !1, this.controlled = !1, this._placement = "right", this.heading = "", this.closable = !1, this.headless = !1, this.dialogLabel = "", this.draggable = !1, this._hasHeaderSlot = !1, this._hasFooterSlot = !1, this._userOpenChange = new l(), this._headerHeight = new f(this, ".wui-drawer-header", (e) => this._setAvoidance("--wui-internal-drawer-header-inset", e)), this._footerHeight = new f(this, ".wui-drawer-footer", (e) => this._setAvoidance("--wui-internal-drawer-footer-inset", e)), this._scrollLock = s().make(), this._presence = d().make({
91
92
  getDialog: () => this.dialog,
92
93
  isConnected: () => this.isConnected,
93
94
  isOpen: () => this.open
94
- }), this._nestedLayers = m().make({
95
+ }), this._nestedLayers = h().make({
95
96
  getDialog: () => this.dialog,
96
97
  getPlacement: () => this._placement
97
- }), this._overlay = a().make({
98
+ }), this._overlay = o().make({
98
99
  requestClose: () => this._closeFromUser(),
99
100
  isConnected: () => this.isConnected
100
101
  }), this._handle = null, this._dragGesture = null, this._dragInitialOffset = 0, this._dragOffset = 0, this._cancelBaseline = 0, this._lastAxisPos = 0, this._swipeIntentDisp = -1, this._swipeCancelled = !1, this._settleFinish = null, this._dragAwaitWriteback = !1, this._handleDragWritebackTimeout = () => {
@@ -117,9 +118,10 @@ var L = class extends g {
117
118
  }
118
119
  static {
119
120
  this.styles = [
120
- y(t),
121
- y(l),
122
- y(h)
121
+ b(t),
122
+ b(u),
123
+ b(i),
124
+ b(g)
123
125
  ];
124
126
  }
125
127
  get placement() {
@@ -127,7 +129,7 @@ var L = class extends g {
127
129
  }
128
130
  set placement(e) {
129
131
  let t = this._placement;
130
- this._placement = r(e, S, "right"), this.requestUpdate("placement", t);
132
+ this._placement = r(e, C, "right"), this.requestUpdate("placement", t);
131
133
  }
132
134
  get dialog() {
133
135
  return this.shadowRoot?.querySelector("dialog") ?? null;
@@ -151,7 +153,7 @@ var L = class extends g {
151
153
  return e ? this._dragAxis === "x" ? e.offsetWidth : e.offsetHeight : 0;
152
154
  }
153
155
  _dragCloseThreshold(e) {
154
- return Math.max(e * C, w);
156
+ return Math.max(e * w, T);
155
157
  }
156
158
  _readDrawerInset(e) {
157
159
  let t = getComputedStyle(e).getPropertyValue("--wui-internal-drawer-inset"), n = Number.parseFloat(t);
@@ -176,7 +178,7 @@ var L = class extends g {
176
178
  }
177
179
  _isInteractivePath(e) {
178
180
  let t = typeof e.composedPath == "function" ? e.composedPath() : [e.target];
179
- for (let e of t) if (e instanceof Element && e.matches(A)) return !0;
181
+ for (let e of t) if (e instanceof Element && e.matches(j)) return !0;
180
182
  return !1;
181
183
  }
182
184
  _handleDragPointerDown(e) {
@@ -186,18 +188,18 @@ var L = class extends g {
186
188
  let n = getComputedStyle(t).transform, r = n && n !== "none" && typeof DOMMatrixReadOnly == "function" ? new DOMMatrixReadOnly(n)[this._dragAxis === "x" ? "m41" : "m42"] : 0;
187
189
  this._abortSettle(), this._cancelDragAwait(), this._dragInitialOffset = r * this._dragCloseSign, this._dragOffset = this._dragInitialOffset;
188
190
  let i = this._dragAxis === "x" ? e.clientX : e.clientY;
189
- this._cancelBaseline = i, this._lastAxisPos = i, this._swipeIntentDisp = -1, this._swipeCancelled = !1, t.classList.add("is-dragging"), this._applyDragOffset(t, this._dragOffset), this._dragGesture = f(e, {
191
+ this._cancelBaseline = i, this._lastAxisPos = i, this._swipeIntentDisp = -1, this._swipeCancelled = !1, t.classList.add("is-dragging"), this._applyDragOffset(t, this._dragOffset), this._dragGesture = p(e, {
190
192
  axis: this._dragAxis,
191
193
  calibrateOnFirstMove: !0,
192
194
  onMove: (e) => {
193
195
  let n = (this._dragAxis === "x" ? e.deltaX : e.deltaY) * this._dragCloseSign;
194
- this._dragOffset = this._dragInitialOffset + p(n), this._trackSwipeCancel(this._dragAxis === "x" ? e.clientX : e.clientY, n);
196
+ this._dragOffset = this._dragInitialOffset + m(n), this._trackSwipeCancel(this._dragAxis === "x" ? e.clientX : e.clientY, n);
195
197
  let r = this._measureDragSize(), i = this._dragOffset - this._dragInitialOffset;
196
198
  t.classList.toggle("is-drag-close", i > this._dragCloseThreshold(r)), this._applyDragOffset(t, this._dragOffset);
197
199
  },
198
200
  onEnd: (e) => {
199
201
  this._dragGesture = null, this._syncDragInert(), t.classList.remove("is-dragging", "is-drag-close");
200
- let n = this._measureDragSize(), r = (this._dragAxis === "x" ? e.velocityX : e.velocityY) * this._dragCloseSign, i = this._dragOffset - this._dragInitialOffset, a = e.duration > 0 ? i / Math.max(e.duration, E) * 1e3 : 0, o = i > 0 && a >= T && !this._swipeCancelled;
202
+ let n = this._measureDragSize(), r = (this._dragAxis === "x" ? e.velocityX : e.velocityY) * this._dragCloseSign, i = this._dragOffset - this._dragInitialOffset, a = e.duration > 0 ? i / Math.max(e.duration, D) * 1e3 : 0, o = i > 0 && a >= E && !this._swipeCancelled;
201
203
  i > this._dragCloseThreshold(n) || o ? this._settleToClose(t, r) : this._settleRebound(t, r, this._dragOffset);
202
204
  },
203
205
  onCancel: () => {
@@ -212,7 +214,7 @@ var L = class extends g {
212
214
  return;
213
215
  }
214
216
  let r = (e - this._cancelBaseline) * this._dragCloseSign;
215
- r > this._dragCloseThreshold(this._measureDragSize()) ? this._swipeCancelled = !1 : this._swipeIntentDisp - r >= D && (this._swipeCancelled = !0);
217
+ r > this._dragCloseThreshold(this._measureDragSize()) ? this._swipeCancelled = !1 : this._swipeIntentDisp - r >= O && (this._swipeCancelled = !0);
216
218
  }
217
219
  _cancelActiveDrag() {
218
220
  if (!this._isDragging()) return;
@@ -235,31 +237,31 @@ var L = class extends g {
235
237
  let r = this._dragCloseDistance(e), i = () => {
236
238
  this.controlled ? this._applyDragOffset(e, r) : (e.classList.remove("is-visible"), this._clearDragStyles(e)), this._closeFromDrag();
237
239
  };
238
- if (s(this) || Math.abs(r - n) < 1) {
240
+ if (c(this) || Math.abs(r - n) < 1) {
239
241
  e.classList.remove("is-dragging"), i();
240
242
  return;
241
243
  }
242
- this._startSettle(e, r, 0, this._settleDuration(Math.abs(r - n), t), N, i);
244
+ this._startSettle(e, r, 0, this._settleDuration(Math.abs(r - n), t), P, i);
243
245
  }
244
246
  _settleRebound(e, t, n) {
245
247
  this._dragOffset = 0;
246
248
  let r = () => {
247
249
  this._clearDragStyles(e);
248
250
  };
249
- if (s(this) || Math.abs(n) < 1) {
251
+ if (c(this) || Math.abs(n) < 1) {
250
252
  e.classList.remove("is-dragging"), r();
251
253
  return;
252
254
  }
253
- this._startSettle(e, 0, 1, this._settleDuration(Math.abs(n), t), P, r);
255
+ this._startSettle(e, 0, 1, this._settleDuration(Math.abs(n), t), F, r);
254
256
  }
255
257
  _settleDuration(e, t) {
256
- let n = e / Math.max(Math.abs(t), F) * 1e3;
257
- return Math.min(M, Math.max(j, n));
258
+ let n = e / Math.max(Math.abs(t), I) * 1e3;
259
+ return Math.min(N, Math.max(M, n));
258
260
  }
259
261
  _startSettle(e, t, n, r, i, a) {
260
262
  this._abortSettle();
261
263
  let o = this._dragCloseSign;
262
- e.style.setProperty("--wui-internal-settle-duration", `${r}ms`), e.style.setProperty("--wui-internal-settle-easing", i), e.style.transform = this._dragAxis === "x" ? `translateX(${t * o}px)` : `translateY(${t * o}px)`, e.style.setProperty("--wui-internal-drag-backdrop-opacity", String(n)), e.classList.add("is-settling"), e.classList.remove("is-dragging"), this._settleFinish = a, this._settleTimer = setTimeout(() => this._finishSettle(), r + I);
264
+ e.style.setProperty("--wui-internal-settle-duration", `${r}ms`), e.style.setProperty("--wui-internal-settle-easing", i), e.style.transform = this._dragAxis === "x" ? `translateX(${t * o}px)` : `translateY(${t * o}px)`, e.style.setProperty("--wui-internal-drag-backdrop-opacity", String(n)), e.classList.add("is-settling"), e.classList.remove("is-dragging"), this._settleFinish = a, this._settleTimer = setTimeout(() => this._finishSettle(), r + L);
263
265
  }
264
266
  _finishSettle() {
265
267
  let e = this._settleFinish;
@@ -271,7 +273,7 @@ var L = class extends g {
271
273
  e && (e.classList.remove("is-settling"), e.style.removeProperty("--wui-internal-settle-duration"), e.style.removeProperty("--wui-internal-settle-easing"));
272
274
  }
273
275
  _closeFromDrag() {
274
- this._closeFromUser(), this.controlled && (this._dragAwaitWriteback = !0, this._dragRequestTimer = setTimeout(this._handleDragWritebackTimeout, O));
276
+ this._closeFromUser(), this.controlled && (this._dragAwaitWriteback = !0, this._dragRequestTimer = setTimeout(this._handleDragWritebackTimeout, k));
275
277
  }
276
278
  connectedCallback() {
277
279
  super.connectedCallback(), this._resyncSlotPresence(), this._presence.reconcile(), this.open && this._nestedLayers.register(), this._syncScrollLock(), this._reclaimIfOpen();
@@ -380,23 +382,23 @@ var L = class extends g {
380
382
  handleBackdropClick(e) {
381
383
  if (e.target !== e.currentTarget || this.noBackdropClose || this._isDragging() || e.detail === 0) return;
382
384
  let t = this._backdropPress;
383
- this._backdropPress = null, t && t.onMask && (Math.hypot(e.clientX - t.x, e.clientY - t.y) > k || this._closeFromUser());
385
+ this._backdropPress = null, t && t.onMask && (Math.hypot(e.clientX - t.x, e.clientY - t.y) > A || this._closeFromUser());
384
386
  }
385
387
  emitOpenChange(e = this.open) {
386
- i(this, e);
388
+ a(this, e);
387
389
  }
388
390
  _syncScrollLock(e = this.open) {
389
391
  this._scrollLock.sync(e && !this.noScrollLock);
390
392
  }
391
393
  render() {
392
- let e = this._hasHeaderSlot || !!this.heading, t = this.dialogLabel.trim(), r = !t && !this.headless && e ? "wui-drawer-heading" : v, i = this.placement === "top" || this.placement === "bottom", a = this.draggable ? _`
393
- ${i ? _`<div class="wui-drawer-drag-edge" @pointerdown=${this._handleDragPointerDown}></div>` : v}
394
+ let e = this._hasHeaderSlot || !!this.heading, t = this.dialogLabel.trim(), r = !t && !this.headless && e ? "wui-drawer-heading" : y, i = this.placement === "top" || this.placement === "bottom", a = this.draggable ? v`
395
+ ${i ? v`<div class="wui-drawer-drag-edge" @pointerdown=${this._handleDragPointerDown}></div>` : y}
394
396
  <div class="wui-drawer-drag-zone" @pointerdown=${this._handleDragPointerDown}></div>
395
397
  <div class="wui-drawer-drag-bar"></div>
396
- ` : v;
397
- return _`
398
+ ` : y;
399
+ return v`
398
400
  <dialog
399
- aria-label=${t || v}
401
+ aria-label=${t || y}
400
402
  aria-labelledby=${r}
401
403
  @cancel=${this.handleCancel}
402
404
  @close=${this.handleNativeClose}
@@ -405,7 +407,7 @@ var L = class extends g {
405
407
  @keydown=${this.handleKeydown}
406
408
  @transitionend=${this.handleTransitionEnd}
407
409
  >
408
- ${this.headless ? _`<slot></slot>${a}` : _`
410
+ ${this.headless ? v`<slot></slot>${a}` : v`
409
411
  <div class="wui-drawer-body wui-glass">
410
412
  <div
411
413
  class="wui-drawer-header"
@@ -414,10 +416,10 @@ var L = class extends g {
414
416
  @pointerdown=${this._handleSectionDragPointerDown}
415
417
  >
416
418
  <slot name="header" @slotchange=${this.handleHeaderSlotChange}>
417
- ${this.heading ? _`<span class="wui-drawer-heading">${this.heading}</span>` : v}
419
+ ${this.heading ? v`<span class="wui-drawer-heading">${this.heading}</span>` : y}
418
420
  </slot>
419
421
  </div>
420
- <div class="wui-drawer-content">
422
+ <div class="wui-drawer-content wui-scroll">
421
423
  <slot></slot>
422
424
  </div>
423
425
  <div
@@ -428,7 +430,7 @@ var L = class extends g {
428
430
  <slot name="footer" @slotchange=${this.handleFooterSlotChange}></slot>
429
431
  </div>
430
432
  </div>
431
- ${this.closable ? _`
433
+ ${this.closable ? v`
432
434
  <web-ui-button
433
435
  class="wui-drawer-close"
434
436
  @click=${this._closeFromUser}
@@ -439,44 +441,44 @@ var L = class extends g {
439
441
  >
440
442
  <web-ui-icon size="14" .icon=${n}></web-ui-icon>
441
443
  </web-ui-button>
442
- ` : v}
444
+ ` : y}
443
445
  ${a}
444
446
  `}
445
447
  </dialog>
446
448
  `;
447
449
  }
448
450
  };
449
- e([x({
451
+ e([S({
450
452
  type: Boolean,
451
453
  reflect: !0
452
- })], L.prototype, "open", void 0), e([x({
454
+ })], R.prototype, "open", void 0), e([S({
453
455
  type: Boolean,
454
456
  reflect: !0,
455
457
  attribute: "no-scroll-lock"
456
- })], L.prototype, "noScrollLock", void 0), e([x({
458
+ })], R.prototype, "noScrollLock", void 0), e([S({
457
459
  type: Boolean,
458
460
  reflect: !0,
459
461
  attribute: "no-backdrop-close"
460
- })], L.prototype, "noBackdropClose", void 0), e([x({
462
+ })], R.prototype, "noBackdropClose", void 0), e([S({
461
463
  type: Boolean,
462
464
  reflect: !0
463
- })], L.prototype, "controlled", void 0), e([x({
465
+ })], R.prototype, "controlled", void 0), e([S({
464
466
  type: String,
465
467
  reflect: !0
466
- })], L.prototype, "placement", null), e([x({ type: String })], L.prototype, "heading", void 0), e([x({
468
+ })], R.prototype, "placement", null), e([S({ type: String })], R.prototype, "heading", void 0), e([S({
467
469
  type: Boolean,
468
470
  reflect: !0
469
- })], L.prototype, "closable", void 0), e([x({
471
+ })], R.prototype, "closable", void 0), e([S({
470
472
  type: Boolean,
471
473
  reflect: !0
472
- })], L.prototype, "headless", void 0), e([x({
474
+ })], R.prototype, "headless", void 0), e([S({
473
475
  type: String,
474
476
  attribute: "dialog-label"
475
- })], L.prototype, "dialogLabel", void 0), e([x({
477
+ })], R.prototype, "dialogLabel", void 0), e([S({
476
478
  type: Boolean,
477
479
  reflect: !0
478
- })], L.prototype, "draggable", void 0), L = e([b("web-ui-drawer")], L);
480
+ })], R.prototype, "draggable", void 0), R = e([x("web-ui-drawer")], R);
479
481
  //#endregion
480
- export { L as WebUiDrawer };
482
+ export { R as WebUiDrawer };
481
483
 
482
484
  //# sourceMappingURL=index.js.map