@greypan/web-ui 4.0.0 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. package/README.CN.md +259 -139
  2. package/README.md +296 -139
  3. package/dist/assets/glass.js +1 -1
  4. package/dist/assets/glass.js.map +1 -1
  5. package/dist/assets/menu-portal.js +1 -1
  6. package/dist/assets/menu-portal.js.map +1 -1
  7. package/dist/components/autocomplete/index.d.ts +8 -0
  8. package/dist/components/autocomplete/index.js +108 -50
  9. package/dist/components/autocomplete/index.js.map +1 -1
  10. package/dist/components/autocomplete/style.js +1 -1
  11. package/dist/components/autocomplete/style.js.map +1 -1
  12. package/dist/components/back-top/index.js +1 -1
  13. package/dist/components/back-top/index.js.map +1 -1
  14. package/dist/components/back-top/style.js +1 -1
  15. package/dist/components/back-top/style.js.map +1 -1
  16. package/dist/components/button/index.js +33 -32
  17. package/dist/components/button/index.js.map +1 -1
  18. package/dist/components/button/style.js +1 -1
  19. package/dist/components/button/style.js.map +1 -1
  20. package/dist/components/button-group/index.js +14 -12
  21. package/dist/components/button-group/index.js.map +1 -1
  22. package/dist/components/button-group/style.js +1 -1
  23. package/dist/components/button-group/style.js.map +1 -1
  24. package/dist/components/checkbox/index.js +23 -16
  25. package/dist/components/checkbox/index.js.map +1 -1
  26. package/dist/components/checkbox/style.js +1 -1
  27. package/dist/components/checkbox/style.js.map +1 -1
  28. package/dist/components/collapse/index.d.ts +66 -0
  29. package/dist/components/collapse/index.js +159 -0
  30. package/dist/components/collapse/index.js.map +1 -0
  31. package/dist/components/collapse/style.js +6 -0
  32. package/dist/components/collapse/style.js.map +1 -0
  33. package/dist/components/context-menu/index.d.ts +9 -0
  34. package/dist/components/context-menu/index.js +109 -51
  35. package/dist/components/context-menu/index.js.map +1 -1
  36. package/dist/components/dialog/index.d.ts +6 -0
  37. package/dist/components/dialog/index.js +28 -7
  38. package/dist/components/dialog/index.js.map +1 -1
  39. package/dist/components/dialog/style.js +1 -1
  40. package/dist/components/dialog/style.js.map +1 -1
  41. package/dist/components/drawer/index.d.ts +32 -2
  42. package/dist/components/drawer/index.js +229 -39
  43. package/dist/components/drawer/index.js.map +1 -1
  44. package/dist/components/drawer/style.js +1 -1
  45. package/dist/components/drawer/style.js.map +1 -1
  46. package/dist/components/dropdown/index.d.ts +8 -0
  47. package/dist/components/dropdown/index.js +84 -40
  48. package/dist/components/dropdown/index.js.map +1 -1
  49. package/dist/components/dropdown-item/style.js +1 -1
  50. package/dist/components/dropdown-item/style.js.map +1 -1
  51. package/dist/components/empty/index.d.ts +1 -0
  52. package/dist/components/empty/index.js +23 -21
  53. package/dist/components/empty/index.js.map +1 -1
  54. package/dist/components/empty/style.js +1 -1
  55. package/dist/components/empty/style.js.map +1 -1
  56. package/dist/components/index.d.ts +1 -0
  57. package/dist/components/index.js +28 -27
  58. package/dist/components/input/style.js +1 -1
  59. package/dist/components/input/style.js.map +1 -1
  60. package/dist/components/input-number/style.js +1 -1
  61. package/dist/components/input-number/style.js.map +1 -1
  62. package/dist/components/layout/index.d.ts +23 -0
  63. package/dist/components/layout/index.js +134 -30
  64. package/dist/components/layout/index.js.map +1 -1
  65. package/dist/components/layout/style.js +1 -1
  66. package/dist/components/layout/style.js.map +1 -1
  67. package/dist/components/option/style.js +1 -1
  68. package/dist/components/option/style.js.map +1 -1
  69. package/dist/components/popover/index.d.ts +4 -1
  70. package/dist/components/popover/index.js +18 -6
  71. package/dist/components/popover/index.js.map +1 -1
  72. package/dist/components/popover/style.js +1 -1
  73. package/dist/components/popover/style.js.map +1 -1
  74. package/dist/components/radio/index.js +18 -15
  75. package/dist/components/radio/index.js.map +1 -1
  76. package/dist/components/radio/style.js +1 -1
  77. package/dist/components/radio/style.js.map +1 -1
  78. package/dist/components/segmented/index.d.ts +8 -1
  79. package/dist/components/segmented/index.js +87 -21
  80. package/dist/components/segmented/index.js.map +1 -1
  81. package/dist/components/segmented/style.js +1 -1
  82. package/dist/components/segmented/style.js.map +1 -1
  83. package/dist/components/segmented-trigger/index.js +17 -14
  84. package/dist/components/segmented-trigger/index.js.map +1 -1
  85. package/dist/components/segmented-trigger/style.js +1 -1
  86. package/dist/components/segmented-trigger/style.js.map +1 -1
  87. package/dist/components/select/index.js +33 -29
  88. package/dist/components/select/index.js.map +1 -1
  89. package/dist/components/select/style.js +1 -1
  90. package/dist/components/select/style.js.map +1 -1
  91. package/dist/components/slider/index.d.ts +3 -2
  92. package/dist/components/slider/index.js +65 -47
  93. package/dist/components/slider/index.js.map +1 -1
  94. package/dist/components/slider/style.js +1 -1
  95. package/dist/components/slider/style.js.map +1 -1
  96. package/dist/components/spinner/index.js.map +1 -1
  97. package/dist/components/switch/index.d.ts +8 -0
  98. package/dist/components/switch/index.js +72 -25
  99. package/dist/components/switch/index.js.map +1 -1
  100. package/dist/components/switch/style.js +1 -1
  101. package/dist/components/switch/style.js.map +1 -1
  102. package/dist/components/textarea/index.js +1 -4
  103. package/dist/components/textarea/index.js.map +1 -1
  104. package/dist/components/textarea/style.js +1 -1
  105. package/dist/components/textarea/style.js.map +1 -1
  106. package/dist/components/theme/style.js +1 -1
  107. package/dist/components/theme/style.js.map +1 -1
  108. package/dist/components/toast/style.js +1 -1
  109. package/dist/components/toast/style.js.map +1 -1
  110. package/dist/components/toast/toast.js.map +1 -1
  111. package/dist/components/tooltip/index.d.ts +1 -0
  112. package/dist/components/tooltip/index.js +9 -4
  113. package/dist/components/tooltip/index.js.map +1 -1
  114. package/dist/components/tooltip/style.js +1 -1
  115. package/dist/components/tooltip/style.js.map +1 -1
  116. package/dist/icons/generated/heroicons-arrow-down-16-solid.d.ts +6 -0
  117. package/dist/icons/generated/heroicons-arrow-up-16-solid.d.ts +6 -0
  118. package/dist/icons/generated/heroicons-bars-arrow-down-16-solid.d.ts +6 -0
  119. package/dist/icons/generated/heroicons-bars-arrow-down-16-solid.js +10 -0
  120. package/dist/icons/generated/heroicons-bars-arrow-down-16-solid.js.map +1 -0
  121. package/dist/icons/generated/heroicons-bars-arrow-up-16-solid.d.ts +6 -0
  122. package/dist/icons/generated/heroicons-bars-arrow-up-16-solid.js +10 -0
  123. package/dist/icons/generated/heroicons-bars-arrow-up-16-solid.js.map +1 -0
  124. package/dist/icons/generated/lucide-code.d.ts +6 -0
  125. package/dist/icons/generated/lucide-code.js +10 -0
  126. package/dist/icons/generated/lucide-code.js.map +1 -0
  127. package/dist/icons/generated/lucide-ellipsis-vertical.d.ts +6 -0
  128. package/dist/icons/generated/lucide-ellipsis-vertical.js +10 -0
  129. package/dist/icons/generated/lucide-ellipsis-vertical.js.map +1 -0
  130. package/dist/icons/generated/lucide-list-filter.d.ts +6 -0
  131. package/dist/icons/generated/lucide-list-filter.js +10 -0
  132. package/dist/icons/generated/lucide-list-filter.js.map +1 -0
  133. package/dist/icons/generated/lucide-list-restart.d.ts +6 -0
  134. package/dist/icons/generated/lucide-list-restart.js +10 -0
  135. package/dist/icons/generated/lucide-list-restart.js.map +1 -0
  136. package/dist/icons/generated/lucide-tag.d.ts +6 -0
  137. package/dist/icons/generated/lucide-tag.js +10 -0
  138. package/dist/icons/generated/lucide-tag.js.map +1 -0
  139. package/dist/icons/generated/tabler-sort-ascending-letters.d.ts +6 -0
  140. package/dist/icons/generated/tabler-sort-ascending-letters.js +10 -0
  141. package/dist/icons/generated/tabler-sort-ascending-letters.js.map +1 -0
  142. package/dist/icons/index.d.ts +8 -0
  143. package/dist/icons/index.js +18 -10
  144. package/dist/shared/gesture/drag-gesture.js +133 -0
  145. package/dist/shared/gesture/drag-gesture.js.map +1 -0
  146. package/dist/shared/gesture/physics.js +45 -0
  147. package/dist/shared/gesture/physics.js.map +1 -0
  148. package/dist/shared/group-management/index.js +49 -47
  149. package/dist/shared/group-management/index.js.map +1 -1
  150. package/dist/shared/menu-portal/menu-portal.js +22 -10
  151. package/dist/shared/menu-portal/menu-portal.js.map +1 -1
  152. package/dist/shared/menu-portal/menu-tree.js +63 -2
  153. package/dist/shared/menu-portal/menu-tree.js.map +1 -1
  154. package/dist/shared/option-portal/index.js +1 -1
  155. package/dist/shared/option-portal/index.js.map +1 -1
  156. package/dist/shared/overlay/native-dialog-presence.js.map +1 -1
  157. package/dist/shared/overlay/nested-drawer-layers.js +55 -0
  158. package/dist/shared/overlay/nested-drawer-layers.js.map +1 -0
  159. package/dist/shared/overlay/overlay.js.map +1 -1
  160. package/dist/shared/overlay/portal.js +119 -16
  161. package/dist/shared/overlay/portal.js.map +1 -1
  162. package/package.json +8 -3
@@ -0,0 +1,66 @@
1
+ import { LitElement } from 'lit';
2
+ /**
3
+ * 文档流内折叠面板:trigger 经默认 slot 提供,内容经 `slot="content"` 提供。
4
+ *
5
+ * trigger 语义完全来自 slot 进来的交互元素(原生 button / web-ui-button 等):
6
+ * Enter/Space 激活走该元素的原生 click,不做 role="button" 补齐;组件把
7
+ * `aria-expanded` / `aria-controls` / `aria-disabled` 回写到首个 assigned
8
+ * element(与 popover trigger 的既定回写模式一致)。wrapper 仅负责点击代理,
9
+ * 不可聚焦,不承载 ARIA。
10
+ *
11
+ * 内容区在 shadow 内以 grid `0fr ↔ 1fr` track 过渡驱动高度/宽度动画(ADR-0038
12
+ * 选型保留)。关闭稳态三态(消费者 light DOM 永不移动,hidden 全部命令式管理,
13
+ * 避免渲染绑定在关闭动画起点抢先 display:none):
14
+ * - 默认:content 容器 `hidden`(display:none,脱离渲染与可访问性树);
15
+ * - `keep-mounted`:inner `inert`(保留在 0fr 轨道内,滚动位置与布局可测量);
16
+ * - 动画进行中:inner `inert` + track pointer-events 禁用,结束后进入对应稳态。
17
+ */
18
+ export declare class WebUiCollapse extends LitElement {
19
+ static styles: import('lit').CSSResult;
20
+ /** 展开状态;严格受控,唯一状态源。 */
21
+ open: boolean;
22
+ /** 禁用触发:点击忽略、aria-disabled 写入 trigger 元素;已展开内容保持现状。 */
23
+ disabled: boolean;
24
+ /** 水平方向:true 时沿宽度展开,默认沿高度。 */
25
+ horizontal: boolean;
26
+ /** 关闭稳态保持内容 `inert` 而非 `hidden`(保留滚动位置,可测量)。 */
27
+ keepMounted: boolean;
28
+ private _triggerWrapper;
29
+ private _contentId;
30
+ private readonly _userOpenChange;
31
+ private _exitTimer?;
32
+ private _generation;
33
+ private _lastOpen?;
34
+ connectedCallback(): void;
35
+ disconnectedCallback(): void;
36
+ protected updated(changed: Map<string, unknown>): void;
37
+ show(): void;
38
+ close(): void;
39
+ toggle(): void;
40
+ private _dispatchChange;
41
+ private _onTriggerClick;
42
+ private _queryTrigger;
43
+ private readonly _onTriggerSlotChange;
44
+ private _syncTriggerAria;
45
+ private _applyOpenSteadyState;
46
+ private _applyClosedSteadyState;
47
+ private _beginExpand;
48
+ private _beginCollapse;
49
+ private _settle;
50
+ private _awaitTransition;
51
+ private _clearExitTimer;
52
+ private _setInnerInert;
53
+ private _contentContainer;
54
+ private _contentTrack;
55
+ render(): import('lit').TemplateResult<1>;
56
+ readonly $events: {
57
+ 'open-change': CustomEvent<{
58
+ open: boolean;
59
+ }>;
60
+ };
61
+ }
62
+ declare global {
63
+ interface HTMLElementTagNameMap {
64
+ 'web-ui-collapse': WebUiCollapse;
65
+ }
66
+ }
@@ -0,0 +1,159 @@
1
+ import e from "../../_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorate.js";
2
+ import { getTransitionDuration as t } from "../../shared/overlay/presence.js";
3
+ import { UserChangeController as n } from "../../shared/events/user-change.js";
4
+ import r from "./style.js";
5
+ import { LitElement as i, html as a, unsafeCSS as o } from "lit";
6
+ import { customElement as s, property as c, query as l } from "lit/decorators.js";
7
+ import { classMap as u } from "lit/directives/class-map.js";
8
+ //#region src/components/collapse/index.ts
9
+ var d = 80, f = 0, p = class extends i {
10
+ constructor(...e) {
11
+ super(...e), this.open = !1, this.disabled = !1, this.horizontal = !1, this.keepMounted = !1, this._contentId = `wui-collapse-content-${++f}`, this._userOpenChange = new n(), this._generation = 0, this._onTriggerClick = (e) => {
12
+ this.disabled || e.composedPath().includes(this._triggerWrapper) && (this._userOpenChange.mark(), this.toggle());
13
+ }, this._onTriggerSlotChange = () => {
14
+ this.requestUpdate();
15
+ };
16
+ }
17
+ static {
18
+ this.styles = o(r);
19
+ }
20
+ connectedCallback() {
21
+ super.connectedCallback(), this.addEventListener("click", this._onTriggerClick), this.hasUpdated && (this._generation++, this._clearExitTimer(), this._lastOpen = void 0, this.requestUpdate());
22
+ }
23
+ disconnectedCallback() {
24
+ super.disconnectedCallback(), this._clearExitTimer();
25
+ }
26
+ updated(e) {
27
+ if (this._syncTriggerAria(), this._lastOpen === void 0) {
28
+ this._lastOpen = this.open, this.open ? this._applyOpenSteadyState() : this._applyClosedSteadyState();
29
+ return;
30
+ }
31
+ e.has("keepMounted") && !this.open && this._applyClosedSteadyState(), !(!e.has("open") || this.open === this._lastOpen) && (this._lastOpen = this.open, this._userOpenChange.consume() && this._dispatchChange(this.open), this.open ? this._beginExpand() : this._beginCollapse());
32
+ }
33
+ show() {
34
+ this.open ||= !0;
35
+ }
36
+ close() {
37
+ this.open &&= !1;
38
+ }
39
+ toggle() {
40
+ this.open ? this.close() : this.show();
41
+ }
42
+ _dispatchChange(e) {
43
+ this.dispatchEvent(new CustomEvent("open-change", {
44
+ detail: { open: e },
45
+ bubbles: !0,
46
+ composed: !0
47
+ }));
48
+ }
49
+ _queryTrigger() {
50
+ let e = (this.shadowRoot?.querySelector(".wui-collapse-trigger-wrapper slot"))?.assignedElements()[0];
51
+ return e instanceof HTMLElement ? e : null;
52
+ }
53
+ _syncTriggerAria() {
54
+ let e = this._queryTrigger();
55
+ e && (e.setAttribute("aria-expanded", String(this.open)), e.setAttribute("aria-controls", this._contentId), this.disabled ? e.setAttribute("aria-disabled", "true") : e.removeAttribute("aria-disabled"));
56
+ }
57
+ _applyOpenSteadyState() {
58
+ this._generation++, this._clearExitTimer();
59
+ let e = this._contentContainer(), t = this._contentTrack();
60
+ e && (e.hidden = !1), t && (t.dataset.wuiPresence = "open", this._setInnerInert(!1));
61
+ }
62
+ _applyClosedSteadyState() {
63
+ this._generation++, this._clearExitTimer();
64
+ let e = this._contentContainer(), t = this._contentTrack();
65
+ if (t && delete t.dataset.wuiPresence, this.keepMounted) {
66
+ e && (e.hidden = !1), this._setInnerInert(!0);
67
+ return;
68
+ }
69
+ this._setInnerInert(!1), e && (e.hidden = !0);
70
+ }
71
+ _beginExpand() {
72
+ this._generation++, this._clearExitTimer();
73
+ let e = this._contentContainer(), t = this._contentTrack();
74
+ if (!e || !t) return;
75
+ let n = this._generation;
76
+ this._setInnerInert(!1), e.hidden = !1, t.offsetWidth, requestAnimationFrame(() => {
77
+ n !== this._generation || !this.open || (t.dataset.wuiPresence = "open", this._awaitTransition(t, n, () => this._settle(!0)));
78
+ });
79
+ }
80
+ _beginCollapse() {
81
+ this._generation++, this._clearExitTimer();
82
+ let e = this._contentTrack();
83
+ if (!e) return;
84
+ this._setInnerInert(!0), e.dataset.wuiPresence = "closing";
85
+ let t = this._generation;
86
+ this._awaitTransition(e, t, () => this._settle(!1));
87
+ }
88
+ _settle(e) {
89
+ if (this._clearExitTimer(), e) {
90
+ this._setInnerInert(!1);
91
+ return;
92
+ }
93
+ let t = this._contentTrack();
94
+ if (t && delete t.dataset.wuiPresence, this.keepMounted) return;
95
+ let n = this._contentContainer();
96
+ this._setInnerInert(!1), n && (n.hidden = !0);
97
+ }
98
+ _awaitTransition(e, n, r) {
99
+ let i = t(e, 0);
100
+ if (i === 0) {
101
+ n === this._generation && r();
102
+ return;
103
+ }
104
+ let a = !1, o = () => {
105
+ e.removeEventListener("transitionend", c), this._exitTimer === s && (this._exitTimer = void 0), clearTimeout(s), !(a || n !== this._generation) && (a = !0, r());
106
+ }, s = setTimeout(o, i + d);
107
+ this._exitTimer = s;
108
+ let c = (t) => {
109
+ t.target === e && (t.propertyName === "grid-template-rows" || t.propertyName === "grid-template-columns") && o();
110
+ };
111
+ e.addEventListener("transitionend", c);
112
+ }
113
+ _clearExitTimer() {
114
+ this._exitTimer !== void 0 && (clearTimeout(this._exitTimer), this._exitTimer = void 0);
115
+ }
116
+ _setInnerInert(e) {
117
+ this.shadowRoot?.querySelector(".wui-collapse-inner")?.toggleAttribute("inert", e);
118
+ }
119
+ _contentContainer() {
120
+ return this.shadowRoot?.querySelector(".wui-collapse-content") ?? null;
121
+ }
122
+ _contentTrack() {
123
+ return this.shadowRoot?.querySelector(".wui-collapse-track") ?? null;
124
+ }
125
+ render() {
126
+ let e = {
127
+ "wui-collapse-track": !0,
128
+ "is-horizontal": this.horizontal
129
+ };
130
+ return a`
131
+ <div class="wui-collapse-trigger-wrapper" @slotchange=${this._onTriggerSlotChange}><slot></slot></div>
132
+ <div class="wui-collapse-content">
133
+ <div class=${u(e)} id=${this._contentId}>
134
+ <div class="wui-collapse-inner">
135
+ <slot name="content"></slot>
136
+ </div>
137
+ </div>
138
+ </div>
139
+ `;
140
+ }
141
+ };
142
+ e([c({
143
+ type: Boolean,
144
+ reflect: !0
145
+ })], p.prototype, "open", void 0), e([c({
146
+ type: Boolean,
147
+ reflect: !0
148
+ })], p.prototype, "disabled", void 0), e([c({
149
+ type: Boolean,
150
+ reflect: !0
151
+ })], p.prototype, "horizontal", void 0), e([c({
152
+ type: Boolean,
153
+ reflect: !0,
154
+ attribute: "keep-mounted"
155
+ })], p.prototype, "keepMounted", void 0), e([l(".wui-collapse-trigger-wrapper")], p.prototype, "_triggerWrapper", void 0), p = e([s("web-ui-collapse")], p);
156
+ //#endregion
157
+ export { p as WebUiCollapse };
158
+
159
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/collapse/index.ts"],"sourcesContent":["import { html, LitElement, unsafeCSS } from 'lit'\nimport { customElement, property, query } from 'lit/decorators.js'\nimport { classMap } from 'lit/directives/class-map.js'\n\nimport { UserChangeController } from '@/shared/events/user-change'\nimport { getTransitionDuration } from '@/shared/overlay/presence'\n\nimport style from './style.css?inline'\n\nconst EXIT_FALLBACK_BUFFER = 80\n\nlet collapseIdCounter = 0\n\n/**\n * 文档流内折叠面板:trigger 经默认 slot 提供,内容经 `slot=\"content\"` 提供。\n *\n * trigger 语义完全来自 slot 进来的交互元素(原生 button / web-ui-button 等):\n * Enter/Space 激活走该元素的原生 click,不做 role=\"button\" 补齐;组件把\n * `aria-expanded` / `aria-controls` / `aria-disabled` 回写到首个 assigned\n * element(与 popover trigger 的既定回写模式一致)。wrapper 仅负责点击代理,\n * 不可聚焦,不承载 ARIA。\n *\n * 内容区在 shadow 内以 grid `0fr ↔ 1fr` track 过渡驱动高度/宽度动画(ADR-0038\n * 选型保留)。关闭稳态三态(消费者 light DOM 永不移动,hidden 全部命令式管理,\n * 避免渲染绑定在关闭动画起点抢先 display:none):\n * - 默认:content 容器 `hidden`(display:none,脱离渲染与可访问性树);\n * - `keep-mounted`:inner `inert`(保留在 0fr 轨道内,滚动位置与布局可测量);\n * - 动画进行中:inner `inert` + track pointer-events 禁用,结束后进入对应稳态。\n */\n@customElement('web-ui-collapse')\nexport class WebUiCollapse extends LitElement {\n static override styles = unsafeCSS(style)\n\n /** 展开状态;严格受控,唯一状态源。 */\n @property({ type: Boolean, reflect: true }) open = false\n\n /** 禁用触发:点击忽略、aria-disabled 写入 trigger 元素;已展开内容保持现状。 */\n @property({ type: Boolean, reflect: true }) disabled = false\n\n /** 水平方向:true 时沿宽度展开,默认沿高度。 */\n @property({ type: Boolean, reflect: true }) horizontal = false\n\n /** 关闭稳态保持内容 `inert` 而非 `hidden`(保留滚动位置,可测量)。 */\n @property({ type: Boolean, reflect: true, attribute: 'keep-mounted' }) keepMounted = false\n\n @query('.wui-collapse-trigger-wrapper') private _triggerWrapper!: HTMLElement\n\n private _contentId = `wui-collapse-content-${++collapseIdCounter}`\n\n private readonly _userOpenChange = new UserChangeController()\n\n private _exitTimer?: ReturnType<typeof setTimeout>\n // 动画代际:每次管线启动自增,旧管线的 transitionend/兜底定时器/rAF 全部失效。\n private _generation = 0\n private _lastOpen?: boolean\n\n override connectedCallback() {\n super.connectedCallback()\n // 点击监听与生命周期同寿(宿主移除即整体销毁),无需 disconnected 清理。\n this.addEventListener('click', this._onTriggerClick)\n // 断连可能打断动画管线(瞬态残留:presence='closing'、容器未 hidden)。\n // 首次连接不必处理(初始 render 走 _lastOpen===undefined 落稳态,请求多余\n // 渲染会触发「scheduled an update after update completed」告警);重连时\n // 重置同步状态并强制一轮 update,由首次同步分支重新落稳态、丢弃在途管线。\n if (this.hasUpdated) {\n this._generation++\n this._clearExitTimer()\n this._lastOpen = undefined\n this.requestUpdate()\n }\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback()\n this._clearExitTimer()\n }\n\n protected override updated(changed: Map<string, unknown>) {\n // ARIA 回写不依赖 open 分支,任何渲染后都保持与宿主状态同步。\n this._syncTriggerAria()\n\n // 首次同步(含 open 为默认值的场景,changed map 不含默认值属性):\n // 直接落到正确稳态,不播动画(popover 初始 open 同款)。\n if (this._lastOpen === undefined) {\n this._lastOpen = this.open\n if (this.open) this._applyOpenSteadyState()\n else this._applyClosedSteadyState()\n return\n }\n\n if (changed.has('keepMounted') && !this.open) {\n // 运行态切换 keep-mounted:关闭稳态需在 hidden/inert 间重新落地。\n this._applyClosedSteadyState()\n }\n if (!changed.has('open') || this.open === this._lastOpen) return\n\n this._lastOpen = this.open\n if (this._userOpenChange.consume()) this._dispatchChange(this.open)\n if (this.open) this._beginExpand()\n else this._beginCollapse()\n }\n\n show() {\n if (this.open) return\n this.open = true\n }\n\n close() {\n if (!this.open) return\n this.open = false\n }\n\n toggle() {\n if (this.open) this.close()\n else this.show()\n }\n\n // ===== trigger slot =====\n\n private _dispatchChange(open: boolean) {\n this.dispatchEvent(\n new CustomEvent('open-change', {\n detail: { open },\n bubbles: true,\n composed: true\n })\n )\n }\n\n /*\n * 点击代理:冒泡路径穿过 trigger wrapper 的 click 一律切换。语义由 slot 内\n * 的交互元素(原生 button / web-ui-button)提供;slot 放纯文本时无激活语义\n * (README 约定)。内容区与嵌套内层 collapse 的 click 不经过本 wrapper,天然隔离。\n */\n private _onTriggerClick = (event: MouseEvent) => {\n if (this.disabled) return\n if (!event.composedPath().includes(this._triggerWrapper)) return\n this._userOpenChange.mark()\n this.toggle()\n }\n\n /*\n * Trigger 元素取首个 assigned element(与 popover `_queryTrigger` 同款惰性查询,\n * 不依赖 slotchange——jsdom 不派发该事件,无法用状态缓存)。slot 为空时无回写目标。\n */\n private _queryTrigger(): HTMLElement | null {\n const slot = this.shadowRoot?.querySelector<HTMLSlotElement>('.wui-collapse-trigger-wrapper slot')\n const el = slot?.assignedElements()[0]\n return el instanceof HTMLElement ? el : null\n }\n\n /*\n * slot 内容增删不触发宿主响应式更新:显式请求一轮渲染,由 updated() 的\n * ARIA 回写覆盖晚到的 trigger 元素(slotchange 在 jsdom 不派发,无法监听)。\n */\n private readonly _onTriggerSlotChange = () => {\n this.requestUpdate()\n }\n\n // ARIA 状态回写到 trigger 元素本身(wrapper 不可聚焦,AT 读不到)。\n private _syncTriggerAria() {\n const trigger = this._queryTrigger()\n if (!trigger) return\n trigger.setAttribute('aria-expanded', String(this.open))\n trigger.setAttribute('aria-controls', this._contentId)\n if (this.disabled) trigger.setAttribute('aria-disabled', 'true')\n else trigger.removeAttribute('aria-disabled')\n }\n\n // ===== 内容区动画管线(三态语义自 ADR-0038 迁移) =====\n\n private _applyOpenSteadyState() {\n this._generation++\n this._clearExitTimer()\n const container = this._contentContainer()\n const track = this._contentTrack()\n if (container) container.hidden = false\n if (track) {\n track.dataset.wuiPresence = 'open'\n this._setInnerInert(false)\n }\n }\n\n private _applyClosedSteadyState() {\n this._generation++\n this._clearExitTimer()\n const container = this._contentContainer()\n const track = this._contentTrack()\n if (track) delete track.dataset.wuiPresence\n if (this.keepMounted) {\n if (container) container.hidden = false\n this._setInnerInert(true)\n return\n }\n this._setInnerInert(false)\n if (container) container.hidden = true\n }\n\n private _beginExpand() {\n this._generation++\n this._clearExitTimer()\n const container = this._contentContainer()\n const track = this._contentTrack()\n if (!container || !track) return\n\n const generation = this._generation\n // 中断关闭动画时 inner 已 inert:先恢复可交互,内容随展开重新可见。\n this._setInnerInert(false)\n // 从 display:none 进入时无先前计算样式可过渡:提交 0fr 起点后再翻转。\n container.hidden = false\n void track.offsetWidth\n\n requestAnimationFrame(() => {\n if (generation !== this._generation || !this.open) return\n track.dataset.wuiPresence = 'open'\n this._awaitTransition(track, generation, () => this._settle(true))\n })\n }\n\n private _beginCollapse() {\n this._generation++\n this._clearExitTimer()\n const track = this._contentTrack()\n if (!track) return\n\n // 收起期间立即阻断交互,防止焦点落入正在消失的区域。\n this._setInnerInert(true)\n track.dataset.wuiPresence = 'closing'\n const generation = this._generation\n this._awaitTransition(track, generation, () => this._settle(false))\n }\n\n // 关闭稳态:keep-mounted → 内容保留在 0fr 轨道内并 inert;默认 → 容器 hidden。\n private _settle(open: boolean) {\n this._clearExitTimer()\n if (open) {\n this._setInnerInert(false)\n return\n }\n // 落稳态必须清除 closing 瞬态标记:否则 track 永久保留 pointer-events:none\n // 与 exit 过渡配置(keep-mounted 分支同样清除,避免残留)。\n const track = this._contentTrack()\n if (track) delete track.dataset.wuiPresence\n if (this.keepMounted) return\n const container = this._contentContainer()\n this._setInnerInert(false)\n if (container) container.hidden = true\n }\n\n // 等待 grid-template-rows/columns 过渡结束;时长为 0(reduced motion、jsdom)\n // 直接稳态,transitionend 不触发时按时长 + 缓冲兜底。\n private _awaitTransition(track: HTMLElement, generation: number, onDone: () => void) {\n const duration = getTransitionDuration(track, 0)\n if (duration === 0) {\n if (generation === this._generation) onDone()\n return\n }\n\n let settled = false\n const finish = () => {\n track.removeEventListener('transitionend', onTransitionEnd)\n if (this._exitTimer === timer) this._exitTimer = undefined\n clearTimeout(timer)\n // 过渡可能被属性匹配的后续动画触发(如中断重开):代际不符时静默退场。\n if (settled || generation !== this._generation) return\n settled = true\n onDone()\n }\n const timer = setTimeout(finish, duration + EXIT_FALLBACK_BUFFER)\n this._exitTimer = timer\n\n const onTransitionEnd = (event: TransitionEvent) => {\n if (\n event.target === track &&\n (event.propertyName === 'grid-template-rows' || event.propertyName === 'grid-template-columns')\n )\n finish()\n }\n track.addEventListener('transitionend', onTransitionEnd)\n }\n\n private _clearExitTimer() {\n if (this._exitTimer !== undefined) {\n clearTimeout(this._exitTimer)\n this._exitTimer = undefined\n }\n }\n\n private _setInnerInert(inert: boolean) {\n this.shadowRoot?.querySelector<HTMLElement>('.wui-collapse-inner')?.toggleAttribute('inert', inert)\n }\n\n private _contentContainer(): HTMLElement | null {\n return this.shadowRoot?.querySelector<HTMLElement>('.wui-collapse-content') ?? null\n }\n\n private _contentTrack(): HTMLElement | null {\n return this.shadowRoot?.querySelector<HTMLElement>('.wui-collapse-track') ?? null\n }\n\n override render() {\n // 三态由 data-wui-presence 驱动(open/closing),缺省即关闭稳态。\n const trackClass = {\n 'wui-collapse-track': true,\n 'is-horizontal': this.horizontal\n }\n\n return html`\n <div class=\"wui-collapse-trigger-wrapper\" @slotchange=${this._onTriggerSlotChange}><slot></slot></div>\n <div class=\"wui-collapse-content\">\n <div class=${classMap(trackClass)} id=${this._contentId}>\n <div class=\"wui-collapse-inner\">\n <slot name=\"content\"></slot>\n </div>\n </div>\n </div>\n `\n }\n\n declare readonly $events: {\n 'open-change': CustomEvent<{ open: boolean }>\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-collapse': WebUiCollapse\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ //#region src/components/collapse/style.css?inline
2
+ var e = "*,:before,:after{box-sizing:border-box}:host{display:block}.wui-collapse-track{transition:grid-template-rows var(--wui-duration-collapse-enter,.2s) var(--wui-ease-enter,cubic-bezier(.23, 1, .32, 1));grid-template-rows:0fr;display:grid}.wui-collapse-track.is-horizontal{width:max-content;transition:grid-template-columns var(--wui-duration-collapse-enter,.2s) var(--wui-ease-enter,cubic-bezier(.23, 1, .32, 1));grid-template-rows:none;grid-template-columns:0fr}.wui-collapse-track[data-wui-presence=open]{grid-template-rows:1fr}.wui-collapse-track[data-wui-presence=open].is-horizontal{grid-template-rows:none;grid-template-columns:1fr}.wui-collapse-track[data-wui-presence=closing]{pointer-events:none;transition:grid-template-rows var(--wui-duration-collapse-exit,.16s) var(--wui-ease-enter,cubic-bezier(.23, 1, .32, 1))}.wui-collapse-track[data-wui-presence=closing].is-horizontal{transition:grid-template-columns var(--wui-duration-collapse-exit,.16s) var(--wui-ease-enter,cubic-bezier(.23, 1, .32, 1))}.wui-collapse-inner{min-height:0;overflow:hidden}.is-horizontal .wui-collapse-inner{min-width:0}";
3
+ //#endregion
4
+ export { e as default };
5
+
6
+ //# sourceMappingURL=style.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"style.js","names":[],"sources":["../../../src/components/collapse/style.css?inline"],"sourcesContent":["*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n/*\n * :host 仅限 display/contain(ADR-0003)。\n * headless 内核(ADR-0039):不携带视觉样式,trigger 与内容的排版由消费者\n * 提供;这里只保留动画与三态关闭稳态必需的结构规则。\n */\n:host {\n display: block;\n}\n\n/*\n * grid 0fr↔1fr 高度动画(ADR-0038 选型):\n * 轨道行高在 0fr(收起)与 1fr(展开)间过渡,内容高度自适应、零 JS 测量;\n * inner 的 min-height/min-width 归零是轨道能收缩到 0 的必要条件,\n * overflow hidden 负责裁剪收起中的内容。\n */\n.wui-collapse-track {\n display: grid;\n grid-template-rows: 0fr;\n transition: grid-template-rows var(--wui-duration-collapse-enter, 200ms)\n var(--wui-ease-enter, cubic-bezier(0.23, 1, 0.32, 1));\n\n /*\n * 水平方向:块级 grid 默认撑满可用宽度,1fr 列会解析为剩余空间而非内容宽度;\n * width: max-content 让 1fr 列按内容 max-content 贡献解析(与行向 auto 高度同理),\n * 0fr 收缩依赖 inner 的 min-width: 0。\n */\n &.is-horizontal {\n grid-template-columns: 0fr;\n grid-template-rows: none;\n width: max-content;\n transition: grid-template-columns var(--wui-duration-collapse-enter, 200ms)\n var(--wui-ease-enter, cubic-bezier(0.23, 1, 0.32, 1));\n }\n}\n\n.wui-collapse-track[data-wui-presence='open'] {\n grid-template-rows: 1fr;\n\n &.is-horizontal {\n grid-template-columns: 1fr;\n grid-template-rows: none;\n }\n}\n\n.wui-collapse-track[data-wui-presence='closing'] {\n pointer-events: none;\n transition: grid-template-rows var(--wui-duration-collapse-exit, 160ms)\n var(--wui-ease-enter, cubic-bezier(0.23, 1, 0.32, 1));\n\n &.is-horizontal {\n transition: grid-template-columns var(--wui-duration-collapse-exit, 160ms)\n var(--wui-ease-enter, cubic-bezier(0.23, 1, 0.32, 1));\n }\n}\n\n.wui-collapse-inner {\n overflow: hidden;\n min-height: 0;\n}\n\n.is-horizontal .wui-collapse-inner {\n min-width: 0;\n}\n"],"mappings":""}
@@ -8,16 +8,20 @@ export declare class WebUiContextMenu extends LitElement {
8
8
  private _y;
9
9
  private _activeSubmenus;
10
10
  private _activeSubmenuItems;
11
+ private readonly _submenuPositionEpochs;
11
12
  private readonly _closingSubmenus;
12
13
  private _submenuTimer?;
13
14
  private _ignoreOutsideClick;
14
15
  private _ignoreOutsideClickTimer?;
15
16
  private _hoverCleanupFns;
16
17
  private _menu?;
18
+ private readonly _menuItemAnchors;
17
19
  private readonly _scrollLock;
18
20
  private readonly _userOpenChange;
19
21
  private _restoreFocusTarget?;
20
22
  private _shouldOpenInstantly;
23
+ private _refreshScheduled;
24
+ private readonly _contentObserver;
21
25
  get isOpen(): boolean;
22
26
  connectedCallback(): void;
23
27
  disconnectedCallback(): void;
@@ -36,7 +40,11 @@ export declare class WebUiContextMenu extends LitElement {
36
40
  */
37
41
  close(): void;
38
42
  private readonly _closeFromUser;
43
+ private _scheduleRefresh;
44
+ private _refreshMenu;
39
45
  private _positionMenu;
46
+ private _positionMenuInViewport;
47
+ private _applyMenuPosition;
40
48
  private _focusFirstItem;
41
49
  private _setupMenuItems;
42
50
  private _returnItemsToSlot;
@@ -71,6 +79,7 @@ export declare class WebUiContextMenu extends LitElement {
71
79
  private _focusMenuItem;
72
80
  private _isMenuPanelEvent;
73
81
  render(): import('lit').TemplateResult<1>;
82
+ private _onSlotChange;
74
83
  readonly $events: {
75
84
  'open-change': CustomEvent<{
76
85
  open: boolean;
@@ -1,19 +1,22 @@
1
1
  import e from "../../_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorate.js";
2
- import { hideOverlayPresence as t, showOverlayPresence as n } from "../../shared/overlay/presence.js";
3
- import { defineScrollLockLease as r } from "../../shared/scroll-lock/scroll-lock.js";
2
+ import { computePosition as t, shift as n } from "../../node_modules/.pnpm/@floating-ui_dom@1.8.0/node_modules/@floating-ui/dom/dist/floating-ui.dom.js";
3
+ import { hideOverlayPresence as r, showOverlayPresence as i } from "../../shared/overlay/presence.js";
4
+ import { defineScrollLockLease as a } from "../../shared/scroll-lock/scroll-lock.js";
5
+ import { UserChangeController as o } from "../../shared/events/user-change.js";
4
6
  import "../dropdown-divider/index.js";
5
7
  import "../dropdown-header/index.js";
6
8
  import "../dropdown-item/index.js";
7
- import { UserChangeController as i } from "../../shared/events/user-change.js";
8
- import { createMenuPortalOverlay as a } from "../../shared/menu-portal/menu-portal.js";
9
- import { findFocusedMenuItem as o, focusMenuItem as s, getEnabledMenuItems as c, getMenuChildren as l, getMenuItemFromEvent as u, hideNestedMenuChildren as d, moveMenuChildren as f } from "../../shared/menu-portal/menu-tree.js";
10
- import p from "./style.js";
11
- import { LitElement as m, html as h, unsafeCSS as g } from "lit";
12
- import { customElement as _, property as v, state as y } from "lit/decorators.js";
9
+ import { createMenuPortalOverlay as s } from "../../shared/menu-portal/menu-portal.js";
10
+ import { captureFrameworkAnchors as c, findFocusedMenuItem as l, focusMenuItem as u, getEnabledMenuItems as d, getMenuChildren as f, getMenuItemFromEvent as p, hideNestedMenuChildren as m, moveMenuChildren as h, orderManagedMenuItems as g, reconcileManagedMenuItems as _, restoreFrameworkAnchors as v, returnManagedMenuItemsToSlot as y } from "../../shared/menu-portal/menu-tree.js";
11
+ import b from "./style.js";
12
+ import { LitElement as x, html as S, unsafeCSS as C } from "lit";
13
+ import { customElement as w, property as T, state as E } from "lit/decorators.js";
13
14
  //#region src/components/context-menu/index.ts
14
- var b = class extends m {
15
+ var D = "wui-context-menu-item", O = class extends x {
15
16
  constructor(...e) {
16
- super(...e), this.disabled = !1, this.noScrollLock = !1, this._isOpen = !1, this._x = 0, this._y = 0, this._activeSubmenus = [], this._activeSubmenuItems = [], this._closingSubmenus = /* @__PURE__ */ new Map(), this._ignoreOutsideClick = !1, this._hoverCleanupFns = [], this._scrollLock = r().make(), this._userOpenChange = new i(), this._shouldOpenInstantly = !0, this._closeFromUser = () => {
17
+ super(...e), this.disabled = !1, this.noScrollLock = !1, this._isOpen = !1, this._x = 0, this._y = 0, this._activeSubmenus = [], this._activeSubmenuItems = [], this._submenuPositionEpochs = /* @__PURE__ */ new WeakMap(), this._closingSubmenus = /* @__PURE__ */ new Map(), this._ignoreOutsideClick = !1, this._hoverCleanupFns = [], this._menuItemAnchors = /* @__PURE__ */ new Map(), this._scrollLock = a().make(), this._userOpenChange = new o(), this._shouldOpenInstantly = !0, this._refreshScheduled = !1, this._contentObserver = new MutationObserver(() => {
18
+ this._isOpen && this._scheduleRefresh();
19
+ }), this._closeFromUser = () => {
17
20
  this._isOpen && (this._userOpenChange.mark(), this.close());
18
21
  }, this._onContextMenu = (e) => {
19
22
  this.disabled || (e.preventDefault(), this._restoreFocusTarget = e.target instanceof HTMLElement ? e.target : void 0, this._openAt(e.clientX, e.clientY, !1) && this._userOpenChange.mark());
@@ -32,7 +35,7 @@ var b = class extends m {
32
35
  }, this._onClickOutside = (e) => {
33
36
  !this._isOpen || this._ignoreOutsideClick || this._isInsideShadowRoot(e) || this._closeFromUser();
34
37
  }, this._onMenuClick = (e) => {
35
- let t = u(e);
38
+ let t = p(e);
36
39
  if (!(!t || t.hasAttribute("disabled"))) {
37
40
  if (t.hasAttribute("submenu")) {
38
41
  this._openSubmenu(t);
@@ -88,7 +91,7 @@ var b = class extends m {
88
91
  };
89
92
  }
90
93
  static {
91
- this.styles = g(p);
94
+ this.styles = C(b);
92
95
  }
93
96
  get isOpen() {
94
97
  return this._isOpen;
@@ -103,34 +106,66 @@ var b = class extends m {
103
106
  }), document.addEventListener("keydown", this._onDocumentKeydown);
104
107
  }
105
108
  disconnectedCallback() {
106
- super.disconnectedCallback(), this.removeEventListener("contextmenu", this._onContextMenu), this.removeEventListener("keydown", this._onKeydown), this.removeEventListener("click", this._onMenuClick), document.removeEventListener("click", this._onClickOutside), document.removeEventListener("contextmenu", this._onContextMenuOutside), document.removeEventListener("wheel", this._onWheel, !0), document.removeEventListener("touchmove", this._onTouchMove, !0), document.removeEventListener("keydown", this._onDocumentKeydown), clearTimeout(this._submenuTimer), clearTimeout(this._ignoreOutsideClickTimer), this._hoverCleanupFns.forEach((e) => e()), this._scrollLock.release(), this._returnItemsToSlot(), this._menu?.panel.remove(), this._menu = void 0, this._closeSubmenusFrom(0, !0), this._restoreClosingSubmenus();
109
+ super.disconnectedCallback(), this.removeEventListener("contextmenu", this._onContextMenu), this.removeEventListener("keydown", this._onKeydown), this.removeEventListener("click", this._onMenuClick), document.removeEventListener("click", this._onClickOutside), document.removeEventListener("contextmenu", this._onContextMenuOutside), document.removeEventListener("wheel", this._onWheel, !0), document.removeEventListener("touchmove", this._onTouchMove, !0), document.removeEventListener("keydown", this._onDocumentKeydown), this._contentObserver.disconnect(), clearTimeout(this._submenuTimer), clearTimeout(this._ignoreOutsideClickTimer), this._hoverCleanupFns.forEach((e) => e()), this._scrollLock.release(), this._returnItemsToSlot(), this._menu?.panel.remove(), this._menu = void 0, this._closeSubmenusFrom(0, !0), this._restoreClosingSubmenus(), this._isOpen = !1, this._restoreFocusTarget = void 0;
107
110
  }
108
111
  updated(e) {
109
112
  if (!this.isConnected) {
110
113
  this._syncScrollLock(!1);
111
114
  return;
112
115
  }
113
- e.has("_isOpen") && (this._isOpen ? (this._syncScrollLock(), this._menu || (this._menu = a("context-menu", this), this._menu.panel.setAttribute("role", "menu"), this._menu.panel.setAttribute("aria-label", "上下文菜单"), this._menu.panel.addEventListener("click", this._onMenuClick)), requestAnimationFrame(() => {
114
- !this._isOpen || !this._menu || (this._setupMenuItems(), this._positionMenu(), n(this._menu.panel, { isInstant: this._shouldOpenInstantly }), this._shouldOpenInstantly = !0, this._focusFirstItem(), this._bindLevelHovers());
115
- })) : (this._syncScrollLock(!1), this._closeMenuAfterPresence()), this._userOpenChange.consume() && this._dispatchChange(this._isOpen)), e.has("noScrollLock") && this._syncScrollLock();
116
+ e.has("_isOpen") && (this._isOpen ? (this._syncScrollLock(), this._menu || (this._menu = s("context-menu", this), this._menu.panel.setAttribute("role", "menu"), this._menu.panel.setAttribute("aria-label", "上下文菜单"), this._menu.panel.addEventListener("click", this._onMenuClick)), this._contentObserver.observe(this._menu.content, {
117
+ childList: !0,
118
+ subtree: !0
119
+ }), requestAnimationFrame(() => {
120
+ !this._isOpen || !this._menu || (this._refreshMenu(), i(this._menu.panel, { isInstant: this._shouldOpenInstantly }), this._shouldOpenInstantly = !0, this._focusFirstItem());
121
+ })) : (this._syncScrollLock(!1), this._contentObserver.disconnect(), this._refreshScheduled = !1, this._closeMenuAfterPresence()), this._userOpenChange.consume() && this._dispatchChange(this._isOpen)), e.has("noScrollLock") && this._syncScrollLock();
116
122
  }
117
123
  openAt(e, t) {
118
124
  this._openAt(e, t, !0);
119
125
  }
120
126
  _openAt(e, t, n) {
121
- return !this.disabled && (this._x = e, this._y = t, this._shouldOpenInstantly = n, this._ignoreCurrentOutsideClick(), this._isOpen ? (this._closeSubmenusFrom(0, !0), this._restoreClosingSubmenus(), requestAnimationFrame(() => this._positionMenu()), !1) : (this._restoreFocusTarget ??= document.activeElement instanceof HTMLElement ? document.activeElement : void 0, this._isOpen = !0, !0));
127
+ return !this.disabled && (this._x = e, this._y = t, this._shouldOpenInstantly = n, this._ignoreCurrentOutsideClick(), this._isOpen ? (this._closeSubmenusFrom(0, !0), this._restoreClosingSubmenus(), this._scheduleRefresh(), !1) : (this._restoreFocusTarget ??= document.activeElement instanceof HTMLElement ? document.activeElement : void 0, this._isOpen = !0, !0));
122
128
  }
123
129
  close() {
124
130
  this._isOpen &&= !1;
125
131
  }
132
+ _scheduleRefresh() {
133
+ this._refreshScheduled || !this._isOpen || (this._refreshScheduled = !0, requestAnimationFrame(() => {
134
+ this._refreshScheduled = !1, !(!this._isOpen || !this._menu) && this._refreshMenu();
135
+ }));
136
+ }
137
+ _refreshMenu() {
138
+ this._menu && (this._setupMenuItems(), this._positionMenu(), this._bindLevelHovers());
139
+ }
126
140
  _positionMenu() {
127
141
  let e = this._menu?.panel;
128
- if (!e) return;
129
- e.style.visibility = "hidden", e.style.display = "";
142
+ if (e) {
143
+ if (e.style.visibility = "hidden", e.style.display = "", !(e.parentElement instanceof HTMLDialogElement && e.parentElement.open)) {
144
+ this._positionMenuInViewport(e);
145
+ return;
146
+ }
147
+ t({ getBoundingClientRect: () => new DOMRect(this._x, this._y, 0, 0) }, e, {
148
+ strategy: "fixed",
149
+ placement: "bottom-start",
150
+ middleware: [n({
151
+ padding: 8,
152
+ crossAxis: !0
153
+ })]
154
+ }).then(({ x: t, y: n, middlewareData: r }) => {
155
+ if (!this._isOpen || this._menu?.panel !== e) return;
156
+ let i = r.shift?.x ?? 0, a = r.shift?.y ?? 0;
157
+ this._applyMenuPosition(e, t, n, i < 0 ? "right" : "left", a < 0 ? "bottom" : "top");
158
+ });
159
+ }
160
+ }
161
+ _positionMenuInViewport(e) {
130
162
  let { width: t, height: n } = e.getBoundingClientRect(), r = window.innerWidth, i = window.innerHeight, a = this._x, o = this._y;
131
- a + t > r && (a = r - t - 8), o + n > i && (o = i - n - 8), a < 0 && (a = 8), o < 0 && (o = 8), e.style.left = `${a}px`, e.style.top = `${o}px`;
132
- let s = a < this._x ? "right" : "left", c = o < this._y ? "bottom" : "top";
133
- e.style.setProperty("--wui-internal-overlay-transform-origin", `${c} ${s}`), e.style.visibility = "";
163
+ a + t > r && (a = r - t - 8), o + n > i && (o = i - n - 8), a < 0 && (a = 8), o < 0 && (o = 8), this._applyMenuPosition(e, a, o);
164
+ }
165
+ _applyMenuPosition(e, t, n, r, i) {
166
+ e.style.left = `${t}px`, e.style.top = `${n}px`;
167
+ let a = r ?? (t < this._x ? "right" : "left"), o = i ?? (n < this._y ? "bottom" : "top");
168
+ e.style.setProperty("--wui-internal-overlay-transform-origin", `${o} ${a}`), e.style.visibility = "";
134
169
  }
135
170
  _focusFirstItem() {
136
171
  let e = this._menu?.content.querySelectorAll("web-ui-dropdown-item:not([disabled])")?.[0];
@@ -138,32 +173,36 @@ var b = class extends m {
138
173
  }
139
174
  _setupMenuItems() {
140
175
  let e = this._menu;
141
- e && (this._hideMenuItems(), f(this, e.content));
176
+ if (!e) return;
177
+ let t = e.content;
178
+ _(this, t, this._menuItemAnchors, D), this._hideMenuItems(), m(t, "context-menu-hidden");
179
+ let n = c(t, D);
180
+ g(this, t, this._menuItemAnchors, D), v(t, n);
142
181
  }
143
182
  _returnItemsToSlot() {
144
183
  let e = this._menu;
145
- e && (this._closeSubmenusFrom(0, !0), this._restoreClosingSubmenus(), f(e.content, this));
184
+ e && (this._closeSubmenusFrom(0, !0), this._restoreClosingSubmenus(), y(this, e.content, this._menuItemAnchors));
146
185
  }
147
186
  async _closeMenuAfterPresence() {
148
187
  let e = this._menu;
149
- e && !await t(e.panel) || this._isOpen || !this.isConnected || this._menu !== e || (this._returnItemsToSlot(), e?.panel.remove(), this._menu = void 0, this._restoreFocusTarget?.focus(), this._restoreFocusTarget = void 0);
188
+ e && !await r(e.panel) || this._isOpen || !this.isConnected || this._menu !== e || (this._returnItemsToSlot(), e?.panel.remove(), this._menu = void 0, this._restoreFocusTarget?.focus(), this._restoreFocusTarget = void 0);
150
189
  }
151
190
  _hideMenuItems() {
152
- l(this).forEach((e) => e.setAttribute("slot", "context-menu-hidden")), d(this, "context-menu-hidden");
191
+ f(this).forEach((e) => e.setAttribute("slot", "context-menu-hidden")), m(this, "context-menu-hidden");
153
192
  }
154
193
  _openSubmenu(e, t = !1) {
155
194
  if (!this._isOpen || !e.hasAttribute("submenu") || e.hasAttribute("disabled")) return;
156
- let r = this._getItemLevel(e);
157
- if (r === -1 || this._activeSubmenuItems[r] === e) return;
158
- this._closeSubmenusFrom(r);
159
- let i = this._closingSubmenus.get(e);
160
- if (i) {
161
- this._closingSubmenus.delete(e), this._activeSubmenus[r] = i, this._activeSubmenuItems[r] = e, e.setAttribute("active", ""), this._positionSubmenu(e, i), n(i.panel, { isInstant: t }), this._bindLevelHovers();
195
+ let n = this._getItemLevel(e);
196
+ if (n === -1 || this._activeSubmenuItems[n] === e) return;
197
+ this._closeSubmenusFrom(n);
198
+ let r = this._closingSubmenus.get(e);
199
+ if (r) {
200
+ this._closingSubmenus.delete(e), this._activeSubmenus[n] = r, this._activeSubmenuItems[n] = e, e.setAttribute("active", ""), this._positionSubmenu(e, r), i(r.panel, { isInstant: t }), this._bindLevelHovers();
162
201
  return;
163
202
  }
164
- if (l(e).length === 0) return;
165
- let o = a("context-submenu", this);
166
- o.panel.dataset.level = String(r), o.panel.setAttribute("role", "menu"), o.panel.setAttribute("aria-label", "子菜单"), o.panel.style.visibility = "hidden", o.panel.addEventListener("click", this._onMenuClick), f(e, o.content), this._activeSubmenus[r] = o, this._activeSubmenuItems[r] = e, e.setAttribute("active", ""), this._positionSubmenu(e, o), n(o.panel, { isInstant: t }), this._bindLevelHovers();
203
+ if (f(e).length === 0) return;
204
+ let a = s("context-submenu", this);
205
+ a.panel.dataset.level = String(n), a.panel.setAttribute("role", "menu"), a.panel.setAttribute("aria-label", "子菜单"), a.panel.style.visibility = "hidden", a.panel.addEventListener("click", this._onMenuClick), h(e, a.content), this._activeSubmenus[n] = a, this._activeSubmenuItems[n] = e, e.setAttribute("active", ""), this._positionSubmenu(e, a), i(a.panel, { isInstant: t }), this._bindLevelHovers();
167
206
  }
168
207
  _closeSubmenusFrom(e, t = !1) {
169
208
  for (let n = this._activeSubmenus.length - 1; n >= e; n--) {
@@ -172,11 +211,11 @@ var b = class extends m {
172
211
  }
173
212
  this._activeSubmenus.length = e, this._activeSubmenuItems.length = e;
174
213
  }
175
- async _closeSubmenuAfterPresence(e, n) {
176
- await t(e.panel) && this._closingSubmenus.get(n) === e && (this._closingSubmenus.delete(n), this._restoreSubmenuItems(e, n), e.panel.remove());
214
+ async _closeSubmenuAfterPresence(e, t) {
215
+ await r(e.panel) && this._closingSubmenus.get(t) === e && (this._closingSubmenus.delete(t), this._restoreSubmenuItems(e, t), e.panel.remove());
177
216
  }
178
217
  _restoreSubmenuItems(e, t) {
179
- t && f(e.content, t);
218
+ t && (h(e.content, t), m(t, "context-menu-hidden"));
180
219
  }
181
220
  _restoreClosingSubmenus() {
182
221
  this._closingSubmenus.forEach((e, t) => {
@@ -188,9 +227,25 @@ var b = class extends m {
188
227
  let t = this._activeSubmenus.findIndex((t) => t.panel.contains(e));
189
228
  return t === -1 ? -1 : t + 1;
190
229
  }
191
- _positionSubmenu(e, t) {
192
- let n = e.getBoundingClientRect(), r = t.panel.getBoundingClientRect(), i = n.right + r.width + 8 <= window.innerWidth, a = i ? n.right : Math.max(8, n.left - r.width), o = Math.min(Math.max(8, n.top), window.innerHeight - r.height - 8);
193
- t.panel.style.left = `${a}px`, t.panel.style.top = `${o}px`, t.panel.style.setProperty("--wui-internal-overlay-transform-origin", i ? "top left" : "top right"), t.panel.style.visibility = "";
230
+ _positionSubmenu(e, r) {
231
+ if (r.panel.parentElement instanceof HTMLDialogElement && r.panel.parentElement.open) {
232
+ let i = e.getBoundingClientRect().right + r.panel.getBoundingClientRect().width + 8 <= window.innerWidth, a = (this._submenuPositionEpochs.get(r.panel) ?? 0) + 1;
233
+ this._submenuPositionEpochs.set(r.panel, a), t(e, r.panel, {
234
+ strategy: "fixed",
235
+ placement: i ? "right-start" : "left-start",
236
+ middleware: [n({
237
+ padding: 8,
238
+ crossAxis: !0
239
+ })]
240
+ }).then(({ x: e, y: t, middlewareData: n }) => {
241
+ if (!this._activeSubmenus.includes(r) || a !== this._submenuPositionEpochs.get(r.panel)) return;
242
+ let i = n.shift?.x ?? 0, o = n.shift?.y ?? 0, s = i < 0 ? "right" : "left", c = o < 0 ? "bottom" : "top";
243
+ r.panel.style.left = `${e}px`, r.panel.style.top = `${t}px`, r.panel.style.setProperty("--wui-internal-overlay-transform-origin", `${c} ${s}`), r.panel.style.visibility = "";
244
+ });
245
+ return;
246
+ }
247
+ let i = e.getBoundingClientRect(), a = r.panel.getBoundingClientRect(), o = i.right + a.width + 8 <= window.innerWidth, s = o ? i.right : Math.max(8, i.left - a.width), c = Math.min(Math.max(8, i.top), window.innerHeight - a.height - 8);
248
+ r.panel.style.left = `${s}px`, r.panel.style.top = `${c}px`, r.panel.style.setProperty("--wui-internal-overlay-transform-origin", o ? "top left" : "top right"), r.panel.style.visibility = "";
194
249
  }
195
250
  _dispatchChange(e) {
196
251
  this.dispatchEvent(new CustomEvent("open-change", {
@@ -205,7 +260,7 @@ var b = class extends m {
205
260
  }
206
261
  _getLevelItems(e) {
207
262
  let t = e === 0 ? this._menu?.content : this._activeSubmenus[e - 1]?.content;
208
- return t ? l(t) : [];
263
+ return t ? f(t) : [];
209
264
  }
210
265
  _bindLevelHovers() {
211
266
  this._hoverCleanupFns.forEach((e) => e()), this._hoverCleanupFns.length = 0;
@@ -236,10 +291,10 @@ var b = class extends m {
236
291
  }
237
292
  _getEnabledLevelItems(e) {
238
293
  let t = e === 0 ? this._menu?.content : this._activeSubmenus[e - 1]?.content;
239
- return t ? c(t) : [];
294
+ return t ? d(t) : [];
240
295
  }
241
296
  _getFocusedItem() {
242
- return o([this._menu?.content, ...this._activeSubmenus.map((e) => e.content)]);
297
+ return l([this._menu?.content, ...this._activeSubmenus.map((e) => e.content)]);
243
298
  }
244
299
  _getFocusedLevel() {
245
300
  let e = this._getFocusedItem();
@@ -249,28 +304,31 @@ var b = class extends m {
249
304
  return t < 0 ? void 0 : t + 1;
250
305
  }
251
306
  _focusMenuItem(e) {
252
- s(e);
307
+ u(e);
253
308
  }
254
309
  _isMenuPanelEvent(e) {
255
310
  return e.composedPath().some((e) => e instanceof HTMLElement && (e.classList.contains("context-menu") || e.classList.contains("context-submenu")));
256
311
  }
257
312
  render() {
258
- return h`
313
+ return S`
259
314
  <div class="context-menu-anchor">
260
- <slot @slotchange=${this._hideMenuItems}></slot>
315
+ <slot @slotchange=${this._onSlotChange}></slot>
261
316
  </div>
262
317
  `;
263
318
  }
319
+ _onSlotChange() {
320
+ this._hideMenuItems(), this._isOpen && this._scheduleRefresh();
321
+ }
264
322
  };
265
- e([v({
323
+ e([T({
266
324
  type: Boolean,
267
325
  reflect: !0
268
- })], b.prototype, "disabled", void 0), e([v({
326
+ })], O.prototype, "disabled", void 0), e([T({
269
327
  type: Boolean,
270
328
  reflect: !0,
271
329
  attribute: "no-scroll-lock"
272
- })], b.prototype, "noScrollLock", void 0), e([y()], b.prototype, "_isOpen", void 0), e([y()], b.prototype, "_x", void 0), e([y()], b.prototype, "_y", void 0), b = e([_("web-ui-context-menu")], b);
330
+ })], O.prototype, "noScrollLock", void 0), e([E()], O.prototype, "_isOpen", void 0), e([E()], O.prototype, "_x", void 0), e([E()], O.prototype, "_y", void 0), O = e([w("web-ui-context-menu")], O);
273
331
  //#endregion
274
- export { b as WebUiContextMenu };
332
+ export { O as WebUiContextMenu };
275
333
 
276
334
  //# sourceMappingURL=index.js.map