@parallelworks/ui 0.40.1 → 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 (289) hide show
  1. package/README.md +25 -0
  2. package/dist/ai/adapter/memoryStore.js +7 -0
  3. package/dist/ai/adapter/mock/mockAdapter.js +52 -17
  4. package/dist/ai/adapter/openai/createOpenAIStream.js +1 -1
  5. package/dist/ai/adapter/openai/index.d.ts +1 -1
  6. package/dist/ai/adapter/openai/sse.d.ts +8 -2
  7. package/dist/ai/adapter/openai/sse.js +40 -4
  8. package/dist/ai/adapter/openai/wire.d.ts +6 -0
  9. package/dist/ai/components/AllocationSelector.d.ts +3 -4
  10. package/dist/ai/components/AllocationSelector.js +37 -33
  11. package/dist/ai/components/AttachmentManager.js +7 -7
  12. package/dist/ai/components/AttachmentUpload.js +5 -5
  13. package/dist/ai/components/BranchNavigator.js +1 -1
  14. package/dist/ai/components/ChatEmptyState.js +2 -2
  15. package/dist/ai/components/ChatInput.js +10 -8
  16. package/dist/ai/components/ChatLayout.js +4 -5
  17. package/dist/ai/components/ChatMessage.js +12 -12
  18. package/dist/ai/components/ChatMessageList.js +9 -9
  19. package/dist/ai/components/ChatSidebar.js +8 -9
  20. package/dist/ai/components/ChatThread.js +4 -4
  21. package/dist/ai/components/ComposerChrome.d.ts +2 -2
  22. package/dist/ai/components/ComposerChrome.js +3 -6
  23. package/dist/ai/components/DragOverlay.js +2 -2
  24. package/dist/ai/components/ModelSelector.d.ts +5 -3
  25. package/dist/ai/components/ModelSelector.js +102 -339
  26. package/dist/ai/components/PastedText.js +11 -11
  27. package/dist/ai/components/ProviderIssueBanner.d.ts +2 -0
  28. package/dist/ai/components/ProviderIssueBanner.js +34 -16
  29. package/dist/ai/components/Reasoning.js +4 -4
  30. package/dist/ai/components/ShareDialog.js +43 -35
  31. package/dist/ai/components/SlashMenu.d.ts +12 -2
  32. package/dist/ai/components/SlashMenu.js +29 -13
  33. package/dist/ai/components/agent/ActivityLine.js +3 -3
  34. package/dist/ai/components/agent/AgentMessageParts.js +20 -20
  35. package/dist/ai/components/agent/PermissionPicker.js +13 -11
  36. package/dist/ai/components/conversationActions.d.ts +4 -6
  37. package/dist/ai/components/conversationActions.js +9 -13
  38. package/dist/ai/core/ChatProvider.js +4 -4
  39. package/dist/ai/core/config.js +1 -1
  40. package/dist/ai/core/parts.d.ts +5 -0
  41. package/dist/ai/core/parts.js +28 -1
  42. package/dist/ai/core/providerIssues.d.ts +7 -0
  43. package/dist/ai/core/providerIssues.js +9 -1
  44. package/dist/ai/core/useKeyboardShortcuts.d.ts +4 -0
  45. package/dist/ai/core/useKeyboardShortcuts.js +8 -2
  46. package/dist/ai/index.d.ts +0 -1
  47. package/dist/ai/index.js +1 -2
  48. package/dist/ai/strings.d.ts +19 -0
  49. package/dist/ai/strings.js +18 -1
  50. package/dist/ai/ui/Avatar.js +5 -5
  51. package/dist/ai/ui/ConversationSidebar.d.ts +0 -1
  52. package/dist/ai/ui/ConversationSidebar.js +6 -7
  53. package/dist/ai/ui/Markdown.js +3 -3
  54. package/dist/ai/ui/Notice.js +6 -6
  55. package/dist/ai/ui/chip.d.ts +4 -0
  56. package/dist/ai/ui/chip.js +8 -0
  57. package/dist/ai/ui/sidebar.d.ts +6 -5
  58. package/dist/ai/ui/sidebar.js +39 -43
  59. package/dist/components/ActionMenu.d.ts +13 -0
  60. package/dist/components/ActionMenu.js +30 -0
  61. package/dist/components/Avatar.js +6 -6
  62. package/dist/components/Breadcrumbs.js +1 -1
  63. package/dist/components/Button.js +10 -10
  64. package/dist/components/Callout.js +2 -2
  65. package/dist/components/Card.js +3 -3
  66. package/dist/components/Combobox.d.ts +66 -0
  67. package/dist/components/Combobox.js +393 -0
  68. package/dist/components/ConfirmModal.js +11 -11
  69. package/dist/components/ContextMenu.d.ts +23 -0
  70. package/dist/components/ContextMenu.js +89 -0
  71. package/dist/components/CopyToClipboard.js +2 -2
  72. package/dist/components/CreateModal.d.ts +17 -36
  73. package/dist/components/CreateModal.js +97 -259
  74. package/dist/components/DescriptionList.js +8 -8
  75. package/dist/components/Filter.js +2 -2
  76. package/dist/components/FilterPill.js +2 -2
  77. package/dist/components/HoverCard.d.ts +14 -0
  78. package/dist/components/HoverCard.js +33 -0
  79. package/dist/components/IconButton.d.ts +5 -5
  80. package/dist/components/IconButton.js +18 -22
  81. package/dist/components/InfoTip.d.ts +15 -0
  82. package/dist/components/InfoTip.js +60 -0
  83. package/dist/components/Input.js +5 -5
  84. package/dist/components/LetterBadge.js +2 -2
  85. package/dist/components/Loader.js +3 -3
  86. package/dist/components/Markdown.js +1 -1
  87. package/dist/components/MarkdownImpl.js +2 -2
  88. package/dist/components/Menu.d.ts +33 -0
  89. package/dist/components/Menu.js +86 -0
  90. package/dist/components/MenuEntries.d.ts +56 -0
  91. package/dist/components/MenuEntries.js +123 -0
  92. package/dist/components/Modal.d.ts +29 -0
  93. package/dist/components/Modal.js +47 -0
  94. package/dist/components/Overview.js +11 -10
  95. package/dist/components/PageHeader.d.ts +10 -16
  96. package/dist/components/PageHeader.js +69 -95
  97. package/dist/components/Pagination.js +5 -5
  98. package/dist/components/Provider.d.ts +5 -11
  99. package/dist/components/Provider.js +11 -14
  100. package/dist/components/Radio.d.ts +8 -0
  101. package/dist/components/Radio.js +16 -0
  102. package/dist/components/SectionHeader.js +2 -2
  103. package/dist/components/SegmentedControl.d.ts +6 -5
  104. package/dist/components/SegmentedControl.js +34 -30
  105. package/dist/components/Select.d.ts +27 -0
  106. package/dist/components/Select.js +78 -0
  107. package/dist/components/SettingModeOption.d.ts +5 -8
  108. package/dist/components/SettingModeOption.js +10 -12
  109. package/dist/components/SettingRow.js +4 -4
  110. package/dist/components/SettingsCard.js +4 -4
  111. package/dist/components/SettingsGroup.js +2 -2
  112. package/dist/components/Sort.js +1 -1
  113. package/dist/components/StatusBadge.js +7 -7
  114. package/dist/components/StatusIcons.js +4 -4
  115. package/dist/components/Switch.d.ts +22 -0
  116. package/dist/components/Switch.js +28 -0
  117. package/dist/components/Table.js +11 -11
  118. package/dist/components/Tooltip.d.ts +24 -20
  119. package/dist/components/Tooltip.js +58 -76
  120. package/dist/components/floating.d.ts +14 -0
  121. package/dist/components/floating.js +21 -0
  122. package/dist/components/search.d.ts +2 -0
  123. package/dist/components/search.js +14 -0
  124. package/dist/editor/Editor.js +1 -1
  125. package/dist/editor/EditorField.js +16 -21
  126. package/dist/editor/FormCodePanel.js +19 -19
  127. package/dist/editor/Monaco.js +3 -3
  128. package/dist/file-explorer/FileExplorer.js +77 -81
  129. package/dist/file-explorer/FileExplorerContext.js +2 -2
  130. package/dist/file-explorer/FilePreview/CodePreviewEditor.js +1 -1
  131. package/dist/file-explorer/FilePreview/Inline.js +1 -1
  132. package/dist/file-explorer/FilePreview/StreamedBadge.js +5 -6
  133. package/dist/file-explorer/FilePreview/Viewer.js +8 -8
  134. package/dist/file-explorer/FilePreview/index.d.ts +1 -1
  135. package/dist/file-explorer/FilePreview/index.js +49 -83
  136. package/dist/file-explorer/FileRow.d.ts +4 -3
  137. package/dist/file-explorer/FileRow.js +32 -38
  138. package/dist/file-explorer/FileRowsWindow.d.ts +3 -2
  139. package/dist/file-explorer/FileRowsWindow.js +4 -4
  140. package/dist/file-explorer/FileTable.d.ts +3 -2
  141. package/dist/file-explorer/FileTable.js +3 -3
  142. package/dist/file-explorer/LoadMoreRow.js +1 -1
  143. package/dist/file-explorer/TreeView.js +11 -16
  144. package/dist/file-explorer/helpers.js +11 -12
  145. package/dist/file-explorer/toasts.js +11 -11
  146. package/dist/form/FieldGroup.d.ts +11 -0
  147. package/dist/form/FieldGroup.js +36 -0
  148. package/dist/form/FieldWrapper.js +4 -4
  149. package/dist/form/Fields/BooleanField.js +23 -33
  150. package/dist/form/Fields/CheckboxGroupField.d.ts +1 -1
  151. package/dist/form/Fields/CheckboxGroupField.js +9 -13
  152. package/dist/form/Fields/ColorField.d.ts +1 -2
  153. package/dist/form/Fields/ColorField.js +6 -11
  154. package/dist/form/Fields/DropdownField.d.ts +3 -13
  155. package/dist/form/Fields/DropdownField.js +40 -27
  156. package/dist/form/Fields/DurationField.d.ts +1 -2
  157. package/dist/form/Fields/DurationField.js +25 -32
  158. package/dist/form/Fields/LabelField.js +2 -2
  159. package/dist/form/Fields/MultiDropdownField.d.ts +6 -12
  160. package/dist/form/Fields/MultiDropdownField.js +31 -22
  161. package/dist/form/Fields/NumberField.d.ts +1 -1
  162. package/dist/form/Fields/NumberField.js +65 -35
  163. package/dist/form/Fields/ObjectField.d.ts +1 -1
  164. package/dist/form/Fields/ObjectField.js +12 -43
  165. package/dist/form/Fields/RadioField.d.ts +1 -1
  166. package/dist/form/Fields/RadioField.js +30 -41
  167. package/dist/form/Fields/RangeField.d.ts +1 -2
  168. package/dist/form/Fields/RangeField.js +23 -16
  169. package/dist/form/Fields/StringField.d.ts +1 -2
  170. package/dist/form/Fields/StringField.js +58 -26
  171. package/dist/form/Fields/TextAreaField.js +12 -10
  172. package/dist/form/Form.d.ts +30 -48
  173. package/dist/form/Form.js +306 -412
  174. package/dist/form/FormLayout.js +9 -9
  175. package/dist/form/Wizard/WizardContainer.d.ts +1 -4
  176. package/dist/form/Wizard/WizardContainer.js +40 -56
  177. package/dist/form/Wizard/WizardNavigation.js +1 -1
  178. package/dist/form/Wizard/WizardStepContent.d.ts +1 -5
  179. package/dist/form/Wizard/WizardStepContent.js +11 -16
  180. package/dist/form/Wizard/WizardStepIndicator.js +6 -6
  181. package/dist/form/Wizard/types.d.ts +0 -14
  182. package/dist/form/fieldRegistry.d.ts +1 -1
  183. package/dist/form/fieldRegistry.js +6 -6
  184. package/dist/form/formEditing.js +1 -1
  185. package/dist/form/index.d.ts +5 -4
  186. package/dist/form/index.js +6 -7
  187. package/dist/form/types/fieldComponentTypes.d.ts +8 -10
  188. package/dist/form/useChoiceField.d.ts +20 -0
  189. package/dist/form/useChoiceField.js +49 -0
  190. package/dist/form/useFieldValue.d.ts +7 -0
  191. package/dist/form/useFieldValue.js +20 -0
  192. package/dist/form/useParsedOpts.d.ts +3 -1
  193. package/dist/form/useParsedOpts.js +12 -17
  194. package/dist/form/utils/dependentOptions.d.ts +8 -4
  195. package/dist/form/utils/dependentOptions.js +25 -17
  196. package/dist/form/utils/fieldItems.d.ts +10 -0
  197. package/dist/form/utils/fieldItems.js +11 -0
  198. package/dist/form/utils/fieldPath.d.ts +10 -0
  199. package/dist/form/utils/fieldPath.js +20 -0
  200. package/dist/form/utils/shareEqual.d.ts +6 -0
  201. package/dist/form/utils/shareEqual.js +38 -0
  202. package/dist/graph/AnnotationBanner.js +8 -8
  203. package/dist/graph/Collapse.d.ts +1 -0
  204. package/dist/graph/Collapse.js +6 -27
  205. package/dist/graph/Connectors.js +5 -5
  206. package/dist/graph/DependencyGraph.js +20 -19
  207. package/dist/graph/GraphEditor.js +30 -41
  208. package/dist/graph/GraphEditorDialogs.js +17 -17
  209. package/dist/graph/GraphNode.js +1 -1
  210. package/dist/graph/InputDialog.js +19 -22
  211. package/dist/graph/InputsEditor.js +58 -66
  212. package/dist/graph/JobSummary.js +42 -39
  213. package/dist/graph/MatrixGroup.js +1 -1
  214. package/dist/graph/ProblemsButton.js +18 -19
  215. package/dist/graph/Reveal.js +1 -1
  216. package/dist/graph/ShortcutsButton.js +15 -18
  217. package/dist/graph/SuggestionInput.d.ts +5 -10
  218. package/dist/graph/SuggestionInput.js +14 -18
  219. package/dist/graph/ToolbarPopover.d.ts +11 -6
  220. package/dist/graph/ToolbarPopover.js +35 -67
  221. package/dist/graph/TreeView.js +24 -37
  222. package/dist/graph/WorkflowSettingsDialog.js +4 -4
  223. package/dist/graph/editorChrome.js +13 -12
  224. package/dist/graph/editorFields.js +29 -29
  225. package/dist/graph/editorPrimitives.d.ts +1 -1
  226. package/dist/graph/editorPrimitives.js +10 -7
  227. package/dist/graph/editorRows.js +103 -97
  228. package/dist/graph/fieldProblems.js +2 -2
  229. package/dist/graph/graphEditorState.d.ts +4 -3
  230. package/dist/graph/inputRefs.js +9 -9
  231. package/dist/graph/settingsViews.js +6 -7
  232. package/dist/graph/stepWith.js +6 -6
  233. package/dist/index.d.ts +14 -15
  234. package/dist/index.js +15 -14
  235. package/dist/list/ActionToolbar.d.ts +6 -14
  236. package/dist/list/ActionToolbar.js +48 -96
  237. package/dist/list/ListPage.d.ts +6 -12
  238. package/dist/list/ListPage.js +46 -29
  239. package/dist/list/ListPager.js +4 -4
  240. package/dist/list/ListTable.js +5 -5
  241. package/dist/list/ListViewControls.d.ts +5 -4
  242. package/dist/list/ListViewControls.js +177 -267
  243. package/dist/list/UserHoverCard.d.ts +3 -17
  244. package/dist/list/UserHoverCard.js +21 -45
  245. package/dist/list/index.d.ts +3 -4
  246. package/dist/list/index.js +3 -4
  247. package/dist/list/listSearch.js +12 -11
  248. package/dist/list/rowMenu.d.ts +63 -0
  249. package/dist/list/rowMenu.js +134 -0
  250. package/dist/logviewer/LogViewer.js +26 -26
  251. package/dist/styles.css +1 -1
  252. package/package.json +7 -13
  253. package/dist/ai/ui/Dropdown.d.ts +0 -31
  254. package/dist/ai/ui/Dropdown.js +0 -110
  255. package/dist/components/AddMenu.d.ts +0 -18
  256. package/dist/components/AddMenu.js +0 -46
  257. package/dist/components/BareModal.d.ts +0 -17
  258. package/dist/components/BareModal.js +0 -77
  259. package/dist/components/CollapsiblePanel.d.ts +0 -27
  260. package/dist/components/CollapsiblePanel.js +0 -77
  261. package/dist/components/Dropdown.d.ts +0 -28
  262. package/dist/components/Dropdown.js +0 -355
  263. package/dist/components/Listbox.d.ts +0 -21
  264. package/dist/components/Listbox.js +0 -119
  265. package/dist/components/MultiSelect.d.ts +0 -22
  266. package/dist/components/MultiSelect.js +0 -168
  267. package/dist/components/SwitchToggle.d.ts +0 -15
  268. package/dist/components/SwitchToggle.js +0 -57
  269. package/dist/components/SwitchToggleSmall.d.ts +0 -6
  270. package/dist/components/SwitchToggleSmall.js +0 -14
  271. package/dist/components/Toggle.d.ts +0 -6
  272. package/dist/components/Toggle.js +0 -20
  273. package/dist/components/dropdownUtils.d.ts +0 -37
  274. package/dist/components/dropdownUtils.js +0 -99
  275. package/dist/form/SpecialTypes/DropdownWrapper.d.ts +0 -25
  276. package/dist/form/SpecialTypes/DropdownWrapper.js +0 -28
  277. package/dist/form/SpecialTypes/FormikCustomDropdown.d.ts +0 -51
  278. package/dist/form/SpecialTypes/FormikCustomDropdown.js +0 -119
  279. package/dist/form/SpecialTypes/FormikCustomInput.d.ts +0 -19
  280. package/dist/form/SpecialTypes/FormikCustomInput.js +0 -121
  281. package/dist/form/SpecialTypes/FormikMultiSelectDropdown.d.ts +0 -13
  282. package/dist/form/SpecialTypes/FormikMultiSelectDropdown.js +0 -31
  283. package/dist/form/SpecialTypes/FormikNumericInput.d.ts +0 -9
  284. package/dist/form/SpecialTypes/FormikNumericInput.js +0 -60
  285. package/dist/form/SpecialTypes/index.d.ts +0 -3
  286. package/dist/list/HoverCard.d.ts +0 -24
  287. package/dist/list/HoverCard.js +0 -73
  288. package/dist/list/RowContextMenu.d.ts +0 -127
  289. package/dist/list/RowContextMenu.js +0 -515
@@ -0,0 +1,89 @@
1
+ import { MenuPopup } from "./Menu.js";
2
+ import { MenuEntries, MenuFilter } from "./MenuEntries.js";
3
+ import { Menu } from "@base-ui/react/menu";
4
+ import { jsx } from "react/jsx-runtime";
5
+ import { useCallback, useRef, useState } from "react";
6
+ //#region src/components/ContextMenu.tsx
7
+ function pointRect(x, y) {
8
+ return {
9
+ x,
10
+ y,
11
+ left: x,
12
+ top: y,
13
+ right: x,
14
+ bottom: y,
15
+ width: 0,
16
+ height: 0
17
+ };
18
+ }
19
+ function toAnchor(at) {
20
+ if (at instanceof Element) return at;
21
+ const { clientX, clientY } = at;
22
+ const on = at.currentTarget instanceof Element ? at.currentTarget : null;
23
+ if (!on) return { getBoundingClientRect: () => pointRect(clientX, clientY) };
24
+ const start = on.getBoundingClientRect();
25
+ const dx = clientX - start.left;
26
+ const dy = clientY - start.top;
27
+ return {
28
+ contextElement: on,
29
+ getBoundingClientRect: () => {
30
+ const now = on.getBoundingClientRect();
31
+ return pointRect(now.left + dx, now.top + dy);
32
+ }
33
+ };
34
+ }
35
+ /**
36
+ * One menu that any number of rows, buttons or a canvas open with their own
37
+ * items, so a long list holds a single menu rather than one per row. Render
38
+ * `contextMenu` once.
39
+ */
40
+ function useContextMenu() {
41
+ const [opened, setOpened] = useState(null);
42
+ const [open, setOpen] = useState(false);
43
+ const owner = useRef(null);
44
+ const release = useCallback(() => {
45
+ const was = owner.current;
46
+ owner.current = null;
47
+ was?.onClose?.();
48
+ }, []);
49
+ const openMenu = useCallback((at, items, options) => {
50
+ if (items.length === 0) return;
51
+ release();
52
+ const next = {
53
+ ...options,
54
+ anchor: toAnchor(at),
55
+ items
56
+ };
57
+ owner.current = next;
58
+ setOpened(next);
59
+ setOpen(true);
60
+ }, [release]);
61
+ const onOpenChange = useCallback((next) => {
62
+ if (!next) setOpen(false);
63
+ }, []);
64
+ const onOpenChangeComplete = useCallback((next) => {
65
+ if (!next) release();
66
+ }, [release]);
67
+ return {
68
+ openMenu,
69
+ contextMenu: opened && /* @__PURE__ */ jsx(MenuFilter, {
70
+ search: opened.search,
71
+ children: (query) => /* @__PURE__ */ jsx(Menu.Root, {
72
+ open,
73
+ onOpenChange,
74
+ onOpenChangeComplete,
75
+ modal: false,
76
+ children: /* @__PURE__ */ jsx(MenuPopup, {
77
+ anchor: opened.anchor,
78
+ children: /* @__PURE__ */ jsx(MenuEntries, {
79
+ entries: opened.items,
80
+ search: opened.search,
81
+ query
82
+ })
83
+ })
84
+ })
85
+ }, opened.search ? "search" : "plain")
86
+ };
87
+ }
88
+ //#endregion
89
+ export { useContextMenu };
@@ -1,11 +1,11 @@
1
- import { useNotify, useStrings } from "./Provider.js";
2
1
  import { FaCheck } from "../icons/fa/FaCheck.js";
3
2
  import { FaEye } from "../icons/fa/FaEye.js";
4
3
  import { FaEyeSlash } from "../icons/fa/FaEyeSlash.js";
5
4
  import { FiCopy } from "../icons/fi/FiCopy.js";
6
5
  import { HiOutlineClipboardCopy } from "../icons/hi/HiOutlineClipboardCopy.js";
7
- import { useState } from "react";
6
+ import { useNotify, useStrings } from "./Provider.js";
8
7
  import { jsx, jsxs } from "react/jsx-runtime";
8
+ import { useState } from "react";
9
9
  //#region src/components/CopyToClipboard.tsx
10
10
  function CopyToClipboard({ as = "div", text, className, children, showIcon = true }) {
11
11
  const notify = useNotify();
@@ -1,12 +1,13 @@
1
1
  import type { InputHTMLAttributes, ReactNode, TextareaHTMLAttributes } from 'react';
2
- import { type HeaderAddMenuItem } from './AddMenu.js';
2
+ import { type ComboboxItems } from './Combobox.js';
3
+ import type { MenuEntry } from './MenuEntries.js';
3
4
  interface CreateModalProps {
4
5
  open: boolean;
5
6
  onClose: () => void;
6
7
  typeIcon?: ReactNode;
7
8
  typeLabel: string;
8
9
  /** Makes the type chip a dropdown to switch between sibling form types. */
9
- typeMenu?: HeaderAddMenuItem[] | undefined;
10
+ typeMenu?: MenuEntry[] | undefined;
10
11
  namePlaceholder: string;
11
12
  hideName?: boolean;
12
13
  initialName?: string | undefined;
@@ -48,58 +49,38 @@ export declare function CreateModalTextarea({ label, tooltip, rows, ...props }:
48
49
  /** Help text surfaced via an info icon next to the label. */
49
50
  tooltip?: string | undefined;
50
51
  }): import("react").JSX.Element;
51
- /** Labeled input row with typeahead suggestions; free-form values allowed. */
52
- export declare function CreateModalAutocompleteField({ label, tooltip, value, onChange, options, placeholder, }: {
52
+ /** Labeled input with suggestions; any text is allowed. */
53
+ export declare function CreateModalAutocompleteField({ label, tooltip, value, onValueChange, items, placeholder, }: {
53
54
  label: string;
54
55
  /** Help text surfaced via an info icon next to the label. */
55
56
  tooltip?: string;
56
57
  value: string;
57
- onChange: (value: string) => void;
58
- options: string[];
58
+ onValueChange: (value: string) => void;
59
+ /** Suggestions, filtered by what's typed. */
60
+ items: string[];
59
61
  placeholder?: string;
60
62
  }): import("react").JSX.Element;
61
63
  /** Compact property pill with an on/off switch. */
62
- export declare function CreateModalSwitch({ label, checked, onChange, tooltip, }: {
64
+ export declare function CreateModalSwitch({ label, checked, onCheckedChange, tooltip, }: {
63
65
  label: string;
64
66
  checked: boolean;
65
- onChange: (checked: boolean) => void;
67
+ onCheckedChange: (checked: boolean) => void;
66
68
  /** Help text surfaced on hover (explains what the toggle does). */
67
69
  tooltip?: string;
68
70
  }): import("react").JSX.Element;
69
- export interface CreateModalSelectOption {
70
- label: string;
71
- value: string;
72
- /** Small leading icon (e.g. the resource's provider icon). */
73
- icon?: ReactNode;
74
- /** Category header to group this option under in the dropdown. */
75
- group?: string;
76
- /** Hidden behind the group's toggle row until the group is expanded. */
77
- collapsible?: boolean;
78
- /** Always listed regardless of the typed filter (e.g. a trailing "Create new…" action). */
79
- pinned?: boolean;
80
- /** Muted second line under the label (e.g. the allocation a group bills to). */
81
- description?: string;
82
- /** Listed but not selectable (e.g. a group whose allocation is exhausted). */
83
- disabled?: boolean;
84
- }
85
- /** Callers gating collapse on list size must use this same threshold so
86
- * collapsed rows always come with a filter to find them. */
87
- export declare const CREATE_MODAL_SELECT_FILTER_THRESHOLD = 8;
88
- /** Compact property pill with a dropdown of options. Options may carry leading
89
- * icons and group headers (e.g. Clusters vs Kubernetes). */
90
- export declare function CreateModalSelect({ label, icon, value, options, onChange, disabled, disabledHint, tooltip, collapsedLabel, }: {
71
+ /** Compact property pill that picks one value from a searchable list. Items may carry
72
+ * icons, descriptions and group headings (e.g. Clusters vs Kubernetes). */
73
+ export declare function CreateModalSelect({ label, icon, value, items, onValueChange, disabled, disabledReason, tooltip, }: {
91
74
  label: string;
92
75
  icon?: ReactNode;
93
76
  value?: string | undefined;
94
- options: CreateModalSelectOption[];
95
- onChange: (value: string) => void;
77
+ items: ComboboxItems<string>;
78
+ onValueChange: (value: string) => void;
96
79
  /** Gray the pill out (e.g. while a prerequisite pill is unset). */
97
80
  disabled?: boolean;
98
- /** Shown as the pill's tooltip while disabled (e.g. the prerequisite). */
99
- disabledHint?: string | undefined;
81
+ /** Why the pill is disabled, shown in place of the tooltip (e.g. the prerequisite). */
82
+ disabledReason?: string | undefined;
100
83
  /** Help text surfaced on hover (explains what the pill selects). */
101
84
  tooltip?: string;
102
- /** Label for the toggle row hiding a group's collapsible options. */
103
- collapsedLabel?: (count: number) => string;
104
85
  }): import("react").JSX.Element;
105
86
  export {};
@@ -1,40 +1,21 @@
1
- import { useStrings } from "./Provider.js";
2
1
  import { FaChevronDown } from "../icons/fa/FaChevronDown.js";
3
- import { HiQuestionMarkCircle } from "../icons/hi/HiQuestionMarkCircle.js";
4
2
  import { IoHeart } from "../icons/io5/IoHeart.js";
5
3
  import { IoHeartOutline } from "../icons/io5/IoHeartOutline.js";
6
4
  import { LuChevronRight } from "../icons/lu/LuChevronRight.js";
7
- import { TOOLTIP_ID } from "./Tooltip.js";
8
- import { AddMenu } from "./AddMenu.js";
9
- import { BareModal, modalPanelClasses } from "./BareModal.js";
5
+ import { Tooltip } from "./Tooltip.js";
6
+ import { useStrings } from "./Provider.js";
7
+ import { ActionMenu } from "./ActionMenu.js";
8
+ import { Autocomplete, Combobox } from "./Combobox.js";
10
9
  import { ghostButtonClasses } from "./ghostButton.js";
11
- import { filterOption, toggleSetMember } from "./dropdownUtils.js";
12
- import { Combobox, ComboboxInput, ComboboxOption, ComboboxOptions, Menu, MenuButton, MenuItem, MenuItems } from "@headlessui/react";
13
- import cx from "classnames";
14
- import { useCallback, useEffect, useRef, useState } from "react";
15
- import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
10
+ import { Modal } from "./Modal.js";
11
+ import { InfoTip } from "./InfoTip.js";
12
+ import { Switch } from "./Switch.js";
13
+ import { clsx } from "clsx";
14
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
15
+ import { useEffect, useId, useRef, useState } from "react";
16
+ import { Button } from "@base-ui/react/button";
17
+ import { Toggle } from "@base-ui/react/toggle";
16
18
  //#region src/components/CreateModal.tsx
17
- /** Small info icon that surfaces field help via the global tooltip. */
18
- function FieldHelp({ tooltip }) {
19
- if (!tooltip) return null;
20
- return /* @__PURE__ */ jsx("span", {
21
- "data-tooltip-id": TOOLTIP_ID,
22
- "data-tooltip-content": tooltip,
23
- className: "inline-flex cursor-help text-(--theme-muted-text-color)",
24
- children: /* @__PURE__ */ jsx(HiQuestionMarkCircle, { className: "w-3 h-3" })
25
- });
26
- }
27
- /** Anchors the tooltip on a non-focusable span so it opens on hover but not when focus returns to the button after its menu/toggle closes (which would leave it stuck). */
28
- function PillTooltip({ content, className, children }) {
29
- return /* @__PURE__ */ jsx("span", {
30
- ...content ? {
31
- "data-tooltip-id": TOOLTIP_ID,
32
- "data-tooltip-content": content
33
- } : {},
34
- className: cx("inline-flex items-center gap-1.5", className),
35
- children
36
- });
37
- }
38
19
  /** Submits on Enter in the name field or ⌘/Ctrl+Enter from anywhere in the panel. */
39
20
  function CreateModal({ open, onClose, typeIcon, typeLabel, typeMenu, namePlaceholder, hideName = false, initialName, showDescription = true, descriptionPlaceholder, submitLabel, submittingLabel, validateName, sanitizeName, nameHint, children, properties, advanced, submitDisabled = false, hideSubmit = false, nameOptional = false, onNameChange, dirty = false, showFavorite = false, onSubmit }) {
40
21
  const t = useStrings().modal;
@@ -59,13 +40,6 @@ function CreateModal({ open, onClose, typeIcon, typeLabel, typeMenu, namePlaceho
59
40
  setFavorite(false);
60
41
  setNameTouched(false);
61
42
  setInteracted(false);
62
- requestAnimationFrame(() => {
63
- const input = nameRef.current;
64
- const dialog = input?.closest("[role=\"dialog\"]");
65
- const active = document.activeElement;
66
- if (dialog?.contains(active) && active !== dialog) return;
67
- input?.focus();
68
- });
69
43
  }
70
44
  }, [open, initialName]);
71
45
  const nameError = name.trim() ? validateName?.(name.trim()) ?? null : null;
@@ -95,16 +69,17 @@ function CreateModal({ open, onClose, typeIcon, typeLabel, typeMenu, namePlaceho
95
69
  }
96
70
  if (close) onClose();
97
71
  };
98
- return /* @__PURE__ */ jsxs(BareModal, {
72
+ return /* @__PURE__ */ jsxs(Modal, {
99
73
  open,
100
74
  onClose,
101
- ariaLabel: `${submitText}: ${typeLabel}`,
102
- className: cx(modalPanelClasses, "w-[560px] max-w-full flex flex-col max-h-full"),
103
- preventClose: isDirty,
104
- onPanelKeyDown: (e) => {
75
+ "aria-label": `${submitText}: ${typeLabel}`,
76
+ className: "w-[560px] max-w-full flex flex-col max-h-full",
77
+ dismissible: !isDirty,
78
+ initialFocus: () => nameRef.current ?? true,
79
+ onKeyDown: (e) => {
105
80
  if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) handleSubmit();
106
81
  },
107
- onPanelMouseDown: (e) => {
82
+ onMouseDown: (e) => {
108
83
  if (e.target instanceof Element && !e.target.closest("input, textarea, button, a, select, label, code, pre, .select-all")) e.preventDefault();
109
84
  },
110
85
  children: [/* @__PURE__ */ jsxs("div", {
@@ -114,11 +89,10 @@ function CreateModal({ open, onClose, typeIcon, typeLabel, typeMenu, namePlaceho
114
89
  /* @__PURE__ */ jsxs("div", {
115
90
  className: "px-4 pt-4",
116
91
  children: [
117
- typeMenu && typeMenu.length > 0 ? /* @__PURE__ */ jsxs(AddMenu, {
92
+ typeMenu && typeMenu.length > 0 ? /* @__PURE__ */ jsxs(ActionMenu, {
118
93
  items: typeMenu,
119
94
  label: typeLabel,
120
- zClassName: "z-[9999]",
121
- buttonClassName: "inline-flex items-center gap-1.5 px-1.5 py-1 rounded-md bg-(--theme-muted-panel-bg) text-xs font-medium text-(--theme-muted-text-color) transition-colors hover:text-(--theme-app)",
95
+ triggerClassName: "inline-flex items-center gap-1.5 px-1.5 py-1 rounded-md bg-(--theme-muted-panel-bg) text-xs font-medium text-(--theme-muted-text-color) transition-colors hover:text-(--theme-app)",
122
96
  children: [
123
97
  typeIcon,
124
98
  typeLabel,
@@ -128,7 +102,7 @@ function CreateModal({ open, onClose, typeIcon, typeLabel, typeMenu, namePlaceho
128
102
  className: "inline-flex items-center gap-1.5 px-1.5 py-1 rounded-md bg-(--theme-muted-panel-bg) text-xs font-medium text-(--theme-muted-text-color)",
129
103
  children: [typeIcon, typeLabel]
130
104
  }),
131
- hideName ? /* @__PURE__ */ jsx("div", { className: "mt-3" }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
105
+ hideName ? /* @__PURE__ */ jsx("div", { className: "mt-3" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
132
106
  /* @__PURE__ */ jsx("input", {
133
107
  ref: nameRef,
134
108
  type: "text",
@@ -153,7 +127,7 @@ function CreateModal({ open, onClose, typeIcon, typeLabel, typeMenu, namePlaceho
153
127
  placeholder: namePlaceholder,
154
128
  "aria-label": namePlaceholder,
155
129
  onAnimationEnd: () => setNameFlash(false),
156
- className: cx("mt-2 -mb-1 -mx-1.5 w-[calc(100%+0.75rem)] rounded-md border-0 px-1.5 py-1 bg-transparent shadow-none text-lg font-medium text-(--theme-app) placeholder:text-(--theme-muted-text-color) focus:outline-none", nameFlash && "animate-field-flash", nameMissing ? "ring-1 ring-(--theme-error)" : "focus:ring-0")
130
+ className: clsx("mt-2 -mb-1 -mx-1.5 w-[calc(100%+0.75rem)] rounded-md border-0 px-1.5 py-1 bg-transparent shadow-none text-lg font-medium text-(--theme-app) placeholder:text-(--theme-muted-text-color) focus:outline-none", nameFlash && "animate-field-flash", nameMissing ? "ring-1 ring-(--theme-error)" : "focus:ring-0")
157
131
  }),
158
132
  nameError && /* @__PURE__ */ jsx("p", {
159
133
  className: "mt-1 text-xs text-(--theme-error)",
@@ -187,7 +161,7 @@ function CreateModal({ open, onClose, typeIcon, typeLabel, typeMenu, namePlaceho
187
161
  type: "button",
188
162
  onClick: () => setAdvancedOpen((o) => !o),
189
163
  className: "inline-flex items-center gap-1 text-xs font-medium text-(--theme-muted-text-color) hover:text-(--theme-app) transition-colors",
190
- children: [/* @__PURE__ */ jsx(LuChevronRight, { className: cx("w-3 h-3 transition-transform", advancedOpen && "rotate-90") }), t.advanced]
164
+ children: [/* @__PURE__ */ jsx(LuChevronRight, { className: clsx("w-3 h-3 transition-transform", advancedOpen && "rotate-90") }), t.advanced]
191
165
  }), advancedOpen && /* @__PURE__ */ jsx("div", {
192
166
  className: "mt-2",
193
167
  children: advanced
@@ -196,16 +170,13 @@ function CreateModal({ open, onClose, typeIcon, typeLabel, typeMenu, namePlaceho
196
170
  ]
197
171
  }), /* @__PURE__ */ jsxs("div", {
198
172
  className: "shrink-0 flex items-center justify-between gap-2 px-4 py-3",
199
- children: [showFavorite ? /* @__PURE__ */ jsx("button", {
200
- type: "button",
201
- role: "switch",
202
- "aria-checked": favorite,
203
- "aria-label": t.favorite,
204
- onClick: () => setFavorite((f) => !f),
205
- className: cx("inline-flex h-8 rounded-md text-sm font-medium transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-(--theme-focus-ring)", favorite ? "text-(--theme-favorite) bg-(--theme-favorite-muted)" : "text-(--theme-muted-text-color) hover:text-(--theme-app) hover:bg-(--theme-muted-panel-bg)"),
206
- children: /* @__PURE__ */ jsxs(PillTooltip, {
207
- content: favorite ? t.favoriteHintOn : t.favoriteHintOff,
208
- className: "px-2.5",
173
+ children: [showFavorite ? /* @__PURE__ */ jsx(Tooltip, {
174
+ content: favorite ? t.favoriteHintOn : t.favoriteHintOff,
175
+ children: /* @__PURE__ */ jsxs(Toggle, {
176
+ pressed: favorite,
177
+ onPressedChange: setFavorite,
178
+ "aria-label": t.favorite,
179
+ className: clsx("inline-flex h-8 items-center gap-1.5 px-2.5 rounded-md text-sm font-medium transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-(--theme-focus-ring)", favorite ? "text-(--theme-favorite) bg-(--theme-favorite-muted)" : "text-(--theme-muted-text-color) hover:text-(--theme-app) hover:bg-(--theme-muted-panel-bg)"),
209
180
  children: [favorite ? /* @__PURE__ */ jsx(IoHeart, { className: "w-4 h-4 scale-110 transition-transform" }) : /* @__PURE__ */ jsx(IoHeartOutline, { className: "w-4 h-4 transition-transform" }), favorite ? t.favorited : t.favorite]
210
181
  })
211
182
  }) : /* @__PURE__ */ jsx("span", {}), /* @__PURE__ */ jsxs("div", {
@@ -233,8 +204,8 @@ function CreateModalField({ label, tooltip, ...props }) {
233
204
  return /* @__PURE__ */ jsxs("label", {
234
205
  className: "block mb-3",
235
206
  children: [/* @__PURE__ */ jsxs("span", {
236
- className: cx(fieldLabelClasses, "flex items-center gap-1"),
237
- children: [label, /* @__PURE__ */ jsx(FieldHelp, { tooltip })]
207
+ className: clsx(fieldLabelClasses, "flex items-center gap-1"),
208
+ children: [label, /* @__PURE__ */ jsx(InfoTip, { text: tooltip ?? "" })]
238
209
  }), /* @__PURE__ */ jsx("input", {
239
210
  type: "text",
240
211
  autoComplete: "off",
@@ -249,235 +220,102 @@ function CreateModalTextarea({ label, tooltip, rows = 4, ...props }) {
249
220
  return /* @__PURE__ */ jsxs("label", {
250
221
  className: "block mb-3",
251
222
  children: [/* @__PURE__ */ jsxs("span", {
252
- className: cx(fieldLabelClasses, "flex items-center gap-1"),
253
- children: [label, /* @__PURE__ */ jsx(FieldHelp, { tooltip })]
223
+ className: clsx(fieldLabelClasses, "flex items-center gap-1"),
224
+ children: [label, /* @__PURE__ */ jsx(InfoTip, { text: tooltip ?? "" })]
254
225
  }), /* @__PURE__ */ jsx("textarea", {
255
226
  autoComplete: "off",
256
227
  "data-1p-ignore": true,
257
228
  rows,
258
229
  ...props,
259
- className: cx(fieldInputClasses, "h-auto py-2 font-mono text-xs leading-relaxed resize-y")
230
+ className: clsx(fieldInputClasses, "h-auto py-2 font-mono text-xs leading-relaxed resize-y")
260
231
  })]
261
232
  });
262
233
  }
263
- /** Labeled input row with typeahead suggestions; free-form values allowed. */
264
- function CreateModalAutocompleteField({ label, tooltip, value, onChange, options, placeholder }) {
265
- const query = value.trim().toLowerCase();
266
- const filtered = query ? options.filter((option) => option.toLowerCase().includes(query)) : options;
234
+ /** Labeled input with suggestions; any text is allowed. */
235
+ function CreateModalAutocompleteField({ label, tooltip, value, onValueChange, items, placeholder }) {
236
+ const id = useId();
267
237
  return /* @__PURE__ */ jsxs("div", {
268
238
  className: "mb-3",
269
- children: [/* @__PURE__ */ jsxs("span", {
270
- className: cx(fieldLabelClasses, "flex items-center gap-1"),
271
- children: [label, /* @__PURE__ */ jsx(FieldHelp, { tooltip })]
272
- }), /* @__PURE__ */ jsxs(Combobox, {
239
+ children: [/* @__PURE__ */ jsxs("div", {
240
+ className: clsx(fieldLabelClasses, "flex items-center gap-1"),
241
+ children: [/* @__PURE__ */ jsx("label", {
242
+ htmlFor: id,
243
+ children: label
244
+ }), tooltip && /* @__PURE__ */ jsx(InfoTip, { text: tooltip })]
245
+ }), /* @__PURE__ */ jsx(Autocomplete, {
246
+ id,
247
+ items,
273
248
  value,
274
- onChange: (next) => onChange(next ?? ""),
275
- immediate: true,
276
- children: [/* @__PURE__ */ jsx(ComboboxInput, {
277
- autoComplete: "off",
278
- "data-1p-ignore": true,
279
- "aria-label": label,
280
- placeholder,
281
- displayValue: (current) => current ?? "",
282
- onChange: (e) => onChange(e.target.value),
283
- className: fieldInputClasses
284
- }), filtered.length > 0 && /* @__PURE__ */ jsx(ComboboxOptions, {
285
- anchor: "bottom start",
286
- className: "z-[9999] mt-1 w-(--input-width) [--anchor-max-height:14rem] overflow-auto rounded-lg border border-(--theme-border) bg-(--theme-app-bg) py-1 shadow-lg focus:outline-none",
287
- children: filtered.map((option) => /* @__PURE__ */ jsx(ComboboxOption, {
288
- value: option,
289
- className: "px-3 py-1.5 text-[13px] text-(--theme-app) data-[focus]:bg-(--theme-muted-panel-bg) cursor-pointer",
290
- children: option
291
- }, option))
292
- })]
249
+ onValueChange,
250
+ placeholder
293
251
  })]
294
252
  });
295
253
  }
254
+ var pillClasses = "inline-flex h-7 items-center gap-1.5 rounded-md border border-(--theme-border) px-2 text-xs font-medium transition-colors";
296
255
  /** Compact property pill with an on/off switch. */
297
- function CreateModalSwitch({ label, checked, onChange, tooltip }) {
298
- return /* @__PURE__ */ jsx("button", {
299
- type: "button",
300
- role: "switch",
301
- "aria-checked": checked,
302
- onClick: () => onChange(!checked),
303
- className: cx("inline-flex h-7 rounded-md border border-(--theme-border) text-xs font-medium transition-colors hover:bg-(--theme-muted-panel-bg)", checked ? "text-(--theme-app)" : "text-(--theme-muted-text-color)"),
304
- children: /* @__PURE__ */ jsxs(PillTooltip, {
305
- content: tooltip,
306
- className: "px-2",
307
- children: [/* @__PURE__ */ jsx("span", {
308
- className: cx("relative inline-flex h-3.5 w-6 shrink-0 rounded-full transition-colors", checked ? "bg-(--theme-element)" : "bg-(--theme-border)"),
309
- children: /* @__PURE__ */ jsx("span", { className: cx("absolute top-0.5 left-0.5 h-2.5 w-2.5 rounded-full bg-white shadow-sm transition-transform", checked && "translate-x-2.5") })
256
+ function CreateModalSwitch({ label, checked, onCheckedChange, tooltip }) {
257
+ return /* @__PURE__ */ jsx(Tooltip, {
258
+ content: tooltip,
259
+ children: /* @__PURE__ */ jsxs("label", {
260
+ className: clsx(pillClasses, "cursor-pointer hover:bg-(--theme-muted-panel-bg)", checked ? "text-(--theme-app)" : "text-(--theme-muted-text-color)"),
261
+ children: [/* @__PURE__ */ jsx(Switch, {
262
+ size: "sm",
263
+ checked,
264
+ onCheckedChange
310
265
  }), label]
311
266
  })
312
267
  });
313
268
  }
314
- /** Callers gating collapse on list size must use this same threshold so
315
- * collapsed rows always come with a filter to find them. */
316
- var CREATE_MODAL_SELECT_FILTER_THRESHOLD = 8;
317
- function collapseToggleRow(group, count, collapse) {
318
- const expanded = collapse.expanded.has(group);
319
- return /* @__PURE__ */ jsxs("button", {
320
- type: "button",
321
- "aria-expanded": expanded,
322
- onMouseDown: (e) => e.preventDefault(),
323
- onClick: () => collapse.onToggle(group),
324
- className: "flex w-full items-center gap-1.5 px-3 py-1.5 text-[13px] text-(--theme-muted-text-color) hover:text-(--theme-app) cursor-pointer",
325
- children: [/* @__PURE__ */ jsx(expanded ? FaChevronDown : LuChevronRight, { className: "w-3 h-3" }), collapse.label(count)]
326
- }, `toggle-${group}`);
327
- }
328
- function collapsibleCountsByGroup(options) {
329
- const counts = /* @__PURE__ */ new Map();
330
- for (const o of options) if (o.collapsible && o.group) counts.set(o.group, (counts.get(o.group) ?? 0) + 1);
331
- return counts;
332
- }
333
- function pushToggleRowOnce(nodes, toggledGroups, group, count, collapse) {
334
- if (!toggledGroups.has(group)) {
335
- toggledGroups.add(group);
336
- nodes.push(collapseToggleRow(group, count, collapse));
337
- }
338
- }
339
- function buildOptionNodes(options, query, onChange, collapse) {
340
- const trimmed = query.trim();
341
- const q = trimmed.toLowerCase().replace(/-/g, "");
342
- const filtered = q ? options.filter((o) => o.pinned || filterOption({
343
- label: o.label,
344
- value: o.value,
345
- secondaryValue: o.group
346
- }, trimmed)) : options;
347
- const collapsibleCounts = !q && collapse ? collapsibleCountsByGroup(filtered) : /* @__PURE__ */ new Map();
348
- const nodes = [];
349
- let lastGroup;
350
- const toggledGroups = /* @__PURE__ */ new Set();
351
- for (const option of filtered) {
352
- const { group } = option;
353
- if (group && group !== lastGroup) {
354
- lastGroup = group;
355
- nodes.push(/* @__PURE__ */ jsx("div", {
356
- className: "px-3 pt-2 pb-1 text-[10px] font-semibold uppercase tracking-wide text-(--theme-muted-text-color)",
357
- children: group
358
- }, `group-${group}`));
359
- }
360
- if (!q && collapse && option.collapsible && group) {
361
- pushToggleRowOnce(nodes, toggledGroups, group, collapsibleCounts.get(group) ?? 0, collapse);
362
- if (!collapse.expanded.has(group)) continue;
363
- }
364
- nodes.push(/* @__PURE__ */ jsx(MenuItem, {
365
- disabled: option.disabled ?? false,
366
- children: /* @__PURE__ */ jsxs("button", {
367
- type: "button",
368
- disabled: option.disabled,
369
- "aria-disabled": option.disabled,
370
- "aria-label": option.description ? option.label : void 0,
371
- "aria-description": option.description,
372
- onClick: () => onChange(option.value),
373
- className: cx("flex w-full items-center gap-2 px-3 py-1.5 text-left text-[13px] data-[focus]:bg-(--theme-muted-panel-bg)", option.disabled ? "cursor-not-allowed text-(--theme-muted-text-color)" : "cursor-pointer text-(--theme-app)", option.pinned && "mt-1 border-t border-(--theme-border) pt-2 font-medium"),
374
- children: [option.icon, option.description ? /* @__PURE__ */ jsxs("span", {
375
- className: "flex min-w-0 flex-col",
376
- children: [/* @__PURE__ */ jsx("span", {
377
- className: "truncate",
378
- children: option.label
379
- }), /* @__PURE__ */ jsx("span", {
380
- className: "truncate text-[11px] font-normal text-(--theme-muted-text-color)",
381
- children: option.description
382
- })]
383
- }) : option.label]
384
- })
385
- }, option.value));
386
- }
387
- return nodes;
388
- }
389
- function CreateModalSelectPanel({ open, options, label, value, collapsedLabel, onChange }) {
269
+ /** Compact property pill that picks one value from a searchable list. Items may carry
270
+ * icons, descriptions and group headings (e.g. Clusters vs Kubernetes). */
271
+ function CreateModalSelect({ label, icon, value, items, onValueChange, disabled = false, disabledReason, tooltip }) {
390
272
  const t = useStrings().modal;
391
- const [query, setQuery] = useState("");
392
- const inputRef = useRef(null);
393
- const showFilter = options.length > 8;
394
- const selectedGroup = options.find((o) => o.value === value)?.group;
395
- const initialExpandedGroups = useCallback(() => new Set(selectedGroup ? [selectedGroup] : []), [selectedGroup]);
396
- const [expandedGroups, setExpandedGroups] = useState(initialExpandedGroups);
397
- useEffect(() => {
398
- if (!open) {
399
- setQuery("");
400
- setExpandedGroups(initialExpandedGroups());
401
- }
402
- }, [open, initialExpandedGroups]);
403
- useEffect(() => {
404
- if (!open || !showFilter) return;
405
- const raf = requestAnimationFrame(() => inputRef.current?.focus());
406
- return () => cancelAnimationFrame(raf);
407
- }, [open, showFilter]);
408
- const rendered = buildOptionNodes(options, query, onChange, collapsedLabel ? {
409
- expanded: expandedGroups,
410
- onToggle: (group) => setExpandedGroups((prev) => toggleSetMember(prev, group)),
411
- label: collapsedLabel
412
- } : void 0);
413
- return /* @__PURE__ */ jsxs(MenuItems, {
414
- anchor: "bottom start",
415
- className: "z-[9999] mt-1 flex min-w-44 max-w-[min(28rem,calc(100vw-2rem))] max-h-64 flex-col [--anchor-max-height:16rem] overflow-hidden rounded-lg border border-(--theme-border) bg-(--theme-app-bg) shadow-lg focus:outline-none",
416
- children: [showFilter && /* @__PURE__ */ jsx("div", {
417
- className: "shrink-0 bg-(--theme-app-bg) px-1.5 pt-1 pb-1.5 border-b border-(--theme-border)",
418
- children: /* @__PURE__ */ jsx("input", {
419
- ref: inputRef,
420
- type: "text",
421
- autoComplete: "off",
422
- "data-1p-ignore": true,
423
- "aria-label": label,
424
- placeholder: t.filter,
425
- value: query,
426
- onChange: (e) => setQuery(e.target.value),
427
- onKeyDown: (e) => {
428
- if (e.key !== "Escape") e.stopPropagation();
429
- },
430
- className: "w-full h-7 px-2 rounded-md border border-(--theme-border) bg-(--theme-input-bg) text-[13px] text-(--theme-app) placeholder:text-(--theme-muted-text-color) focus:outline-none focus:border-(--theme-element)"
431
- })
432
- }), /* @__PURE__ */ jsx("div", {
433
- className: "min-h-0 flex-1 overflow-y-auto py-1",
434
- children: rendered.length === 0 ? /* @__PURE__ */ jsx("div", {
435
- className: "px-3 py-1.5 text-[13px] text-(--theme-muted-text-color)",
436
- children: t.noOptions
437
- }) : rendered
438
- })]
439
- });
440
- }
441
- /** Compact property pill with a dropdown of options. Options may carry leading
442
- * icons and group headers (e.g. Clusters vs Kubernetes). */
443
- function CreateModalSelect({ label, icon, value, options, onChange, disabled = false, disabledHint, tooltip, collapsedLabel }) {
444
- const selected = options.find((o) => o.value === value);
273
+ const selected = leafItems(items).find((item) => item.value === value);
445
274
  const selectedLabel = selected?.label ?? (value || void 0);
446
- const pillContent = /* @__PURE__ */ jsxs(Fragment$1, { children: [
275
+ const pillContent = /* @__PURE__ */ jsxs(Fragment, { children: [
447
276
  selected?.icon ?? icon,
448
- selectedLabel ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
277
+ selectedLabel ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
449
278
  className: "font-normal text-(--theme-muted-text-color)",
450
279
  children: label
451
280
  }), selectedLabel] }) : label,
452
281
  /* @__PURE__ */ jsx(FaChevronDown, { className: "w-3 h-3 opacity-60" })
453
282
  ] });
454
- if (disabled) return /* @__PURE__ */ jsx("button", {
455
- type: "button",
456
- "aria-disabled": "true",
457
- "aria-label": label,
458
- className: cx("inline-flex h-7 rounded-md border border-(--theme-border) text-xs font-medium", "opacity-50 cursor-not-allowed text-(--theme-muted-text-color)"),
459
- children: /* @__PURE__ */ jsx(PillTooltip, {
460
- content: disabledHint ?? tooltip,
461
- className: "px-2",
283
+ if (disabled) return /* @__PURE__ */ jsx(Tooltip, {
284
+ content: disabledReason ?? tooltip,
285
+ children: /* @__PURE__ */ jsx(Button, {
286
+ disabled: true,
287
+ focusableWhenDisabled: true,
288
+ "aria-label": label,
289
+ className: clsx(pillClasses, "opacity-50 cursor-not-allowed text-(--theme-muted-text-color)"),
462
290
  children: pillContent
463
291
  })
464
292
  });
465
- return /* @__PURE__ */ jsx(Menu, { children: ({ open }) => /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(MenuButton, {
293
+ return /* @__PURE__ */ jsx(Combobox, {
294
+ items,
295
+ value: value ?? null,
296
+ onValueChange: (next) => {
297
+ if (next !== null) onValueChange(next);
298
+ },
299
+ itemToStringLabel: (next) => next,
466
300
  "aria-label": label,
467
- className: cx("inline-flex h-7 rounded-md border border-(--theme-border) text-xs font-medium transition-colors hover:bg-(--theme-muted-panel-bg)", selectedLabel ? "text-(--theme-app)" : "text-(--theme-muted-text-color)"),
468
- children: /* @__PURE__ */ jsx(PillTooltip, {
469
- content: tooltip,
470
- className: "px-2",
301
+ placeholder: t.filter,
302
+ triggerTooltip: tooltip,
303
+ trigger: /* @__PURE__ */ jsx("button", {
304
+ type: "button",
305
+ className: clsx(pillClasses, "cursor-pointer hover:bg-(--theme-muted-panel-bg)", selectedLabel ? "text-(--theme-app)" : "text-(--theme-muted-text-color)"),
471
306
  children: pillContent
472
307
  })
473
- }), /* @__PURE__ */ jsx(CreateModalSelectPanel, {
474
- open,
475
- options,
476
- label,
477
- value,
478
- collapsedLabel,
479
- onChange
480
- })] }) });
308
+ });
309
+ }
310
+ function leafItems(items) {
311
+ return items.flatMap((node) => {
312
+ if (typeof node === "string") return [{
313
+ value: node,
314
+ label: node
315
+ }];
316
+ if ("items" in node) return leafItems(node.items);
317
+ return [node];
318
+ });
481
319
  }
482
320
  //#endregion
483
- export { CREATE_MODAL_SELECT_FILTER_THRESHOLD, CreateModal, CreateModalAutocompleteField, CreateModalField, CreateModalSelect, CreateModalSwitch, CreateModalTextarea };
321
+ export { CreateModal, CreateModalAutocompleteField, CreateModalField, CreateModalSelect, CreateModalSwitch, CreateModalTextarea };