@parallelworks/ui 0.41.0 → 0.42.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 (280) hide show
  1. package/README.md +25 -0
  2. package/dist/ai/components/AllocationSelector.d.ts +3 -4
  3. package/dist/ai/components/AllocationSelector.js +37 -33
  4. package/dist/ai/components/AttachmentManager.js +7 -7
  5. package/dist/ai/components/AttachmentUpload.js +5 -5
  6. package/dist/ai/components/BranchNavigator.js +1 -1
  7. package/dist/ai/components/ChatEmptyState.js +2 -2
  8. package/dist/ai/components/ChatInput.js +10 -8
  9. package/dist/ai/components/ChatLayout.js +4 -5
  10. package/dist/ai/components/ChatMessage.js +12 -12
  11. package/dist/ai/components/ChatMessageList.js +9 -9
  12. package/dist/ai/components/ChatSidebar.js +8 -9
  13. package/dist/ai/components/ChatThread.js +4 -4
  14. package/dist/ai/components/ComposerChrome.d.ts +2 -2
  15. package/dist/ai/components/ComposerChrome.js +3 -6
  16. package/dist/ai/components/DragOverlay.js +2 -2
  17. package/dist/ai/components/ModelSelector.d.ts +5 -3
  18. package/dist/ai/components/ModelSelector.js +102 -339
  19. package/dist/ai/components/PastedText.js +11 -11
  20. package/dist/ai/components/ProviderIssueBanner.d.ts +2 -0
  21. package/dist/ai/components/ProviderIssueBanner.js +34 -16
  22. package/dist/ai/components/Reasoning.js +4 -4
  23. package/dist/ai/components/ShareDialog.js +43 -35
  24. package/dist/ai/components/SlashMenu.d.ts +12 -2
  25. package/dist/ai/components/SlashMenu.js +29 -13
  26. package/dist/ai/components/agent/ActivityLine.js +3 -3
  27. package/dist/ai/components/agent/AgentMessageParts.js +20 -20
  28. package/dist/ai/components/agent/PermissionPicker.js +13 -11
  29. package/dist/ai/components/conversationActions.d.ts +4 -6
  30. package/dist/ai/components/conversationActions.js +9 -13
  31. package/dist/ai/core/ChatProvider.js +1 -1
  32. package/dist/ai/core/config.js +1 -1
  33. package/dist/ai/core/providerIssues.d.ts +7 -0
  34. package/dist/ai/core/providerIssues.js +9 -1
  35. package/dist/ai/core/useKeyboardShortcuts.d.ts +4 -0
  36. package/dist/ai/core/useKeyboardShortcuts.js +8 -2
  37. package/dist/ai/index.d.ts +0 -1
  38. package/dist/ai/index.js +1 -2
  39. package/dist/ai/strings.d.ts +19 -0
  40. package/dist/ai/strings.js +18 -1
  41. package/dist/ai/ui/Avatar.js +5 -5
  42. package/dist/ai/ui/ConversationSidebar.d.ts +0 -1
  43. package/dist/ai/ui/ConversationSidebar.js +6 -7
  44. package/dist/ai/ui/Markdown.js +3 -3
  45. package/dist/ai/ui/Notice.js +6 -6
  46. package/dist/ai/ui/chip.d.ts +4 -0
  47. package/dist/ai/ui/chip.js +8 -0
  48. package/dist/ai/ui/sidebar.d.ts +6 -5
  49. package/dist/ai/ui/sidebar.js +39 -43
  50. package/dist/components/ActionMenu.d.ts +13 -0
  51. package/dist/components/ActionMenu.js +30 -0
  52. package/dist/components/Avatar.js +6 -6
  53. package/dist/components/Breadcrumbs.js +1 -1
  54. package/dist/components/Button.js +10 -10
  55. package/dist/components/Callout.js +2 -2
  56. package/dist/components/Card.js +3 -3
  57. package/dist/components/Combobox.d.ts +66 -0
  58. package/dist/components/Combobox.js +393 -0
  59. package/dist/components/ConfirmModal.js +11 -11
  60. package/dist/components/ContextMenu.d.ts +23 -0
  61. package/dist/components/ContextMenu.js +89 -0
  62. package/dist/components/CopyToClipboard.js +2 -2
  63. package/dist/components/CreateModal.d.ts +17 -36
  64. package/dist/components/CreateModal.js +97 -259
  65. package/dist/components/DescriptionList.js +8 -8
  66. package/dist/components/Filter.js +2 -2
  67. package/dist/components/FilterPill.js +2 -2
  68. package/dist/components/HoverCard.d.ts +14 -0
  69. package/dist/components/HoverCard.js +33 -0
  70. package/dist/components/IconButton.d.ts +5 -5
  71. package/dist/components/IconButton.js +18 -22
  72. package/dist/components/InfoTip.d.ts +15 -0
  73. package/dist/components/InfoTip.js +60 -0
  74. package/dist/components/Input.js +5 -5
  75. package/dist/components/LetterBadge.js +2 -2
  76. package/dist/components/Loader.js +3 -3
  77. package/dist/components/Markdown.js +1 -1
  78. package/dist/components/MarkdownImpl.js +2 -2
  79. package/dist/components/Menu.d.ts +33 -0
  80. package/dist/components/Menu.js +86 -0
  81. package/dist/components/MenuEntries.d.ts +56 -0
  82. package/dist/components/MenuEntries.js +123 -0
  83. package/dist/components/Modal.d.ts +29 -0
  84. package/dist/components/Modal.js +47 -0
  85. package/dist/components/Overview.js +11 -10
  86. package/dist/components/PageHeader.d.ts +10 -16
  87. package/dist/components/PageHeader.js +69 -95
  88. package/dist/components/Pagination.js +5 -5
  89. package/dist/components/Provider.d.ts +5 -11
  90. package/dist/components/Provider.js +11 -14
  91. package/dist/components/Radio.d.ts +8 -0
  92. package/dist/components/Radio.js +16 -0
  93. package/dist/components/SectionHeader.js +2 -2
  94. package/dist/components/SegmentedControl.d.ts +6 -5
  95. package/dist/components/SegmentedControl.js +34 -30
  96. package/dist/components/Select.d.ts +27 -0
  97. package/dist/components/Select.js +78 -0
  98. package/dist/components/SettingModeOption.d.ts +5 -8
  99. package/dist/components/SettingModeOption.js +10 -12
  100. package/dist/components/SettingRow.js +4 -4
  101. package/dist/components/SettingsCard.js +4 -4
  102. package/dist/components/SettingsGroup.js +2 -2
  103. package/dist/components/Sort.js +1 -1
  104. package/dist/components/StatusBadge.js +7 -7
  105. package/dist/components/StatusIcons.js +4 -4
  106. package/dist/components/Switch.d.ts +22 -0
  107. package/dist/components/Switch.js +28 -0
  108. package/dist/components/Table.js +11 -11
  109. package/dist/components/Tooltip.d.ts +24 -20
  110. package/dist/components/Tooltip.js +58 -76
  111. package/dist/components/floating.d.ts +14 -0
  112. package/dist/components/floating.js +21 -0
  113. package/dist/components/search.d.ts +2 -0
  114. package/dist/components/search.js +14 -0
  115. package/dist/editor/Editor.js +1 -1
  116. package/dist/editor/EditorField.js +16 -21
  117. package/dist/editor/FormCodePanel.js +19 -19
  118. package/dist/editor/Monaco.js +3 -3
  119. package/dist/file-explorer/FileExplorer.js +77 -81
  120. package/dist/file-explorer/FileExplorerContext.js +2 -2
  121. package/dist/file-explorer/FilePreview/CodePreviewEditor.js +1 -1
  122. package/dist/file-explorer/FilePreview/Inline.js +1 -1
  123. package/dist/file-explorer/FilePreview/StreamedBadge.js +5 -6
  124. package/dist/file-explorer/FilePreview/Viewer.js +8 -8
  125. package/dist/file-explorer/FilePreview/index.d.ts +1 -1
  126. package/dist/file-explorer/FilePreview/index.js +49 -83
  127. package/dist/file-explorer/FileRow.d.ts +4 -3
  128. package/dist/file-explorer/FileRow.js +32 -38
  129. package/dist/file-explorer/FileRowsWindow.d.ts +3 -2
  130. package/dist/file-explorer/FileRowsWindow.js +4 -4
  131. package/dist/file-explorer/FileTable.d.ts +3 -2
  132. package/dist/file-explorer/FileTable.js +3 -3
  133. package/dist/file-explorer/LoadMoreRow.js +1 -1
  134. package/dist/file-explorer/TreeView.js +11 -16
  135. package/dist/file-explorer/helpers.js +11 -12
  136. package/dist/file-explorer/toasts.js +11 -11
  137. package/dist/form/FieldGroup.d.ts +11 -0
  138. package/dist/form/FieldGroup.js +36 -0
  139. package/dist/form/FieldWrapper.js +4 -4
  140. package/dist/form/Fields/BooleanField.js +23 -33
  141. package/dist/form/Fields/CheckboxGroupField.d.ts +1 -1
  142. package/dist/form/Fields/CheckboxGroupField.js +9 -13
  143. package/dist/form/Fields/ColorField.d.ts +1 -2
  144. package/dist/form/Fields/ColorField.js +6 -11
  145. package/dist/form/Fields/DropdownField.d.ts +3 -13
  146. package/dist/form/Fields/DropdownField.js +40 -27
  147. package/dist/form/Fields/DurationField.d.ts +1 -2
  148. package/dist/form/Fields/DurationField.js +25 -32
  149. package/dist/form/Fields/LabelField.js +2 -2
  150. package/dist/form/Fields/MultiDropdownField.d.ts +6 -12
  151. package/dist/form/Fields/MultiDropdownField.js +31 -22
  152. package/dist/form/Fields/NumberField.d.ts +1 -1
  153. package/dist/form/Fields/NumberField.js +65 -35
  154. package/dist/form/Fields/ObjectField.d.ts +1 -1
  155. package/dist/form/Fields/ObjectField.js +12 -43
  156. package/dist/form/Fields/RadioField.d.ts +1 -1
  157. package/dist/form/Fields/RadioField.js +30 -41
  158. package/dist/form/Fields/RangeField.d.ts +1 -2
  159. package/dist/form/Fields/RangeField.js +23 -16
  160. package/dist/form/Fields/StringField.d.ts +1 -2
  161. package/dist/form/Fields/StringField.js +58 -26
  162. package/dist/form/Fields/TextAreaField.js +12 -10
  163. package/dist/form/Form.d.ts +30 -48
  164. package/dist/form/Form.js +306 -412
  165. package/dist/form/FormLayout.js +9 -9
  166. package/dist/form/Wizard/WizardContainer.d.ts +1 -4
  167. package/dist/form/Wizard/WizardContainer.js +40 -56
  168. package/dist/form/Wizard/WizardNavigation.js +1 -1
  169. package/dist/form/Wizard/WizardStepContent.d.ts +1 -5
  170. package/dist/form/Wizard/WizardStepContent.js +11 -16
  171. package/dist/form/Wizard/WizardStepIndicator.js +6 -6
  172. package/dist/form/Wizard/types.d.ts +0 -14
  173. package/dist/form/fieldRegistry.d.ts +1 -1
  174. package/dist/form/fieldRegistry.js +6 -6
  175. package/dist/form/formEditing.js +1 -1
  176. package/dist/form/index.d.ts +5 -4
  177. package/dist/form/index.js +6 -7
  178. package/dist/form/types/fieldComponentTypes.d.ts +8 -10
  179. package/dist/form/useChoiceField.d.ts +20 -0
  180. package/dist/form/useChoiceField.js +49 -0
  181. package/dist/form/useFieldValue.d.ts +7 -0
  182. package/dist/form/useFieldValue.js +20 -0
  183. package/dist/form/useParsedOpts.d.ts +3 -1
  184. package/dist/form/useParsedOpts.js +12 -17
  185. package/dist/form/utils/dependentOptions.d.ts +8 -4
  186. package/dist/form/utils/dependentOptions.js +25 -17
  187. package/dist/form/utils/fieldItems.d.ts +10 -0
  188. package/dist/form/utils/fieldItems.js +11 -0
  189. package/dist/form/utils/fieldPath.d.ts +10 -0
  190. package/dist/form/utils/fieldPath.js +20 -0
  191. package/dist/form/utils/shareEqual.d.ts +6 -0
  192. package/dist/form/utils/shareEqual.js +38 -0
  193. package/dist/graph/AnnotationBanner.js +8 -8
  194. package/dist/graph/Collapse.d.ts +1 -0
  195. package/dist/graph/Collapse.js +6 -27
  196. package/dist/graph/Connectors.js +5 -5
  197. package/dist/graph/DependencyGraph.js +20 -19
  198. package/dist/graph/GraphEditor.js +30 -41
  199. package/dist/graph/GraphEditorDialogs.js +17 -17
  200. package/dist/graph/GraphNode.js +1 -1
  201. package/dist/graph/InputDialog.js +19 -22
  202. package/dist/graph/InputsEditor.js +58 -66
  203. package/dist/graph/JobSummary.js +42 -39
  204. package/dist/graph/MatrixGroup.js +1 -1
  205. package/dist/graph/ProblemsButton.js +18 -19
  206. package/dist/graph/Reveal.js +1 -1
  207. package/dist/graph/ShortcutsButton.js +15 -18
  208. package/dist/graph/SuggestionInput.d.ts +5 -10
  209. package/dist/graph/SuggestionInput.js +14 -18
  210. package/dist/graph/ToolbarPopover.d.ts +11 -6
  211. package/dist/graph/ToolbarPopover.js +35 -67
  212. package/dist/graph/TreeView.js +24 -37
  213. package/dist/graph/WorkflowSettingsDialog.js +4 -4
  214. package/dist/graph/editorChrome.js +13 -12
  215. package/dist/graph/editorFields.js +29 -29
  216. package/dist/graph/editorPrimitives.d.ts +1 -1
  217. package/dist/graph/editorPrimitives.js +10 -7
  218. package/dist/graph/editorRows.js +103 -97
  219. package/dist/graph/fieldProblems.js +2 -2
  220. package/dist/graph/graphEditorState.d.ts +4 -3
  221. package/dist/graph/inputRefs.js +9 -9
  222. package/dist/graph/settingsViews.js +6 -7
  223. package/dist/graph/stepWith.js +6 -6
  224. package/dist/index.d.ts +14 -15
  225. package/dist/index.js +15 -14
  226. package/dist/list/ActionToolbar.d.ts +6 -14
  227. package/dist/list/ActionToolbar.js +48 -96
  228. package/dist/list/ListPage.d.ts +6 -12
  229. package/dist/list/ListPage.js +46 -29
  230. package/dist/list/ListPager.js +4 -4
  231. package/dist/list/ListTable.js +5 -5
  232. package/dist/list/ListViewControls.d.ts +5 -4
  233. package/dist/list/ListViewControls.js +177 -267
  234. package/dist/list/UserHoverCard.d.ts +3 -17
  235. package/dist/list/UserHoverCard.js +21 -45
  236. package/dist/list/index.d.ts +3 -4
  237. package/dist/list/index.js +3 -4
  238. package/dist/list/listSearch.js +12 -11
  239. package/dist/list/rowMenu.d.ts +63 -0
  240. package/dist/list/rowMenu.js +134 -0
  241. package/dist/logviewer/LogViewer.js +26 -26
  242. package/dist/styles.css +1 -1
  243. package/package.json +7 -13
  244. package/dist/ai/ui/Dropdown.d.ts +0 -31
  245. package/dist/ai/ui/Dropdown.js +0 -110
  246. package/dist/components/AddMenu.d.ts +0 -18
  247. package/dist/components/AddMenu.js +0 -46
  248. package/dist/components/BareModal.d.ts +0 -17
  249. package/dist/components/BareModal.js +0 -77
  250. package/dist/components/CollapsiblePanel.d.ts +0 -27
  251. package/dist/components/CollapsiblePanel.js +0 -77
  252. package/dist/components/Dropdown.d.ts +0 -28
  253. package/dist/components/Dropdown.js +0 -355
  254. package/dist/components/Listbox.d.ts +0 -21
  255. package/dist/components/Listbox.js +0 -119
  256. package/dist/components/MultiSelect.d.ts +0 -22
  257. package/dist/components/MultiSelect.js +0 -168
  258. package/dist/components/SwitchToggle.d.ts +0 -15
  259. package/dist/components/SwitchToggle.js +0 -57
  260. package/dist/components/SwitchToggleSmall.d.ts +0 -6
  261. package/dist/components/SwitchToggleSmall.js +0 -14
  262. package/dist/components/Toggle.d.ts +0 -6
  263. package/dist/components/Toggle.js +0 -20
  264. package/dist/components/dropdownUtils.d.ts +0 -37
  265. package/dist/components/dropdownUtils.js +0 -99
  266. package/dist/form/SpecialTypes/DropdownWrapper.d.ts +0 -25
  267. package/dist/form/SpecialTypes/DropdownWrapper.js +0 -28
  268. package/dist/form/SpecialTypes/FormikCustomDropdown.d.ts +0 -51
  269. package/dist/form/SpecialTypes/FormikCustomDropdown.js +0 -119
  270. package/dist/form/SpecialTypes/FormikCustomInput.d.ts +0 -19
  271. package/dist/form/SpecialTypes/FormikCustomInput.js +0 -121
  272. package/dist/form/SpecialTypes/FormikMultiSelectDropdown.d.ts +0 -13
  273. package/dist/form/SpecialTypes/FormikMultiSelectDropdown.js +0 -31
  274. package/dist/form/SpecialTypes/FormikNumericInput.d.ts +0 -9
  275. package/dist/form/SpecialTypes/FormikNumericInput.js +0 -60
  276. package/dist/form/SpecialTypes/index.d.ts +0 -3
  277. package/dist/list/HoverCard.d.ts +0 -24
  278. package/dist/list/HoverCard.js +0 -73
  279. package/dist/list/RowContextMenu.d.ts +0 -127
  280. package/dist/list/RowContextMenu.js +0 -515
@@ -1,515 +0,0 @@
1
- import { safeUrl } from "../safeUrl.js";
2
- import { useLink, useNotify, useStrings } from "../components/Provider.js";
3
- import { BsThreeDots } from "../icons/bs/BsThreeDots.js";
4
- import { FaCheck } from "../icons/fa/FaCheck.js";
5
- import { FaKey } from "../icons/fa/FaKey.js";
6
- import { FaLink } from "../icons/fa/FaLink.js";
7
- import { FaUser } from "../icons/fa/FaUser.js";
8
- import { FiCopy } from "../icons/fi/FiCopy.js";
9
- import { HiOutlineClipboardCopy } from "../icons/hi/HiOutlineClipboardCopy.js";
10
- import { LuChevronRight } from "../icons/lu/LuChevronRight.js";
11
- import { LuIdCard } from "../icons/lu/LuIdCard.js";
12
- import { LuMail } from "../icons/lu/LuMail.js";
13
- import { keyedByContent } from "../components/keys.js";
14
- import { TOOLTIP_ID } from "../components/Tooltip.js";
15
- import cx from "classnames";
16
- import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
17
- import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
18
- import { createPortal } from "react-dom";
19
- //#region src/list/RowContextMenu.tsx
20
- /** Converts a single row action to a menu item: a `to` makes it a link, otherwise an action. */
21
- function toRowMenuItem(action) {
22
- if (action.to) return {
23
- kind: "link",
24
- label: action.label,
25
- icon: action.icon,
26
- to: action.to,
27
- destructive: action.destructive
28
- };
29
- return {
30
- kind: "action",
31
- label: action.label,
32
- icon: action.icon,
33
- onSelect: action.onSelect,
34
- disabled: action.disabled,
35
- destructive: action.destructive,
36
- tooltip: action.tooltip
37
- };
38
- }
39
- /** Bridges a toolbar action (icon component, `onClick`/`href`) onto the shared
40
- * `RowAction` shape. Typed structurally so any toolbar action shape matching
41
- * these fields is assignable. */
42
- function toolbarActionToRowAction(action) {
43
- const Icon = action.icon;
44
- return {
45
- key: action.key,
46
- label: action.label,
47
- icon: /* @__PURE__ */ jsx(Icon, {}),
48
- onSelect: action.onClick,
49
- to: action.href,
50
- disabled: action.disabled,
51
- destructive: action.key === "delete" || action.key === "destroy",
52
- tooltip: action.disabledTooltip
53
- };
54
- }
55
- /** Assembles a row's context menu in the shared order: leading link(s) → middle
56
- * actions → extra items → Copy submenu → trailing (destructive) actions last. */
57
- function assembleRowMenu({ lead = [], actions, exclude, order, trailing = ["delete"], extra = [], copy }) {
58
- const trailingSet = new Set(trailing);
59
- const excludeSet = new Set(exclude);
60
- let middle = actions.filter((a) => !trailingSet.has(a.key) && !excludeSet.has(a.key));
61
- if (order) {
62
- const byKey = new Map(middle.map((a) => [a.key, a]));
63
- middle = order.map((key) => byKey.get(key)).filter((a) => a !== void 0);
64
- }
65
- const trailingItems = trailing.map((key) => actions.find((a) => a.key === key)).filter((a) => a !== void 0);
66
- return [
67
- ...lead,
68
- ...middle.map(toRowMenuItem),
69
- ...extra,
70
- ...copy ? [copy] : [],
71
- ...trailingItems.map(toRowMenuItem)
72
- ];
73
- }
74
- function useRowMenu(zClassName = "z-50") {
75
- const [menu, setMenu] = useState(null);
76
- const onCloseRef = useRef(void 0);
77
- const openMenu = useCallback((x, y, items, onClose, search) => {
78
- if (items.length > 0) {
79
- onCloseRef.current?.();
80
- onCloseRef.current = onClose;
81
- setMenu({
82
- x,
83
- y,
84
- items,
85
- search
86
- });
87
- }
88
- }, []);
89
- const close = useCallback(() => {
90
- onCloseRef.current?.();
91
- onCloseRef.current = void 0;
92
- setMenu(null);
93
- }, []);
94
- return {
95
- openMenu,
96
- contextMenu: /* @__PURE__ */ jsx(RowContextMenu, {
97
- state: menu,
98
- onClose: close,
99
- zClassName
100
- })
101
- };
102
- }
103
- /** The full-screen menu overlay drops CSS :hover on the triggering row; wrap openMenu here to substitute a JS-driven `active` highlight. */
104
- function useRowMenuActive(openMenu) {
105
- const [active, setActive] = useState(false);
106
- return {
107
- active,
108
- openMenu: useCallback((x, y, items, onClose, search) => {
109
- setActive(true);
110
- openMenu(x, y, items, () => {
111
- setActive(false);
112
- onClose?.();
113
- }, search);
114
- }, [openMenu])
115
- };
116
- }
117
- function rowContextMenuProps(openMenu, items) {
118
- return { onContextMenu: (e) => {
119
- e.preventDefault();
120
- if (items.length > 0) e.stopPropagation();
121
- openMenu(e.clientX, e.clientY, items);
122
- } };
123
- }
124
- function RowContextMenu({ state, onClose, zClassName }) {
125
- const ref = useRef(null);
126
- const [pos, setPos] = useState(null);
127
- const opener = useRef(null);
128
- const wasOpen = useRef(false);
129
- useLayoutEffect(() => {
130
- const menu = ref.current;
131
- if (!state || !menu) {
132
- setPos(null);
133
- wasOpen.current = false;
134
- return;
135
- }
136
- if (!wasOpen.current) {
137
- opener.current = document.activeElement;
138
- wasOpen.current = true;
139
- }
140
- const { width, height } = menu.getBoundingClientRect();
141
- const pad = MENU_EDGE;
142
- const openUp = height > window.innerHeight - pad - state.y && state.y - height - pad >= 0;
143
- const top = Math.max(pad, openUp ? state.y - height : Math.min(state.y, window.innerHeight - height - pad));
144
- setPos({
145
- left: Math.max(pad, Math.min(state.x, window.innerWidth - width - pad)),
146
- top,
147
- maxHeight: (openUp ? state.y : window.innerHeight - pad) - top
148
- });
149
- }, [state]);
150
- const placed = pos !== null;
151
- useEffect(() => {
152
- const menu = ref.current;
153
- if (!placed || !menu) return;
154
- const from = opener.current;
155
- const target = menu.querySelector(":scope > div > input[type=\"search\"]") ?? (focusVisible(from) ? menu.querySelector(MENU_ITEMS) : null);
156
- if (!target) return;
157
- target.focus({ preventScroll: true });
158
- return () => {
159
- if (from instanceof HTMLElement && document.activeElement === document.body) from.focus({ preventScroll: true });
160
- };
161
- }, [placed, state]);
162
- useEffect(() => {
163
- if (!state) return;
164
- const close = (e) => {
165
- if (!(e.target instanceof Node && ref.current?.contains(e.target))) onClose();
166
- };
167
- const onKey = (e) => {
168
- if (e.key === "Escape") onClose();
169
- };
170
- window.addEventListener("scroll", close, true);
171
- window.addEventListener("resize", close);
172
- window.addEventListener("keydown", onKey);
173
- return () => {
174
- window.removeEventListener("scroll", close, true);
175
- window.removeEventListener("resize", close);
176
- window.removeEventListener("keydown", onKey);
177
- };
178
- }, [state, onClose]);
179
- if (!state) return null;
180
- const side = (pos?.left ?? state.x) > window.innerWidth * .6 ? "left" : "right";
181
- return createPortal(/* @__PURE__ */ jsx("div", {
182
- role: "none",
183
- className: cx("fixed inset-0", zClassName),
184
- onClick: (e) => {
185
- if (e.target === e.currentTarget) onClose();
186
- },
187
- onContextMenu: (e) => {
188
- e.preventDefault();
189
- if (ref.current?.contains(e.target)) {
190
- onClose();
191
- return;
192
- }
193
- const { clientX, clientY } = e;
194
- const overlay = e.currentTarget;
195
- overlay.style.pointerEvents = "none";
196
- const under = document.elementFromPoint(clientX, clientY);
197
- overlay.style.pointerEvents = "";
198
- onClose();
199
- under?.dispatchEvent(new MouseEvent("contextmenu", {
200
- bubbles: true,
201
- cancelable: true,
202
- clientX,
203
- clientY
204
- }));
205
- },
206
- children: /* @__PURE__ */ jsx("div", {
207
- ref,
208
- role: "menu",
209
- className: "absolute flex min-w-44 flex-col rounded-lg border border-(--theme-border) bg-(--theme-panel-bg) py-1 shadow-lg backdrop-blur-xl",
210
- style: {
211
- left: pos?.left ?? state.x,
212
- top: pos?.top ?? state.y,
213
- maxHeight: state.search ? pos?.maxHeight : void 0,
214
- visibility: pos ? "visible" : "hidden"
215
- },
216
- onKeyDown: (e) => {
217
- const step = {
218
- ArrowDown: 1,
219
- ArrowUp: -1
220
- }[e.key];
221
- if (!step || e.defaultPrevented || !ref.current) return;
222
- e.preventDefault();
223
- const items = [...ref.current.querySelectorAll(MENU_ITEMS)];
224
- const at = items.indexOf(document.activeElement);
225
- focusItem(items[((at < 0 ? step > 0 ? 0 : items.length - 1 : at + step) + items.length) % items.length]);
226
- },
227
- children: /* @__PURE__ */ jsx(MenuItemList, {
228
- items: state.items,
229
- search: state.search,
230
- onClose,
231
- side
232
- })
233
- })
234
- }), document.body);
235
- }
236
- var MENU_EDGE = 8;
237
- function focusItem(item) {
238
- item?.focus({ preventScroll: true });
239
- item?.scrollIntoView?.({ block: "nearest" });
240
- }
241
- function SubmenuPanel({ side, panelRef, searchable, children }) {
242
- const [place, setPlace] = useState(null);
243
- useLayoutEffect(() => {
244
- const rect = panelRef.current?.getBoundingClientRect();
245
- if (rect) {
246
- const bottom = window.innerHeight - MENU_EDGE;
247
- const lift = Math.max(0, Math.min(rect.bottom - bottom, rect.top - MENU_EDGE));
248
- setPlace({
249
- lift,
250
- maxHeight: bottom - (rect.top - lift)
251
- });
252
- }
253
- }, [panelRef]);
254
- return /* @__PURE__ */ jsx("div", {
255
- ref: panelRef,
256
- className: cx("absolute -top-1 z-10 flex min-w-44 flex-col rounded-lg border border-(--theme-border) bg-(--theme-panel-bg) py-1 shadow-lg", side === "left" ? "right-full" : "left-full"),
257
- style: place ? {
258
- ...searchable ? { maxHeight: place.maxHeight } : {},
259
- ...place.lift > 0 ? { transform: `translateY(-${place.lift}px)` } : {}
260
- } : void 0,
261
- children
262
- });
263
- }
264
- var MENU_ITEMS = "button:not([disabled]), a[href]";
265
- function focusVisible(element) {
266
- try {
267
- return !!element?.matches(":focus-visible");
268
- } catch {
269
- return false;
270
- }
271
- }
272
- var MENU_ITEM_CLASSES = "flex w-full items-center gap-2.5 px-3 py-1.5 text-left text-[13px] hover:bg-(--theme-muted-panel-bg) transition-colors cursor-pointer [&>svg]:h-4 [&>svg]:w-4 [&>svg]:shrink-0";
273
- function matchesSearch(item, needle) {
274
- const keywords = item.kind === "action" ? item.keywords ?? "" : "";
275
- return `${item.label} ${keywords}`.toLowerCase().includes(needle);
276
- }
277
- function searchMatches(items, needle) {
278
- const found = /* @__PURE__ */ new Map();
279
- const walk = (list) => {
280
- for (const item of list) if (item.kind === "submenu") walk(item.items);
281
- else if (item.kind !== "divider" && !found.has(item.label) && matchesSearch(item, needle)) found.set(item.label, item);
282
- };
283
- walk(items);
284
- return [...found.values()];
285
- }
286
- function MenuSearchField({ search, query, onQuery, onEnter }) {
287
- return /* @__PURE__ */ jsx("div", {
288
- className: "px-2 pt-0.5 pb-1",
289
- children: /* @__PURE__ */ jsx("input", {
290
- type: "search",
291
- "aria-label": search.placeholder,
292
- placeholder: search.placeholder,
293
- value: query,
294
- onChange: (e) => onQuery(e.target.value),
295
- onKeyDown: (e) => {
296
- if (e.key === "Enter") {
297
- e.preventDefault();
298
- onEnter();
299
- } else if (e.key === "ArrowDown") {
300
- e.preventDefault();
301
- focusItem(e.currentTarget.parentElement?.parentElement?.querySelector(MENU_ITEMS));
302
- }
303
- },
304
- className: "w-full rounded-md border border-(--theme-border) bg-(--theme-muted-panel-bg) px-2 py-1 text-[13px] text-(--theme-app) placeholder:text-(--theme-muted-text-color) focus:border-(--theme-element) focus:outline-none"
305
- })
306
- });
307
- }
308
- function MenuItemList({ items, search, onClose, side }) {
309
- const [query, setQuery] = useState("");
310
- const needle = query.trim().toLowerCase();
311
- const searching = !!search && !!needle;
312
- const shown = searching ? searchMatches(items, needle) : items;
313
- const run = useRunItem(onClose);
314
- const pickFirst = () => {
315
- const first = shown.find((item) => item.kind === "action" && !item.disabled);
316
- if (first?.kind === "action") run(first);
317
- };
318
- const rows = keyedByContent(shown, (it) => it.kind === "divider" ? "divider" : it.label).map(({ key, item }) => item.kind === "divider" ? /* @__PURE__ */ jsx("div", {
319
- "aria-hidden": "true",
320
- className: "my-1 h-px bg-(--theme-border)"
321
- }, key) : /* @__PURE__ */ jsx(MenuRow, {
322
- item,
323
- onClose,
324
- side
325
- }, key));
326
- return /* @__PURE__ */ jsxs(Fragment$1, { children: [
327
- search && /* @__PURE__ */ jsx(MenuSearchField, {
328
- search,
329
- query,
330
- onQuery: setQuery,
331
- onEnter: pickFirst
332
- }),
333
- searching ? /* @__PURE__ */ jsx("div", {
334
- className: "max-h-[min(20rem,60vh)] min-h-0 overflow-y-auto",
335
- children: rows
336
- }) : rows,
337
- searching && shown.length === 0 && /* @__PURE__ */ jsx("div", {
338
- className: "px-3 py-1.5 text-[13px] text-(--theme-muted-text-color)",
339
- children: search.empty
340
- })
341
- ] });
342
- }
343
- function MenuRow({ item, onClose, side }) {
344
- const Link = useLink();
345
- const run = useRunItem(onClose);
346
- const [openSub, setOpenSub] = useState(false);
347
- const closeTimer = useRef(void 0);
348
- const panel = useRef(null);
349
- const hovered = useRef(false);
350
- if (item.kind === "submenu") {
351
- const open = () => {
352
- clearTimeout(closeTimer.current);
353
- setOpenSub(true);
354
- };
355
- const close = () => {
356
- closeTimer.current = setTimeout(() => setOpenSub(false), 160);
357
- };
358
- return /* @__PURE__ */ jsxs("div", {
359
- className: "relative",
360
- role: "none",
361
- onMouseEnter: () => {
362
- hovered.current = true;
363
- open();
364
- },
365
- onMouseLeave: () => {
366
- hovered.current = false;
367
- if (!panel.current?.contains(document.activeElement)) close();
368
- },
369
- onFocus: open,
370
- onBlur: (e) => {
371
- const to = e.relatedTarget;
372
- if (!e.currentTarget.contains(to) && (to !== null || !hovered.current)) close();
373
- },
374
- children: [/* @__PURE__ */ jsxs("button", {
375
- type: "button",
376
- "aria-expanded": openSub,
377
- "aria-haspopup": "menu",
378
- onClick: open,
379
- className: cx(MENU_ITEM_CLASSES, "justify-between text-(--theme-app)"),
380
- children: [/* @__PURE__ */ jsxs("span", {
381
- className: "flex items-center gap-2.5",
382
- children: [item.icon, item.label]
383
- }), /* @__PURE__ */ jsx(LuChevronRight, { className: "h-3 w-3 shrink-0 opacity-60" })]
384
- }), openSub && /* @__PURE__ */ jsx(SubmenuPanel, {
385
- side,
386
- panelRef: panel,
387
- searchable: !!item.search,
388
- children: /* @__PURE__ */ jsx(MenuItemList, {
389
- items: item.items,
390
- search: item.search,
391
- onClose,
392
- side
393
- })
394
- })]
395
- });
396
- }
397
- if (item.kind === "link") {
398
- const className = cx(MENU_ITEM_CLASSES, item.destructive ? "text-red-500" : "text-(--theme-app)");
399
- if (item.reloadDocument) return /* @__PURE__ */ jsxs("a", {
400
- href: safeUrl(item.to),
401
- onClick: onClose,
402
- className,
403
- children: [item.icon, item.label]
404
- });
405
- return /* @__PURE__ */ jsxs(Link, {
406
- to: item.to,
407
- onClick: onClose,
408
- className,
409
- children: [item.icon, item.label]
410
- });
411
- }
412
- return /* @__PURE__ */ jsxs("button", {
413
- type: "button",
414
- disabled: item.disabled && !item.tooltip,
415
- "aria-disabled": item.disabled,
416
- ...item.disabled && item.tooltip ? {
417
- "data-tooltip-id": TOOLTIP_ID,
418
- "data-tooltip-content": item.tooltip
419
- } : {},
420
- onClick: () => run(item),
421
- className: cx(MENU_ITEM_CLASSES, item.destructive ? "text-red-500" : "text-(--theme-app)", item.disabled && "opacity-40 cursor-not-allowed hover:bg-transparent"),
422
- children: [
423
- item.icon,
424
- item.label,
425
- item.selected && /* @__PURE__ */ jsx(FaCheck, { className: "ml-auto opacity-80" })
426
- ]
427
- });
428
- }
429
- function useRunItem(onClose) {
430
- const notify = useNotify();
431
- const { list: t } = useStrings();
432
- return (item) => {
433
- if (item.disabled) return;
434
- if (item.copy) {
435
- const { text, label } = item.copy;
436
- navigator.clipboard?.writeText(text).then(() => notify.success(t.copied(label)), () => notify.error(t.couldntCopy(label)));
437
- }
438
- item.onSelect?.();
439
- onClose();
440
- };
441
- }
442
- /** Returns a builder for the Copy submenu, labeled from the provider's strings. */
443
- function useCopySubmenu() {
444
- const { list: t } = useStrings();
445
- return useCallback(({ name, nameLabel, email, username, uid, id, url, extra }) => {
446
- const items = [
447
- {
448
- label: nameLabel ?? t.labelName,
449
- value: name,
450
- icon: /* @__PURE__ */ jsx(FiCopy, {})
451
- },
452
- {
453
- label: t.labelEmail,
454
- value: email,
455
- icon: /* @__PURE__ */ jsx(LuMail, {})
456
- },
457
- {
458
- label: t.labelUsername,
459
- value: username,
460
- icon: /* @__PURE__ */ jsx(FaUser, {})
461
- },
462
- {
463
- label: t.labelUid,
464
- value: uid === null || uid === void 0 ? null : String(uid),
465
- icon: /* @__PURE__ */ jsx(FaKey, {})
466
- },
467
- {
468
- label: t.labelId,
469
- value: id,
470
- icon: /* @__PURE__ */ jsx(LuIdCard, {})
471
- },
472
- {
473
- label: t.labelUrl,
474
- value: url,
475
- icon: /* @__PURE__ */ jsx(FaLink, {})
476
- },
477
- ...(extra ?? []).map((e) => ({
478
- label: e.label,
479
- value: e.value,
480
- icon: e.icon ?? /* @__PURE__ */ jsx(FiCopy, {})
481
- }))
482
- ].flatMap(({ label, value, icon }) => value ? [{
483
- kind: "action",
484
- label: t.copy(label),
485
- icon,
486
- copy: {
487
- text: value,
488
- label
489
- }
490
- }] : []);
491
- return {
492
- kind: "submenu",
493
- label: t.copyMenu,
494
- icon: /* @__PURE__ */ jsx(HiOutlineClipboardCopy, {}),
495
- items
496
- };
497
- }, [t]);
498
- }
499
- /** Stays visible while active (menu open) so it doesn't vanish behind the overlay. */
500
- function MoreButton({ onOpen, active, className }) {
501
- const { list: t } = useStrings();
502
- return /* @__PURE__ */ jsx("button", {
503
- type: "button",
504
- "aria-label": t.moreActions,
505
- onClick: (e) => {
506
- e.stopPropagation();
507
- const r = e.currentTarget.getBoundingClientRect();
508
- onOpen(r.right, r.bottom);
509
- },
510
- className: cx("rounded p-1 cursor-pointer text-(--theme-muted-text-color) transition-colors hover:bg-(--theme-muted-panel-bg) hover:text-(--theme-app) focus-visible:opacity-100", active ? "opacity-100" : "opacity-0 group-hover:opacity-100", className),
511
- children: /* @__PURE__ */ jsx(BsThreeDots, { className: "h-4 w-4" })
512
- });
513
- }
514
- //#endregion
515
- export { MoreButton, assembleRowMenu, rowContextMenuProps, toRowMenuItem, toolbarActionToRowAction, useCopySubmenu, useRowMenu, useRowMenuActive };