@seekbuddy/dsh-ui 0.0.0-stage → 0.2.0-beta.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 (213) hide show
  1. package/CHANGELOG.md +83 -0
  2. package/LICENSE +22 -0
  3. package/README.md +175 -2
  4. package/README.zh.md +176 -0
  5. package/THIRD_PARTY_NOTICES.md +42 -0
  6. package/UPSTREAM.md +59 -0
  7. package/dist/blocks.js +1617 -0
  8. package/dist/chunks/chunk-23KOEBWJ.js +104 -0
  9. package/dist/chunks/chunk-6SGSJ5KW.js +57 -0
  10. package/dist/chunks/chunk-7NH2UOZO.js +155 -0
  11. package/dist/chunks/chunk-7SKW4S35.js +66 -0
  12. package/dist/chunks/chunk-B3EEK7A2.js +408 -0
  13. package/dist/chunks/chunk-CEBTQO6W.js +24 -0
  14. package/dist/chunks/chunk-ENKPNYOB.js +26 -0
  15. package/dist/chunks/chunk-EWAZFWLW.js +27 -0
  16. package/dist/chunks/chunk-F352IICS.js +57 -0
  17. package/dist/chunks/chunk-FFJ6TCCF.js +31 -0
  18. package/dist/chunks/chunk-FZXK4LTI.js +129 -0
  19. package/dist/chunks/chunk-JA3XDLIA.js +1115 -0
  20. package/dist/chunks/chunk-L7XALG4J.js +36 -0
  21. package/dist/chunks/chunk-OCAQFVT3.js +3172 -0
  22. package/dist/chunks/chunk-RAXDA2PT.js +202 -0
  23. package/dist/chunks/chunk-RNVSCTW7.js +24 -0
  24. package/dist/chunks/chunk-RZOP2PFC.js +152 -0
  25. package/dist/chunks/chunk-SN6RSUWC.js +61 -0
  26. package/dist/chunks/chunk-SNVSQWCU.js +22 -0
  27. package/dist/chunks/chunk-SO3DCAWG.js +170 -0
  28. package/dist/chunks/chunk-UAEFKTV3.js +144 -0
  29. package/dist/chunks/chunk-UGEORMLD.js +89 -0
  30. package/dist/chunks/chunk-W6FLI3AK.js +112 -0
  31. package/dist/chunks/chunk-WX46TSKP.js +328 -0
  32. package/dist/chunks/chunk-XL33IPCV.js +261 -0
  33. package/dist/chunks/chunk-XW4L4ZGL.js +967 -0
  34. package/dist/chunks/chunk-YOBY6PSL.js +850 -0
  35. package/dist/chunks/chunk-ZA47FP7C.js +7 -0
  36. package/dist/icons.js +859 -0
  37. package/dist/index.js +1675 -0
  38. package/dist/katex/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  39. package/dist/katex/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  40. package/dist/katex/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  41. package/dist/katex/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  42. package/dist/katex/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  43. package/dist/katex/fonts/KaTeX_Main-Bold.woff2 +0 -0
  44. package/dist/katex/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  45. package/dist/katex/fonts/KaTeX_Main-Italic.woff2 +0 -0
  46. package/dist/katex/fonts/KaTeX_Main-Regular.woff2 +0 -0
  47. package/dist/katex/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  48. package/dist/katex/fonts/KaTeX_Math-Italic.woff2 +0 -0
  49. package/dist/katex/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  50. package/dist/katex/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  51. package/dist/katex/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  52. package/dist/katex/fonts/KaTeX_Script-Regular.woff2 +0 -0
  53. package/dist/katex/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  54. package/dist/katex/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  55. package/dist/katex/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  56. package/dist/katex/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  57. package/dist/katex/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  58. package/dist/katex.css +1 -0
  59. package/dist/katex.css.d.ts +2 -0
  60. package/dist/markdown.js +225 -0
  61. package/dist/meta.json +7575 -0
  62. package/dist/root-inputs.json +86 -0
  63. package/dist/seekbuddy-card.js +7 -0
  64. package/dist/seekbuddy-empty.js +8 -0
  65. package/dist/seekbuddy-internal/DangerButton.js +7 -0
  66. package/dist/seekbuddy-internal/Mark.js +7 -0
  67. package/dist/seekbuddy-internal/Message.js +10 -0
  68. package/dist/seekbuddy-internal/Pagination.js +9 -0
  69. package/dist/seekbuddy-internal/StatusBadge.js +7 -0
  70. package/dist/seekbuddy-internal/SubtleButton.js +7 -0
  71. package/dist/seekbuddy-internal/Switch.js +7 -0
  72. package/dist/seekbuddy-internal/Upload.js +7 -0
  73. package/dist/seekbuddy-internal/date/Calendar.js +8 -0
  74. package/dist/seekbuddy-internal/date/DatePicker.js +9 -0
  75. package/dist/seekbuddy-internal/date/DateRangePicker.js +9 -0
  76. package/dist/seekbuddy-internal/date/TimePicker.js +8 -0
  77. package/dist/seekbuddy-internal/date/fields.js +16 -0
  78. package/dist/seekbuddy-internal/qr/QrCode.js +7 -0
  79. package/dist/seekbuddy-spinner.js +7 -0
  80. package/dist/seekbuddy-tabs.js +7 -0
  81. package/dist/seekbuddy-timeline.js +8 -0
  82. package/dist/seekbuddy.js +89 -0
  83. package/dist/theme-bootstrap.js +13 -0
  84. package/dist/theme.js +37 -0
  85. package/dist/tokens.css +974 -0
  86. package/dist/tokens.css.d.ts +2 -0
  87. package/dist/types/AnimatedRows.d.ts +45 -0
  88. package/dist/types/Button.d.ts +18 -0
  89. package/dist/types/Checkbox.d.ts +19 -0
  90. package/dist/types/CodeFileIcon.d.ts +11 -0
  91. package/dist/types/CodeToolbar.d.ts +29 -0
  92. package/dist/types/CommandText.d.ts +15 -0
  93. package/dist/types/ConfigField.d.ts +32 -0
  94. package/dist/types/ConnectionIndicator.d.ts +25 -0
  95. package/dist/types/DetailDocument.d.ts +77 -0
  96. package/dist/types/DiffBlock.d.ts +51 -0
  97. package/dist/types/DisclosureRow.d.ts +37 -0
  98. package/dist/types/DraggableImage.d.ts +18 -0
  99. package/dist/types/FileTypeIcon.d.ts +42 -0
  100. package/dist/types/FoldToggle.d.ts +19 -0
  101. package/dist/types/HoverCard.d.ts +44 -0
  102. package/dist/types/HoverCardGroup.d.ts +24 -0
  103. package/dist/types/ImageLightbox.d.ts +29 -0
  104. package/dist/types/ImagePreview.d.ts +11 -0
  105. package/dist/types/InlineEditor.d.ts +22 -0
  106. package/dist/types/Input.d.ts +11 -0
  107. package/dist/types/JsonTree.d.ts +58 -0
  108. package/dist/types/LinkIcon.d.ts +33 -0
  109. package/dist/types/Menu.d.ts +163 -0
  110. package/dist/types/MenuGroup.d.ts +21 -0
  111. package/dist/types/MenuSurface.d.ts +24 -0
  112. package/dist/types/Modal.d.ts +43 -0
  113. package/dist/types/PermissionIcon.d.ts +50 -0
  114. package/dist/types/Pill.d.ts +12 -0
  115. package/dist/types/QuotePreview.d.ts +23 -0
  116. package/dist/types/ReadBlock.d.ts +47 -0
  117. package/dist/types/ReferenceIcon.d.ts +20 -0
  118. package/dist/types/RiskConfirmation.d.ts +19 -0
  119. package/dist/types/SearchBlock.d.ts +71 -0
  120. package/dist/types/SegmentedControl.d.ts +34 -0
  121. package/dist/types/SegmentedTabs.d.ts +27 -0
  122. package/dist/types/ShortcutKeys.d.ts +10 -0
  123. package/dist/types/SiteGlyph.d.ts +24 -0
  124. package/dist/types/StateDot.d.ts +19 -0
  125. package/dist/types/Switch.d.ts +19 -0
  126. package/dist/types/Tag.d.ts +31 -0
  127. package/dist/types/TerminalBlock.d.ts +85 -0
  128. package/dist/types/TextShimmer.d.ts +22 -0
  129. package/dist/types/Toast.d.ts +48 -0
  130. package/dist/types/Tooltip.d.ts +69 -0
  131. package/dist/types/UserMessageBubble.d.ts +10 -0
  132. package/dist/types/WebBlock.d.ts +60 -0
  133. package/dist/types/ZoomControls.d.ts +26 -0
  134. package/dist/types/ansi.d.ts +16 -0
  135. package/dist/types/blocks.d.ts +21 -0
  136. package/dist/types/clipboard.d.ts +8 -0
  137. package/dist/types/code-file-icon-artwork.d.ts +53 -0
  138. package/dist/types/code-file-types.d.ts +29 -0
  139. package/dist/types/code-highlighting.d.ts +11 -0
  140. package/dist/types/css-modules.d.ts +6 -0
  141. package/dist/types/darwin-desktop.d.ts +8 -0
  142. package/dist/types/document-zoom.d.ts +16 -0
  143. package/dist/types/file-drag-actions.d.ts +36 -0
  144. package/dist/types/file-size.d.ts +7 -0
  145. package/dist/types/focus.d.ts +15 -0
  146. package/dist/types/guide-artwork.d.ts +14 -0
  147. package/dist/types/head-tail-cap.d.ts +22 -0
  148. package/dist/types/icons/artwork.d.ts +421 -0
  149. package/dist/types/icons/index.d.ts +397 -0
  150. package/dist/types/icons/props.d.ts +8 -0
  151. package/dist/types/icons/shared-artwork.d.ts +36 -0
  152. package/dist/types/icons.d.ts +6 -0
  153. package/dist/types/image-drag.d.ts +16 -0
  154. package/dist/types/index.d.ts +71 -0
  155. package/dist/types/input-modality.d.ts +18 -0
  156. package/dist/types/keyboard-composition.d.ts +10 -0
  157. package/dist/types/markdown/CodeBlock.d.ts +34 -0
  158. package/dist/types/markdown/JsonBlock.d.ts +7 -0
  159. package/dist/types/markdown/MarkdownDelegate.d.ts +68 -0
  160. package/dist/types/markdown/MarkdownText.d.ts +54 -0
  161. package/dist/types/markdown/cjkFriendlyStrong.d.ts +8 -0
  162. package/dist/types/markdown/file-link.d.ts +22 -0
  163. package/dist/types/markdown/highlight.d.ts +169 -0
  164. package/dist/types/markdown/incremental.d.ts +79 -0
  165. package/dist/types/markdown/katex.d.ts +18 -0
  166. package/dist/types/markdown/local-image-syntax.d.ts +9 -0
  167. package/dist/types/markdown/mathCompatibility.d.ts +10 -0
  168. package/dist/types/markdown/parse.d.ts +24 -0
  169. package/dist/types/markdown/plain-text.d.ts +21 -0
  170. package/dist/types/markdown/quote-text.d.ts +7 -0
  171. package/dist/types/markdown/render.d.ts +145 -0
  172. package/dist/types/markdown/useViewportHighlighting.d.ts +10 -0
  173. package/dist/types/markdown/windows-path-destinations.d.ts +10 -0
  174. package/dist/types/markdown.d.ts +12 -0
  175. package/dist/types/overlay-top-margin.d.ts +14 -0
  176. package/dist/types/plugin-artwork.d.ts +16 -0
  177. package/dist/types/pointer-grace.d.ts +21 -0
  178. package/dist/types/rank-by-name.d.ts +21 -0
  179. package/dist/types/relative-time.d.ts +22 -0
  180. package/dist/types/seekbuddy/Card.d.ts +28 -0
  181. package/dist/types/seekbuddy/DangerButton.d.ts +31 -0
  182. package/dist/types/seekbuddy/Empty.d.ts +34 -0
  183. package/dist/types/seekbuddy/Mark.d.ts +27 -0
  184. package/dist/types/seekbuddy/Message.d.ts +52 -0
  185. package/dist/types/seekbuddy/Pagination.d.ts +66 -0
  186. package/dist/types/seekbuddy/Spinner.d.ts +20 -0
  187. package/dist/types/seekbuddy/StatusBadge.d.ts +33 -0
  188. package/dist/types/seekbuddy/SubtleButton.d.ts +30 -0
  189. package/dist/types/seekbuddy/Switch.d.ts +36 -0
  190. package/dist/types/seekbuddy/Tabs.d.ts +49 -0
  191. package/dist/types/seekbuddy/Timeline.d.ts +42 -0
  192. package/dist/types/seekbuddy/Upload.d.ts +101 -0
  193. package/dist/types/seekbuddy/date/Calendar.d.ts +94 -0
  194. package/dist/types/seekbuddy/date/DatePicker.d.ts +29 -0
  195. package/dist/types/seekbuddy/date/DateRangePicker.d.ts +41 -0
  196. package/dist/types/seekbuddy/date/TimePicker.d.ts +35 -0
  197. package/dist/types/seekbuddy/date/date.d.ts +79 -0
  198. package/dist/types/seekbuddy/date/fields.d.ts +45 -0
  199. package/dist/types/seekbuddy/index.d.ts +49 -0
  200. package/dist/types/seekbuddy/qr/QrCode.d.ts +37 -0
  201. package/dist/types/seekbuddy/qr/qr.d.ts +38 -0
  202. package/dist/types/theme-bootstrap.d.ts +3 -0
  203. package/dist/types/theme-core.d.ts +14 -0
  204. package/dist/types/theme.d.ts +4 -0
  205. package/dist/types/tooltip-placement.d.ts +34 -0
  206. package/dist/types/use-clipboard-copy.d.ts +13 -0
  207. package/dist/types/useAnchoredMaxHeight.d.ts +13 -0
  208. package/dist/types/useAnchoredPosition.d.ts +35 -0
  209. package/dist/types/useDismissOnOutsidePointer.d.ts +10 -0
  210. package/dist/types/useModalLayer.d.ts +26 -0
  211. package/dist/types/useRowDragPreview.d.ts +19 -0
  212. package/dist/types/user-text.d.ts +31 -0
  213. package/package.json +151 -4
@@ -0,0 +1,850 @@
1
+ import {
2
+ ShortcutKeys,
3
+ Tooltip,
4
+ overlayTopMargin,
5
+ usePointerGrace,
6
+ writeClipboard
7
+ } from "./chunk-JA3XDLIA.js";
8
+ import {
9
+ IconCheckOutlineRegular
10
+ } from "./chunk-XW4L4ZGL.js";
11
+
12
+ // src/Pill.tsx
13
+ import clsx from "clsx";
14
+
15
+ // .build/css/Pill.module.mjs
16
+ var id = "@seekbuddy/dsh-ui/Pill.module.css";
17
+ if (typeof document !== "undefined" && !document.querySelector('style[data-dsh-ui-primitives="' + id + '"]')) {
18
+ const element = document.createElement("style");
19
+ element.setAttribute("data-dsh-ui-primitives", id);
20
+ element.textContent = ".c2tx2W_pill{corner-shape:round;height:24px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2);border:none;border-radius:999px;align-items:center;gap:4px;padding:0 8px;font-size:12px;line-height:18px;display:inline-flex}.c2tx2W_interactive{cursor:pointer}@media (hover:hover){.c2tx2W_interactive:hover{background:var(--dsw-alias-interactive-bg-hover)}}.c2tx2W_active{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-button-ghost-active-fill);box-shadow:inset 0 0 0 1px var(--dsw-alias-button-ghost-active-border)}";
21
+ document.head.appendChild(element);
22
+ }
23
+ var Pill_module_default = { "active": "c2tx2W_active", "interactive": "c2tx2W_interactive", "pill": "c2tx2W_pill" };
24
+
25
+ // src/Pill.tsx
26
+ import { jsx } from "react/jsx-runtime";
27
+ function Pill({
28
+ active = false,
29
+ className,
30
+ children,
31
+ onClick,
32
+ ...rest
33
+ }) {
34
+ if (!onClick) {
35
+ return /* @__PURE__ */ jsx("span", { className: clsx(Pill_module_default.pill, active && Pill_module_default.active, className), children });
36
+ }
37
+ return /* @__PURE__ */ jsx(
38
+ "button",
39
+ {
40
+ type: "button",
41
+ className: clsx(Pill_module_default.pill, Pill_module_default.interactive, active && Pill_module_default.active, className),
42
+ onClick,
43
+ ...rest,
44
+ children
45
+ }
46
+ );
47
+ }
48
+
49
+ // src/useAnchoredPosition.ts
50
+ import { useLayoutEffect, useState } from "react";
51
+ function useAnchoredPosition(options) {
52
+ const {
53
+ open,
54
+ anchorRef,
55
+ panelRef,
56
+ side = "bottom",
57
+ align = "start",
58
+ gap,
59
+ margin,
60
+ getAnchorRect,
61
+ layoutKey,
62
+ minWidthFromAnchor = false,
63
+ frameClearance = true
64
+ } = options;
65
+ const [position, setPosition] = useState(null);
66
+ useLayoutEffect(() => {
67
+ if (!open) {
68
+ setPosition(null);
69
+ return;
70
+ }
71
+ const anchor = anchorRef.current;
72
+ const panel = panelRef.current;
73
+ if (anchor === null || panel === null) return;
74
+ const win = anchor.ownerDocument.defaultView;
75
+ if (win === null) return;
76
+ const viewport = win.visualViewport;
77
+ const visualOrigin = typeof win.CSS !== "undefined" && win.CSS.supports("-webkit-backdrop-filter", "none");
78
+ const properties = [
79
+ "--dsh-anchored-max-width",
80
+ "--dsh-anchored-max-height",
81
+ ...minWidthFromAnchor ? ["min-width"] : []
82
+ ];
83
+ const previous = properties.map(
84
+ (name) => [name, panel.style.getPropertyValue(name), panel.style.getPropertyPriority(name)]
85
+ );
86
+ let active = true;
87
+ let lastRect = null;
88
+ const readRect = () => getAnchorRect === void 0 ? anchor.getBoundingClientRect() : getAnchorRect();
89
+ const place = (rect) => {
90
+ lastRect = rect === null ? null : { x: rect.x, y: rect.y, width: rect.width, height: rect.height };
91
+ let next = null;
92
+ if (rect !== null) {
93
+ const x = viewport?.offsetLeft ?? 0;
94
+ const y = viewport?.offsetTop ?? 0;
95
+ const width = viewport?.width ?? (anchor.ownerDocument.documentElement.clientWidth || win.innerWidth);
96
+ const height = viewport?.height ?? (anchor.ownerDocument.documentElement.clientHeight || win.innerHeight);
97
+ const topMargin = frameClearance ? overlayTopMargin(margin, rect, gap) : margin;
98
+ const maxWidth = Math.max(0, width - margin * 2);
99
+ panel.style.setProperty("--dsh-anchored-max-width", `${maxWidth}px`);
100
+ panel.style.setProperty("--dsh-anchored-max-height", `${Math.max(0, height - topMargin - margin)}px`);
101
+ if (minWidthFromAnchor) panel.style.minWidth = `${Math.min(rect.width, maxWidth)}px`;
102
+ const panelWidth = panel.offsetWidth;
103
+ const panelHeight = panel.offsetHeight;
104
+ const offsetX = visualOrigin ? x : 0;
105
+ const offsetY = visualOrigin ? y : 0;
106
+ const left = offsetX + (side === "right" ? rect.right + gap : align === "end" ? rect.right - panelWidth : rect.left);
107
+ const top = offsetY + (side === "right" ? rect.top : side === "top" ? rect.top - gap - panelHeight : rect.bottom + gap);
108
+ next = {
109
+ left: Math.max(x + margin, Math.min(left, x + width - margin - panelWidth)),
110
+ top: Math.max(y + topMargin, Math.min(top, y + height - margin - panelHeight))
111
+ };
112
+ }
113
+ setPosition((current) => current?.left === next?.left && current?.top === next?.top ? current : next);
114
+ };
115
+ const update = () => {
116
+ if (!active) return;
117
+ place(readRect());
118
+ };
119
+ update();
120
+ win.addEventListener("scroll", update, true);
121
+ win.addEventListener("resize", update);
122
+ viewport?.addEventListener("scroll", update);
123
+ viewport?.addEventListener("resize", update);
124
+ const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(update);
125
+ observer?.observe(anchor);
126
+ observer?.observe(panel);
127
+ let frame = 0;
128
+ const track = () => {
129
+ if (!active) return;
130
+ const rect = readRect();
131
+ if (rect?.x !== lastRect?.x || rect?.y !== lastRect?.y || rect?.width !== lastRect?.width || rect?.height !== lastRect?.height)
132
+ place(rect);
133
+ frame = win.requestAnimationFrame(track);
134
+ };
135
+ frame = win.requestAnimationFrame(track);
136
+ return () => {
137
+ active = false;
138
+ win.cancelAnimationFrame(frame);
139
+ observer?.disconnect();
140
+ win.removeEventListener("scroll", update, true);
141
+ win.removeEventListener("resize", update);
142
+ viewport?.removeEventListener("scroll", update);
143
+ viewport?.removeEventListener("resize", update);
144
+ for (const [name, value, priority] of previous) panel.style.setProperty(name, value, priority);
145
+ };
146
+ }, [
147
+ open,
148
+ anchorRef,
149
+ panelRef,
150
+ side,
151
+ align,
152
+ gap,
153
+ margin,
154
+ getAnchorRect,
155
+ layoutKey,
156
+ minWidthFromAnchor,
157
+ frameClearance
158
+ ]);
159
+ return position;
160
+ }
161
+
162
+ // src/keyboard-composition.ts
163
+ function observeComposition(document2) {
164
+ let composing = false;
165
+ let ended = false;
166
+ const start = () => {
167
+ composing = true;
168
+ };
169
+ const end = () => {
170
+ composing = false;
171
+ ended = true;
172
+ };
173
+ const release = () => {
174
+ ended = false;
175
+ };
176
+ const blur = () => {
177
+ composing = false;
178
+ ended = false;
179
+ };
180
+ document2.addEventListener("compositionstart", start, true);
181
+ document2.addEventListener("compositionend", end, true);
182
+ document2.addEventListener("keyup", release, true);
183
+ document2.defaultView?.addEventListener("blur", blur);
184
+ return {
185
+ guards: (event) => {
186
+ const guarded = composing || ended || event.isComposing || event.keyCode === 229;
187
+ ended = false;
188
+ return guarded;
189
+ },
190
+ dispose: () => {
191
+ document2.removeEventListener("compositionstart", start, true);
192
+ document2.removeEventListener("compositionend", end, true);
193
+ document2.removeEventListener("keyup", release, true);
194
+ document2.defaultView?.removeEventListener("blur", blur);
195
+ }
196
+ };
197
+ }
198
+
199
+ // src/focus.ts
200
+ var releases = /* @__PURE__ */ new WeakMap();
201
+ var navigationKeys = /* @__PURE__ */ new Set(["Tab", "ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Home", "End"]);
202
+ var mobileDevice;
203
+ function isMobileDevice() {
204
+ if (mobileDevice !== void 0) return mobileDevice;
205
+ const { userAgent, platform, maxTouchPoints } = navigator;
206
+ mobileDevice = /Android|iPhone|iPad|iPod/i.test(userAgent) || platform === "MacIntel" && maxTouchPoints > 1;
207
+ return mobileDevice;
208
+ }
209
+ function focusWithoutRing(element, options) {
210
+ releases.get(element)?.();
211
+ const release = () => {
212
+ element.removeAttribute("data-dsh-automatic-focus");
213
+ element.removeEventListener("blur", release);
214
+ element.removeEventListener("keydown", navigate, true);
215
+ releases.delete(element);
216
+ };
217
+ const navigate = (event) => {
218
+ if (!event.isComposing && !event.ctrlKey && !event.altKey && !event.metaKey && navigationKeys.has(event.key))
219
+ release();
220
+ };
221
+ releases.set(element, release);
222
+ element.setAttribute("data-dsh-automatic-focus", "");
223
+ element.addEventListener("blur", release);
224
+ element.addEventListener("keydown", navigate, true);
225
+ element.focus(options);
226
+ if (!element.matches(":focus")) release();
227
+ }
228
+
229
+ // src/useModalLayer.ts
230
+ import { useLayoutEffect as useLayoutEffect2, useRef } from "react";
231
+ var modalSelector = '[role="dialog"][aria-modal="true"], [role="menu"]';
232
+ var layers = /* @__PURE__ */ new WeakMap();
233
+ function closeTopModal(document2) {
234
+ const top = layers.get(document2)?.at(-1);
235
+ if (top === void 0) return;
236
+ const foreground = [...document2.querySelectorAll(modalSelector)].at(-1);
237
+ if (foreground === top.element) top.close();
238
+ }
239
+ function isBehindModal(anchor) {
240
+ if (anchor === null) return false;
241
+ const top = layers.get(anchor.ownerDocument)?.at(-1);
242
+ return top !== void 0 && !top.element.contains(anchor);
243
+ }
244
+ var focusable = 'button:not(:disabled), input:not(:disabled), textarea:not(:disabled), select:not(:disabled), a[href], [tabindex="0"]';
245
+ function useModalLayer(dialog, open, onClose) {
246
+ const close = useRef(onClose);
247
+ const pendingRestore = useRef(void 0);
248
+ close.current = onClose;
249
+ useLayoutEffect2(() => {
250
+ const target = pendingRestore.current;
251
+ pendingRestore.current = void 0;
252
+ const element = dialog.current;
253
+ if (!open && element?.contains(element.ownerDocument.activeElement) && target?.isConnected && !target.closest("[inert], [hidden]") && !isBehindModal(target)) {
254
+ focusWithoutRing(target, { preventScroll: true });
255
+ }
256
+ if (!open || element === null) return;
257
+ const document2 = element.ownerDocument;
258
+ const composition = observeComposition(document2);
259
+ const previous = document2.activeElement;
260
+ const stack = layers.get(document2) ?? [];
261
+ layers.set(document2, stack);
262
+ const layer = {
263
+ element,
264
+ close: () => {
265
+ close.current();
266
+ }
267
+ };
268
+ stack.push(layer);
269
+ const initial = element.querySelector("[data-modal-autofocus]") ?? element.querySelector(focusable) ?? element;
270
+ if (!element.contains(document2.activeElement)) focusWithoutRing(initial);
271
+ const keydown = (event) => {
272
+ const composing = composition.guards(event);
273
+ if (stack.at(-1) !== layer || event.defaultPrevented || composing || event.ctrlKey || event.altKey || event.metaKey)
274
+ return;
275
+ if (event.key === "Escape" && !event.shiftKey) {
276
+ event.preventDefault();
277
+ if (!event.repeat) close.current();
278
+ }
279
+ if (event.key !== "Tab") return;
280
+ if (document2.activeElement?.closest('[role="menu"]')) return;
281
+ const items = [...element.querySelectorAll(focusable)].filter(
282
+ (item) => !item.closest("[inert], [hidden]")
283
+ );
284
+ const first = items[0] ?? element;
285
+ const last = items.at(-1) ?? element;
286
+ const atEdge = event.shiftKey ? document2.activeElement === first : document2.activeElement === last;
287
+ if (document2.activeElement === element || !element.contains(document2.activeElement) || atEdge) {
288
+ event.preventDefault();
289
+ const target2 = event.shiftKey ? last : first;
290
+ target2.focus();
291
+ }
292
+ };
293
+ document2.addEventListener("keydown", keydown);
294
+ return () => {
295
+ composition.dispose();
296
+ const wasTop = stack.at(-1) === layer;
297
+ stack.splice(stack.indexOf(layer), 1);
298
+ document2.removeEventListener("keydown", keydown);
299
+ if (stack.length === 0) layers.delete(document2);
300
+ if (wasTop) {
301
+ const target2 = previous instanceof HTMLElement && previous.isConnected ? previous : stack.at(-1)?.element;
302
+ if (target2 !== void 0) {
303
+ pendingRestore.current = target2;
304
+ focusWithoutRing(target2);
305
+ }
306
+ }
307
+ };
308
+ }, [dialog, open]);
309
+ }
310
+
311
+ // src/MenuSurface.tsx
312
+ import { forwardRef, useId, useLayoutEffect as useLayoutEffect3, useRef as useRef2 } from "react";
313
+ import { createPortal } from "react-dom";
314
+ import clsx2 from "clsx";
315
+
316
+ // .build/css/MenuSurface.module.mjs
317
+ var id2 = "@seekbuddy/dsh-ui/MenuSurface.module.css";
318
+ if (typeof document !== "undefined" && !document.querySelector('style[data-dsh-ui-primitives="' + id2 + '"]')) {
319
+ const element = document.createElement("style");
320
+ element.setAttribute("data-dsh-ui-primitives", id2);
321
+ element.textContent = "._J1jQq_surface,._J1jQq_backing{border-radius:var(--dsw-radius-lg)}._J1jQq_compact{border-radius:var(--dsw-radius-md)}:where(._J1jQq_surface){position:relative}._J1jQq_surface{anchor-name:var(--dsh-menu-anchor);isolation:isolate}._J1jQq_material{z-index:-1;border-radius:inherit;background:var(--dsw-menu-surface-fill);backdrop-filter:var(--dsw-menu-backdrop-filter);pointer-events:none;position:absolute;inset:0}._J1jQq_backing{display:none}html[data-platform=darwin] ._J1jQq_backing{position-anchor:var(--dsh-menu-anchor);top:anchor(top);left:anchor(left);width:anchor-size(width, 0px);height:anchor-size(height, 0px);z-index:-1;background:var(--dsw-alias-bg-base);pointer-events:none;display:block;position:fixed}._J1jQq_surface[data-menu-phase=enter]>*,._J1jQq_backing[data-menu-phase=enter]{animation:_J1jQq_dsh-context-menu-in var(--dsh-menu-enter) ease-out}._J1jQq_surface[data-menu-phase=dismiss]>*,._J1jQq_backing[data-menu-phase=dismiss],._J1jQq_surface[data-menu-phase=replace]>*,._J1jQq_backing[data-menu-phase=replace]{animation:_J1jQq_dsh-context-menu-out var(--dsh-menu-dismiss) ease-in both}._J1jQq_surface[data-menu-phase=replace]>*,._J1jQq_backing[data-menu-phase=replace]{animation-duration:var(--dsh-menu-replace)}@keyframes _J1jQq_dsh-context-menu-in{0%{opacity:0}to{opacity:1}}@keyframes _J1jQq_dsh-context-menu-out{0%{opacity:1}to{opacity:0}}@media (prefers-reduced-motion:reduce){._J1jQq_surface[data-menu-phase]>*,._J1jQq_backing[data-menu-phase]{animation-duration:0s}}";
322
+ document.head.appendChild(element);
323
+ }
324
+ var MenuSurface_module_default = { "surface": "_J1jQq_surface", "backing": "_J1jQq_backing", "compact": "_J1jQq_compact", "material": "_J1jQq_material", "dsh-context-menu-in": "_J1jQq_dsh-context-menu-in", "dsh-context-menu-out": "_J1jQq_dsh-context-menu-out" };
325
+
326
+ // src/MenuSurface.tsx
327
+ import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
328
+ var MENU_TRANSITION_MS = Object.freeze({ enter: 40, dismiss: 80, replace: 10, prepare: 20 });
329
+ var MenuSurface = forwardRef(function MenuSurface2({ compact = false, phase, className, style, children, ...props }, ref) {
330
+ const id4 = useId();
331
+ const backingRef = useRef2(null);
332
+ useLayoutEffect3(() => {
333
+ document.body.appendChild(backingRef.current);
334
+ }, []);
335
+ const anchorStyle = {
336
+ "--dsh-menu-anchor": `--dsh-menu-${id4.replaceAll(":", "")}`,
337
+ "--dsh-menu-enter": `${MENU_TRANSITION_MS.enter}ms`,
338
+ "--dsh-menu-dismiss": `${MENU_TRANSITION_MS.dismiss}ms`,
339
+ "--dsh-menu-replace": `${MENU_TRANSITION_MS.replace}ms`
340
+ };
341
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
342
+ /* @__PURE__ */ jsxs(
343
+ "div",
344
+ {
345
+ ...props,
346
+ ref,
347
+ "data-menu-material": "translucent",
348
+ "data-menu-phase": phase,
349
+ className: clsx2(MenuSurface_module_default.surface, compact && MenuSurface_module_default.compact, className),
350
+ style: { ...style, ...anchorStyle },
351
+ children: [
352
+ /* @__PURE__ */ jsx2("div", { "aria-hidden": "true", className: MenuSurface_module_default.material }),
353
+ children
354
+ ]
355
+ }
356
+ ),
357
+ createPortal(
358
+ /* @__PURE__ */ jsx2(
359
+ "div",
360
+ {
361
+ ref: backingRef,
362
+ "aria-hidden": "true",
363
+ "data-menu-backing": "",
364
+ "data-menu-phase": phase,
365
+ className: clsx2(MenuSurface_module_default.backing, compact && MenuSurface_module_default.compact),
366
+ style: { ...anchorStyle, visibility: style?.visibility }
367
+ }
368
+ ),
369
+ document.body
370
+ )
371
+ ] });
372
+ });
373
+
374
+ // src/Menu.tsx
375
+ import { useCallback, useEffect, useId as useId2, useLayoutEffect as useLayoutEffect4, useRef as useRef3, useState as useState2 } from "react";
376
+ import { createPortal as createPortal2 } from "react-dom";
377
+ import clsx3 from "clsx";
378
+
379
+ // .build/css/Menu.module.mjs
380
+ var id3 = "@seekbuddy/dsh-ui/Menu.module.css";
381
+ if (typeof document !== "undefined" && !document.querySelector('style[data-dsh-ui-primitives="' + id3 + '"]')) {
382
+ const element = document.createElement("style");
383
+ element.setAttribute("data-dsh-ui-primitives", id3);
384
+ element.textContent = '.or0wJW_root{display:inline-flex;position:relative}.or0wJW_list,.or0wJW_submenu{box-sizing:border-box;--dsw-elevation-stroke-color:var(--dsw-alias-border-l1);box-shadow:var(--dsw-elevation-prominent);--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);border:0;flex-direction:column;gap:0;padding:4px;display:flex}.or0wJW_list{z-index:100;min-width:min(144px, var(--dsh-anchored-max-width,100vw));max-width:min(360px, var(--dsh-anchored-max-width,100vw));position:absolute;top:calc(100% + 4px);left:0}.or0wJW_list[data-context-menu]:not(:has([role=menuitem])){display:none}.or0wJW_portal{z-index:1100;position:fixed;top:auto;left:auto}.or0wJW_sideTop{top:auto;bottom:calc(100% + 4px)}.or0wJW_alignEnd{left:auto;right:0}.or0wJW_scrollable{max-height:min(calc(100vh - 12px - max(12px, var(--dsh-frame-overlay-top,12px))), var(--dsh-anchored-max-height,100vh));max-height:min(calc(100dvh - 12px - max(12px, var(--dsh-frame-overlay-top,12px))), var(--dsh-anchored-max-height,100vh))}.or0wJW_viewport{flex-direction:column;min-height:0;display:flex}.or0wJW_scrollable .or0wJW_viewport{overflow-y:auto}.or0wJW_footer{border-top:.5px solid var(--dsw-alias-border-l2);flex-direction:column;flex:none;margin-top:3px;padding-top:3px;display:flex}.or0wJW_list[data-context-menu] .or0wJW_footer:has(>.or0wJW_separator:first-child){border-top:none;margin-top:0;padding-top:0}.or0wJW_itemWrap{position:relative}.or0wJW_item{border-radius:var(--dsw-radius-md);cursor:pointer;width:100%;min-height:34px;color:var(--dsw-alias-label-primary);text-align:left;background:0 0;border:none;align-items:center;gap:6px;padding:6px 8px;font-size:13px;line-height:20px;display:flex}@media (hover:hover){.or0wJW_item:hover:not(:disabled):not([aria-disabled=true]){background:var(--dsw-alias-interactive-bg-hover)}}.or0wJW_item:focus-visible:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);outline:none}.or0wJW_denseList .or0wJW_item{min-height:30px;padding-block:4px}.or0wJW_denseList .or0wJW_label{padding-block:3px}.or0wJW_list.or0wJW_compactList,.or0wJW_submenu.or0wJW_compactList{min-width:min(156px, var(--dsh-anchored-max-width,100vw));padding:4px}.or0wJW_compactList .or0wJW_item{border-radius:var(--dsw-radius-sm);gap:5px;min-height:24px;padding:2px 6px;font-size:11px;line-height:17px}.or0wJW_compactList .or0wJW_itemIcon{width:12px;height:12px}.or0wJW_compactList .or0wJW_separator{margin:2px 6px}.or0wJW_compactList .or0wJW_label{padding:3px 6px;font-size:10px;line-height:15px}.or0wJW_item:disabled,.or0wJW_item[aria-disabled=true]{opacity:.4;cursor:not-allowed}.or0wJW_itemIcon{width:14px;height:14px;color:var(--dsw-alias-menu-icon);flex:none;justify-content:center;align-items:center;display:inline-flex}.or0wJW_itemIcon svg,.or0wJW_check{width:14px;height:14px}.or0wJW_compactList .or0wJW_itemIcon svg,.or0wJW_compactList .or0wJW_check{width:12px;height:12px}.or0wJW_itemLabel{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.or0wJW_shortcut{flex:none;margin-inline-start:auto}.or0wJW_shortcut .or0wJW_shortcutKeys{color:var(--dsw-alias-label-caption);font-size:11px;line-height:16px}@media (pointer:coarse){html:not([data-input-modality=keyboard]) .or0wJW_shortcut{display:none}}.or0wJW_check{color:var(--dsw-alias-label-primary);flex:none}.or0wJW_selected{background:0 0}.or0wJW_selectedFill{background:var(--dsw-alias-interactive-bg-hover)}.or0wJW_danger,.or0wJW_danger .or0wJW_itemIcon{color:var(--dsw-alias-state-error-primary)}@media (hover:hover){.or0wJW_danger:hover:not(:disabled):not([aria-disabled=true]){background:var(--dsw-alias-interactive-bg-hover-danger)}}.or0wJW_danger:focus-visible:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger);outline:none}.or0wJW_label{color:var(--dsw-alias-label-tertiary);padding:6px 8px;font-size:11px;line-height:15px}.or0wJW_separator{background:var(--dsw-alias-border-l2);height:.5px;margin:3px 8px}.or0wJW_separator+.or0wJW_separator,.or0wJW_separator+.or0wJW_itemWrap>.or0wJW_separator:first-child,.or0wJW_itemWrap:first-child>.or0wJW_separator:first-child{display:none}.or0wJW_list[data-context-menu] .or0wJW_viewport>.or0wJW_itemWrap~[data-slot] .or0wJW_itemWrap:first-child>.or0wJW_separator:first-child{display:block}.or0wJW_submenu{z-index:101;min-width:163px;position:absolute;top:auto;bottom:-4px;left:calc(100% + 10px)}.or0wJW_submenu:before{content:"";width:10px;position:absolute;top:0;bottom:0;left:-10px}.or0wJW_list[data-menu-phase=dismiss],.or0wJW_list[data-menu-phase=replace]{pointer-events:none}.or0wJW_list[data-context-menu].or0wJW_scrollable{max-height:min(calc(100vh - 24px), var(--dsh-anchored-max-height,100vh));max-height:min(calc(100dvh - 24px), var(--dsh-anchored-max-height,100vh))}';
385
+ document.head.appendChild(element);
386
+ }
387
+ var Menu_module_default = { "viewport": "or0wJW_viewport", "sideTop": "or0wJW_sideTop", "item": "or0wJW_item", "list": "or0wJW_list", "label": "or0wJW_label", "scrollable": "or0wJW_scrollable", "root": "or0wJW_root", "check": "or0wJW_check", "compactList": "or0wJW_compactList", "submenu": "or0wJW_submenu", "selected": "or0wJW_selected", "separator": "or0wJW_separator", "footer": "or0wJW_footer", "itemWrap": "or0wJW_itemWrap", "denseList": "or0wJW_denseList", "itemIcon": "or0wJW_itemIcon", "itemLabel": "or0wJW_itemLabel", "shortcut": "or0wJW_shortcut", "portal": "or0wJW_portal", "selectedFill": "or0wJW_selectedFill", "alignEnd": "or0wJW_alignEnd", "shortcutKeys": "or0wJW_shortcutKeys", "danger": "or0wJW_danger" };
388
+
389
+ // src/Menu.tsx
390
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
391
+ function MenuItemButton({
392
+ children,
393
+ shortcut,
394
+ icon,
395
+ current,
396
+ disabled = false,
397
+ disabledReason,
398
+ danger = false,
399
+ separatorBefore = false,
400
+ onSelect
401
+ }) {
402
+ const reasonId = useId2();
403
+ const explained = disabled && disabledReason !== void 0;
404
+ return /* @__PURE__ */ jsxs2("div", { className: Menu_module_default.itemWrap, children: [
405
+ separatorBefore && /* @__PURE__ */ jsx3("div", { className: Menu_module_default.separator, role: "separator" }),
406
+ /* @__PURE__ */ jsx3(Tooltip, { label: disabledReason ?? "", disabled: !explained, side: "bottom", delayMs: 500, maxWidth: 300, portal: true, children: /* @__PURE__ */ jsxs2(
407
+ "button",
408
+ {
409
+ type: "button",
410
+ role: "menuitem",
411
+ className: clsx3(Menu_module_default.item, danger && Menu_module_default.danger),
412
+ disabled: disabled && !explained,
413
+ "aria-disabled": explained || void 0,
414
+ "aria-describedby": explained ? reasonId : void 0,
415
+ "aria-keyshortcuts": shortcut?.aria,
416
+ "aria-current": current === true ? "page" : void 0,
417
+ onClick: explained ? void 0 : onSelect,
418
+ children: [
419
+ icon !== void 0 && /* @__PURE__ */ jsx3("span", { className: Menu_module_default.itemIcon, children: icon }),
420
+ /* @__PURE__ */ jsx3("span", { className: Menu_module_default.itemLabel, children }),
421
+ shortcut !== void 0 && /* @__PURE__ */ jsx3("span", { "aria-hidden": "true", className: Menu_module_default.shortcut, children: /* @__PURE__ */ jsx3(ShortcutKeys, { keys: shortcut.keys, className: Menu_module_default.shortcutKeys }) })
422
+ ]
423
+ }
424
+ ) }),
425
+ explained && /* @__PURE__ */ jsx3("span", { id: reasonId, hidden: true, children: disabledReason })
426
+ ] });
427
+ }
428
+ function isSeparator(entry) {
429
+ return "type" in entry && entry.type === "separator";
430
+ }
431
+ function isLabel(entry) {
432
+ return "type" in entry && entry.type === "label";
433
+ }
434
+ function iconColumns(entries, contextual) {
435
+ const aligned = /* @__PURE__ */ new Set();
436
+ if (!contextual) return aligned;
437
+ let group = [];
438
+ const finish = () => {
439
+ if (group.some((entry) => !isLabel(entry) && !isSeparator(entry) && entry.icon !== void 0)) {
440
+ for (const entry of group) aligned.add(entry);
441
+ }
442
+ group = [];
443
+ };
444
+ for (const entry of entries) {
445
+ if (isSeparator(entry)) finish();
446
+ else group.push(entry);
447
+ }
448
+ finish();
449
+ return aligned;
450
+ }
451
+ var MEASURE_STYLE = { visibility: "hidden", left: 0, top: 0 };
452
+ function Menu({
453
+ open,
454
+ anchor,
455
+ items = [],
456
+ children,
457
+ selectedId,
458
+ selectedIds,
459
+ onSelect,
460
+ onClose,
461
+ align = "start",
462
+ side = "bottom",
463
+ portal = false,
464
+ closeOnPointerLeave = false,
465
+ dense = false,
466
+ compact = false,
467
+ autoFocus = false,
468
+ selection = "check",
469
+ getAnchorRect,
470
+ minWidthFromAnchor = false,
471
+ footer,
472
+ className,
473
+ listClassName,
474
+ contextual = false,
475
+ exiting,
476
+ returnFocus
477
+ }) {
478
+ const rootRef = useRef3(null);
479
+ const listRef = useRef3(null);
480
+ const menuId = useId2();
481
+ useLayoutEffect4(() => {
482
+ const trigger = returnFocus?.();
483
+ if (!open || !contextual || trigger == null) return;
484
+ const previous = trigger.getAttribute("aria-controls");
485
+ trigger.setAttribute("aria-controls", [previous, menuId].filter(Boolean).join(" "));
486
+ return () => {
487
+ if (previous === null) trigger.removeAttribute("aria-controls");
488
+ else trigger.setAttribute("aria-controls", previous);
489
+ };
490
+ }, [open, contextual, returnFocus, menuId]);
491
+ const walkIndex = useRef3(null);
492
+ const triggerRef = useRef3(null);
493
+ const selectingWithTab = useRef3(false);
494
+ const refocusAnchor = (navigation = false) => {
495
+ const trigger = returnFocus?.() ?? triggerRef.current;
496
+ const target = trigger !== null && document.contains(trigger) && !trigger.disabled ? trigger : rootRef.current?.querySelector("button:not(:disabled)");
497
+ if (target == null) return;
498
+ if (navigation) target.focus();
499
+ else focusWithoutRing(target);
500
+ };
501
+ const refocusAfterSelection = () => {
502
+ const navigation = selectingWithTab.current;
503
+ queueMicrotask(() => {
504
+ if (openRef.current && exitingRef.current === void 0) return;
505
+ const active = document.activeElement;
506
+ if (active === null || active === document.body || listRef.current?.contains(active) === true)
507
+ refocusAnchor(navigation);
508
+ });
509
+ };
510
+ const exitingRef = useRef3(exiting);
511
+ exitingRef.current = exiting;
512
+ const openRef = useRef3(open);
513
+ openRef.current = open;
514
+ const [openSubmenuId, setOpenSubmenuId] = useState2(null);
515
+ const close = useRef3(onClose);
516
+ close.current = onClose;
517
+ const lastAnchorRect = useRef3(null);
518
+ const readAnchor = useCallback(() => {
519
+ const anchorElement = returnFocus?.() ?? rootRef.current;
520
+ if (anchorElement?.closest("[inert], [hidden]") != null) {
521
+ if (exitingRef.current === void 0) close.current();
522
+ return lastAnchorRect.current;
523
+ }
524
+ lastAnchorRect.current = getAnchorRect === void 0 ? rootRef.current?.getBoundingClientRect() ?? null : getAnchorRect();
525
+ return lastAnchorRect.current;
526
+ }, [getAnchorRect, returnFocus]);
527
+ const fixedPos = useAnchoredPosition({
528
+ open: open && portal,
529
+ anchorRef: rootRef,
530
+ panelRef: listRef,
531
+ side,
532
+ align,
533
+ gap: 4,
534
+ margin: 12,
535
+ frameClearance: !contextual,
536
+ minWidthFromAnchor,
537
+ getAnchorRect: readAnchor
538
+ });
539
+ const positioned = !portal || fixedPos !== null;
540
+ const { arm: armClose, cancel: cancelClose } = usePointerGrace(onClose);
541
+ useEffect(() => {
542
+ if (!open) return;
543
+ const active = document.activeElement;
544
+ triggerRef.current = active instanceof HTMLElement && (getAnchorRect !== void 0 || rootRef.current?.contains(active) === true) ? active : null;
545
+ }, [open]);
546
+ useEffect(() => {
547
+ if (!open || exiting !== void 0 || !autoFocus || !positioned || listRef.current?.contains(document.activeElement))
548
+ return;
549
+ const first = listRef.current?.querySelector("button:not(:disabled)");
550
+ walkIndex.current = first === void 0 || first === null ? null : 0;
551
+ if (first != null) focusWithoutRing(first);
552
+ }, [open, autoFocus, items.length, footer?.length, positioned, exiting]);
553
+ useEffect(() => {
554
+ if (!open || exiting !== void 0) {
555
+ setOpenSubmenuId(null);
556
+ walkIndex.current = null;
557
+ return;
558
+ }
559
+ const composition = observeComposition(document);
560
+ const onPointerDown = (e) => {
561
+ if (!(e.target instanceof Node) || contextual && e.button === 2) return;
562
+ if (rootRef.current?.contains(e.target) === true) return;
563
+ if (listRef.current?.contains(e.target) === true) return;
564
+ onClose();
565
+ };
566
+ const onKeyDown = (e) => {
567
+ if (composition.guards(e) || isBehindModal(triggerRef.current ?? rootRef.current) || e.defaultPrevented || e.ctrlKey || e.altKey || e.metaKey)
568
+ return;
569
+ const focused = document.activeElement;
570
+ const insideList = listRef.current?.contains(focused) === true;
571
+ const anchored = focused === returnFocus?.() || rootRef.current?.contains(focused) === true || insideList;
572
+ if (e.key === "Escape" && !e.shiftKey) {
573
+ e.preventDefault();
574
+ if (e.repeat) return;
575
+ onClose();
576
+ if (anchored || autoFocus) refocusAnchor();
577
+ }
578
+ if (e.key === "Tab") {
579
+ const list3 = listRef.current;
580
+ if (list3 === null || !anchored) return;
581
+ if (e.shiftKey) {
582
+ e.preventDefault();
583
+ onClose();
584
+ refocusAnchor(true);
585
+ return;
586
+ }
587
+ if (insideList) {
588
+ if (focused instanceof Element && focused.getAttribute("role") === "menuitem") {
589
+ e.preventDefault();
590
+ if (focused.getAttribute("aria-disabled") === "true") {
591
+ onClose();
592
+ refocusAnchor(true);
593
+ return;
594
+ }
595
+ selectingWithTab.current = true;
596
+ try {
597
+ ;
598
+ focused.click();
599
+ } finally {
600
+ selectingWithTab.current = false;
601
+ }
602
+ }
603
+ return;
604
+ }
605
+ const row = list3.querySelector("button:not(:disabled)");
606
+ if (row === null) return;
607
+ e.preventDefault();
608
+ row.focus();
609
+ walkIndex.current = 0;
610
+ return;
611
+ }
612
+ if (!["ArrowDown", "ArrowUp", "Home", "End"].includes(e.key)) return;
613
+ const list2 = listRef.current;
614
+ if (list2 === null || !anchored) return;
615
+ const buttons = Array.from(list2.querySelectorAll("button:not(:disabled)"));
616
+ if (buttons.length === 0) return;
617
+ const index = buttons.indexOf(focused);
618
+ const from = index >= 0 ? index : walkIndex.current;
619
+ const next = e.key === "Home" ? 0 : e.key === "End" ? buttons.length - 1 : from === null ? e.key === "ArrowDown" ? 0 : buttons.length - 1 : (from + (e.key === "ArrowDown" ? 1 : -1) + buttons.length) % buttons.length;
620
+ e.preventDefault();
621
+ walkIndex.current = next;
622
+ buttons[next]?.focus();
623
+ };
624
+ const onWindowBlur = () => {
625
+ if (document.activeElement instanceof HTMLIFrameElement) onClose();
626
+ };
627
+ document.addEventListener("pointerdown", onPointerDown);
628
+ const onEscape = (event) => {
629
+ if (event.key === "Escape") onKeyDown(event);
630
+ };
631
+ const onOtherKey = (event) => {
632
+ if (event.key !== "Escape") onKeyDown(event);
633
+ };
634
+ document.addEventListener("keydown", onOtherKey);
635
+ document.addEventListener("keydown", onEscape, true);
636
+ window.addEventListener("blur", onWindowBlur);
637
+ return () => {
638
+ composition.dispose();
639
+ document.removeEventListener("pointerdown", onPointerDown);
640
+ document.removeEventListener("keydown", onOtherKey);
641
+ document.removeEventListener("keydown", onEscape, true);
642
+ window.removeEventListener("blur", onWindowBlur);
643
+ };
644
+ }, [open, onClose, autoFocus, contextual, exiting]);
645
+ useLayoutEffect4(() => {
646
+ if (exiting === "dismiss" && listRef.current?.contains(document.activeElement)) refocusAnchor();
647
+ }, [exiting]);
648
+ useEffect(() => {
649
+ if (!open) cancelClose();
650
+ }, [open, cancelClose]);
651
+ const scrollable = !items.some(
652
+ (entry) => !isSeparator(entry) && !isLabel(entry) && entry.submenu !== void 0 && entry.submenu.length > 0
653
+ );
654
+ const itemColumns = iconColumns(items, contextual);
655
+ const footerColumns = iconColumns(footer ?? [], contextual);
656
+ const renderEntry = (entry, columns) => {
657
+ if (isSeparator(entry)) {
658
+ return /* @__PURE__ */ jsx3("div", { className: Menu_module_default.separator, role: "separator" }, entry.id);
659
+ }
660
+ if (isLabel(entry)) {
661
+ return /* @__PURE__ */ jsx3("div", { className: Menu_module_default.label, role: "presentation", children: entry.text }, entry.id);
662
+ }
663
+ const hasSub = entry.submenu !== void 0 && entry.submenu.length > 0;
664
+ const subOpen = hasSub && openSubmenuId === entry.id;
665
+ const subColumns = iconColumns(entry.submenu ?? [], contextual);
666
+ const selected = entry.id === selectedId || selectedIds?.includes(entry.id) === true;
667
+ return /* @__PURE__ */ jsxs2(
668
+ "div",
669
+ {
670
+ className: Menu_module_default.itemWrap,
671
+ onMouseEnter: hasSub ? () => {
672
+ setOpenSubmenuId(entry.id);
673
+ } : void 0,
674
+ onMouseLeave: () => {
675
+ setOpenSubmenuId(null);
676
+ },
677
+ children: [
678
+ /* @__PURE__ */ jsxs2(
679
+ "button",
680
+ {
681
+ type: "button",
682
+ role: "menuitem",
683
+ className: clsx3(
684
+ Menu_module_default.item,
685
+ selected && (selection === "fill" ? Menu_module_default.selectedFill : Menu_module_default.selected),
686
+ entry.danger === true && Menu_module_default.danger
687
+ ),
688
+ disabled: entry.disabled,
689
+ "aria-keyshortcuts": entry.shortcut?.aria,
690
+ "aria-haspopup": hasSub ? "menu" : void 0,
691
+ "aria-expanded": hasSub ? subOpen : void 0,
692
+ onFocus: hasSub ? () => {
693
+ setOpenSubmenuId(entry.id);
694
+ } : void 0,
695
+ onClick: () => {
696
+ if (hasSub) {
697
+ setOpenSubmenuId(entry.id);
698
+ return;
699
+ }
700
+ onSelect?.(entry.id);
701
+ },
702
+ children: [
703
+ (entry.icon !== void 0 || columns.has(entry)) && /* @__PURE__ */ jsx3("span", { "aria-hidden": "true", className: Menu_module_default.itemIcon, children: entry.icon }),
704
+ /* @__PURE__ */ jsx3("span", { className: Menu_module_default.itemLabel, children: entry.label }),
705
+ entry.shortcut !== void 0 && /* @__PURE__ */ jsx3("span", { "aria-hidden": "true", className: Menu_module_default.shortcut, children: /* @__PURE__ */ jsx3(ShortcutKeys, { keys: entry.shortcut.keys, className: Menu_module_default.shortcutKeys }) }),
706
+ selected && selection === "check" && /* @__PURE__ */ jsx3(IconCheckOutlineRegular, { className: Menu_module_default.check })
707
+ ]
708
+ }
709
+ ),
710
+ subOpen && entry.submenu !== void 0 && /* @__PURE__ */ jsx3(MenuSurface, { compact, className: clsx3(Menu_module_default.submenu, compact && Menu_module_default.compactList), role: "menu", children: entry.submenu.map(
711
+ (sub) => isSeparator(sub) ? /* @__PURE__ */ jsx3("div", { className: Menu_module_default.separator, role: "separator" }, sub.id) : isLabel(sub) ? /* @__PURE__ */ jsx3("div", { className: Menu_module_default.label, children: sub.text }, sub.id) : /* @__PURE__ */ jsxs2(
712
+ "button",
713
+ {
714
+ type: "button",
715
+ role: "menuitem",
716
+ className: Menu_module_default.item,
717
+ disabled: sub.disabled,
718
+ "aria-keyshortcuts": sub.shortcut?.aria,
719
+ onClick: () => {
720
+ onSelect?.(sub.id);
721
+ refocusAfterSelection();
722
+ },
723
+ children: [
724
+ (sub.icon !== void 0 || subColumns.has(sub)) && /* @__PURE__ */ jsx3("span", { "aria-hidden": "true", className: Menu_module_default.itemIcon, children: sub.icon }),
725
+ /* @__PURE__ */ jsx3("span", { className: Menu_module_default.itemLabel, children: sub.label }),
726
+ sub.shortcut !== void 0 && /* @__PURE__ */ jsx3("span", { "aria-hidden": "true", className: Menu_module_default.shortcut, children: /* @__PURE__ */ jsx3(ShortcutKeys, { keys: sub.shortcut.keys, className: Menu_module_default.shortcutKeys }) })
727
+ ]
728
+ },
729
+ sub.id
730
+ )
731
+ ) })
732
+ ]
733
+ },
734
+ entry.id
735
+ );
736
+ };
737
+ const collapseSubmenuFrom = (e) => {
738
+ const row = e.target instanceof Element ? e.target.closest('button[role="menuitem"]') : null;
739
+ if (row === null || row.getAttribute("aria-haspopup") === "menu") return;
740
+ if (row.closest('[role="menu"]') !== e.currentTarget) return;
741
+ setOpenSubmenuId(null);
742
+ };
743
+ const list = open && /* @__PURE__ */ jsxs2(
744
+ MenuSurface,
745
+ {
746
+ compact,
747
+ id: contextual ? menuId : void 0,
748
+ ref: listRef,
749
+ className: clsx3(
750
+ Menu_module_default.list,
751
+ listClassName,
752
+ dense && Menu_module_default.denseList,
753
+ compact && Menu_module_default.compactList,
754
+ scrollable && Menu_module_default.scrollable,
755
+ portal && Menu_module_default.portal,
756
+ side === "top" && !portal && Menu_module_default.sideTop,
757
+ align === "end" && !portal && Menu_module_default.alignEnd
758
+ ),
759
+ style: portal ? fixedPos ?? MEASURE_STYLE : void 0,
760
+ role: exiting === void 0 ? "menu" : void 0,
761
+ "aria-hidden": exiting === void 0 ? void 0 : true,
762
+ "data-context-menu": contextual ? "" : void 0,
763
+ phase: contextual ? exiting ?? "enter" : void 0,
764
+ onClick: (e) => {
765
+ e.stopPropagation();
766
+ const row = e.target instanceof Element ? e.target.closest('button[role="menuitem"]') : null;
767
+ if (row !== null && row.getAttribute("aria-haspopup") !== "menu") refocusAfterSelection();
768
+ },
769
+ onContextMenu: contextual ? (event) => {
770
+ event.preventDefault();
771
+ event.stopPropagation();
772
+ onClose();
773
+ } : void 0,
774
+ onMouseOver: collapseSubmenuFrom,
775
+ onFocus: collapseSubmenuFrom,
776
+ children: [
777
+ /* @__PURE__ */ jsxs2("div", { className: Menu_module_default.viewport, role: "presentation", children: [
778
+ items.map((entry) => renderEntry(entry, itemColumns)),
779
+ children
780
+ ] }),
781
+ footer !== void 0 && footer.length > 0 && /* @__PURE__ */ jsx3("div", { className: Menu_module_default.footer, role: "presentation", children: footer.map((entry) => renderEntry(entry, footerColumns)) })
782
+ ]
783
+ }
784
+ );
785
+ return /* @__PURE__ */ jsxs2(
786
+ "span",
787
+ {
788
+ ref: rootRef,
789
+ className: clsx3(Menu_module_default.root, className),
790
+ onPointerEnter: closeOnPointerLeave ? cancelClose : void 0,
791
+ onPointerLeave: closeOnPointerLeave ? () => {
792
+ if (open) armClose();
793
+ } : void 0,
794
+ children: [
795
+ anchor,
796
+ portal ? list !== false && createPortal2(list, document.body) : list
797
+ ]
798
+ }
799
+ );
800
+ }
801
+
802
+ // src/use-clipboard-copy.ts
803
+ import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef4, useState as useState3 } from "react";
804
+ function useClipboardCopy(text) {
805
+ const [copied, setCopied] = useState3(false);
806
+ const copyPending = useRef4(false);
807
+ const copyTimer = useRef4(null);
808
+ const copyEpoch = useRef4(0);
809
+ useEffect2(
810
+ () => () => {
811
+ copyEpoch.current += 1;
812
+ copyPending.current = false;
813
+ if (copyTimer.current !== null) clearTimeout(copyTimer.current);
814
+ },
815
+ []
816
+ );
817
+ const onCopy = useCallback2(() => {
818
+ if (copied || copyPending.current) return;
819
+ const epoch = copyEpoch.current;
820
+ copyPending.current = true;
821
+ void writeClipboard(text).then((ok) => {
822
+ if (epoch !== copyEpoch.current) return;
823
+ copyPending.current = false;
824
+ if (!ok) return;
825
+ setCopied(true);
826
+ copyTimer.current = window.setTimeout(() => {
827
+ copyTimer.current = null;
828
+ setCopied(false);
829
+ }, 1e3);
830
+ });
831
+ }, [copied, text]);
832
+ return { copied, onCopy };
833
+ }
834
+
835
+ export {
836
+ Pill,
837
+ useAnchoredPosition,
838
+ observeComposition,
839
+ isMobileDevice,
840
+ focusWithoutRing,
841
+ modalSelector,
842
+ closeTopModal,
843
+ isBehindModal,
844
+ useModalLayer,
845
+ MENU_TRANSITION_MS,
846
+ MenuSurface,
847
+ MenuItemButton,
848
+ Menu,
849
+ useClipboardCopy
850
+ };