@greypan/web-ui 10.1.0 → 10.1.1

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 +16 -0
  10. package/dist/components/context-menu/index.js +9 -4
  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
@@ -5,22 +5,23 @@ import n from "../../icons/generated/lucide-chevron-down.js";
5
5
  import { installPointerFocusSuppression as r } from "../../shared/focus/pointer-focus.js";
6
6
  import { FormAssociated as i, FormAssociationController as a, defineFormAssociation as o } from "../../shared/form-association/index.js";
7
7
  import s from "../../assets/overlay-motion.js";
8
- import { dispatchOpenChangeEvent as c } from "../../shared/open-state/index.js";
9
- import { createComboboxOpenController as l, createOptionListenerBinding as u, handleComboboxFocusOut as d, nextWrappingIndex as f } from "../../shared/option-portal/combobox-shell.js";
10
- import { defineOptionPortal as p } from "../../shared/option-portal/index.js";
11
- import { defineAnchoredPanel as m } from "../../shared/overlay/anchored-panel.js";
12
- import { defineOpenOverlay as h } from "../../shared/overlay/open-overlay.js";
13
- import { applyOverlayVariables as g, defineOverlayPortal as _ } from "../../shared/overlay/portal.js";
14
- import { defineScrollLockLease as v } from "../../shared/scroll-lock/scroll-lock.js";
15
- import y from "./style.js";
16
- import { LitElement as b, html as x, nothing as S, unsafeCSS as C } from "lit";
17
- import { customElement as w, property as T, state as E } from "lit/decorators.js";
8
+ import c from "../../scrollbars/internal.js";
9
+ import { dispatchOpenChangeEvent as l } from "../../shared/open-state/index.js";
10
+ import { createComboboxOpenController as u, createOptionListenerBinding as d, handleComboboxFocusOut as f, nextWrappingIndex as p } from "../../shared/option-portal/combobox-shell.js";
11
+ import { defineOptionPortal as m } from "../../shared/option-portal/index.js";
12
+ import { defineAnchoredPanel as h } from "../../shared/overlay/anchored-panel.js";
13
+ import { defineOpenOverlay as g } from "../../shared/overlay/open-overlay.js";
14
+ import { applyOverlayVariables as _, defineOverlayPortal as v } from "../../shared/overlay/portal.js";
15
+ import { defineScrollLockLease as y } from "../../shared/scroll-lock/scroll-lock.js";
16
+ import b from "./style.js";
17
+ import { LitElement as x, html as S, nothing as C, unsafeCSS as w } from "lit";
18
+ import { customElement as T, property as E, state as D } from "lit/decorators.js";
18
19
  //#region src/components/select/index.ts
19
- var D;
20
+ var O;
20
21
  r();
21
- var O = class extends i(b) {
22
+ var k = class extends i(x) {
22
23
  static {
23
- D = this;
24
+ O = this;
24
25
  }
25
26
  constructor(...e) {
26
27
  super(...e), this.placeholder = "", this.disabled = !1, this.required = !1, this.portal = !1, this.noScrollLock = !1, this.name = "", this._value = "", this._formAssociation = o({
@@ -39,9 +40,9 @@ var O = class extends i(b) {
39
40
  typeof e == "string" && (this.value = e);
40
41
  },
41
42
  syncValidity: () => this._syncValidity()
42
- }).make(), this._formAssociationController = new a(this, this._formAssociation), this._isOpen = !1, this._activeIndex = -1, this._selectedLabel = "", this._hasTriggerSlot = !1, this._options = [], this._scrollLock = v().make(), this._optionPortal = p().make({
43
+ }).make(), this._formAssociationController = new a(this, this._formAssociation), this._isOpen = !1, this._activeIndex = -1, this._selectedLabel = "", this._hasTriggerSlot = !1, this._options = [], this._scrollLock = y().make(), this._optionPortal = m().make({
43
44
  element: this,
44
- idPrefix: `web-ui-select-${++D._nextInstanceId}`,
45
+ idPrefix: `web-ui-select-${++O._nextInstanceId}`,
45
46
  getPortal: () => this._portal,
46
47
  getPortalContent: () => this._portalContent,
47
48
  isOpen: () => this.portal && this._isOpen,
@@ -53,10 +54,10 @@ var O = class extends i(b) {
53
54
  onImmediateSync: () => {
54
55
  this._refreshOptions(), this._syncSelected(), this._syncActiveOption();
55
56
  }
56
- }), this._overlay = h().make({
57
+ }), this._overlay = g().make({
57
58
  requestClose: () => this._close(),
58
59
  isConnected: () => this.isConnected
59
- }), this._panel = m().make({
60
+ }), this._panel = h().make({
60
61
  getAnchor: () => this.shadowRoot?.querySelector(".select-trigger") ?? null,
61
62
  getLocalPanel: () => this.shadowRoot?.querySelector(".select-overlay") ?? null,
62
63
  getPositioning: () => ({
@@ -72,11 +73,11 @@ var O = class extends i(b) {
72
73
  let t = e.composedPath().includes(this) || !!this._panel.getHandle()?.containsEvent(e);
73
74
  this._isOpen && !t && this._close();
74
75
  }, this._onFocusOut = () => {
75
- d(this, () => this._panel.getHandle(), () => this._isOpen, () => this._close());
76
+ f(this, () => this._panel.getHandle(), () => this._isOpen, () => this._close());
76
77
  }, this._onTriggerSlotChange = (e) => {
77
78
  let t = e.target.assignedElements().length > 0;
78
79
  this._hasTriggerSlot !== t && (this._hasTriggerSlot = t, this._reconfigureOverlay());
79
- }, this._optionListeners = u({
80
+ }, this._optionListeners = d({
80
81
  onClick: (e) => this._handleOptionClick(e),
81
82
  onPointerOver: (e) => this._handleOptionPointerOver(e),
82
83
  onPointerDown: (e) => this._handleOptionPointerDown(e),
@@ -106,7 +107,7 @@ var O = class extends i(b) {
106
107
  t && this._isLive(t) && !t.disabled && (this.value = t.value, this._notifyValueChange()), this._close(), e.preventDefault();
107
108
  }
108
109
  }
109
- }, this._openController = l({
110
+ }, this._openController = u({
110
111
  canOpen: () => !this._isDisabled,
111
112
  getIsOpen: () => this._isOpen,
112
113
  setIsOpen: (e) => {
@@ -127,9 +128,10 @@ var O = class extends i(b) {
127
128
  }
128
129
  static {
129
130
  this.styles = [
130
- C(t),
131
- C(s),
132
- C(y)
131
+ w(t),
132
+ w(s),
133
+ w(c),
134
+ w(b)
133
135
  ];
134
136
  }
135
137
  get value() {
@@ -199,7 +201,7 @@ var O = class extends i(b) {
199
201
  _navigateActive(e) {
200
202
  let t = this._options.filter((e) => !e.disabled && this._isLive(e));
201
203
  if (!t.length) return;
202
- let n = this._options.find((e) => e.value === this.value && !e.disabled && this._isLive(e)), r = this._activeIndex >= 0 && this._isLive(this._options[this._activeIndex]) ? t.indexOf(this._options[this._activeIndex]) : n ? t.indexOf(n) : e > 0 ? -1 : 0, i = t[f(t.length, r, e)];
204
+ let n = this._options.find((e) => e.value === this.value && !e.disabled && this._isLive(e)), r = this._activeIndex >= 0 && this._isLive(this._options[this._activeIndex]) ? t.indexOf(this._options[this._activeIndex]) : n ? t.indexOf(n) : e > 0 ? -1 : 0, i = t[p(t.length, r, e)];
203
205
  this._activeIndex = this._options.indexOf(i), this._syncActiveOption();
204
206
  }
205
207
  _open(e = !1) {
@@ -209,7 +211,7 @@ var O = class extends i(b) {
209
211
  this._openController.close();
210
212
  }
211
213
  _dispatchOpenChange() {
212
- c(this, this._isOpen);
214
+ l(this, this._isOpen);
213
215
  }
214
216
  _setInitialActiveOption() {
215
217
  let e = this._options.findIndex((e) => e.value === this.value && !e.disabled && this._isLive(e)), t = this._options.findIndex((e) => !e.disabled && this._isLive(e));
@@ -237,20 +239,20 @@ var O = class extends i(b) {
237
239
  this._panel.open(e), this._syncOverlayInert();
238
240
  }
239
241
  _createPortal() {
240
- let e = _().make({
242
+ let e = v().make({
241
243
  container: this.overlayContainer,
242
244
  target: this,
243
- style: `${t}\n${s}\n${y}`,
245
+ style: `${t}\n${s}\n${b}`,
244
246
  className: "wui-glass select-overlay portal wui-floating-panel",
245
247
  onContentChange: () => this._optionPortal.scheduleRefresh()
246
248
  });
247
- this._portal = e, g(e.panel, this, [
249
+ this._portal = e, _(e.panel, this, [
248
250
  "--wui-overlay-min-width",
249
251
  "--wui-select-max-width",
250
252
  "--wui-select-max-height"
251
253
  ]), e.panel.setAttribute("role", "listbox");
252
254
  let n = document.createElement("div");
253
- n.className = "select-scroll";
255
+ n.className = "select-scroll wui-scroll";
254
256
  let r = document.createElement("div");
255
257
  return r.className = "select-content", this._portalContent = r, n.append(r), e.panel.append(n), e.moveContent(Array.from(this.children).filter((e) => e.slot !== "trigger"), r), e;
256
258
  }
@@ -261,8 +263,8 @@ var O = class extends i(b) {
261
263
  await this._panel.close(() => this._isOpen) && (this._portal = void 0, this._portalContent = void 0);
262
264
  }
263
265
  render() {
264
- let e = `${this.localName}-listbox`, t = !this.portal && this._isOpen && this._activeIndex >= 0 ? this._options[this._activeIndex]?.id ?? S : S;
265
- return x`
266
+ let e = `${this.localName}-listbox`, t = !this.portal && this._isOpen && this._activeIndex >= 0 ? this._options[this._activeIndex]?.id ?? C : C;
267
+ return S`
266
268
  <div class="wui-select-inner">
267
269
  <div
268
270
  class="wui-glass select-trigger"
@@ -272,16 +274,16 @@ var O = class extends i(b) {
272
274
  role="combobox"
273
275
  aria-expanded=${this._isOpen}
274
276
  aria-haspopup="listbox"
275
- aria-controls=${this.portal ? S : e}
277
+ aria-controls=${this.portal ? C : e}
276
278
  aria-disabled=${String(this._isDisabled)}
277
279
  aria-activedescendant=${t}
278
280
  >
279
281
  <slot name="trigger" @slotchange=${this._onTriggerSlotChange}></slot>
280
- ${this._hasTriggerSlot ? S : x`<span class="label">${this._selectedLabel}</span>`}
282
+ ${this._hasTriggerSlot ? C : S`<span class="label">${this._selectedLabel}</span>`}
281
283
  <web-ui-icon class="arrow" .icon=${n}></web-ui-icon>
282
284
  </div>
283
285
  <div class="select-overlay wui-floating-panel wui-glass" hidden role="listbox" id=${e}>
284
- <div class="select-scroll">
286
+ <div class="select-scroll wui-scroll">
285
287
  <div class="select-content">
286
288
  <slot @slotchange=${this._onSlotChange}></slot>
287
289
  </div>
@@ -291,30 +293,30 @@ var O = class extends i(b) {
291
293
  `;
292
294
  }
293
295
  };
294
- e([T({
296
+ e([E({
295
297
  type: String,
296
298
  reflect: !0
297
- })], O.prototype, "placeholder", void 0), e([T({
299
+ })], k.prototype, "placeholder", void 0), e([E({
298
300
  type: Boolean,
299
301
  reflect: !0
300
- })], O.prototype, "disabled", void 0), e([T({
302
+ })], k.prototype, "disabled", void 0), e([E({
301
303
  type: Boolean,
302
304
  reflect: !0
303
- })], O.prototype, "required", void 0), e([T({
305
+ })], k.prototype, "required", void 0), e([E({
304
306
  type: Boolean,
305
307
  reflect: !0
306
- })], O.prototype, "portal", void 0), e([T({
308
+ })], k.prototype, "portal", void 0), e([E({
307
309
  type: Boolean,
308
310
  reflect: !0,
309
311
  attribute: "no-scroll-lock"
310
- })], O.prototype, "noScrollLock", void 0), e([T({ attribute: !1 })], O.prototype, "overlayContainer", void 0), e([T({
312
+ })], k.prototype, "noScrollLock", void 0), e([E({ attribute: !1 })], k.prototype, "overlayContainer", void 0), e([E({
311
313
  type: String,
312
314
  reflect: !0
313
- })], O.prototype, "name", void 0), e([E()], O.prototype, "_value", void 0), e([T({
315
+ })], k.prototype, "name", void 0), e([D()], k.prototype, "_value", void 0), e([E({
314
316
  type: String,
315
317
  reflect: !0
316
- })], O.prototype, "value", null), e([E()], O.prototype, "_isOpen", void 0), e([E()], O.prototype, "_activeIndex", void 0), e([E()], O.prototype, "_selectedLabel", void 0), e([E()], O.prototype, "_hasTriggerSlot", void 0), O = D = e([w("web-ui-select")], O);
318
+ })], k.prototype, "value", null), e([D()], k.prototype, "_isOpen", void 0), e([D()], k.prototype, "_activeIndex", void 0), e([D()], k.prototype, "_selectedLabel", void 0), e([D()], k.prototype, "_hasTriggerSlot", void 0), k = O = e([T("web-ui-select")], k);
317
319
  //#endregion
318
- export { O as WebUiSelect };
320
+ export { k as WebUiSelect };
319
321
 
320
322
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/select/index.ts"],"sourcesContent":["import '@/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 overlayMotion from '@/assets/overlay-motion.css?inline'\nimport type { WebUiOption } from '@/components/option'\nimport { lucideChevronDown } from '@/icons'\nimport { installPointerFocusSuppression } from '@/shared/focus/pointer-focus'\nimport { FormAssociated, defineFormAssociation, FormAssociationController } from '@/shared/form-association'\nimport { dispatchOpenChangeEvent } from '@/shared/open-state'\nimport {\n createComboboxOpenController,\n createOptionListenerBinding,\n defineOptionPortal,\n handleComboboxFocusOut,\n nextWrappingIndex\n} from '@/shared/option-portal'\nimport { defineAnchoredPanel } from '@/shared/overlay/anchored-panel'\nimport { defineOpenOverlay } from '@/shared/overlay/open-overlay'\nimport {\n applyOverlayVariables,\n defineOverlayPortal,\n type OverlayContainer,\n type OverlayPortal\n} from '@/shared/overlay/portal'\nimport { defineScrollLockLease } from '@/shared/scroll-lock/scroll-lock'\n\nimport style from './style.css?inline'\n\ninstallPointerFocusSuppression()\n\n@customElement('web-ui-select')\nexport class WebUiSelect extends FormAssociated(LitElement) {\n static override styles = [unsafeCSS(glass), unsafeCSS(overlayMotion), unsafeCSS(style)]\n\n @property({ type: String, reflect: true }) placeholder = ''\n @property({ type: Boolean, reflect: true }) disabled = false\n @property({ type: Boolean, reflect: true }) required = false\n @property({ type: Boolean, reflect: true }) portal = false\n @property({ type: Boolean, reflect: true, attribute: 'no-scroll-lock' }) noScrollLock = false\n @property({ attribute: false }) overlayContainer?: OverlayContainer\n @property({ type: String, reflect: true }) name = ''\n\n // value 使用内部状态 + 访问器模式,在变更时同步 ElementInternals;\n // @property + reflect 让它与 input/textarea/slider/input-number 的 value 反射语义保持一致。\n @state() private _value = ''\n @property({ type: String, reflect: true })\n get value(): string {\n return this._value\n }\n set value(v: string) {\n const old = this._value\n this._value = v\n this._formAssociation.sync()\n this.toggleAttribute('data-has-value', !!v)\n this.requestUpdate('value', old)\n }\n\n private readonly _formAssociation = defineFormAssociation<string>({\n host: this,\n initialize: () => {\n // 同步 HTML 属性中预设的 value,补全访问器模式不处理 attribute 反射。\n const attrValue = this.getAttribute('value')\n if (attrValue !== null) {\n this._value = attrValue\n this.toggleAttribute('data-has-value', !!attrValue)\n }\n },\n getState: () => this._value,\n setState: value => {\n this.value = value\n },\n getFormValue: () => this._value,\n getFormState: () => this._value,\n restoreState: state => {\n if (typeof state === 'string') this.value = state\n },\n syncValidity: () => this._syncValidity()\n }).make()\n\n private readonly _formAssociationController = new FormAssociationController(this, this._formAssociation)\n\n private get _isDisabled(): boolean {\n return this.disabled || this._formAssociation.isFormDisabled()\n }\n\n @state() private _isOpen = false\n @state() private _activeIndex = -1\n @state() private _selectedLabel = ''\n @state() private _hasTriggerSlot = false\n\n private _options: WebUiOption[] = []\n private _portal?: OverlayPortal\n private _portalContent?: HTMLElement\n private readonly _scrollLock = defineScrollLockLease().make()\n\n private static _nextInstanceId = 0\n\n // option 注册表 / portal 同步 / 微任务调度收敛到 shared 模块(autocomplete 同款)\n private readonly _optionPortal = defineOptionPortal().make({\n element: this,\n // 实例级前缀保证多实例同时展开时 option id 不冲突(aria 引用按 id 定位)\n idPrefix: `web-ui-select-${++WebUiSelect._nextInstanceId}`,\n getPortal: () => this._portal,\n getPortalContent: () => this._portalContent,\n isOpen: () => this.portal && this._isOpen,\n getMigratableNodes: () => [...this.querySelectorAll<WebUiOption>('web-ui-option')],\n hasUpdated: () => this.hasUpdated,\n requestUpdate: () => this.requestUpdate(),\n bindOption: option => this._bindOption(option),\n unbindOption: option => this._unbindOption(option),\n onImmediateSync: () => {\n this._refreshOptions()\n this._syncSelected()\n this._syncActiveOption()\n }\n })\n /*\n * 开启态浮层承载「我开着吗」(issue #120 Block 1):本组件不再在 keydown 里自行关闭,\n * 只声明「我开着」。宿主级监听在 portal 模式下收不到面板内的 Escape,正是原实现的\n * 缺陷来源;改由唯一仲裁者在 document 捕获阶段判定最内层。\n *\n * 宿主接口除 requestClose 外只再提供一个 isConnected(仅供惰性回收读取):开启状态是\n * 声明而非询问,仲裁时不再回头问 isOpen()。\n */\n private readonly _overlay = defineOpenOverlay().make({\n requestClose: () => this._close(),\n isConnected: () => this.isConnected\n })\n private readonly _panel = defineAnchoredPanel().make({\n getAnchor: () => this.shadowRoot?.querySelector<HTMLElement>('.select-trigger') ?? null,\n getLocalPanel: () => this.shadowRoot?.querySelector<HTMLElement>('.select-overlay') ?? null,\n getPositioning: () => ({\n placement: 'bottom-start',\n offset: 4,\n // 面板宽度统一为 max(内容, trigger, --wui-overlay-min-width),内容自适应展开\n minAnchorWidth: true,\n strategy: this.portal ? 'fixed' : 'absolute'\n }),\n isPortal: () => this.portal,\n createPortal: () => this._createPortal(),\n openOverlay: this._overlay\n })\n\n get isOpen(): boolean {\n return this._isOpen\n }\n\n get open(): boolean {\n return this._isOpen\n }\n\n private _onClickOutside = (e: MouseEvent) => {\n // 在监听器内部判定:composedPath() 在派发结束后会被清空。\n const isInside = e.composedPath().includes(this) || !!this._panel.getHandle()?.containsEvent(e)\n if (this._isOpen && !isInside) {\n this._close()\n }\n }\n\n private _onFocusOut = () => {\n handleComboboxFocusOut(\n this,\n () => this._panel.getHandle(),\n () => this._isOpen,\n () => this._close()\n )\n }\n\n override connectedCallback() {\n super.connectedCallback()\n this._optionPortal.bindHost()\n this.addEventListener('keydown', this._onKeydown)\n this.addEventListener('focusout', this._onFocusOut)\n document.addEventListener('click', this._onClickOutside)\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback()\n this._optionPortal.dispose()\n this.removeEventListener('keydown', this._onKeydown)\n this.removeEventListener('focusout', this._onFocusOut)\n document.removeEventListener('click', this._onClickOutside)\n this._options.forEach(this._unbindOption)\n this._options = []\n this._portal = undefined\n this._portalContent = undefined\n this._close()\n // 撤销登记由 _panel.dispose() 完成(句柄归它持有),不再需要单独 dispose 仲裁者。\n this._panel.dispose()\n this._scrollLock.release()\n }\n\n override firstUpdated() {\n requestAnimationFrame(() => {\n if (this.isConnected) this._optionPortal.scheduleRefresh()\n })\n }\n\n override willUpdate() {\n this._refreshOptions()\n this._syncSelected()\n }\n\n override updated(changed: PropertyValues) {\n if (changed.has('portal') || changed.has('overlayContainer'))\n requestAnimationFrame(() => this._reconfigureOverlay())\n if (changed.has('noScrollLock')) this._syncScrollLock()\n this._syncOpenAttribute()\n this._syncOverlayInert()\n this._syncValidity()\n }\n\n private _syncValidity() {\n const internals = this._formAssociation.getInternals()\n if (!internals || typeof internals.setValidity !== 'function') return\n if (this._isDisabled || !this.required || this.value) internals.setValidity({})\n else internals.setValidity({ valueMissing: true }, '请选择一项')\n }\n\n private _syncOpenAttribute() {\n this.toggleAttribute('open', this._isOpen)\n }\n\n /*\n * 禁用态不响应 Escape。仲裁者仍在(吞掉按键并压掉原生 cancel),只是不走关闭入口\n * —— 与旧的「跳过候选」语义不同:旧语义放任事件落到下层浮层,把外层一起关掉。\n * 每次渲染后同步即可覆盖 disabled 属性与表单禁用两条来源。\n */\n private _syncOverlayInert() {\n this._panel.getHandle()?.setInert(this._isDisabled)\n }\n\n private _onTriggerSlotChange = (e: Event) => {\n const slot = e.target as HTMLSlotElement\n const hasContent = slot.assignedElements().length > 0\n if (this._hasTriggerSlot === hasContent) return\n this._hasTriggerSlot = hasContent\n this._reconfigureOverlay()\n }\n\n private _syncSelected() {\n this._options.forEach(o => {\n o.selected = o.value === this.value\n })\n if (!this.value) {\n this._selectedLabel = this.placeholder\n return\n }\n const option = this._options.find(o => o.value === this.value)\n this._selectedLabel = option?.label || this.placeholder\n }\n\n private readonly _optionListeners = createOptionListenerBinding({\n // 惰性解引用:handler 字段声明在后者仍可在调用期取到\n onClick: event => this._handleOptionClick(event),\n onPointerOver: event => this._handleOptionPointerOver(event),\n onPointerDown: event => this._handleOptionPointerDown(event),\n onUpdate: () => this._onOptionUpdate()\n })\n private _bindOption = (option: WebUiOption) => this._optionListeners.bind(option)\n private _unbindOption = (option: WebUiOption) => this._optionListeners.unbind(option)\n\n private _refreshOptions() {\n const activeOption = this._options[this._activeIndex]\n // diff 绑定/解绑与激活索引按 option 身份保持在 shared 内完成,\n // 避免移除中间项后激活静默偏移到相邻项\n const next = this._optionPortal.refresh(this._options, activeOption)\n this._options = next.options\n this._activeIndex = next.activeIndex\n // 非 portal 模式 aria-activedescendant 引用真实 option id,需保证已分配\n this._optionPortal.ensureOptionIds(this._options)\n }\n\n private _handleOptionClick = (e: Event) => {\n if (!(e.currentTarget instanceof HTMLElement)) return\n const option = e.currentTarget as WebUiOption\n if (option.disabled || !this._isLive(option)) return\n this.value = option.value\n this._close()\n this._notifyValueChange()\n }\n\n private _onOptionUpdate = () => {\n this._syncSelected()\n this._syncActiveOption()\n }\n\n private _handleOptionPointerOver = (e: PointerEvent) => {\n if (e.pointerType === 'touch') return\n if (!this._isOpen || this._activeIndex < 0 || !(e.currentTarget instanceof HTMLElement)) return\n\n // 指针接管后只保留 :hover,避免键盘激活项与悬停项同时高亮。\n this._activeIndex = -1\n this._syncActiveOption()\n }\n\n private _handleOptionPointerDown = (e: PointerEvent) => {\n // 保持 trigger 焦点,避免 focusout 在 click 前关闭浮层。\n e.preventDefault()\n }\n\n // slotchange 不冒泡,只能由模板内 slot 自行监听;稳定引用避免 Lit 重挂载\n private _onSlotChange = () => {\n this._optionPortal.scheduleRefresh()\n }\n\n // slotchange/unregister 的刷新依赖 Lit 渲染调度;键盘或 click 事件可能先于其触发,\n // 已从 DOM 移除或被移出 Select/portal 面板的选项不得再被激活或选中。\n private _isLive(option: WebUiOption): boolean {\n const panel = this.portal ? this._panel.getPanel() : null\n return option.isConnected && (this.contains(option) || (!!panel && panel.contains(option)))\n }\n\n private _onKeydown = (e: KeyboardEvent) => {\n if (this._isDisabled) return\n\n // Escape 不在本组件处理:由共享仲裁者在 document 捕获阶段归属(issue #120 Block 1)。\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault()\n if (!this._isOpen) {\n this._open(true)\n } else {\n this._navigateActive(1)\n }\n break\n case 'ArrowUp':\n e.preventDefault()\n if (!this._isOpen) this._open(true)\n else this._navigateActive(-1)\n break\n case 'Enter': {\n if (this._isOpen && this._activeIndex >= 0) {\n const option = this._options[this._activeIndex]\n if (option && this._isLive(option) && !option.disabled) {\n this.value = option.value\n this._notifyValueChange()\n }\n this._close()\n e.preventDefault()\n }\n break\n }\n }\n }\n\n private _navigateActive(delta: number) {\n const enabled = this._options.filter(o => !o.disabled && this._isLive(o))\n if (!enabled.length) return\n\n const selectedOption = this._options.find(\n option => option.value === this.value && !option.disabled && this._isLive(option)\n )\n const currentIdx =\n this._activeIndex >= 0 && this._isLive(this._options[this._activeIndex])\n ? enabled.indexOf(this._options[this._activeIndex])\n : selectedOption\n ? enabled.indexOf(selectedOption)\n : delta > 0\n ? -1\n : 0\n const nextOption = enabled[nextWrappingIndex(enabled.length, currentIdx, delta)]\n this._activeIndex = this._options.indexOf(nextOption)\n\n this._syncActiveOption()\n }\n\n // 开合生命周期不变量(guard / 派发顺序 / scroll-lock 配对 / portal rAF dance)\n // 收敛在 shared combobox-shell;select 只注入差异点\n private readonly _openController = createComboboxOpenController({\n canOpen: () => !this._isDisabled,\n getIsOpen: () => this._isOpen,\n setIsOpen: open => {\n this._isOpen = open\n },\n dispatchOpenChange: () => this._dispatchOpenChange(),\n syncScrollLock: open => this._syncScrollLock(open),\n isPortal: () => this.portal,\n openOverlay: isKeyboardNavigation => this._openOverlay(isKeyboardNavigation),\n closeOverlay: () => void this._closeOverlay(),\n onOpen: isKeyboardNavigation => {\n this._syncSelected()\n if (isKeyboardNavigation) this._setInitialActiveOption()\n else this._syncActiveOption()\n },\n onAfterClose: () => {\n this._activeIndex = -1\n this._options.forEach(o => o.removeAttribute('active'))\n }\n })\n\n private _open(isKeyboardNavigation = false) {\n this._openController.open(isKeyboardNavigation)\n }\n\n private _close() {\n this._openController.close()\n }\n\n private _dispatchOpenChange() {\n dispatchOpenChangeEvent(this, this._isOpen)\n }\n\n private _setInitialActiveOption() {\n const selectedIndex = this._options.findIndex(\n option => option.value === this.value && !option.disabled && this._isLive(option)\n )\n const firstEnabledIndex = this._options.findIndex(option => !option.disabled && this._isLive(option))\n this._activeIndex = selectedIndex >= 0 ? selectedIndex : firstEnabledIndex\n this._syncActiveOption()\n }\n\n private _syncActiveOption() {\n this._options.forEach((option, index) =>\n option.toggleAttribute('active', index === this._activeIndex && this._isLive(option))\n )\n }\n\n private _notifyValueChange() {\n this.dispatchEvent(new Event('input', { bubbles: true, composed: true }))\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n }\n\n private _syncScrollLock(isOpen = this._isOpen) {\n this._scrollLock.sync(isOpen && !this.noScrollLock)\n }\n\n private _togglePopup() {\n if (this._isDisabled) return\n if (this._isOpen) this._close()\n else this._open()\n }\n\n private _openOverlay(isKeyboardNavigation = false) {\n this._panel.open(isKeyboardNavigation)\n // 新会话的句柄刚建立,惰性状态需按当前 disabled 重新同步。\n this._syncOverlayInert()\n }\n\n private _createPortal(): OverlayPortal {\n const portal = defineOverlayPortal().make({\n container: this.overlayContainer,\n target: this,\n style: `${glass}\\n${overlayMotion}\\n${style}`,\n className: 'wui-glass select-overlay portal wui-floating-panel',\n onContentChange: () => this._optionPortal.scheduleRefresh()\n })\n this._portal = portal\n applyOverlayVariables(portal.panel, this, [\n '--wui-overlay-min-width',\n '--wui-select-max-width',\n '--wui-select-max-height'\n ])\n portal.panel.setAttribute('role', 'listbox')\n const scroll = document.createElement('div')\n scroll.className = 'select-scroll'\n const content = document.createElement('div')\n content.className = 'select-content'\n this._portalContent = content\n scroll.append(content)\n portal.panel.append(scroll)\n // 自定义 trigger 必须留在宿主内,不能随 options 迁入 portal 面板\n portal.moveContent(\n Array.from(this.children).filter(child => child.slot !== 'trigger'),\n content\n )\n return portal\n }\n\n private _reconfigureOverlay() {\n // reconfigure 会 dispose 并按需重建 portal,旧引用必须同步失效\n this._portal = undefined\n this._portalContent = undefined\n this._panel.reconfigure(this._isOpen)\n /*\n * reconfigure 重新 claim = 新会话,inert 回到 false。实测这条重推是**冗余**的(摘掉本行\n * 全部用例仍绿):这条路径后面跟着一轮渲染,而 `updated()` 每次渲染都会调\n * `_syncOverlayInert()`。留在这里是不想依赖「这条路径必然跟着一次渲染」这个隐含前提。\n */\n this._syncOverlayInert()\n }\n\n private async _closeOverlay() {\n const closed = await this._panel.close(() => this._isOpen)\n // portal 已随关闭 dispose,与 autocomplete 对齐同步失效引用\n if (closed) {\n this._portal = undefined\n this._portalContent = undefined\n }\n }\n\n override render() {\n // portal 模式下 options 被 moveContent 移入浮层 shadow root,id 引用无法跨树作用域\n // 解析,因此 activedescendant 仅在非 portal 模式输出;aria-controls 同理指向同根内 listbox\n const listboxId = `${this.localName}-listbox`\n const activeDescendant =\n !this.portal && this._isOpen && this._activeIndex >= 0\n ? (this._options[this._activeIndex]?.id ?? nothing)\n : nothing\n\n return html`\n <div class=\"wui-select-inner\">\n <div\n class=\"wui-glass select-trigger\"\n ?data-custom-trigger=${this._hasTriggerSlot}\n @click=${this._togglePopup}\n tabindex=${this._isDisabled ? '-1' : '0'}\n role=\"combobox\"\n aria-expanded=${this._isOpen}\n aria-haspopup=\"listbox\"\n aria-controls=${this.portal ? nothing : listboxId}\n aria-disabled=${String(this._isDisabled)}\n aria-activedescendant=${activeDescendant}\n >\n <slot name=\"trigger\" @slotchange=${this._onTriggerSlotChange}></slot>\n ${!this._hasTriggerSlot ? html`<span class=\"label\">${this._selectedLabel}</span>` : nothing}\n <web-ui-icon class=\"arrow\" .icon=${lucideChevronDown}></web-ui-icon>\n </div>\n <div class=\"select-overlay wui-floating-panel wui-glass\" hidden role=\"listbox\" id=${listboxId}>\n <div class=\"select-scroll\">\n <div class=\"select-content\">\n <slot @slotchange=${this._onSlotChange}></slot>\n </div>\n </div>\n </div>\n </div>\n `\n }\n\n declare readonly $events: {\n input: Event\n change: Event\n 'open-change': CustomEvent<{ open: boolean }>\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-select': WebUiSelect\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/select/index.ts"],"sourcesContent":["import '@/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 overlayMotion from '@/assets/overlay-motion.css?inline'\nimport type { WebUiOption } from '@/components/option'\nimport { lucideChevronDown } from '@/icons'\nimport internalScrollbars from '@/scrollbars/internal.css?inline'\nimport { installPointerFocusSuppression } from '@/shared/focus/pointer-focus'\nimport { FormAssociated, defineFormAssociation, FormAssociationController } from '@/shared/form-association'\nimport { dispatchOpenChangeEvent } from '@/shared/open-state'\nimport {\n createComboboxOpenController,\n createOptionListenerBinding,\n defineOptionPortal,\n handleComboboxFocusOut,\n nextWrappingIndex\n} from '@/shared/option-portal'\nimport { defineAnchoredPanel } from '@/shared/overlay/anchored-panel'\nimport { defineOpenOverlay } from '@/shared/overlay/open-overlay'\nimport {\n applyOverlayVariables,\n defineOverlayPortal,\n type OverlayContainer,\n type OverlayPortal\n} from '@/shared/overlay/portal'\nimport { defineScrollLockLease } from '@/shared/scroll-lock/scroll-lock'\n\nimport style from './style.css?inline'\n\ninstallPointerFocusSuppression()\n\n@customElement('web-ui-select')\nexport class WebUiSelect extends FormAssociated(LitElement) {\n static override styles = [unsafeCSS(glass), unsafeCSS(overlayMotion), unsafeCSS(internalScrollbars), unsafeCSS(style)]\n\n @property({ type: String, reflect: true }) placeholder = ''\n @property({ type: Boolean, reflect: true }) disabled = false\n @property({ type: Boolean, reflect: true }) required = false\n @property({ type: Boolean, reflect: true }) portal = false\n @property({ type: Boolean, reflect: true, attribute: 'no-scroll-lock' }) noScrollLock = false\n @property({ attribute: false }) overlayContainer?: OverlayContainer\n @property({ type: String, reflect: true }) name = ''\n\n // value 使用内部状态 + 访问器模式,在变更时同步 ElementInternals;\n // @property + reflect 让它与 input/textarea/slider/input-number 的 value 反射语义保持一致。\n @state() private _value = ''\n @property({ type: String, reflect: true })\n get value(): string {\n return this._value\n }\n set value(v: string) {\n const old = this._value\n this._value = v\n this._formAssociation.sync()\n this.toggleAttribute('data-has-value', !!v)\n this.requestUpdate('value', old)\n }\n\n private readonly _formAssociation = defineFormAssociation<string>({\n host: this,\n initialize: () => {\n // 同步 HTML 属性中预设的 value,补全访问器模式不处理 attribute 反射。\n const attrValue = this.getAttribute('value')\n if (attrValue !== null) {\n this._value = attrValue\n this.toggleAttribute('data-has-value', !!attrValue)\n }\n },\n getState: () => this._value,\n setState: value => {\n this.value = value\n },\n getFormValue: () => this._value,\n getFormState: () => this._value,\n restoreState: state => {\n if (typeof state === 'string') this.value = state\n },\n syncValidity: () => this._syncValidity()\n }).make()\n\n private readonly _formAssociationController = new FormAssociationController(this, this._formAssociation)\n\n private get _isDisabled(): boolean {\n return this.disabled || this._formAssociation.isFormDisabled()\n }\n\n @state() private _isOpen = false\n @state() private _activeIndex = -1\n @state() private _selectedLabel = ''\n @state() private _hasTriggerSlot = false\n\n private _options: WebUiOption[] = []\n private _portal?: OverlayPortal\n private _portalContent?: HTMLElement\n private readonly _scrollLock = defineScrollLockLease().make()\n\n private static _nextInstanceId = 0\n\n // option 注册表 / portal 同步 / 微任务调度收敛到 shared 模块(autocomplete 同款)\n private readonly _optionPortal = defineOptionPortal().make({\n element: this,\n // 实例级前缀保证多实例同时展开时 option id 不冲突(aria 引用按 id 定位)\n idPrefix: `web-ui-select-${++WebUiSelect._nextInstanceId}`,\n getPortal: () => this._portal,\n getPortalContent: () => this._portalContent,\n isOpen: () => this.portal && this._isOpen,\n getMigratableNodes: () => [...this.querySelectorAll<WebUiOption>('web-ui-option')],\n hasUpdated: () => this.hasUpdated,\n requestUpdate: () => this.requestUpdate(),\n bindOption: option => this._bindOption(option),\n unbindOption: option => this._unbindOption(option),\n onImmediateSync: () => {\n this._refreshOptions()\n this._syncSelected()\n this._syncActiveOption()\n }\n })\n /*\n * 开启态浮层承载「我开着吗」(issue #120 Block 1):本组件不再在 keydown 里自行关闭,\n * 只声明「我开着」。宿主级监听在 portal 模式下收不到面板内的 Escape,正是原实现的\n * 缺陷来源;改由唯一仲裁者在 document 捕获阶段判定最内层。\n *\n * 宿主接口除 requestClose 外只再提供一个 isConnected(仅供惰性回收读取):开启状态是\n * 声明而非询问,仲裁时不再回头问 isOpen()。\n */\n private readonly _overlay = defineOpenOverlay().make({\n requestClose: () => this._close(),\n isConnected: () => this.isConnected\n })\n private readonly _panel = defineAnchoredPanel().make({\n getAnchor: () => this.shadowRoot?.querySelector<HTMLElement>('.select-trigger') ?? null,\n getLocalPanel: () => this.shadowRoot?.querySelector<HTMLElement>('.select-overlay') ?? null,\n getPositioning: () => ({\n placement: 'bottom-start',\n offset: 4,\n // 面板宽度统一为 max(内容, trigger, --wui-overlay-min-width),内容自适应展开\n minAnchorWidth: true,\n strategy: this.portal ? 'fixed' : 'absolute'\n }),\n isPortal: () => this.portal,\n createPortal: () => this._createPortal(),\n openOverlay: this._overlay\n })\n\n get isOpen(): boolean {\n return this._isOpen\n }\n\n get open(): boolean {\n return this._isOpen\n }\n\n private _onClickOutside = (e: MouseEvent) => {\n // 在监听器内部判定:composedPath() 在派发结束后会被清空。\n const isInside = e.composedPath().includes(this) || !!this._panel.getHandle()?.containsEvent(e)\n if (this._isOpen && !isInside) {\n this._close()\n }\n }\n\n private _onFocusOut = () => {\n handleComboboxFocusOut(\n this,\n () => this._panel.getHandle(),\n () => this._isOpen,\n () => this._close()\n )\n }\n\n override connectedCallback() {\n super.connectedCallback()\n this._optionPortal.bindHost()\n this.addEventListener('keydown', this._onKeydown)\n this.addEventListener('focusout', this._onFocusOut)\n document.addEventListener('click', this._onClickOutside)\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback()\n this._optionPortal.dispose()\n this.removeEventListener('keydown', this._onKeydown)\n this.removeEventListener('focusout', this._onFocusOut)\n document.removeEventListener('click', this._onClickOutside)\n this._options.forEach(this._unbindOption)\n this._options = []\n this._portal = undefined\n this._portalContent = undefined\n this._close()\n // 撤销登记由 _panel.dispose() 完成(句柄归它持有),不再需要单独 dispose 仲裁者。\n this._panel.dispose()\n this._scrollLock.release()\n }\n\n override firstUpdated() {\n requestAnimationFrame(() => {\n if (this.isConnected) this._optionPortal.scheduleRefresh()\n })\n }\n\n override willUpdate() {\n this._refreshOptions()\n this._syncSelected()\n }\n\n override updated(changed: PropertyValues) {\n if (changed.has('portal') || changed.has('overlayContainer'))\n requestAnimationFrame(() => this._reconfigureOverlay())\n if (changed.has('noScrollLock')) this._syncScrollLock()\n this._syncOpenAttribute()\n this._syncOverlayInert()\n this._syncValidity()\n }\n\n private _syncValidity() {\n const internals = this._formAssociation.getInternals()\n if (!internals || typeof internals.setValidity !== 'function') return\n if (this._isDisabled || !this.required || this.value) internals.setValidity({})\n else internals.setValidity({ valueMissing: true }, '请选择一项')\n }\n\n private _syncOpenAttribute() {\n this.toggleAttribute('open', this._isOpen)\n }\n\n /*\n * 禁用态不响应 Escape。仲裁者仍在(吞掉按键并压掉原生 cancel),只是不走关闭入口\n * —— 与旧的「跳过候选」语义不同:旧语义放任事件落到下层浮层,把外层一起关掉。\n * 每次渲染后同步即可覆盖 disabled 属性与表单禁用两条来源。\n */\n private _syncOverlayInert() {\n this._panel.getHandle()?.setInert(this._isDisabled)\n }\n\n private _onTriggerSlotChange = (e: Event) => {\n const slot = e.target as HTMLSlotElement\n const hasContent = slot.assignedElements().length > 0\n if (this._hasTriggerSlot === hasContent) return\n this._hasTriggerSlot = hasContent\n this._reconfigureOverlay()\n }\n\n private _syncSelected() {\n this._options.forEach(o => {\n o.selected = o.value === this.value\n })\n if (!this.value) {\n this._selectedLabel = this.placeholder\n return\n }\n const option = this._options.find(o => o.value === this.value)\n this._selectedLabel = option?.label || this.placeholder\n }\n\n private readonly _optionListeners = createOptionListenerBinding({\n // 惰性解引用:handler 字段声明在后者仍可在调用期取到\n onClick: event => this._handleOptionClick(event),\n onPointerOver: event => this._handleOptionPointerOver(event),\n onPointerDown: event => this._handleOptionPointerDown(event),\n onUpdate: () => this._onOptionUpdate()\n })\n private _bindOption = (option: WebUiOption) => this._optionListeners.bind(option)\n private _unbindOption = (option: WebUiOption) => this._optionListeners.unbind(option)\n\n private _refreshOptions() {\n const activeOption = this._options[this._activeIndex]\n // diff 绑定/解绑与激活索引按 option 身份保持在 shared 内完成,\n // 避免移除中间项后激活静默偏移到相邻项\n const next = this._optionPortal.refresh(this._options, activeOption)\n this._options = next.options\n this._activeIndex = next.activeIndex\n // 非 portal 模式 aria-activedescendant 引用真实 option id,需保证已分配\n this._optionPortal.ensureOptionIds(this._options)\n }\n\n private _handleOptionClick = (e: Event) => {\n if (!(e.currentTarget instanceof HTMLElement)) return\n const option = e.currentTarget as WebUiOption\n if (option.disabled || !this._isLive(option)) return\n this.value = option.value\n this._close()\n this._notifyValueChange()\n }\n\n private _onOptionUpdate = () => {\n this._syncSelected()\n this._syncActiveOption()\n }\n\n private _handleOptionPointerOver = (e: PointerEvent) => {\n if (e.pointerType === 'touch') return\n if (!this._isOpen || this._activeIndex < 0 || !(e.currentTarget instanceof HTMLElement)) return\n\n // 指针接管后只保留 :hover,避免键盘激活项与悬停项同时高亮。\n this._activeIndex = -1\n this._syncActiveOption()\n }\n\n private _handleOptionPointerDown = (e: PointerEvent) => {\n // 保持 trigger 焦点,避免 focusout 在 click 前关闭浮层。\n e.preventDefault()\n }\n\n // slotchange 不冒泡,只能由模板内 slot 自行监听;稳定引用避免 Lit 重挂载\n private _onSlotChange = () => {\n this._optionPortal.scheduleRefresh()\n }\n\n // slotchange/unregister 的刷新依赖 Lit 渲染调度;键盘或 click 事件可能先于其触发,\n // 已从 DOM 移除或被移出 Select/portal 面板的选项不得再被激活或选中。\n private _isLive(option: WebUiOption): boolean {\n const panel = this.portal ? this._panel.getPanel() : null\n return option.isConnected && (this.contains(option) || (!!panel && panel.contains(option)))\n }\n\n private _onKeydown = (e: KeyboardEvent) => {\n if (this._isDisabled) return\n\n // Escape 不在本组件处理:由共享仲裁者在 document 捕获阶段归属(issue #120 Block 1)。\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault()\n if (!this._isOpen) {\n this._open(true)\n } else {\n this._navigateActive(1)\n }\n break\n case 'ArrowUp':\n e.preventDefault()\n if (!this._isOpen) this._open(true)\n else this._navigateActive(-1)\n break\n case 'Enter': {\n if (this._isOpen && this._activeIndex >= 0) {\n const option = this._options[this._activeIndex]\n if (option && this._isLive(option) && !option.disabled) {\n this.value = option.value\n this._notifyValueChange()\n }\n this._close()\n e.preventDefault()\n }\n break\n }\n }\n }\n\n private _navigateActive(delta: number) {\n const enabled = this._options.filter(o => !o.disabled && this._isLive(o))\n if (!enabled.length) return\n\n const selectedOption = this._options.find(\n option => option.value === this.value && !option.disabled && this._isLive(option)\n )\n const currentIdx =\n this._activeIndex >= 0 && this._isLive(this._options[this._activeIndex])\n ? enabled.indexOf(this._options[this._activeIndex])\n : selectedOption\n ? enabled.indexOf(selectedOption)\n : delta > 0\n ? -1\n : 0\n const nextOption = enabled[nextWrappingIndex(enabled.length, currentIdx, delta)]\n this._activeIndex = this._options.indexOf(nextOption)\n\n this._syncActiveOption()\n }\n\n // 开合生命周期不变量(guard / 派发顺序 / scroll-lock 配对 / portal rAF dance)\n // 收敛在 shared combobox-shell;select 只注入差异点\n private readonly _openController = createComboboxOpenController({\n canOpen: () => !this._isDisabled,\n getIsOpen: () => this._isOpen,\n setIsOpen: open => {\n this._isOpen = open\n },\n dispatchOpenChange: () => this._dispatchOpenChange(),\n syncScrollLock: open => this._syncScrollLock(open),\n isPortal: () => this.portal,\n openOverlay: isKeyboardNavigation => this._openOverlay(isKeyboardNavigation),\n closeOverlay: () => void this._closeOverlay(),\n onOpen: isKeyboardNavigation => {\n this._syncSelected()\n if (isKeyboardNavigation) this._setInitialActiveOption()\n else this._syncActiveOption()\n },\n onAfterClose: () => {\n this._activeIndex = -1\n this._options.forEach(o => o.removeAttribute('active'))\n }\n })\n\n private _open(isKeyboardNavigation = false) {\n this._openController.open(isKeyboardNavigation)\n }\n\n private _close() {\n this._openController.close()\n }\n\n private _dispatchOpenChange() {\n dispatchOpenChangeEvent(this, this._isOpen)\n }\n\n private _setInitialActiveOption() {\n const selectedIndex = this._options.findIndex(\n option => option.value === this.value && !option.disabled && this._isLive(option)\n )\n const firstEnabledIndex = this._options.findIndex(option => !option.disabled && this._isLive(option))\n this._activeIndex = selectedIndex >= 0 ? selectedIndex : firstEnabledIndex\n this._syncActiveOption()\n }\n\n private _syncActiveOption() {\n this._options.forEach((option, index) =>\n option.toggleAttribute('active', index === this._activeIndex && this._isLive(option))\n )\n }\n\n private _notifyValueChange() {\n this.dispatchEvent(new Event('input', { bubbles: true, composed: true }))\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n }\n\n private _syncScrollLock(isOpen = this._isOpen) {\n this._scrollLock.sync(isOpen && !this.noScrollLock)\n }\n\n private _togglePopup() {\n if (this._isDisabled) return\n if (this._isOpen) this._close()\n else this._open()\n }\n\n private _openOverlay(isKeyboardNavigation = false) {\n this._panel.open(isKeyboardNavigation)\n // 新会话的句柄刚建立,惰性状态需按当前 disabled 重新同步。\n this._syncOverlayInert()\n }\n\n private _createPortal(): OverlayPortal {\n const portal = defineOverlayPortal().make({\n container: this.overlayContainer,\n target: this,\n style: `${glass}\\n${overlayMotion}\\n${style}`,\n className: 'wui-glass select-overlay portal wui-floating-panel',\n onContentChange: () => this._optionPortal.scheduleRefresh()\n })\n this._portal = portal\n applyOverlayVariables(portal.panel, this, [\n '--wui-overlay-min-width',\n '--wui-select-max-width',\n '--wui-select-max-height'\n ])\n portal.panel.setAttribute('role', 'listbox')\n const scroll = document.createElement('div')\n scroll.className = 'select-scroll wui-scroll'\n const content = document.createElement('div')\n content.className = 'select-content'\n this._portalContent = content\n scroll.append(content)\n portal.panel.append(scroll)\n // 自定义 trigger 必须留在宿主内,不能随 options 迁入 portal 面板\n portal.moveContent(\n Array.from(this.children).filter(child => child.slot !== 'trigger'),\n content\n )\n return portal\n }\n\n private _reconfigureOverlay() {\n // reconfigure 会 dispose 并按需重建 portal,旧引用必须同步失效\n this._portal = undefined\n this._portalContent = undefined\n this._panel.reconfigure(this._isOpen)\n /*\n * reconfigure 重新 claim = 新会话,inert 回到 false。实测这条重推是**冗余**的(摘掉本行\n * 全部用例仍绿):这条路径后面跟着一轮渲染,而 `updated()` 每次渲染都会调\n * `_syncOverlayInert()`。留在这里是不想依赖「这条路径必然跟着一次渲染」这个隐含前提。\n */\n this._syncOverlayInert()\n }\n\n private async _closeOverlay() {\n const closed = await this._panel.close(() => this._isOpen)\n // portal 已随关闭 dispose,与 autocomplete 对齐同步失效引用\n if (closed) {\n this._portal = undefined\n this._portalContent = undefined\n }\n }\n\n override render() {\n // portal 模式下 options 被 moveContent 移入浮层 shadow root,id 引用无法跨树作用域\n // 解析,因此 activedescendant 仅在非 portal 模式输出;aria-controls 同理指向同根内 listbox\n const listboxId = `${this.localName}-listbox`\n const activeDescendant =\n !this.portal && this._isOpen && this._activeIndex >= 0\n ? (this._options[this._activeIndex]?.id ?? nothing)\n : nothing\n\n return html`\n <div class=\"wui-select-inner\">\n <div\n class=\"wui-glass select-trigger\"\n ?data-custom-trigger=${this._hasTriggerSlot}\n @click=${this._togglePopup}\n tabindex=${this._isDisabled ? '-1' : '0'}\n role=\"combobox\"\n aria-expanded=${this._isOpen}\n aria-haspopup=\"listbox\"\n aria-controls=${this.portal ? nothing : listboxId}\n aria-disabled=${String(this._isDisabled)}\n aria-activedescendant=${activeDescendant}\n >\n <slot name=\"trigger\" @slotchange=${this._onTriggerSlotChange}></slot>\n ${!this._hasTriggerSlot ? html`<span class=\"label\">${this._selectedLabel}</span>` : nothing}\n <web-ui-icon class=\"arrow\" .icon=${lucideChevronDown}></web-ui-icon>\n </div>\n <div class=\"select-overlay wui-floating-panel wui-glass\" hidden role=\"listbox\" id=${listboxId}>\n <div class=\"select-scroll wui-scroll\">\n <div class=\"select-content\">\n <slot @slotchange=${this._onSlotChange}></slot>\n </div>\n </div>\n </div>\n </div>\n `\n }\n\n declare readonly $events: {\n input: Event\n change: Event\n 'open-change': CustomEvent<{ open: boolean }>\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-select': WebUiSelect\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAgCA,EAA+B;AAGxB,IAAM,IAAN,cAA0B,EAAe,CAAU,CAAC,CAAC;;;;;EAkVvB,aA/UsB,KAAA,cAAA,IACF,KAAA,WAAA,IACA,KAAA,WAAA,IACF,KAAA,SAAA,IACmC,KAAA,eAAA,IAEtC,KAAA,OAAA,IAIxB,KAAA,SAAA,IAaU,KAAA,mBAAA,EAA8B;GAChE,MAAM;GACN,kBAAkB;IAEhB,IAAM,IAAY,KAAK,aAAa,OAAO;IAC3C,AAAI,MAAc,SAChB,KAAK,SAAS,GACd,KAAK,gBAAgB,kBAAkB,CAAC,CAAC,CAAS;GAEtD;GACA,gBAAgB,KAAK;GACrB,WAAU,MAAS;IACjB,KAAK,QAAQ;GACf;GACA,oBAAoB,KAAK;GACzB,oBAAoB,KAAK;GACzB,eAAc,MAAS;IACrB,AAAI,OAAO,KAAU,aAAU,KAAK,QAAQ;GAC9C;GACA,oBAAoB,KAAK,cAAc;EACzC,CAAC,CAAC,CAAC,KAAK,GAEsC,KAAA,6BAAA,IAAI,EAA0B,MAAM,KAAK,gBAAgB,GAM5E,KAAA,UAAA,IACK,KAAA,eAAA,IACE,KAAA,iBAAA,IACC,KAAA,kBAAA,IAED,KAAA,WAAA,CAAC,GAGJ,KAAA,cAAA,EAAsB,CAAC,CAAC,KAAK,GAK3B,KAAA,gBAAA,EAAmB,CAAC,CAAC,KAAK;GACzD,SAAS;GAET,UAAU,iBAAiB,EAAA,EAAc;GACzC,iBAAiB,KAAK;GACtB,wBAAwB,KAAK;GAC7B,cAAc,KAAK,UAAU,KAAK;GAClC,0BAA0B,CAAC,GAAG,KAAK,iBAA8B,eAAe,CAAC;GACjF,kBAAkB,KAAK;GACvB,qBAAqB,KAAK,cAAc;GACxC,aAAY,MAAU,KAAK,YAAY,CAAM;GAC7C,eAAc,MAAU,KAAK,cAAc,CAAM;GACjD,uBAAuB;IAGrB,AAFA,KAAK,gBAAgB,GACrB,KAAK,cAAc,GACnB,KAAK,kBAAkB;GACzB;EACF,CAAC,GAS2B,KAAA,WAAA,EAAkB,CAAC,CAAC,KAAK;GACnD,oBAAoB,KAAK,OAAO;GAChC,mBAAmB,KAAK;EAC1B,CAAC,GACyB,KAAA,SAAA,EAAoB,CAAC,CAAC,KAAK;GACnD,iBAAiB,KAAK,YAAY,cAA2B,iBAAiB,KAAK;GACnF,qBAAqB,KAAK,YAAY,cAA2B,iBAAiB,KAAK;GACvF,uBAAuB;IACrB,WAAW;IACX,QAAQ;IAER,gBAAgB;IAChB,UAAU,KAAK,SAAS,UAAU;GACpC;GACA,gBAAgB,KAAK;GACrB,oBAAoB,KAAK,cAAc;GACvC,aAAa,KAAK;EACpB,CAAC,GAU0B,KAAA,mBAAA,MAAkB;GAE3C,IAAM,IAAW,EAAE,aAAa,CAAC,CAAC,SAAS,IAAI,KAAK,CAAC,CAAC,KAAK,OAAO,UAAU,CAAC,EAAE,cAAc,CAAC;GAC9F,AAAI,KAAK,WAAW,CAAC,KACnB,KAAK,OAAO;EAEhB,GAE4B,KAAA,oBAAA;GAC1B,EACE,YACM,KAAK,OAAO,UAAU,SACtB,KAAK,eACL,KAAK,OAAO,CACpB;EACF,GAkEgC,KAAA,wBAAA,MAAa;GAE3C,IAAM,IADO,EAAE,OACS,iBAAiB,CAAC,CAAC,SAAS;GAChD,KAAK,oBAAoB,MAC7B,KAAK,kBAAkB,GACvB,KAAK,oBAAoB;EAC3B,GAcoC,KAAA,mBAAA,EAA4B;GAE9D,UAAS,MAAS,KAAK,mBAAmB,CAAK;GAC/C,gBAAe,MAAS,KAAK,yBAAyB,CAAK;GAC3D,gBAAe,MAAS,KAAK,yBAAyB,CAAK;GAC3D,gBAAgB,KAAK,gBAAgB;EACvC,CAAC,GACsB,KAAA,eAAA,MAAwB,KAAK,iBAAiB,KAAK,CAAM,GACvD,KAAA,iBAAA,MAAwB,KAAK,iBAAiB,OAAO,CAAM,GAatD,KAAA,sBAAA,MAAa;GACzC,IAAI,EAAE,EAAE,yBAAyB,cAAc;GAC/C,IAAM,IAAS,EAAE;GACb,GAAO,YAAa,KAAK,QAAQ,CAAM,MAC3C,KAAK,QAAQ,EAAO,OACpB,KAAK,OAAO,GACZ,KAAK,mBAAmB;EAC1B,GAEgC,KAAA,wBAAA;GAE9B,AADA,KAAK,cAAc,GACnB,KAAK,kBAAkB;EACzB,GAEoC,KAAA,4BAAA,MAAoB;GAClD,EAAE,gBAAgB,YAClB,CAAC,KAAK,WAAW,KAAK,eAAe,KAAK,EAAE,EAAE,yBAAyB,iBAG3E,KAAK,eAAe,IACpB,KAAK,kBAAkB;EACzB,GAEoC,KAAA,4BAAA,MAAoB;GAEtD,EAAE,eAAe;EACnB,GAG8B,KAAA,sBAAA;GAC5B,KAAK,cAAc,gBAAgB;EACrC,GASsB,KAAA,cAAA,MAAqB;GACrC,UAAK,aAGT,QAAQ,EAAE,KAAV;IACE,KAAK;KAEH,AADA,EAAE,eAAe,GACZ,KAAK,UAGR,KAAK,gBAAgB,CAAC,IAFtB,KAAK,MAAM,EAAI;KAIjB;IACF,KAAK;KAEH,AADA,EAAE,eAAe,GACZ,KAAK,UACL,KAAK,gBAAgB,EAAE,IADT,KAAK,MAAM,EAAI;KAElC;IACF,KAAK,SACH,IAAI,KAAK,WAAW,KAAK,gBAAgB,GAAG;KAC1C,IAAM,IAAS,KAAK,SAAS,KAAK;KAMlC,AALI,KAAU,KAAK,QAAQ,CAAM,KAAK,CAAC,EAAO,aAC5C,KAAK,QAAQ,EAAO,OACpB,KAAK,mBAAmB,IAE1B,KAAK,OAAO,GACZ,EAAE,eAAe;IACnB;GAGJ;EACF,GAyBmC,KAAA,kBAAA,EAA6B;GAC9D,eAAe,CAAC,KAAK;GACrB,iBAAiB,KAAK;GACtB,YAAW,MAAQ;IACjB,KAAK,UAAU;GACjB;GACA,0BAA0B,KAAK,oBAAoB;GACnD,iBAAgB,MAAQ,KAAK,gBAAgB,CAAI;GACjD,gBAAgB,KAAK;GACrB,cAAa,MAAwB,KAAK,aAAa,CAAoB;GAC3E,oBAAoB,KAAK,KAAK,cAAc;GAC5C,SAAQ,MAAwB;IAE9B,AADA,KAAK,cAAc,GACf,IAAsB,KAAK,wBAAwB,IAClD,KAAK,kBAAkB;GAC9B;GACA,oBAAoB;IAElB,AADA,KAAK,eAAe,IACpB,KAAK,SAAS,SAAQ,MAAK,EAAE,gBAAgB,QAAQ,CAAC;GACxD;EACF,CAAC;;;EArWwB,KAAA,SAAA;GAAC,EAAU,CAAK;GAAG,EAAU,CAAa;GAAG,EAAU,CAAkB;GAAG,EAAU,CAAK;EAAC;;CAarH,IACI,QAAgB;EAClB,OAAO,KAAK;CACd;CACA,IAAI,MAAM,GAAW;EACnB,IAAM,IAAM,KAAK;EAIjB,AAHA,KAAK,SAAS,GACd,KAAK,iBAAiB,KAAK,GAC3B,KAAK,gBAAgB,kBAAkB,CAAC,CAAC,CAAC,GAC1C,KAAK,cAAc,SAAS,CAAG;CACjC;CA0BA,IAAY,cAAuB;EACjC,OAAO,KAAK,YAAY,KAAK,iBAAiB,eAAe;CAC/D;;EAYiC,KAAA,kBAAA;;CAgDjC,IAAI,SAAkB;EACpB,OAAO,KAAK;CACd;CAEA,IAAI,OAAgB;EAClB,OAAO,KAAK;CACd;CAmBA,oBAA6B;EAK3B,AAJA,MAAM,kBAAkB,GACxB,KAAK,cAAc,SAAS,GAC5B,KAAK,iBAAiB,WAAW,KAAK,UAAU,GAChD,KAAK,iBAAiB,YAAY,KAAK,WAAW,GAClD,SAAS,iBAAiB,SAAS,KAAK,eAAe;CACzD;CAEA,uBAAgC;EAa9B,AAZA,MAAM,qBAAqB,GAC3B,KAAK,cAAc,QAAQ,GAC3B,KAAK,oBAAoB,WAAW,KAAK,UAAU,GACnD,KAAK,oBAAoB,YAAY,KAAK,WAAW,GACrD,SAAS,oBAAoB,SAAS,KAAK,eAAe,GAC1D,KAAK,SAAS,QAAQ,KAAK,aAAa,GACxC,KAAK,WAAW,CAAC,GACjB,KAAK,UAAU,KAAA,GACf,KAAK,iBAAiB,KAAA,GACtB,KAAK,OAAO,GAEZ,KAAK,OAAO,QAAQ,GACpB,KAAK,YAAY,QAAQ;CAC3B;CAEA,eAAwB;EACtB,4BAA4B;GAC1B,AAAI,KAAK,eAAa,KAAK,cAAc,gBAAgB;EAC3D,CAAC;CACH;CAEA,aAAsB;EAEpB,AADA,KAAK,gBAAgB,GACrB,KAAK,cAAc;CACrB;CAEA,QAAiB,GAAyB;EAMxC,CALI,EAAQ,IAAI,QAAQ,KAAK,EAAQ,IAAI,kBAAkB,MACzD,4BAA4B,KAAK,oBAAoB,CAAC,GACpD,EAAQ,IAAI,cAAc,KAAG,KAAK,gBAAgB,GACtD,KAAK,mBAAmB,GACxB,KAAK,kBAAkB,GACvB,KAAK,cAAc;CACrB;CAEA,gBAAwB;EACtB,IAAM,IAAY,KAAK,iBAAiB,aAAa;EACjD,AAAC,KAAa,OAAO,EAAU,eAAgB,eAC/C,KAAK,eAAe,CAAC,KAAK,YAAY,KAAK,QAAO,EAAU,YAAY,CAAC,CAAC,IACzE,EAAU,YAAY,EAAE,cAAc,GAAK,GAAG,OAAO;CAC5D;CAEA,qBAA6B;EAC3B,KAAK,gBAAgB,QAAQ,KAAK,OAAO;CAC3C;CAOA,oBAA4B;EAC1B,KAAK,OAAO,UAAU,CAAC,EAAE,SAAS,KAAK,WAAW;CACpD;CAUA,gBAAwB;EAItB,IAHA,KAAK,SAAS,SAAQ,MAAK;GACzB,EAAE,WAAW,EAAE,UAAU,KAAK;EAChC,CAAC,GACG,CAAC,KAAK,OAAO;GACf,KAAK,iBAAiB,KAAK;GAC3B;EACF;EACA,IAAM,IAAS,KAAK,SAAS,MAAK,MAAK,EAAE,UAAU,KAAK,KAAK;EAC7D,KAAK,iBAAiB,GAAQ,SAAS,KAAK;CAC9C;CAYA,kBAA0B;EACxB,IAAM,IAAe,KAAK,SAAS,KAAK,eAGlC,IAAO,KAAK,cAAc,QAAQ,KAAK,UAAU,CAAY;EAInE,AAHA,KAAK,WAAW,EAAK,SACrB,KAAK,eAAe,EAAK,aAEzB,KAAK,cAAc,gBAAgB,KAAK,QAAQ;CAClD;CAqCA,QAAgB,GAA8B;EAC5C,IAAM,IAAQ,KAAK,SAAS,KAAK,OAAO,SAAS,IAAI;EACrD,OAAO,EAAO,gBAAgB,KAAK,SAAS,CAAM,KAAM,CAAC,CAAC,KAAS,EAAM,SAAS,CAAM;CAC1F;CAmCA,gBAAwB,GAAe;EACrC,IAAM,IAAU,KAAK,SAAS,QAAO,MAAK,CAAC,EAAE,YAAY,KAAK,QAAQ,CAAC,CAAC;EACxE,IAAI,CAAC,EAAQ,QAAQ;EAErB,IAAM,IAAiB,KAAK,SAAS,MACnC,MAAU,EAAO,UAAU,KAAK,SAAS,CAAC,EAAO,YAAY,KAAK,QAAQ,CAAM,CAClF,GACM,IACJ,KAAK,gBAAgB,KAAK,KAAK,QAAQ,KAAK,SAAS,KAAK,aAAa,IACnE,EAAQ,QAAQ,KAAK,SAAS,KAAK,aAAa,IAChD,IACE,EAAQ,QAAQ,CAAc,IAC9B,IAAQ,IACN,KACA,GACJ,IAAa,EAAQ,EAAkB,EAAQ,QAAQ,GAAY,CAAK;EAG9E,AAFA,KAAK,eAAe,KAAK,SAAS,QAAQ,CAAU,GAEpD,KAAK,kBAAkB;CACzB;CA0BA,MAAc,IAAuB,IAAO;EAC1C,KAAK,gBAAgB,KAAK,CAAoB;CAChD;CAEA,SAAiB;EACf,KAAK,gBAAgB,MAAM;CAC7B;CAEA,sBAA8B;EAC5B,EAAwB,MAAM,KAAK,OAAO;CAC5C;CAEA,0BAAkC;EAChC,IAAM,IAAgB,KAAK,SAAS,WAClC,MAAU,EAAO,UAAU,KAAK,SAAS,CAAC,EAAO,YAAY,KAAK,QAAQ,CAAM,CAClF,GACM,IAAoB,KAAK,SAAS,WAAU,MAAU,CAAC,EAAO,YAAY,KAAK,QAAQ,CAAM,CAAC;EAEpG,AADA,KAAK,eAAe,KAAiB,IAAI,IAAgB,GACzD,KAAK,kBAAkB;CACzB;CAEA,oBAA4B;EAC1B,KAAK,SAAS,SAAS,GAAQ,MAC7B,EAAO,gBAAgB,UAAU,MAAU,KAAK,gBAAgB,KAAK,QAAQ,CAAM,CAAC,CACtF;CACF;CAEA,qBAA6B;EAE3B,AADA,KAAK,cAAc,IAAI,MAAM,SAAS;GAAE,SAAS;GAAM,UAAU;EAAK,CAAC,CAAC,GACxE,KAAK,cAAc,IAAI,MAAM,UAAU;GAAE,SAAS;GAAM,UAAU;EAAK,CAAC,CAAC;CAC3E;CAEA,gBAAwB,IAAS,KAAK,SAAS;EAC7C,KAAK,YAAY,KAAK,KAAU,CAAC,KAAK,YAAY;CACpD;CAEA,eAAuB;EACjB,KAAK,gBACL,KAAK,UAAS,KAAK,OAAO,IACzB,KAAK,MAAM;CAClB;CAEA,aAAqB,IAAuB,IAAO;EAGjD,AAFA,KAAK,OAAO,KAAK,CAAoB,GAErC,KAAK,kBAAkB;CACzB;CAEA,gBAAuC;EACrC,IAAM,IAAS,EAAoB,CAAC,CAAC,KAAK;GACxC,WAAW,KAAK;GAChB,QAAQ;GACR,OAAO,GAAG,EAAM,IAAI,EAAc,IAAI;GACtC,WAAW;GACX,uBAAuB,KAAK,cAAc,gBAAgB;EAC5D,CAAC;EAOD,AANA,KAAK,UAAU,GACf,EAAsB,EAAO,OAAO,MAAM;GACxC;GACA;GACA;EACF,CAAC,GACD,EAAO,MAAM,aAAa,QAAQ,SAAS;EAC3C,IAAM,IAAS,SAAS,cAAc,KAAK;EAC3C,EAAO,YAAY;EACnB,IAAM,IAAU,SAAS,cAAc,KAAK;EAU5C,OATA,EAAQ,YAAY,kBACpB,KAAK,iBAAiB,GACtB,EAAO,OAAO,CAAO,GACrB,EAAO,MAAM,OAAO,CAAM,GAE1B,EAAO,YACL,MAAM,KAAK,KAAK,QAAQ,CAAC,CAAC,QAAO,MAAS,EAAM,SAAS,SAAS,GAClE,CACF,GACO;CACT;CAEA,sBAA8B;EAU5B,AARA,KAAK,UAAU,KAAA,GACf,KAAK,iBAAiB,KAAA,GACtB,KAAK,OAAO,YAAY,KAAK,OAAO,GAMpC,KAAK,kBAAkB;CACzB;CAEA,MAAc,gBAAgB;EAG5B,AAAI,MAFiB,KAAK,OAAO,YAAY,KAAK,OAAO,MAGvD,KAAK,UAAU,KAAA,GACf,KAAK,iBAAiB,KAAA;CAE1B;CAEA,SAAkB;EAGhB,IAAM,IAAY,GAAG,KAAK,UAAU,WAC9B,IACJ,CAAC,KAAK,UAAU,KAAK,WAAW,KAAK,gBAAgB,IAChD,KAAK,SAAS,KAAK,aAAa,EAAE,MAAM,IACzC;EAEN,OAAO,CAAI;;;;iCAIkB,KAAK,gBAAgB;mBACnC,KAAK,aAAa;qBAChB,KAAK,cAAc,OAAO,IAAI;;0BAEzB,KAAK,QAAQ;;0BAEb,KAAK,SAAS,IAAU,EAAU;0BAClC,OAAO,KAAK,WAAW,EAAE;kCACjB,EAAiB;;6CAEN,KAAK,qBAAqB;YAC1D,KAAK,kBAA4E,IAA1D,CAAI,uBAAuB,KAAK,eAAe,SAAmB;6CACzD,EAAkB;;4FAE6B,EAAU;;;kCAGpE,KAAK,cAAc;;;;;;CAMnD;AAOF;AApfG,EAAA,CAAA,EAAS;CAAE,MAAM;CAAQ,SAAS;AAAK,CAAC,CAAA,GAAA,EAAA,WAAA,eAAA,KAAA,CAAA,GACxC,EAAA,CAAA,EAAS;CAAE,MAAM;CAAS,SAAS;AAAK,CAAC,CAAA,GAAA,EAAA,WAAA,YAAA,KAAA,CAAA,GACzC,EAAA,CAAA,EAAS;CAAE,MAAM;CAAS,SAAS;AAAK,CAAC,CAAA,GAAA,EAAA,WAAA,YAAA,KAAA,CAAA,GACzC,EAAA,CAAA,EAAS;CAAE,MAAM;CAAS,SAAS;AAAK,CAAC,CAAA,GAAA,EAAA,WAAA,UAAA,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,EAAE,WAAW,GAAM,CAAC,CAAA,GAAA,EAAA,WAAA,oBAAA,KAAA,CAAA,GAC7B,EAAA,CAAA,EAAS;CAAE,MAAM;CAAQ,SAAS;AAAK,CAAC,CAAA,GAAA,EAAA,WAAA,QAAA,KAAA,CAAA,GAIxC,EAAA,CAAA,EAAM,CAAA,GAAA,EAAA,WAAA,UAAA,KAAA,CAAA,GACN,EAAA,CAAA,EAAS;CAAE,MAAM;CAAQ,SAAS;AAAK,CAAC,CAAA,GAAA,EAAA,WAAA,SAAA,IAAA,GAwCxC,EAAA,CAAA,EAAM,CAAA,GAAA,EAAA,WAAA,WAAA,KAAA,CAAA,GACN,EAAA,CAAA,EAAM,CAAA,GAAA,EAAA,WAAA,gBAAA,KAAA,CAAA,GACN,EAAA,CAAA,EAAM,CAAA,GAAA,EAAA,WAAA,kBAAA,KAAA,CAAA,GACN,EAAA,CAAA,EAAM,CAAA,GAAA,EAAA,WAAA,mBAAA,KAAA,CAAA,GA1DR,IAAA,IAAA,EAAA,CAAA,EAAc,eAAe,CAAA,GAAA,CAAA"}
@@ -13,7 +13,7 @@ function u(e, t) {
13
13
  let n = Array.from(t.children).find((t) => t instanceof HTMLElement && t.dataset.wuiToastPosition === e);
14
14
  if (n) return n;
15
15
  let r = document.createElement("div");
16
- return r.className = `wui-toast-container wui-toast-${e}`, r.dataset.wuiToastPosition = e, r.setAttribute("role", "log"), r.setAttribute("aria-live", "polite"), r.setAttribute("aria-relevant", "additions"), t.appendChild(r), i.add(r), r.addEventListener("pointerenter", () => r.dataset.hovered = "true"), r.addEventListener("pointerleave", () => {
16
+ return r.className = `wui-toast-container wui-toast-${e} wui-scroll`, r.dataset.wuiToastPosition = e, r.setAttribute("role", "log"), r.setAttribute("aria-live", "polite"), r.setAttribute("aria-relevant", "additions"), t.appendChild(r), i.add(r), r.addEventListener("pointerenter", () => r.dataset.hovered = "true"), r.addEventListener("pointerleave", () => {
17
17
  delete r.dataset.hovered, f(r);
18
18
  }), r;
19
19
  }
@@ -1 +1 @@
1
- {"version":3,"file":"manager.js","names":[],"sources":["../../../src/components/toast/manager.ts"],"sourcesContent":["import { getFallbackOverlayRoot } from '@/shared/overlay/overlay-root'\nimport { findNearestTheme, findRootTheme } from '@/shared/overlay/theme-overlay-scope'\n\nimport type { WebUiToast } from './toast'\nimport type { ToastInstanceOptions, ToastOptions, ToastPosition } from './types'\n\nlet toastIdCounter = 0\n\nconst toastContainers = new Set<HTMLElement>()\nconst visibleToasts = new Map<string, WebUiToast>()\n\nlet pendingBatch: Array<{ id: string; options: ToastInstanceOptions; root: HTMLElement }> | undefined\nlet isBatchScheduled = false\n\ntype ToastShortcutOptions = Omit<ToastOptions, 'message' | 'type'> & Pick<ToastInstanceOptions, 'position'>\n\n// moveBefore 尚未进入 Baseline,DOM lib 未必声明;按能力探测调用,缺失时走降级分支。\ntype MaybeMoveBefore = HTMLElement & { moveBefore?: (node: Node, child: Node | null) => void }\n\nfunction generateId(): string {\n return `toast-${++toastIdCounter}`\n}\n\nfunction resolveRoot(options: ToastInstanceOptions): HTMLElement {\n const targetTheme = options.target ? findNearestTheme(options.target) : findRootTheme()\n return options.container ?? targetTheme?.getOverlayRoot() ?? getFallbackOverlayRoot()\n}\n\nfunction ensureContainer(position: ToastPosition, root: HTMLElement): HTMLElement {\n const existing = Array.from(root.children).find(\n (child): child is HTMLElement => child instanceof HTMLElement && child.dataset.wuiToastPosition === position\n )\n if (existing) return existing\n\n const container = document.createElement('div')\n container.className = `wui-toast-container wui-toast-${position}`\n container.dataset.wuiToastPosition = position\n container.setAttribute('role', 'log')\n container.setAttribute('aria-live', 'polite')\n container.setAttribute('aria-relevant', 'additions')\n root.appendChild(container)\n toastContainers.add(container)\n\n container.addEventListener('pointerenter', () => (container.dataset.hovered = 'true'))\n container.addEventListener('pointerleave', () => {\n delete container.dataset.hovered\n scrollToBottom(container)\n })\n return container\n}\n\nfunction pruneContainer(container: HTMLElement | null) {\n if (!container || container.children.length > 0) return\n container.remove()\n toastContainers.delete(container)\n}\n\nfunction scrollToBottom(container: HTMLElement) {\n if (container.dataset.hovered === 'true') return\n requestAnimationFrame(() => {\n container.scrollTop = container.scrollHeight\n })\n}\n\nfunction scheduleBatchFlush() {\n if (isBatchScheduled) return\n isBatchScheduled = true\n void Promise.resolve().then(flushBatch)\n}\n\nfunction flushBatch() {\n isBatchScheduled = false\n const batch = pendingBatch\n pendingBatch = undefined\n if (!batch) return\n\n const containersToScroll = new Set<HTMLElement>()\n for (const item of batch) {\n // 容器在挂载时才解析:待挂载条目被 patch 改了 position 也能落到正确容器。\n const container = ensureContainer(item.options.position || 'top-right', item.root)\n mountToast(item.id, item.options, container)\n containersToScroll.add(container)\n }\n for (const container of containersToScroll) scrollToBottom(container)\n}\n\n/**\n * 命令式 API 的 upsert 语义:指定同一个 id 的多次调用收敛到同一条 toast。\n * 给出的字段覆盖,未给出的保持原值;`container` 与 `target` 以首次为准,换 overlay root 牵连更广。\n */\nfunction mergePatch(base: ToastInstanceOptions, patch: ToastInstanceOptions): ToastInstanceOptions {\n return {\n ...base,\n message: patch.message ?? base.message,\n heading: patch.heading ?? base.heading,\n type: patch.type ?? base.type,\n duration: patch.duration ?? base.duration,\n closable: patch.closable ?? base.closable,\n position: patch.position ?? base.position,\n container: base.container,\n target: base.target\n }\n}\n\nfunction applyPatch(el: WebUiToast, options: ToastInstanceOptions) {\n if (options.message !== undefined) el.message = options.message\n if (options.heading !== undefined) el.heading = options.heading\n if (options.type !== undefined) el.type = options.type\n if (options.closable !== undefined) el.noCloseButton = options.closable === false\n // duration 只有显式传入才重启计时;message/heading/type 都是 Lit 属性,赋值不碰 _closeTimer。\n // 悬停暂停期间交给元素自己裁决:setDuration() 只记新值不点火,否则鼠标还压在 toast 上\n // 时倒计时就被重启,「悬停暂停」在这条路径上会静默失效。\n if (options.duration !== undefined) el.setDuration(options.duration)\n if (options.position !== undefined && options.position !== el.position) relocateToast(el, options.position)\n}\n\nfunction relocateToast(el: WebUiToast, position: ToastPosition) {\n const prev = el.parentElement\n const root = prev?.parentElement\n if (!root) return\n const next = ensureContainer(position, root)\n\n const movable = next as MaybeMoveBefore\n if (typeof movable.moveBefore === 'function') {\n // 不摘出节点,disconnected/connected 都不触发:计时、焦点与动画状态原样保留。\n movable.moveBefore(el, null)\n } else {\n el.pauseAutoClose()\n next.appendChild(el)\n el.resumeAutoClose()\n }\n\n el.position = position\n pruneContainer(prev)\n scrollToBottom(next)\n}\n\nfunction createToast(options: ToastInstanceOptions): string {\n const id = options.id || generateId()\n\n const mounted = visibleToasts.get(id)\n if (mounted) {\n /*\n * 「已挂载但 show() 还没跑」的窗口(mountToast 之后、rAF 里的 show() 之前)仍复用同一条元素;\n * 正在退场、或被宿主摘出 DOM 的元素则不再接受更新。后者的补丁只会落在一条即将(或已经)\n * 离开文档的元素上:调用方拿到同一个 id,却没有任何可见通知,`relocateToast` 也会因\n * 找不到 root 而静默吞掉 position。因此摘掉映射、改走新建路径。\n * 退场元素仍在 DOM 里,由它自己延迟派发的 `toast-close` 收尾(按元素身份解绑,见 removeToastElement)。\n */\n if (mounted.isConnected && !mounted.dismissing) {\n applyPatch(mounted, options)\n return id\n }\n visibleToasts.delete(id)\n }\n\n const pending = pendingBatch?.find(item => item.id === id)\n if (pending) {\n pending.options = mergePatch(pending.options, options)\n return id\n }\n\n if (!pendingBatch) pendingBatch = []\n pendingBatch.push({ id, options, root: resolveRoot(options) })\n scheduleBatchFlush()\n return id\n}\n\nfunction mountToast(id: string, options: ToastInstanceOptions, container: HTMLElement) {\n const el = document.createElement('web-ui-toast')\n const type = options.type || 'info'\n el.toastId = id\n el.type = type\n el.position = options.position || 'top-right'\n el.heading = options.heading || ''\n el.message = options.message\n // error 的 5000 只在挂载时兜底,不写进 options:写进去 applyPatch 就会把它当成「显式传入\n // duration」而每次 upsert 都重启倒计时,与「duration 只有显式传入才重启」的契约冲突。\n el.duration = options.duration ?? (type === 'error' ? 5000 : 3000)\n el.noCloseButton = options.closable === false\n\n el.addEventListener('toast-close', () => removeToastElement(el))\n\n container.appendChild(el)\n visibleToasts.set(id, el)\n requestAnimationFrame(() => {\n // close()/clear() 可能已经在这段窗口里收走了它(见 dismiss 的无退场动画分支):\n // 不再入场,否则一条孤儿元素会在 duration 之后补派一次同 id 的 toast-close。\n if (el.dismissing) return\n el.show()\n })\n}\n\n/**\n * 按元素身份解绑并移除。退场中的旧元素会在稍后派发 `toast-close`,而此刻同 id 可能已经有\n * 新元素(退场窗口内的 upsert 会新建一条);若按 `detail.id` 删映射,就会把刚建好的那条\n * 一起删掉 —— 与「孤儿元素收不走」是同一类缺陷,只是换了个入口。\n */\nfunction removeToastElement(el: WebUiToast) {\n if (visibleToasts.get(el.toastId) === el) visibleToasts.delete(el.toastId)\n const container = el.parentElement\n el.remove()\n pruneContainer(container)\n}\n\n/**\n * id 不在 Map 里时仍要清掉同 id 的残留节点:早先的按 id 删除路径在这里直接 return,\n * 重复挂载留下的孤儿 toast 因此连自关闭、close() 和 clear() 都收不走,会永久留在\n * role=\"log\" 容器里。强删不派发 `toast-close`,但对不可从管理器触达的元素来说没有消费方状态可言。\n */\nfunction removeStray(id: string) {\n for (const container of toastContainers) {\n for (const child of Array.from(container.children)) {\n if (child.tagName !== 'WEB-UI-TOAST') continue\n if ((child as Partial<WebUiToast>).toastId === id) child.remove()\n }\n }\n for (const container of Array.from(toastContainers)) pruneContainer(container)\n}\n\n/**\n * 关闭一个 id 时,「还没开始显示」的两种状态都要能取消,否则 close() 会静默失效:\n *\n * - 同 tick 仍在 `pendingBatch` 里:还不是 DOM 元素,`removeStray` 扫不到 —— 出队即取消挂载。\n * - 已 `flushBatch`、rAF 里的 `show()` 未跑:元素在映射里但 `visible` 仍是 false,\n * `dismiss()` 现在对这段窗口立即派发(无退场动画可播),由 toast-close 监听摘除。\n *\n * 已在退场的元素不必也不该再动:它自己延迟派发的 toast-close 会收尾,重复 close 不改变结果。\n */\nfunction close(id: string) {\n const queued = pendingBatch\n if (queued) {\n const index = queued.findIndex(item => item.id === id)\n if (index >= 0) {\n queued.splice(index, 1)\n return\n }\n }\n\n const el = visibleToasts.get(id)\n if (el) {\n el.dismiss('programmatic')\n return\n }\n removeStray(id)\n}\n\n/**\n * 队列条目与已挂载元素一起取消。二者都会在同一个微任务内落地,只遍历已挂载元素会让\n * 「clear() 之后仍冒出一条」成为唯一的例外 —— 与 close() 的可用范围对齐。\n */\nfunction clear() {\n pendingBatch = undefined\n for (const el of visibleToasts.values()) el.dismiss('clear')\n}\n\nfunction toast(options: ToastInstanceOptions): string {\n return createToast(options)\n}\n\ntoast.success = (message: string, options?: ToastShortcutOptions) =>\n createToast({ ...options, message, type: 'success' })\ntoast.info = (message: string, options?: ToastShortcutOptions) => createToast({ ...options, message, type: 'info' })\ntoast.warning = (message: string, options?: ToastShortcutOptions) =>\n createToast({ ...options, message, type: 'warning' })\n// 不注入 duration:error 的 5000 默认值在 mountToast 兜底,否则 upsert 每次都判定为\n// 「显式传入 duration」而重启倒计时(success/info/warning 都没有这个不对称)。\ntoast.error = (message: string, options?: ToastShortcutOptions) => createToast({ ...options, message, type: 'error' })\ntoast.close = close\ntoast.clear = clear\n\n// 重置全部状态(测试用)\ntoast._reset = () => {\n pendingBatch = undefined\n isBatchScheduled = false\n for (const el of visibleToasts.values()) el.remove()\n visibleToasts.clear()\n for (const container of toastContainers) container.remove()\n toastContainers.clear()\n}\n\nexport { toast }\n"],"mappings":"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"}
1
+ {"version":3,"file":"manager.js","names":[],"sources":["../../../src/components/toast/manager.ts"],"sourcesContent":["import { getFallbackOverlayRoot } from '@/shared/overlay/overlay-root'\nimport { findNearestTheme, findRootTheme } from '@/shared/overlay/theme-overlay-scope'\n\nimport type { WebUiToast } from './toast'\nimport type { ToastInstanceOptions, ToastOptions, ToastPosition } from './types'\n\nlet toastIdCounter = 0\n\nconst toastContainers = new Set<HTMLElement>()\nconst visibleToasts = new Map<string, WebUiToast>()\n\nlet pendingBatch: Array<{ id: string; options: ToastInstanceOptions; root: HTMLElement }> | undefined\nlet isBatchScheduled = false\n\ntype ToastShortcutOptions = Omit<ToastOptions, 'message' | 'type'> & Pick<ToastInstanceOptions, 'position'>\n\n// moveBefore 尚未进入 Baseline,DOM lib 未必声明;按能力探测调用,缺失时走降级分支。\ntype MaybeMoveBefore = HTMLElement & { moveBefore?: (node: Node, child: Node | null) => void }\n\nfunction generateId(): string {\n return `toast-${++toastIdCounter}`\n}\n\nfunction resolveRoot(options: ToastInstanceOptions): HTMLElement {\n const targetTheme = options.target ? findNearestTheme(options.target) : findRootTheme()\n return options.container ?? targetTheme?.getOverlayRoot() ?? getFallbackOverlayRoot()\n}\n\nfunction ensureContainer(position: ToastPosition, root: HTMLElement): HTMLElement {\n const existing = Array.from(root.children).find(\n (child): child is HTMLElement => child instanceof HTMLElement && child.dataset.wuiToastPosition === position\n )\n if (existing) return existing\n\n const container = document.createElement('div')\n container.className = `wui-toast-container wui-toast-${position} wui-scroll`\n container.dataset.wuiToastPosition = position\n container.setAttribute('role', 'log')\n container.setAttribute('aria-live', 'polite')\n container.setAttribute('aria-relevant', 'additions')\n root.appendChild(container)\n toastContainers.add(container)\n\n container.addEventListener('pointerenter', () => (container.dataset.hovered = 'true'))\n container.addEventListener('pointerleave', () => {\n delete container.dataset.hovered\n scrollToBottom(container)\n })\n return container\n}\n\nfunction pruneContainer(container: HTMLElement | null) {\n if (!container || container.children.length > 0) return\n container.remove()\n toastContainers.delete(container)\n}\n\nfunction scrollToBottom(container: HTMLElement) {\n if (container.dataset.hovered === 'true') return\n requestAnimationFrame(() => {\n container.scrollTop = container.scrollHeight\n })\n}\n\nfunction scheduleBatchFlush() {\n if (isBatchScheduled) return\n isBatchScheduled = true\n void Promise.resolve().then(flushBatch)\n}\n\nfunction flushBatch() {\n isBatchScheduled = false\n const batch = pendingBatch\n pendingBatch = undefined\n if (!batch) return\n\n const containersToScroll = new Set<HTMLElement>()\n for (const item of batch) {\n // 容器在挂载时才解析:待挂载条目被 patch 改了 position 也能落到正确容器。\n const container = ensureContainer(item.options.position || 'top-right', item.root)\n mountToast(item.id, item.options, container)\n containersToScroll.add(container)\n }\n for (const container of containersToScroll) scrollToBottom(container)\n}\n\n/**\n * 命令式 API 的 upsert 语义:指定同一个 id 的多次调用收敛到同一条 toast。\n * 给出的字段覆盖,未给出的保持原值;`container` 与 `target` 以首次为准,换 overlay root 牵连更广。\n */\nfunction mergePatch(base: ToastInstanceOptions, patch: ToastInstanceOptions): ToastInstanceOptions {\n return {\n ...base,\n message: patch.message ?? base.message,\n heading: patch.heading ?? base.heading,\n type: patch.type ?? base.type,\n duration: patch.duration ?? base.duration,\n closable: patch.closable ?? base.closable,\n position: patch.position ?? base.position,\n container: base.container,\n target: base.target\n }\n}\n\nfunction applyPatch(el: WebUiToast, options: ToastInstanceOptions) {\n if (options.message !== undefined) el.message = options.message\n if (options.heading !== undefined) el.heading = options.heading\n if (options.type !== undefined) el.type = options.type\n if (options.closable !== undefined) el.noCloseButton = options.closable === false\n // duration 只有显式传入才重启计时;message/heading/type 都是 Lit 属性,赋值不碰 _closeTimer。\n // 悬停暂停期间交给元素自己裁决:setDuration() 只记新值不点火,否则鼠标还压在 toast 上\n // 时倒计时就被重启,「悬停暂停」在这条路径上会静默失效。\n if (options.duration !== undefined) el.setDuration(options.duration)\n if (options.position !== undefined && options.position !== el.position) relocateToast(el, options.position)\n}\n\nfunction relocateToast(el: WebUiToast, position: ToastPosition) {\n const prev = el.parentElement\n const root = prev?.parentElement\n if (!root) return\n const next = ensureContainer(position, root)\n\n const movable = next as MaybeMoveBefore\n if (typeof movable.moveBefore === 'function') {\n // 不摘出节点,disconnected/connected 都不触发:计时、焦点与动画状态原样保留。\n movable.moveBefore(el, null)\n } else {\n el.pauseAutoClose()\n next.appendChild(el)\n el.resumeAutoClose()\n }\n\n el.position = position\n pruneContainer(prev)\n scrollToBottom(next)\n}\n\nfunction createToast(options: ToastInstanceOptions): string {\n const id = options.id || generateId()\n\n const mounted = visibleToasts.get(id)\n if (mounted) {\n /*\n * 「已挂载但 show() 还没跑」的窗口(mountToast 之后、rAF 里的 show() 之前)仍复用同一条元素;\n * 正在退场、或被宿主摘出 DOM 的元素则不再接受更新。后者的补丁只会落在一条即将(或已经)\n * 离开文档的元素上:调用方拿到同一个 id,却没有任何可见通知,`relocateToast` 也会因\n * 找不到 root 而静默吞掉 position。因此摘掉映射、改走新建路径。\n * 退场元素仍在 DOM 里,由它自己延迟派发的 `toast-close` 收尾(按元素身份解绑,见 removeToastElement)。\n */\n if (mounted.isConnected && !mounted.dismissing) {\n applyPatch(mounted, options)\n return id\n }\n visibleToasts.delete(id)\n }\n\n const pending = pendingBatch?.find(item => item.id === id)\n if (pending) {\n pending.options = mergePatch(pending.options, options)\n return id\n }\n\n if (!pendingBatch) pendingBatch = []\n pendingBatch.push({ id, options, root: resolveRoot(options) })\n scheduleBatchFlush()\n return id\n}\n\nfunction mountToast(id: string, options: ToastInstanceOptions, container: HTMLElement) {\n const el = document.createElement('web-ui-toast')\n const type = options.type || 'info'\n el.toastId = id\n el.type = type\n el.position = options.position || 'top-right'\n el.heading = options.heading || ''\n el.message = options.message\n // error 的 5000 只在挂载时兜底,不写进 options:写进去 applyPatch 就会把它当成「显式传入\n // duration」而每次 upsert 都重启倒计时,与「duration 只有显式传入才重启」的契约冲突。\n el.duration = options.duration ?? (type === 'error' ? 5000 : 3000)\n el.noCloseButton = options.closable === false\n\n el.addEventListener('toast-close', () => removeToastElement(el))\n\n container.appendChild(el)\n visibleToasts.set(id, el)\n requestAnimationFrame(() => {\n // close()/clear() 可能已经在这段窗口里收走了它(见 dismiss 的无退场动画分支):\n // 不再入场,否则一条孤儿元素会在 duration 之后补派一次同 id 的 toast-close。\n if (el.dismissing) return\n el.show()\n })\n}\n\n/**\n * 按元素身份解绑并移除。退场中的旧元素会在稍后派发 `toast-close`,而此刻同 id 可能已经有\n * 新元素(退场窗口内的 upsert 会新建一条);若按 `detail.id` 删映射,就会把刚建好的那条\n * 一起删掉 —— 与「孤儿元素收不走」是同一类缺陷,只是换了个入口。\n */\nfunction removeToastElement(el: WebUiToast) {\n if (visibleToasts.get(el.toastId) === el) visibleToasts.delete(el.toastId)\n const container = el.parentElement\n el.remove()\n pruneContainer(container)\n}\n\n/**\n * id 不在 Map 里时仍要清掉同 id 的残留节点:早先的按 id 删除路径在这里直接 return,\n * 重复挂载留下的孤儿 toast 因此连自关闭、close() 和 clear() 都收不走,会永久留在\n * role=\"log\" 容器里。强删不派发 `toast-close`,但对不可从管理器触达的元素来说没有消费方状态可言。\n */\nfunction removeStray(id: string) {\n for (const container of toastContainers) {\n for (const child of Array.from(container.children)) {\n if (child.tagName !== 'WEB-UI-TOAST') continue\n if ((child as Partial<WebUiToast>).toastId === id) child.remove()\n }\n }\n for (const container of Array.from(toastContainers)) pruneContainer(container)\n}\n\n/**\n * 关闭一个 id 时,「还没开始显示」的两种状态都要能取消,否则 close() 会静默失效:\n *\n * - 同 tick 仍在 `pendingBatch` 里:还不是 DOM 元素,`removeStray` 扫不到 —— 出队即取消挂载。\n * - 已 `flushBatch`、rAF 里的 `show()` 未跑:元素在映射里但 `visible` 仍是 false,\n * `dismiss()` 现在对这段窗口立即派发(无退场动画可播),由 toast-close 监听摘除。\n *\n * 已在退场的元素不必也不该再动:它自己延迟派发的 toast-close 会收尾,重复 close 不改变结果。\n */\nfunction close(id: string) {\n const queued = pendingBatch\n if (queued) {\n const index = queued.findIndex(item => item.id === id)\n if (index >= 0) {\n queued.splice(index, 1)\n return\n }\n }\n\n const el = visibleToasts.get(id)\n if (el) {\n el.dismiss('programmatic')\n return\n }\n removeStray(id)\n}\n\n/**\n * 队列条目与已挂载元素一起取消。二者都会在同一个微任务内落地,只遍历已挂载元素会让\n * 「clear() 之后仍冒出一条」成为唯一的例外 —— 与 close() 的可用范围对齐。\n */\nfunction clear() {\n pendingBatch = undefined\n for (const el of visibleToasts.values()) el.dismiss('clear')\n}\n\nfunction toast(options: ToastInstanceOptions): string {\n return createToast(options)\n}\n\ntoast.success = (message: string, options?: ToastShortcutOptions) =>\n createToast({ ...options, message, type: 'success' })\ntoast.info = (message: string, options?: ToastShortcutOptions) => createToast({ ...options, message, type: 'info' })\ntoast.warning = (message: string, options?: ToastShortcutOptions) =>\n createToast({ ...options, message, type: 'warning' })\n// 不注入 duration:error 的 5000 默认值在 mountToast 兜底,否则 upsert 每次都判定为\n// 「显式传入 duration」而重启倒计时(success/info/warning 都没有这个不对称)。\ntoast.error = (message: string, options?: ToastShortcutOptions) => createToast({ ...options, message, type: 'error' })\ntoast.close = close\ntoast.clear = clear\n\n// 重置全部状态(测试用)\ntoast._reset = () => {\n pendingBatch = undefined\n isBatchScheduled = false\n for (const el of visibleToasts.values()) el.remove()\n visibleToasts.clear()\n for (const container of toastContainers) container.remove()\n toastContainers.clear()\n}\n\nexport { toast }\n"],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ //#region src/scrollbars/internal.css?inline
2
+ var e = ".wui-scroll{scrollbar-color:var(--wui-scrollbar-handle,#00000063) transparent;scrollbar-width:thin}";
3
+ //#endregion
4
+ export { e as default };
5
+
6
+ //# sourceMappingURL=internal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"internal.js","names":[],"sources":["../../src/scrollbars/internal.css?inline"],"sourcesContent":["/*\n * 组件内部滚动容器的原生滚动条主题。\n *\n * 为什么不是直接上 OverlayScrollbars:库的结构样式挂在 `.os-scrollbar` 上、主题类\n * `.os-theme-macos` 也靠文档层的选择器命中,两者都进不了组件的 shadow——shadow 里既匹配不到\n * `web-ui-theme[...]` 这类后代选择器,也匹配不到 `html:has()`。库还会把宿主改成 flex 行、把\n * 子节点搬进它生成的 viewport,会破坏组件自身承重的布局。唯一能跨 shadow 边界的是继承的\n * 自定义属性,所以这里只**读**主题里同一组 `--wui-scrollbar-*`(由 `web-ui-theme` 的外观分支\n * 写、`.os-theme-macos` 的回退值兜底),颜色因此与整页的覆盖式滚动条同源。\n *\n * 刻意**不写 `::-webkit-scrollbar`**:一旦给某条滚动条声明了自定义 webkit 伪元素,引擎就会\n * 放弃平台自带的覆盖式滚动条,改用经典滚动条并固定占一条布局宽度。实测(macOS Chromium,一条\n * `width: 7px` 的 `::-webkit-scrollbar`):同尺寸容器的 `offsetWidth - clientWidth` 从 0 变成 7。\n * 那正好违反 issue #225 的第 (1) 条「不占用宽度」。`scrollbar-color` / `scrollbar-width` 不触发\n * 这个切换:覆盖式平台上滚动条保持零宽覆盖,经典平台上才按它们上色、收窄。\n *\n * 覆盖式平台(macOS、无头 Chromium)上这条规则几乎不可见——原生的覆盖滚动条由系统绘制,并不\n * 看这两个属性;它真正的用处在经典滚动条的平台(Windows / Linux)。想拿到与库逐像素一致的\n * 几何、圆角与自动隐藏只有上库这一条路,那需要把库初始化进每个 shadow root,超出 issue #225\n * 「组件不必引入第三方依赖」的范围。\n *\n * `scrollbar-color` 与 `scrollbar-width` 都是**继承属性**,写一次即可作用于容器自身及其后代;\n * 调用方只在组件自己的 shadow 里 adopted 本文件(见各组件 `static styles`),所以样式天然隔离,\n * 不会漏到文档层,满足第 (3) 条。\n */\n.wui-scroll {\n scrollbar-color: var(--wui-scrollbar-handle, rgb(0 0 0 / 0.39)) transparent;\n scrollbar-width: thin;\n}\n"],"mappings":""}
@@ -7,4 +7,4 @@
7
7
  *
8
8
  * Released under the MIT license.
9
9
  */
10
- .os-size-observer,.os-size-observer-listener{direction:inherit;pointer-events:none;visibility:hidden;box-sizing:border-box;overflow:hidden;scroll-behavior:auto!important}.os-size-observer,.os-size-observer-listener,.os-size-observer-listener-item,.os-size-observer-listener-item-final{writing-mode:horizontal-tb;position:absolute;top:0;left:0}.os-size-observer{z-index:-1;contain:strict;padding:inherit;border:inherit;box-sizing:inherit;flex-flow:row;margin:-133px;display:flex;inset:0;transform:scale(.1)}.os-size-observer:before{content:"";box-sizing:inherit;flex:none;width:10px;height:10px;padding:10px}.os-size-observer-appear{animation:1ms forwards os-size-observer-appear-animation}.os-size-observer-listener{box-sizing:border-box;padding:inherit;border:inherit;flex:auto;margin:-133px;position:relative;transform:scale(10)}.os-size-observer-listener.ltr{margin-left:0;margin-right:-266px}.os-size-observer-listener.rtl{margin-left:-266px;margin-right:0}.os-size-observer-listener:empty:before{content:"";width:100%;height:100%}.os-size-observer-listener:empty:before,.os-size-observer-listener>.os-size-observer-listener-item{padding:inherit;border:inherit;box-sizing:content-box;flex:auto;display:block;position:relative}.os-size-observer-listener-scroll{box-sizing:border-box;display:flex}.os-size-observer-listener-item{direction:ltr;flex:none;bottom:0;right:0;overflow:hidden}.os-size-observer-listener-item-final{transition:none}@keyframes os-size-observer-appear-animation{0%{cursor:auto}to{cursor:none}}.os-trinsic-observer{box-sizing:border-box;z-index:-1;contain:strict;border:none;flex:none;max-width:0;height:0;max-height:1px;margin:0;padding:0;position:relative;top:calc(100% + 1px);overflow:hidden}.os-trinsic-observer:not(:empty){height:calc(100% + 1px);top:-1px}.os-trinsic-observer:not(:empty)>.os-size-observer{width:1000%;min-width:1px;height:1000%;min-height:1px}html[data-overlayscrollbars-body]{overflow:hidden}html[data-overlayscrollbars-body],html[data-overlayscrollbars-body]>body{width:100%;height:100%;margin:0}html[data-overlayscrollbars-body]>body{margin:0;overflow:visible}[data-overlayscrollbars]{position:relative}[data-overlayscrollbars~=host],[data-overlayscrollbars-padding]{display:flex;scroll-behavior:auto!important;flex-flow:row!important;align-items:stretch!important}[data-overlayscrollbars-padding],[data-overlayscrollbars-viewport]:not([data-overlayscrollbars]){box-sizing:inherit;z-index:0;border:none;flex:auto;width:100%;min-width:0;height:auto;margin:0;padding:0;position:relative}[data-overlayscrollbars-viewport]:not([data-overlayscrollbars]){--os-vaw:0;--os-vah:0;outline:none}[data-overlayscrollbars-viewport]:not([data-overlayscrollbars]):focus{outline:none}[data-overlayscrollbars-viewport][data-overlayscrollbars-viewport~=arrange]:before{content:"";pointer-events:none;z-index:-1;min-width:1px;min-height:1px;width:var(--os-vaw);height:var(--os-vah);position:absolute}[data-overlayscrollbars~=host],[data-overlayscrollbars-padding]{overflow:hidden!important}[data-overlayscrollbars~=host][data-overlayscrollbars~=noClipping],[data-overlayscrollbars-padding~=noClipping]{overflow:visible!important}[data-overlayscrollbars-viewport]{--os-viewport-overflow-x:hidden;--os-viewport-overflow-y:hidden;overflow-x:var(--os-viewport-overflow-x);overflow-y:var(--os-viewport-overflow-y)}[data-overlayscrollbars-viewport~=overflowXVisible]{--os-viewport-overflow-x:visible}[data-overlayscrollbars-viewport~=overflowXHidden]{--os-viewport-overflow-x:hidden}[data-overlayscrollbars-viewport~=overflowXScroll]{--os-viewport-overflow-x:scroll}[data-overlayscrollbars-viewport~=overflowYVisible]{--os-viewport-overflow-y:visible}[data-overlayscrollbars-viewport~=overflowYHidden]{--os-viewport-overflow-y:hidden}[data-overlayscrollbars-viewport~=overflowYScroll]{--os-viewport-overflow-y:scroll}[data-overlayscrollbars-viewport~=overflowImportant]{overflow-x:var(--os-viewport-overflow-x)!important;overflow-y:var(--os-viewport-overflow-y)!important}[data-overlayscrollbars-viewport~=noContent]:not(#osFakeId){font-size:0!important;line-height:0!important}[data-overlayscrollbars-viewport~=noContent]:not(#osFakeId):before,[data-overlayscrollbars-viewport~=noContent]:not(#osFakeId):after,[data-overlayscrollbars-viewport~=noContent]:not(#osFakeId)>:not(#osFakeId){clip:rect(0, 0, 0, 0)!important;white-space:nowrap!important;border-width:0!important;width:1px!important;height:1px!important;margin:-1px!important;padding:0!important;display:none!important;position:absolute!important;overflow:hidden!important}[data-overlayscrollbars-viewport~=measuring],[data-overlayscrollbars-viewport~=scrolling]{scroll-behavior:auto!important;scroll-snap-type:none!important}[data-overlayscrollbars-viewport~=measuring][data-overlayscrollbars-viewport~=overflowXVisible]{overflow-x:hidden!important}[data-overlayscrollbars-viewport~=measuring][data-overlayscrollbars-viewport~=overflowYVisible]{overflow-y:hidden!important}[data-overlayscrollbars-content]{box-sizing:inherit}[data-overlayscrollbars-contents]:not(#osFakeId):not([data-overlayscrollbars-padding]):not([data-overlayscrollbars-viewport]):not([data-overlayscrollbars-content]){display:contents}[data-overlayscrollbars-grid],[data-overlayscrollbars-grid] [data-overlayscrollbars-padding]{grid-template:1fr/1fr;display:grid}[data-overlayscrollbars-grid]>[data-overlayscrollbars-padding],[data-overlayscrollbars-grid]>[data-overlayscrollbars-viewport],[data-overlayscrollbars-grid]>[data-overlayscrollbars-padding]>[data-overlayscrollbars-viewport]{width:auto!important;height:auto!important}@property --os-scroll-percent{syntax:"<number>";inherits:true;initial-value:0}@property --os-viewport-percent{syntax:"<number>";inherits:true;initial-value:0}.os-scrollbar{--os-viewport-percent:0;--os-scroll-percent:0;--os-scroll-direction:0;--os-scroll-percent-directional:calc(var(--os-scroll-percent) - (var(--os-scroll-percent) + (1 - var(--os-scroll-percent)) * -1) * var(--os-scroll-direction));contain:size layout;contain:size layout style;pointer-events:none;opacity:0;visibility:hidden;transition:opacity .15s,visibility .15s,top .15s,right .15s,bottom .15s,left .15s;position:absolute}body>.os-scrollbar{z-index:99999;position:fixed}.os-scrollbar-transitionless{transition:none!important}.os-scrollbar-track{position:relative;border:none!important;padding:0!important}.os-scrollbar-handle{position:absolute}.os-scrollbar-track,.os-scrollbar-handle{pointer-events:none;width:100%;height:100%}.os-scrollbar.os-scrollbar-track-interactive .os-scrollbar-track,.os-scrollbar.os-scrollbar-handle-interactive .os-scrollbar-handle{pointer-events:auto;touch-action:none}.os-scrollbar-horizontal{bottom:0;left:0}.os-scrollbar-vertical{top:0;right:0}.os-scrollbar-rtl.os-scrollbar-horizontal{right:0}.os-scrollbar-rtl.os-scrollbar-vertical{left:0;right:auto}.os-scrollbar-visible{opacity:1;visibility:visible}.os-scrollbar-auto-hide.os-scrollbar-auto-hide-hidden{opacity:0;visibility:hidden}.os-scrollbar-interaction.os-scrollbar-visible{opacity:1;visibility:visible}.os-scrollbar-unusable,.os-scrollbar-unusable *,.os-scrollbar-wheel,.os-scrollbar-wheel *{pointer-events:none!important}.os-scrollbar-unusable .os-scrollbar-handle{opacity:0!important;transition:none!important}.os-scrollbar-horizontal .os-scrollbar-handle{bottom:0;left:calc(var(--os-scroll-percent-directional) * 100%);transform:translateX(calc(var(--os-scroll-percent-directional) * -100%));width:calc(var(--os-viewport-percent) * 100%)}.os-scrollbar-vertical .os-scrollbar-handle{right:0;top:calc(var(--os-scroll-percent-directional) * 100%);transform:translateY(calc(var(--os-scroll-percent-directional) * -100%));height:calc(var(--os-viewport-percent) * 100%)}@supports (container-type:size){.os-scrollbar-track{container-type:size}.os-scrollbar-horizontal .os-scrollbar-handle{transform:translateX(calc(var(--os-scroll-percent-directional) * 100cqw + var(--os-scroll-percent-directional) * -100%));left:auto}.os-scrollbar-vertical .os-scrollbar-handle{transform:translateY(calc(var(--os-scroll-percent-directional) * 100cqh + var(--os-scroll-percent-directional) * -100%));top:auto}.os-scrollbar-rtl.os-scrollbar-horizontal .os-scrollbar-handle{left:0;right:auto}}.os-scrollbar-rtl.os-scrollbar-vertical .os-scrollbar-handle{left:0;right:auto}.os-scrollbar.os-scrollbar-horizontal.os-scrollbar-cornerless,.os-scrollbar.os-scrollbar-horizontal.os-scrollbar-cornerless.os-scrollbar-rtl{left:0;right:0}.os-scrollbar.os-scrollbar-vertical.os-scrollbar-cornerless,.os-scrollbar.os-scrollbar-vertical.os-scrollbar-cornerless.os-scrollbar-rtl{top:0;bottom:0}@media print{.os-scrollbar{display:none}}.os-scrollbar{--os-size:0;--os-padding-perpendicular:0;--os-padding-axis:0;--os-track-border-radius:0;--os-track-bg:none;--os-track-bg-hover:none;--os-track-bg-active:none;--os-track-border:none;--os-track-border-hover:none;--os-track-border-active:none;--os-handle-border-radius:0;--os-handle-bg:none;--os-handle-bg-hover:none;--os-handle-bg-active:none;--os-handle-border:none;--os-handle-border-hover:none;--os-handle-border-active:none;--os-handle-min-size:33px;--os-handle-max-size:none;--os-handle-perpendicular-size:100%;--os-handle-perpendicular-size-hover:100%;--os-handle-perpendicular-size-active:100%;--os-handle-interactive-area-offset:0}.os-scrollbar-track{border:var(--os-track-border);border-radius:var(--os-track-border-radius);background:var(--os-track-bg);transition:opacity .15s,background-color .15s,border-color .15s}.os-scrollbar-track:hover{border:var(--os-track-border-hover);background:var(--os-track-bg-hover)}.os-scrollbar-track:active{border:var(--os-track-border-active);background:var(--os-track-bg-active)}.os-scrollbar-handle{border:var(--os-handle-border);border-radius:var(--os-handle-border-radius);background:var(--os-handle-bg)}.os-scrollbar-handle:hover{border:var(--os-handle-border-hover);background:var(--os-handle-bg-hover)}.os-scrollbar-handle:active{border:var(--os-handle-border-active);background:var(--os-handle-bg-active)}.os-scrollbar-track:before,.os-scrollbar-handle:before{content:"";display:block;position:absolute;inset:0}.os-scrollbar-horizontal{padding:var(--os-padding-perpendicular) var(--os-padding-axis);right:var(--os-size);height:var(--os-size)}.os-scrollbar-horizontal.os-scrollbar-rtl{left:var(--os-size);right:0}.os-scrollbar-horizontal .os-scrollbar-track:before{top:calc(var(--os-padding-perpendicular) * -1);bottom:calc(var(--os-padding-perpendicular) * -1)}.os-scrollbar-horizontal .os-scrollbar-handle{min-width:var(--os-handle-min-size);max-width:var(--os-handle-max-size);height:var(--os-handle-perpendicular-size);transition:opacity .15s,background-color .15s,border-color .15s,height .15s}.os-scrollbar-horizontal .os-scrollbar-handle:before{top:calc((var(--os-padding-perpendicular) + var(--os-handle-interactive-area-offset)) * -1);bottom:calc(var(--os-padding-perpendicular) * -1)}.os-scrollbar-horizontal:hover .os-scrollbar-handle{height:var(--os-handle-perpendicular-size-hover)}.os-scrollbar-horizontal:active .os-scrollbar-handle{height:var(--os-handle-perpendicular-size-active)}.os-scrollbar-vertical{padding:var(--os-padding-axis) var(--os-padding-perpendicular);bottom:var(--os-size);width:var(--os-size)}.os-scrollbar-vertical .os-scrollbar-track:before{left:calc(var(--os-padding-perpendicular) * -1);right:calc(var(--os-padding-perpendicular) * -1)}.os-scrollbar-vertical .os-scrollbar-handle{min-height:var(--os-handle-min-size);max-height:var(--os-handle-max-size);width:var(--os-handle-perpendicular-size);transition:opacity .15s,background-color .15s,border-color .15s,width .15s}.os-scrollbar-vertical .os-scrollbar-handle:before{left:calc((var(--os-padding-perpendicular) + var(--os-handle-interactive-area-offset)) * -1);right:calc(var(--os-padding-perpendicular) * -1)}.os-scrollbar-vertical.os-scrollbar-rtl .os-scrollbar-handle:before{right:calc((var(--os-padding-perpendicular) + var(--os-handle-interactive-area-offset)) * -1);left:calc(var(--os-padding-perpendicular) * -1)}.os-scrollbar-vertical:hover .os-scrollbar-handle{width:var(--os-handle-perpendicular-size-hover)}.os-scrollbar-vertical:active .os-scrollbar-handle{width:var(--os-handle-perpendicular-size-active)}[data-overlayscrollbars-viewport~=measuring]>.os-scrollbar,.os-theme-none.os-scrollbar{display:none!important}.os-theme-dark,.os-theme-light{box-sizing:border-box;--os-size:10px;--os-padding-perpendicular:2px;--os-padding-axis:2px;--os-track-border-radius:10px;--os-handle-interactive-area-offset:4px;--os-handle-border-radius:10px}.os-theme-dark{--os-handle-bg:#00000070;--os-handle-bg-hover:#0000008c;--os-handle-bg-active:#000000a8}.os-theme-light{--os-handle-bg:#ffffff70;--os-handle-bg-hover:#ffffff8c;--os-handle-bg-active:#ffffffa8}[data-overlayscrollbars-initialize]:not([data-overlayscrollbars-viewport]),[data-overlayscrollbars-viewport~=scrollbarHidden],html[data-overlayscrollbars-viewport~=scrollbarHidden]>body{scrollbar-width:none!important}[data-overlayscrollbars-initialize]:not([data-overlayscrollbars-viewport])::-webkit-scrollbar{appearance:none!important;width:0!important;height:0!important;display:none!important}[data-overlayscrollbars-initialize]:not([data-overlayscrollbars-viewport])::-webkit-scrollbar-corner{appearance:none!important;width:0!important;height:0!important;display:none!important}[data-overlayscrollbars-viewport~=scrollbarHidden]::-webkit-scrollbar{appearance:none!important;width:0!important;height:0!important;display:none!important}[data-overlayscrollbars-viewport~=scrollbarHidden]::-webkit-scrollbar-corner{appearance:none!important;width:0!important;height:0!important;display:none!important}html[data-overlayscrollbars-viewport~=scrollbarHidden]>body::-webkit-scrollbar{appearance:none!important;width:0!important;height:0!important;display:none!important}html[data-overlayscrollbars-viewport~=scrollbarHidden]>body::-webkit-scrollbar-corner{appearance:none!important;width:0!important;height:0!important;display:none!important}[data-overlayscrollbars-initialize]:not([data-overlayscrollbars]):not(html):not(body){overflow:auto}.os-theme-macos{--os-size:13px;--os-padding-perpendicular:3px;--os-padding-axis:2px;--os-track-border-radius:100px;--os-handle-border-radius:100px;--os-handle-interactive-area-offset:4px;--os-track-bg:none;--os-track-bg-hover:none;--os-track-bg-active:none;--os-handle-bg:var(--wui-scrollbar-handle,#00000080);--os-handle-bg-hover:var(--wui-scrollbar-handle-hover,#0000009e);--os-handle-bg-active:var(--wui-scrollbar-handle-active,#000000bd);box-sizing:border-box}web-ui-theme[resolved-appearance=light]{--wui-scrollbar-handle:#00000080;--wui-scrollbar-handle-hover:#0000009e;--wui-scrollbar-handle-active:#000000bd}web-ui-theme[resolved-appearance=dark]{--wui-scrollbar-handle:#ffffff80;--wui-scrollbar-handle-hover:#ffffff9e;--wui-scrollbar-handle-active:#ffffffbd}html:has(web-ui-theme[resolved-appearance=dark]:not(web-ui-theme *)){--wui-scrollbar-handle:#ffffff80;--wui-scrollbar-handle-hover:#ffffff9e;--wui-scrollbar-handle-active:#ffffffbd}
10
+ .os-size-observer,.os-size-observer-listener{direction:inherit;pointer-events:none;visibility:hidden;box-sizing:border-box;overflow:hidden;scroll-behavior:auto!important}.os-size-observer,.os-size-observer-listener,.os-size-observer-listener-item,.os-size-observer-listener-item-final{writing-mode:horizontal-tb;position:absolute;top:0;left:0}.os-size-observer{z-index:-1;contain:strict;padding:inherit;border:inherit;box-sizing:inherit;flex-flow:row;margin:-133px;display:flex;inset:0;transform:scale(.1)}.os-size-observer:before{content:"";box-sizing:inherit;flex:none;width:10px;height:10px;padding:10px}.os-size-observer-appear{animation:1ms forwards os-size-observer-appear-animation}.os-size-observer-listener{box-sizing:border-box;padding:inherit;border:inherit;flex:auto;margin:-133px;position:relative;transform:scale(10)}.os-size-observer-listener.ltr{margin-left:0;margin-right:-266px}.os-size-observer-listener.rtl{margin-left:-266px;margin-right:0}.os-size-observer-listener:empty:before{content:"";width:100%;height:100%}.os-size-observer-listener:empty:before,.os-size-observer-listener>.os-size-observer-listener-item{padding:inherit;border:inherit;box-sizing:content-box;flex:auto;display:block;position:relative}.os-size-observer-listener-scroll{box-sizing:border-box;display:flex}.os-size-observer-listener-item{direction:ltr;flex:none;bottom:0;right:0;overflow:hidden}.os-size-observer-listener-item-final{transition:none}@keyframes os-size-observer-appear-animation{0%{cursor:auto}to{cursor:none}}.os-trinsic-observer{box-sizing:border-box;z-index:-1;contain:strict;border:none;flex:none;max-width:0;height:0;max-height:1px;margin:0;padding:0;position:relative;top:calc(100% + 1px);overflow:hidden}.os-trinsic-observer:not(:empty){height:calc(100% + 1px);top:-1px}.os-trinsic-observer:not(:empty)>.os-size-observer{width:1000%;min-width:1px;height:1000%;min-height:1px}html[data-overlayscrollbars-body]{overflow:hidden}html[data-overlayscrollbars-body],html[data-overlayscrollbars-body]>body{width:100%;height:100%;margin:0}html[data-overlayscrollbars-body]>body{margin:0;overflow:visible}[data-overlayscrollbars]{position:relative}[data-overlayscrollbars~=host],[data-overlayscrollbars-padding]{display:flex;scroll-behavior:auto!important;flex-flow:row!important;align-items:stretch!important}[data-overlayscrollbars-padding],[data-overlayscrollbars-viewport]:not([data-overlayscrollbars]){box-sizing:inherit;z-index:0;border:none;flex:auto;width:100%;min-width:0;height:auto;margin:0;padding:0;position:relative}[data-overlayscrollbars-viewport]:not([data-overlayscrollbars]){--os-vaw:0;--os-vah:0;outline:none}[data-overlayscrollbars-viewport]:not([data-overlayscrollbars]):focus{outline:none}[data-overlayscrollbars-viewport][data-overlayscrollbars-viewport~=arrange]:before{content:"";pointer-events:none;z-index:-1;min-width:1px;min-height:1px;width:var(--os-vaw);height:var(--os-vah);position:absolute}[data-overlayscrollbars~=host],[data-overlayscrollbars-padding]{overflow:hidden!important}[data-overlayscrollbars~=host][data-overlayscrollbars~=noClipping],[data-overlayscrollbars-padding~=noClipping]{overflow:visible!important}[data-overlayscrollbars-viewport]{--os-viewport-overflow-x:hidden;--os-viewport-overflow-y:hidden;overflow-x:var(--os-viewport-overflow-x);overflow-y:var(--os-viewport-overflow-y)}[data-overlayscrollbars-viewport~=overflowXVisible]{--os-viewport-overflow-x:visible}[data-overlayscrollbars-viewport~=overflowXHidden]{--os-viewport-overflow-x:hidden}[data-overlayscrollbars-viewport~=overflowXScroll]{--os-viewport-overflow-x:scroll}[data-overlayscrollbars-viewport~=overflowYVisible]{--os-viewport-overflow-y:visible}[data-overlayscrollbars-viewport~=overflowYHidden]{--os-viewport-overflow-y:hidden}[data-overlayscrollbars-viewport~=overflowYScroll]{--os-viewport-overflow-y:scroll}[data-overlayscrollbars-viewport~=overflowImportant]{overflow-x:var(--os-viewport-overflow-x)!important;overflow-y:var(--os-viewport-overflow-y)!important}[data-overlayscrollbars-viewport~=noContent]:not(#osFakeId){font-size:0!important;line-height:0!important}[data-overlayscrollbars-viewport~=noContent]:not(#osFakeId):before,[data-overlayscrollbars-viewport~=noContent]:not(#osFakeId):after,[data-overlayscrollbars-viewport~=noContent]:not(#osFakeId)>:not(#osFakeId){clip:rect(0, 0, 0, 0)!important;white-space:nowrap!important;border-width:0!important;width:1px!important;height:1px!important;margin:-1px!important;padding:0!important;display:none!important;position:absolute!important;overflow:hidden!important}[data-overlayscrollbars-viewport~=measuring],[data-overlayscrollbars-viewport~=scrolling]{scroll-behavior:auto!important;scroll-snap-type:none!important}[data-overlayscrollbars-viewport~=measuring][data-overlayscrollbars-viewport~=overflowXVisible]{overflow-x:hidden!important}[data-overlayscrollbars-viewport~=measuring][data-overlayscrollbars-viewport~=overflowYVisible]{overflow-y:hidden!important}[data-overlayscrollbars-content]{box-sizing:inherit}[data-overlayscrollbars-contents]:not(#osFakeId):not([data-overlayscrollbars-padding]):not([data-overlayscrollbars-viewport]):not([data-overlayscrollbars-content]){display:contents}[data-overlayscrollbars-grid],[data-overlayscrollbars-grid] [data-overlayscrollbars-padding]{grid-template:1fr/1fr;display:grid}[data-overlayscrollbars-grid]>[data-overlayscrollbars-padding],[data-overlayscrollbars-grid]>[data-overlayscrollbars-viewport],[data-overlayscrollbars-grid]>[data-overlayscrollbars-padding]>[data-overlayscrollbars-viewport]{width:auto!important;height:auto!important}@property --os-scroll-percent{syntax:"<number>";inherits:true;initial-value:0}@property --os-viewport-percent{syntax:"<number>";inherits:true;initial-value:0}.os-scrollbar{--os-viewport-percent:0;--os-scroll-percent:0;--os-scroll-direction:0;--os-scroll-percent-directional:calc(var(--os-scroll-percent) - (var(--os-scroll-percent) + (1 - var(--os-scroll-percent)) * -1) * var(--os-scroll-direction));contain:size layout;contain:size layout style;pointer-events:none;opacity:0;visibility:hidden;transition:opacity .15s,visibility .15s,top .15s,right .15s,bottom .15s,left .15s;position:absolute}body>.os-scrollbar{z-index:99999;position:fixed}.os-scrollbar-transitionless{transition:none!important}.os-scrollbar-track{position:relative;border:none!important;padding:0!important}.os-scrollbar-handle{position:absolute}.os-scrollbar-track,.os-scrollbar-handle{pointer-events:none;width:100%;height:100%}.os-scrollbar.os-scrollbar-track-interactive .os-scrollbar-track,.os-scrollbar.os-scrollbar-handle-interactive .os-scrollbar-handle{pointer-events:auto;touch-action:none}.os-scrollbar-horizontal{bottom:0;left:0}.os-scrollbar-vertical{top:0;right:0}.os-scrollbar-rtl.os-scrollbar-horizontal{right:0}.os-scrollbar-rtl.os-scrollbar-vertical{left:0;right:auto}.os-scrollbar-visible{opacity:1;visibility:visible}.os-scrollbar-auto-hide.os-scrollbar-auto-hide-hidden{opacity:0;visibility:hidden}.os-scrollbar-interaction.os-scrollbar-visible{opacity:1;visibility:visible}.os-scrollbar-unusable,.os-scrollbar-unusable *,.os-scrollbar-wheel,.os-scrollbar-wheel *{pointer-events:none!important}.os-scrollbar-unusable .os-scrollbar-handle{opacity:0!important;transition:none!important}.os-scrollbar-horizontal .os-scrollbar-handle{bottom:0;left:calc(var(--os-scroll-percent-directional) * 100%);transform:translateX(calc(var(--os-scroll-percent-directional) * -100%));width:calc(var(--os-viewport-percent) * 100%)}.os-scrollbar-vertical .os-scrollbar-handle{right:0;top:calc(var(--os-scroll-percent-directional) * 100%);transform:translateY(calc(var(--os-scroll-percent-directional) * -100%));height:calc(var(--os-viewport-percent) * 100%)}@supports (container-type:size){.os-scrollbar-track{container-type:size}.os-scrollbar-horizontal .os-scrollbar-handle{transform:translateX(calc(var(--os-scroll-percent-directional) * 100cqw + var(--os-scroll-percent-directional) * -100%));left:auto}.os-scrollbar-vertical .os-scrollbar-handle{transform:translateY(calc(var(--os-scroll-percent-directional) * 100cqh + var(--os-scroll-percent-directional) * -100%));top:auto}.os-scrollbar-rtl.os-scrollbar-horizontal .os-scrollbar-handle{left:0;right:auto}}.os-scrollbar-rtl.os-scrollbar-vertical .os-scrollbar-handle{left:0;right:auto}.os-scrollbar.os-scrollbar-horizontal.os-scrollbar-cornerless,.os-scrollbar.os-scrollbar-horizontal.os-scrollbar-cornerless.os-scrollbar-rtl{left:0;right:0}.os-scrollbar.os-scrollbar-vertical.os-scrollbar-cornerless,.os-scrollbar.os-scrollbar-vertical.os-scrollbar-cornerless.os-scrollbar-rtl{top:0;bottom:0}@media print{.os-scrollbar{display:none}}.os-scrollbar{--os-size:0;--os-padding-perpendicular:0;--os-padding-axis:0;--os-track-border-radius:0;--os-track-bg:none;--os-track-bg-hover:none;--os-track-bg-active:none;--os-track-border:none;--os-track-border-hover:none;--os-track-border-active:none;--os-handle-border-radius:0;--os-handle-bg:none;--os-handle-bg-hover:none;--os-handle-bg-active:none;--os-handle-border:none;--os-handle-border-hover:none;--os-handle-border-active:none;--os-handle-min-size:33px;--os-handle-max-size:none;--os-handle-perpendicular-size:100%;--os-handle-perpendicular-size-hover:100%;--os-handle-perpendicular-size-active:100%;--os-handle-interactive-area-offset:0}.os-scrollbar-track{border:var(--os-track-border);border-radius:var(--os-track-border-radius);background:var(--os-track-bg);transition:opacity .15s,background-color .15s,border-color .15s}.os-scrollbar-track:hover{border:var(--os-track-border-hover);background:var(--os-track-bg-hover)}.os-scrollbar-track:active{border:var(--os-track-border-active);background:var(--os-track-bg-active)}.os-scrollbar-handle{border:var(--os-handle-border);border-radius:var(--os-handle-border-radius);background:var(--os-handle-bg)}.os-scrollbar-handle:hover{border:var(--os-handle-border-hover);background:var(--os-handle-bg-hover)}.os-scrollbar-handle:active{border:var(--os-handle-border-active);background:var(--os-handle-bg-active)}.os-scrollbar-track:before,.os-scrollbar-handle:before{content:"";display:block;position:absolute;inset:0}.os-scrollbar-horizontal{padding:var(--os-padding-perpendicular) var(--os-padding-axis);right:var(--os-size);height:var(--os-size)}.os-scrollbar-horizontal.os-scrollbar-rtl{left:var(--os-size);right:0}.os-scrollbar-horizontal .os-scrollbar-track:before{top:calc(var(--os-padding-perpendicular) * -1);bottom:calc(var(--os-padding-perpendicular) * -1)}.os-scrollbar-horizontal .os-scrollbar-handle{min-width:var(--os-handle-min-size);max-width:var(--os-handle-max-size);height:var(--os-handle-perpendicular-size);transition:opacity .15s,background-color .15s,border-color .15s,height .15s}.os-scrollbar-horizontal .os-scrollbar-handle:before{top:calc((var(--os-padding-perpendicular) + var(--os-handle-interactive-area-offset)) * -1);bottom:calc(var(--os-padding-perpendicular) * -1)}.os-scrollbar-horizontal:hover .os-scrollbar-handle{height:var(--os-handle-perpendicular-size-hover)}.os-scrollbar-horizontal:active .os-scrollbar-handle{height:var(--os-handle-perpendicular-size-active)}.os-scrollbar-vertical{padding:var(--os-padding-axis) var(--os-padding-perpendicular);bottom:var(--os-size);width:var(--os-size)}.os-scrollbar-vertical .os-scrollbar-track:before{left:calc(var(--os-padding-perpendicular) * -1);right:calc(var(--os-padding-perpendicular) * -1)}.os-scrollbar-vertical .os-scrollbar-handle{min-height:var(--os-handle-min-size);max-height:var(--os-handle-max-size);width:var(--os-handle-perpendicular-size);transition:opacity .15s,background-color .15s,border-color .15s,width .15s}.os-scrollbar-vertical .os-scrollbar-handle:before{left:calc((var(--os-padding-perpendicular) + var(--os-handle-interactive-area-offset)) * -1);right:calc(var(--os-padding-perpendicular) * -1)}.os-scrollbar-vertical.os-scrollbar-rtl .os-scrollbar-handle:before{right:calc((var(--os-padding-perpendicular) + var(--os-handle-interactive-area-offset)) * -1);left:calc(var(--os-padding-perpendicular) * -1)}.os-scrollbar-vertical:hover .os-scrollbar-handle{width:var(--os-handle-perpendicular-size-hover)}.os-scrollbar-vertical:active .os-scrollbar-handle{width:var(--os-handle-perpendicular-size-active)}[data-overlayscrollbars-viewport~=measuring]>.os-scrollbar,.os-theme-none.os-scrollbar{display:none!important}.os-theme-dark,.os-theme-light{box-sizing:border-box;--os-size:10px;--os-padding-perpendicular:2px;--os-padding-axis:2px;--os-track-border-radius:10px;--os-handle-interactive-area-offset:4px;--os-handle-border-radius:10px}.os-theme-dark{--os-handle-bg:#00000070;--os-handle-bg-hover:#0000008c;--os-handle-bg-active:#000000a8}.os-theme-light{--os-handle-bg:#ffffff70;--os-handle-bg-hover:#ffffff8c;--os-handle-bg-active:#ffffffa8}[data-overlayscrollbars-initialize]:not([data-overlayscrollbars-viewport]),[data-overlayscrollbars-viewport~=scrollbarHidden],html[data-overlayscrollbars-viewport~=scrollbarHidden]>body{scrollbar-width:none!important}[data-overlayscrollbars-initialize]:not([data-overlayscrollbars-viewport])::-webkit-scrollbar{appearance:none!important;width:0!important;height:0!important;display:none!important}[data-overlayscrollbars-initialize]:not([data-overlayscrollbars-viewport])::-webkit-scrollbar-corner{appearance:none!important;width:0!important;height:0!important;display:none!important}[data-overlayscrollbars-viewport~=scrollbarHidden]::-webkit-scrollbar{appearance:none!important;width:0!important;height:0!important;display:none!important}[data-overlayscrollbars-viewport~=scrollbarHidden]::-webkit-scrollbar-corner{appearance:none!important;width:0!important;height:0!important;display:none!important}html[data-overlayscrollbars-viewport~=scrollbarHidden]>body::-webkit-scrollbar{appearance:none!important;width:0!important;height:0!important;display:none!important}html[data-overlayscrollbars-viewport~=scrollbarHidden]>body::-webkit-scrollbar-corner{appearance:none!important;width:0!important;height:0!important;display:none!important}[data-overlayscrollbars-initialize]:not([data-overlayscrollbars]):not(html):not(body){overflow:auto}.os-theme-macos{--os-size:13px;--os-padding-perpendicular:3px;--os-padding-axis:2px;--os-track-border-radius:100px;--os-handle-border-radius:100px;--os-handle-interactive-area-offset:4px;--os-track-bg:none;--os-track-bg-hover:none;--os-track-bg-active:none;--os-handle-bg:var(--wui-scrollbar-handle,#00000063);--os-handle-bg-hover:var(--wui-scrollbar-handle-hover,#00000082);--os-handle-bg-active:var(--wui-scrollbar-handle-active,#000000a1);box-sizing:border-box}web-ui-theme[resolved-appearance=light]{--wui-scrollbar-handle:#00000063;--wui-scrollbar-handle-hover:#00000082;--wui-scrollbar-handle-active:#000000a1}web-ui-theme[resolved-appearance=dark]{--wui-scrollbar-handle:#ffffff80;--wui-scrollbar-handle-hover:#ffffff9e;--wui-scrollbar-handle-active:#ffffffbd}html:has(web-ui-theme[resolved-appearance=dark]:not(web-ui-theme *)){--wui-scrollbar-handle:#ffffff80;--wui-scrollbar-handle-hover:#ffffff9e;--wui-scrollbar-handle-active:#ffffffbd}
@@ -1,29 +1,31 @@
1
1
  import e from "../../assets/glass.js";
2
2
  import t from "../../assets/overlay-motion.js";
3
- import n from "../../assets/menu-portal.js";
4
- import { resolveOverlayContainer as r } from "../overlay/portal.js";
3
+ import n from "../../scrollbars/internal.js";
4
+ import r from "../../assets/menu-portal.js";
5
+ import { resolveOverlayContainer as i } from "../overlay/portal.js";
5
6
  //#region src/shared/menu-portal/menu-portal.ts
6
- var i = `${e}
7
- ${n}
8
- ${t}`;
9
- function a(e, t, n) {
10
- let a = document.createElement("div");
11
- a.className = `wui-menu-portal-overlay wui-floating-panel wui-glass ${e}`, a.dataset.wuiPresence = "entering";
7
+ var a = `${e}
8
+ ${r}
9
+ ${t}
10
+ ${n}`;
11
+ function o(e, t, n) {
12
+ let r = document.createElement("div");
13
+ r.className = `wui-menu-portal-overlay wui-floating-panel wui-glass ${e}`, r.dataset.wuiPresence = "entering";
12
14
  let o = document.createElement("div");
13
- o.className = "wui-menu-scroll";
15
+ o.className = "wui-menu-scroll wui-scroll";
14
16
  let s = document.createElement("div");
15
17
  s.className = "wui-menu-content", o.append(s);
16
- let c = n ?? r(void 0, t ?? document.body, { preferRootTheme: t === void 0 });
18
+ let c = n ?? i(void 0, t ?? document.body, { preferRootTheme: t === void 0 });
17
19
  if (c instanceof HTMLDialogElement) {
18
20
  let e = document.createElement("style");
19
- e.textContent = i, a.append(e);
21
+ e.textContent = a, r.append(e);
20
22
  }
21
- return a.append(o), c.appendChild(a), {
22
- panel: a,
23
+ return r.append(o), c.appendChild(r), {
24
+ panel: r,
23
25
  content: s
24
26
  };
25
27
  }
26
28
  //#endregion
27
- export { a as createMenuPortalOverlay };
29
+ export { o as createMenuPortalOverlay };
28
30
 
29
31
  //# sourceMappingURL=menu-portal.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"menu-portal.js","names":[],"sources":["../../../src/shared/menu-portal/menu-portal.ts"],"sourcesContent":["import glass from '@/assets/glass.css?inline'\nimport menuPortalStyle from '@/assets/menu-portal.css?inline'\nimport overlayMotion from '@/assets/overlay-motion.css?inline'\nimport { resolveOverlayContainer } from '@/shared/overlay/portal'\n\nexport interface MenuPortalOverlay {\n readonly panel: HTMLElement\n readonly content: HTMLElement\n}\n\n// 菜单面板所需的共享样式。theme-owned/fallback overlay root 已预注入这些样式;但当 overlay 因\n// target 位于已打开原生 dialog 内而被挂到该 dialog(top layer)时,dialog 的\n// shadow root 并不包含这些样式。故面板自携带一份,保证在任何容器下都能正确渲染。\nconst MENU_PANEL_STYLES = `${glass}\n${menuPortalStyle}\n${overlayMotion}`\n\n/**\n * 菜单浮层面板:面板直接挂到 overlay 容器(与普通 Overlay Portal 共用容器解析),\n * 样式复用容器内已注入的共享菜单样式(glass/menu-portal/overlay-motion),\n * 因此无需自建 shadow。菜单内容由 dropdown/context-menu 自行构建(menu-tree),\n * 这里只提供 panel 与 content 挂载点。\n */\n/**\n * @param scrim 显式容器。模态化的 context-menu 传自己的 scrim `<dialog>`,面板因此\n * 与 scrim 同处 top layer,「点外面」由 scrim 吃掉,面板不会被下层抢走命中。\n *\n 不能靠 `resolveOverlayContainer` 的祖先发现自动找到它:该函数查的是「**target 的祖先**里\n 有没有已打开的 dialog」,而 scrim 是面板的**后代方向**,方向相反,查不到。菜单面板\n * 又不在 host 的 shadow root 里(它在 `web-ui-theme` 的 overlay root 内),这条路径也断。\n *\n * 省略时行为与从前完全一致 —— dropdown 的 `target` 承重调用点(dropdown-in-dialog)\n * 一个字都不用改。\n */\nexport function createMenuPortalOverlay(className: string, target?: Element, scrim?: HTMLElement): MenuPortalOverlay {\n const panel = document.createElement('div')\n // 单层玻璃:wui-glass 留在面板自身,背景/阴影/backdrop-filter 随面板 opacity\n // 与 blur 插值一起过渡(见 overlay-motion.css),无需 blur/surface 双层。\n panel.className = `wui-menu-portal-overlay wui-floating-panel wui-glass ${className}`\n panel.dataset.wuiPresence = 'entering'\n const scroll = document.createElement('div')\n scroll.className = 'wui-menu-scroll'\n const content = document.createElement('div')\n content.className = 'wui-menu-content'\n scroll.append(content)\n\n const container =\n scrim ??\n resolveOverlayContainer(undefined, target ?? document.body, {\n preferRootTheme: target === undefined\n })\n // theme-owned/fallback overlay root 已预注入共享样式;只有 dialog 没有,因此按容器条件注入。\n if (container instanceof HTMLDialogElement) {\n const style = document.createElement('style')\n style.textContent = MENU_PANEL_STYLES\n panel.append(style)\n }\n panel.append(scroll)\n container.appendChild(panel)\n\n return { panel, content }\n}\n"],"mappings":";;;;;AAaA,IAAM,IAAoB,GAAG,EAAM;EACjC,EAAgB;EAChB;AAmBF,SAAgB,EAAwB,GAAmB,GAAkB,GAAwC;CACnH,IAAM,IAAQ,SAAS,cAAc,KAAK;CAI1C,AADA,EAAM,YAAY,wDAAwD,KAC1E,EAAM,QAAQ,cAAc;CAC5B,IAAM,IAAS,SAAS,cAAc,KAAK;CAC3C,EAAO,YAAY;CACnB,IAAM,IAAU,SAAS,cAAc,KAAK;CAE5C,AADA,EAAQ,YAAY,oBACpB,EAAO,OAAO,CAAO;CAErB,IAAM,IACJ,KACA,EAAwB,KAAA,GAAW,KAAU,SAAS,MAAM,EAC1D,iBAAiB,MAAW,KAAA,EAC9B,CAAC;CAEH,IAAI,aAAqB,mBAAmB;EAC1C,IAAM,IAAQ,SAAS,cAAc,OAAO;EAE5C,AADA,EAAM,cAAc,GACpB,EAAM,OAAO,CAAK;CACpB;CAIA,OAHA,EAAM,OAAO,CAAM,GACnB,EAAU,YAAY,CAAK,GAEpB;EAAE;EAAO;CAAQ;AAC1B"}
1
+ {"version":3,"file":"menu-portal.js","names":[],"sources":["../../../src/shared/menu-portal/menu-portal.ts"],"sourcesContent":["import glass from '@/assets/glass.css?inline'\nimport menuPortalStyle from '@/assets/menu-portal.css?inline'\nimport overlayMotion from '@/assets/overlay-motion.css?inline'\nimport internalScrollbars from '@/scrollbars/internal.css?inline'\nimport { resolveOverlayContainer } from '@/shared/overlay/portal'\n\nexport interface MenuPortalOverlay {\n readonly panel: HTMLElement\n readonly content: HTMLElement\n}\n\n// 菜单面板所需的共享样式。theme-owned/fallback overlay root 已预注入这些样式;但当 overlay 因\n// target 位于已打开原生 dialog 内而被挂到该 dialog(top layer)时,dialog 的\n// shadow root 并不包含这些样式。故面板自携带一份,保证在任何容器下都能正确渲染。\nconst MENU_PANEL_STYLES = `${glass}\n${menuPortalStyle}\n${overlayMotion}\n${internalScrollbars}`\n\n/**\n * 菜单浮层面板:面板直接挂到 overlay 容器(与普通 Overlay Portal 共用容器解析),\n * 样式复用容器内已注入的共享菜单样式(glass/menu-portal/overlay-motion),\n * 因此无需自建 shadow。菜单内容由 dropdown/context-menu 自行构建(menu-tree),\n * 这里只提供 panel 与 content 挂载点。\n */\n/**\n * @param scrim 显式容器。模态化的 context-menu 传自己的 scrim `<dialog>`,面板因此\n * 与 scrim 同处 top layer,「点外面」由 scrim 吃掉,面板不会被下层抢走命中。\n *\n 不能靠 `resolveOverlayContainer` 的祖先发现自动找到它:该函数查的是「**target 的祖先**里\n 有没有已打开的 dialog」,而 scrim 是面板的**后代方向**,方向相反,查不到。菜单面板\n * 又不在 host 的 shadow root 里(它在 `web-ui-theme` 的 overlay root 内),这条路径也断。\n *\n * 省略时行为与从前完全一致 —— dropdown 的 `target` 承重调用点(dropdown-in-dialog)\n * 一个字都不用改。\n */\nexport function createMenuPortalOverlay(className: string, target?: Element, scrim?: HTMLElement): MenuPortalOverlay {\n const panel = document.createElement('div')\n // 单层玻璃:wui-glass 留在面板自身,背景/阴影/backdrop-filter 随面板 opacity\n // 与 blur 插值一起过渡(见 overlay-motion.css),无需 blur/surface 双层。\n panel.className = `wui-menu-portal-overlay wui-floating-panel wui-glass ${className}`\n panel.dataset.wuiPresence = 'entering'\n const scroll = document.createElement('div')\n scroll.className = 'wui-menu-scroll wui-scroll'\n const content = document.createElement('div')\n content.className = 'wui-menu-content'\n scroll.append(content)\n\n const container =\n scrim ??\n resolveOverlayContainer(undefined, target ?? document.body, {\n preferRootTheme: target === undefined\n })\n // theme-owned/fallback overlay root 已预注入共享样式;只有 dialog 没有,因此按容器条件注入。\n if (container instanceof HTMLDialogElement) {\n const style = document.createElement('style')\n style.textContent = MENU_PANEL_STYLES\n panel.append(style)\n }\n panel.append(scroll)\n container.appendChild(panel)\n\n return { panel, content }\n}\n"],"mappings":";;;;;;AAcA,IAAM,IAAoB,GAAG,EAAM;EACjC,EAAgB;EAChB,EAAc;EACd;AAmBF,SAAgB,EAAwB,GAAmB,GAAkB,GAAwC;CACnH,IAAM,IAAQ,SAAS,cAAc,KAAK;CAI1C,AADA,EAAM,YAAY,wDAAwD,KAC1E,EAAM,QAAQ,cAAc;CAC5B,IAAM,IAAS,SAAS,cAAc,KAAK;CAC3C,EAAO,YAAY;CACnB,IAAM,IAAU,SAAS,cAAc,KAAK;CAE5C,AADA,EAAQ,YAAY,oBACpB,EAAO,OAAO,CAAO;CAErB,IAAM,IACJ,KACA,EAAwB,KAAA,GAAW,KAAU,SAAS,MAAM,EAC1D,iBAAiB,MAAW,KAAA,EAC9B,CAAC;CAEH,IAAI,aAAqB,mBAAmB;EAC1C,IAAM,IAAQ,SAAS,cAAc,OAAO;EAE5C,AADA,EAAM,cAAc,GACpB,EAAM,OAAO,CAAK;CACpB;CAIA,OAHA,EAAM,OAAO,CAAM,GACnB,EAAU,YAAY,CAAK,GAEpB;EAAE;EAAO;CAAQ;AAC1B"}