@parallelworks/ui 0.18.0 → 0.19.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 (349) hide show
  1. package/README.md +86 -6
  2. package/dist/ai/adapter/memoryStore.d.ts +8 -0
  3. package/dist/ai/adapter/memoryStore.js +152 -0
  4. package/dist/ai/adapter/mock/index.d.ts +2 -0
  5. package/dist/ai/adapter/mock/index.js +3 -0
  6. package/dist/ai/adapter/mock/mockAdapter.d.ts +4 -0
  7. package/dist/ai/adapter/mock/mockAdapter.js +81 -0
  8. package/dist/ai/adapter/openai/createOpenAIStream.d.ts +9 -0
  9. package/dist/ai/adapter/openai/createOpenAIStream.js +33 -0
  10. package/dist/ai/adapter/openai/errors.d.ts +3 -0
  11. package/dist/ai/adapter/openai/errors.js +37 -0
  12. package/dist/ai/adapter/openai/index.d.ts +6 -0
  13. package/dist/ai/adapter/openai/index.js +6 -0
  14. package/dist/ai/adapter/openai/openaiAdapter.d.ts +9 -0
  15. package/dist/ai/adapter/openai/openaiAdapter.js +54 -0
  16. package/dist/ai/adapter/openai/responses.d.ts +4 -0
  17. package/dist/ai/adapter/openai/responses.js +108 -0
  18. package/dist/ai/adapter/openai/sse.d.ts +24 -0
  19. package/dist/ai/adapter/openai/sse.js +112 -0
  20. package/dist/ai/adapter/openai/wire.d.ts +40 -0
  21. package/dist/ai/adapter/types.d.ts +131 -0
  22. package/dist/ai/adapter/types.js +11 -0
  23. package/dist/ai/agent/classifier.d.ts +2 -0
  24. package/dist/ai/agent/classifier.js +12 -0
  25. package/dist/ai/agent/diff.d.ts +35 -0
  26. package/dist/ai/agent/diff.js +223 -0
  27. package/dist/ai/agent/diffHighlight.d.ts +6 -0
  28. package/dist/ai/agent/diffHighlight.js +101 -0
  29. package/dist/ai/agent/index.d.ts +6 -0
  30. package/dist/ai/agent/permissions.d.ts +11 -0
  31. package/dist/ai/agent/permissions.js +29 -0
  32. package/dist/ai/agent/toolfmt.d.ts +18 -0
  33. package/dist/ai/agent/toolfmt.js +383 -0
  34. package/dist/ai/agent/transcript.d.ts +38 -0
  35. package/dist/ai/agent/transcript.js +127 -0
  36. package/dist/ai/ai.css +325 -0
  37. package/dist/ai/components/AllocationSelector.d.ts +4 -0
  38. package/dist/ai/components/AllocationSelector.js +56 -0
  39. package/dist/ai/components/AttachmentManager.d.ts +1 -0
  40. package/dist/ai/components/AttachmentManager.js +144 -0
  41. package/dist/ai/components/AttachmentUpload.d.ts +24 -0
  42. package/dist/ai/components/AttachmentUpload.js +179 -0
  43. package/dist/ai/components/BlockedGroupBanner.d.ts +1 -0
  44. package/dist/ai/components/BlockedGroupBanner.js +29 -0
  45. package/dist/ai/components/BranchNavigator.d.ts +8 -0
  46. package/dist/ai/components/BranchNavigator.js +57 -0
  47. package/dist/ai/components/ChatEmptyState.d.ts +3 -0
  48. package/dist/ai/components/ChatEmptyState.js +171 -0
  49. package/dist/ai/components/ChatInput.d.ts +45 -0
  50. package/dist/ai/components/ChatInput.js +265 -0
  51. package/dist/ai/components/ChatLayout.d.ts +5 -0
  52. package/dist/ai/components/ChatLayout.js +15 -0
  53. package/dist/ai/components/ChatMessage.d.ts +24 -0
  54. package/dist/ai/components/ChatMessage.js +338 -0
  55. package/dist/ai/components/ChatMessageList.d.ts +35 -0
  56. package/dist/ai/components/ChatMessageList.js +301 -0
  57. package/dist/ai/components/ChatSidebar.d.ts +5 -0
  58. package/dist/ai/components/ChatSidebar.js +345 -0
  59. package/dist/ai/components/ChatThread.d.ts +3 -0
  60. package/dist/ai/components/ChatThread.js +198 -0
  61. package/dist/ai/components/ComposerChrome.d.ts +18 -0
  62. package/dist/ai/components/ComposerChrome.js +50 -0
  63. package/dist/ai/components/ComposerFrame.d.ts +16 -0
  64. package/dist/ai/components/ComposerFrame.js +33 -0
  65. package/dist/ai/components/DragOverlay.d.ts +5 -0
  66. package/dist/ai/components/DragOverlay.js +20 -0
  67. package/dist/ai/components/ModelSelector.d.ts +6 -0
  68. package/dist/ai/components/ModelSelector.js +343 -0
  69. package/dist/ai/components/ShareDialog.d.ts +11 -0
  70. package/dist/ai/components/ShareDialog.js +260 -0
  71. package/dist/ai/components/SlashMenu.d.ts +28 -0
  72. package/dist/ai/components/SlashMenu.js +91 -0
  73. package/dist/ai/components/agent/ActivityLine.d.ts +21 -0
  74. package/dist/ai/components/agent/ActivityLine.js +151 -0
  75. package/dist/ai/components/agent/AgentMessageParts.d.ts +54 -0
  76. package/dist/ai/components/agent/AgentMessageParts.js +721 -0
  77. package/dist/ai/components/agent/PermissionPicker.d.ts +11 -0
  78. package/dist/ai/components/agent/PermissionPicker.js +38 -0
  79. package/dist/ai/components/attachmentKeys.d.ts +4 -0
  80. package/dist/ai/components/attachmentKeys.js +10 -0
  81. package/dist/ai/core/ChatProvider.d.ts +66 -0
  82. package/dist/ai/core/ChatProvider.js +848 -0
  83. package/dist/ai/core/KeyboardShortcutsProvider.d.ts +5 -0
  84. package/dist/ai/core/KeyboardShortcutsProvider.js +9 -0
  85. package/dist/ai/core/chatReducer.d.ts +114 -0
  86. package/dist/ai/core/chatReducer.js +223 -0
  87. package/dist/ai/core/config.d.ts +47 -0
  88. package/dist/ai/core/config.js +45 -0
  89. package/dist/ai/core/events.d.ts +1 -0
  90. package/dist/ai/core/events.js +4 -0
  91. package/dist/ai/core/exportConversation.d.ts +4 -0
  92. package/dist/ai/core/exportConversation.js +42 -0
  93. package/dist/ai/core/greeting.d.ts +3 -0
  94. package/dist/ai/core/greeting.js +38 -0
  95. package/dist/ai/core/parts.d.ts +11 -0
  96. package/dist/ai/core/parts.js +84 -0
  97. package/dist/ai/core/useDragDrop.d.ts +14 -0
  98. package/dist/ai/core/useDragDrop.js +36 -0
  99. package/dist/ai/core/useKeyboardShortcuts.d.ts +12 -0
  100. package/dist/ai/core/useKeyboardShortcuts.js +77 -0
  101. package/dist/ai/core/useSSEStream.d.ts +34 -0
  102. package/dist/ai/core/useSSEStream.js +98 -0
  103. package/dist/ai/index.d.ts +40 -0
  104. package/dist/ai/index.js +44 -0
  105. package/dist/ai/strings.d.ts +167 -0
  106. package/dist/ai/strings.js +175 -0
  107. package/dist/ai/types.d.ts +236 -0
  108. package/dist/ai/ui/Avatar.d.ts +8 -0
  109. package/dist/ai/ui/Avatar.js +35 -0
  110. package/dist/ai/ui/Dropdown.d.ts +31 -0
  111. package/dist/ai/ui/Dropdown.js +110 -0
  112. package/dist/ai/ui/Markdown.d.ts +8 -0
  113. package/dist/ai/ui/Markdown.js +79 -0
  114. package/dist/ai/ui/Notice.d.ts +21 -0
  115. package/dist/ai/ui/Notice.js +77 -0
  116. package/dist/ai/ui/buttons.d.ts +1 -0
  117. package/dist/ai/ui/codeBreaks.d.ts +16 -0
  118. package/dist/ai/ui/codeBreaks.js +36 -0
  119. package/dist/ai/ui/prose.d.ts +1 -0
  120. package/dist/ai/ui/prose.js +13 -0
  121. package/dist/ai/ui/sidebar.d.ts +81 -0
  122. package/dist/ai/ui/sidebar.js +148 -0
  123. package/dist/ai/utils.d.ts +4 -0
  124. package/dist/ai/utils.js +23 -0
  125. package/dist/assets/{editor.worker-CuBRIwi-.js → editor.worker-BmHUIVG4.js} +194 -163
  126. package/dist/assets/{json.worker-8ytS02vz.js → json.worker-D8bJ92nw.js} +384 -338
  127. package/dist/assets/{yaml.worker-DJC-VUcv.js → yaml.worker-C4gyGAko.js} +15657 -13823
  128. package/dist/components/AddMenu.js +46 -0
  129. package/dist/components/Avatar.d.ts +1 -1
  130. package/dist/components/Avatar.js +46 -0
  131. package/dist/{chunks/BareModal-BtRiQezg.js → components/BareModal.js} +1 -1
  132. package/dist/components/Breadcrumbs.d.ts +1 -1
  133. package/dist/components/Breadcrumbs.js +83 -0
  134. package/dist/components/Button.js +83 -0
  135. package/dist/components/Callout.js +23 -0
  136. package/dist/components/Card.js +31 -0
  137. package/dist/components/CollapsiblePanel.js +77 -0
  138. package/dist/components/ConfirmModal.js +74 -0
  139. package/dist/components/CopyToClipboard.js +81 -0
  140. package/dist/{chunks/CreateModal-BY6DLmur.js → components/CreateModal.js} +10 -189
  141. package/dist/components/DescriptionList.js +42 -0
  142. package/dist/components/Dropdown.d.ts +1 -1
  143. package/dist/components/Dropdown.js +349 -0
  144. package/dist/components/EmptyState.d.ts +1 -1
  145. package/dist/components/EmptyState.js +27 -0
  146. package/dist/components/Filter.js +32 -0
  147. package/dist/components/FilterPill.js +13 -0
  148. package/dist/components/IconButton.js +34 -0
  149. package/dist/components/Indicator.d.ts +2 -2
  150. package/dist/{chunks/Indicator-hBSIxOPK.js → components/Indicator.js} +1 -1
  151. package/dist/components/Input.d.ts +1 -1
  152. package/dist/components/Input.js +88 -0
  153. package/dist/components/LetterBadge.d.ts +1 -1
  154. package/dist/components/LetterBadge.js +13 -0
  155. package/dist/components/Listbox.js +123 -0
  156. package/dist/{chunks/Loader-BXoXTZGc.js → components/Loader.js} +1 -1
  157. package/dist/components/Markdown.js +15 -0
  158. package/dist/components/MarkdownImpl.d.ts +1 -1
  159. package/dist/components/MultiSelect.js +171 -0
  160. package/dist/components/Overview.js +39 -0
  161. package/dist/components/PageHeader.js +194 -0
  162. package/dist/components/Pagination.js +35 -0
  163. package/dist/components/Provider.d.ts +16 -65
  164. package/dist/{chunks/Provider-Ghua9PkN.js → components/Provider.js} +54 -20
  165. package/dist/{chunks/RequiredMark-DfLdYbYZ.js → components/RequiredMark.js} +1 -1
  166. package/dist/components/SectionHeader.js +11 -0
  167. package/dist/components/SettingModeOption.js +30 -0
  168. package/dist/components/SettingRow.d.ts +1 -1
  169. package/dist/components/SettingRow.js +35 -0
  170. package/dist/components/SettingsCard.js +31 -0
  171. package/dist/components/SettingsGroup.d.ts +1 -1
  172. package/dist/components/SettingsGroup.js +17 -0
  173. package/dist/components/Sort.js +42 -0
  174. package/dist/components/StatusBadge.js +63 -0
  175. package/dist/components/SwitchToggle.js +59 -0
  176. package/dist/{chunks/SwitchToggleSmall-FcsaYSTs.js → components/SwitchToggleSmall.js} +1 -1
  177. package/dist/{chunks/Table-4OHjdKdO.js → components/Table.js} +7 -5
  178. package/dist/components/Toggle.js +20 -0
  179. package/dist/{chunks/Tooltip-Crxo_eU_.js → components/Tooltip.js} +6 -4
  180. package/dist/{chunks/StatusBadge-C1Blx_cW.js → components/dropdownUtils.js} +2 -63
  181. package/dist/components/focus.d.ts +2 -0
  182. package/dist/components/focus.js +7 -0
  183. package/dist/components/ghostButton.js +7 -0
  184. package/dist/components/inputClasses.js +4 -0
  185. package/dist/components/keys.d.ts +12 -0
  186. package/dist/components/keys.js +27 -0
  187. package/dist/components/relativeTime.js +32 -0
  188. package/dist/{chunks/useCssIsDark-DHr05dG2.js → components/useCssIsDark.js} +1 -1
  189. package/dist/duration.d.ts +3 -0
  190. package/dist/duration.js +17 -0
  191. package/dist/editor/Editor.js +13 -0
  192. package/dist/editor/EditorField.js +53 -0
  193. package/dist/editor/FormCodePanel.js +173 -0
  194. package/dist/editor/Monaco.d.ts +1 -20
  195. package/dist/editor/Monaco.js +112 -0
  196. package/dist/editor/index.d.ts +2 -1
  197. package/dist/editor/index.js +7 -2
  198. package/dist/editor/themes.js +91 -0
  199. package/dist/editor/workers.js +29 -0
  200. package/dist/editor/yaml.d.ts +5 -0
  201. package/dist/editor/yaml.js +15 -0
  202. package/dist/engine.d.ts +98 -0
  203. package/dist/file-explorer/FileExplorer.d.ts +11 -2
  204. package/dist/file-explorer/FileExplorer.js +1402 -0
  205. package/dist/file-explorer/FileExplorerContext.d.ts +1 -1
  206. package/dist/file-explorer/FileExplorerContext.js +370 -0
  207. package/dist/file-explorer/FilePreview/CodePreviewEditor.d.ts +1 -1
  208. package/dist/{chunks/CodePreviewEditor-4lx3kE-w.js → file-explorer/FilePreview/CodePreviewEditor.js} +3 -2
  209. package/dist/file-explorer/FilePreview/Inline.js +45 -0
  210. package/dist/file-explorer/FilePreview/StreamedBadge.js +16 -0
  211. package/dist/file-explorer/FilePreview/Viewer.js +526 -0
  212. package/dist/file-explorer/FilePreview/index.js +266 -0
  213. package/dist/file-explorer/FilePreview/parse.js +63 -0
  214. package/dist/file-explorer/FilePreview/previewCache.js +98 -0
  215. package/dist/file-explorer/FilePreview/previewType.d.ts +1 -1
  216. package/dist/file-explorer/FilePreview/previewType.js +151 -0
  217. package/dist/file-explorer/FilePreview/readCappedBytes.js +196 -0
  218. package/dist/file-explorer/FilePreview/useObjectBlobUrl.js +95 -0
  219. package/dist/file-explorer/FilePreview/useObjectText.js +109 -0
  220. package/dist/file-explorer/FilePreview/usePresignedUrl.js +73 -0
  221. package/dist/file-explorer/FileRow.d.ts +1 -1
  222. package/dist/file-explorer/FileRow.js +165 -0
  223. package/dist/file-explorer/FileRowsWindow.js +92 -0
  224. package/dist/file-explorer/FileTable.js +172 -0
  225. package/dist/file-explorer/FileTableHeaders.js +49 -0
  226. package/dist/file-explorer/LoadMoreRow.js +46 -0
  227. package/dist/file-explorer/TreeView.js +376 -0
  228. package/dist/file-explorer/helpers.d.ts +4 -4
  229. package/dist/file-explorer/helpers.js +537 -0
  230. package/dist/file-explorer/index.d.ts +3 -3
  231. package/dist/file-explorer/index.js +7 -5705
  232. package/dist/file-explorer/lib/batch.js +39 -0
  233. package/dist/file-explorer/lib/errors.js +9 -0
  234. package/dist/file-explorer/lib/paging.js +124 -0
  235. package/dist/file-explorer/lib/types.d.ts +6 -1
  236. package/dist/file-explorer/lib/utils.d.ts +1 -3
  237. package/dist/file-explorer/lib/utils.js +322 -0
  238. package/dist/file-explorer/toasts.js +400 -0
  239. package/dist/file-explorer/treeRows.js +73 -0
  240. package/dist/file-explorer/useNearEnd.d.ts +1 -1
  241. package/dist/file-explorer/useNearEnd.js +27 -0
  242. package/dist/form/FieldWrapper.d.ts +2 -2
  243. package/dist/form/FieldWrapper.js +54 -0
  244. package/dist/form/Fields/BooleanField.js +51 -0
  245. package/dist/form/Fields/CheckboxGroupField.js +72 -0
  246. package/dist/form/Fields/ColorField.js +33 -0
  247. package/dist/form/Fields/DropdownField.js +49 -0
  248. package/dist/form/Fields/DurationField.js +130 -0
  249. package/dist/form/Fields/LabelField.js +19 -0
  250. package/dist/form/Fields/MultiDropdownField.js +40 -0
  251. package/dist/form/Fields/NumberField.js +51 -0
  252. package/dist/form/Fields/ObjectField.js +54 -0
  253. package/dist/form/Fields/RadioField.js +49 -0
  254. package/dist/form/Fields/RangeField.js +58 -0
  255. package/dist/form/Fields/ResourceOptionIcon.d.ts +1 -1
  256. package/dist/form/Fields/ResourceOptionIcon.js +21 -0
  257. package/dist/form/Fields/StepField.js +6 -0
  258. package/dist/form/Fields/StringField.js +37 -0
  259. package/dist/form/Fields/TextAreaField.js +31 -0
  260. package/dist/form/Fields/durationValidity.js +11 -0
  261. package/dist/form/Form.d.ts +4 -21
  262. package/dist/form/Form.js +643 -0
  263. package/dist/form/SpecialTypes/DropdownWrapper.js +30 -0
  264. package/dist/form/SpecialTypes/FormikCustomDropdown.js +122 -0
  265. package/dist/form/SpecialTypes/FormikCustomInput.js +111 -0
  266. package/dist/form/SpecialTypes/FormikMultiSelectDropdown.js +31 -0
  267. package/dist/form/SpecialTypes/FormikNumericInput.js +60 -0
  268. package/dist/form/Wizard/WizardContainer.js +138 -0
  269. package/dist/form/Wizard/WizardNavigation.js +92 -0
  270. package/dist/form/Wizard/WizardStepContent.js +35 -0
  271. package/dist/form/Wizard/WizardStepIndicator.js +108 -0
  272. package/dist/form/Wizard/useWizardState.js +118 -0
  273. package/dist/form/Wizard/utils.js +34 -0
  274. package/dist/form/fieldContext.js +26 -0
  275. package/dist/form/fieldRegistry.js +50 -0
  276. package/dist/form/index.d.ts +11 -12
  277. package/dist/form/index.js +18 -2445
  278. package/dist/form/lib.d.ts +6 -1
  279. package/dist/{chunks/FieldWrapper-Bef01EWO.js → form/lib.js} +5 -104
  280. package/dist/form/types/fieldComponentTypes.d.ts +3 -2
  281. package/dist/form/types/fieldTypes.d.ts +6 -20
  282. package/dist/form/useParsedOpts.d.ts +1 -1
  283. package/dist/form/useParsedOpts.js +62 -0
  284. package/dist/form/usePrevious.js +11 -0
  285. package/dist/form/utils/checkShowIfObject.js +14 -0
  286. package/dist/form/utils/{slurmDuration.d.ts → duration.d.ts} +2 -2
  287. package/dist/form/utils/duration.js +69 -0
  288. package/dist/form/utils/getParentValue.js +23 -0
  289. package/dist/form/utils/getPlaceholder.js +9 -0
  290. package/dist/form/utils/getValueUsingPath.js +23 -0
  291. package/dist/form/utils/secondaryField.js +22 -0
  292. package/dist/graph/AnnotationBanner.js +141 -0
  293. package/dist/graph/Collapse.js +41 -0
  294. package/dist/{workflow/index.js → graph/DependencyGraph.js} +76 -1163
  295. package/dist/graph/JobSummary.js +104 -0
  296. package/dist/{workflow → graph}/MatrixGroup.d.ts +2 -2
  297. package/dist/graph/MatrixGroup.js +107 -0
  298. package/dist/graph/Reveal.js +57 -0
  299. package/dist/graph/TreeView.js +711 -0
  300. package/dist/{workflow → graph}/index.d.ts +1 -1
  301. package/dist/graph/index.js +2 -0
  302. package/dist/{workflow → graph}/types.d.ts +3 -3
  303. package/dist/graph/types.js +6 -0
  304. package/dist/icons.d.ts +20 -21
  305. package/dist/index.d.ts +38 -40
  306. package/dist/index.js +47 -599
  307. package/dist/list/ActionToolbar.js +184 -0
  308. package/dist/list/HoverCard.js +73 -0
  309. package/dist/list/ListPage.js +145 -0
  310. package/dist/list/ListPager.js +59 -0
  311. package/dist/list/ListTable.d.ts +1 -1
  312. package/dist/list/ListTable.js +163 -0
  313. package/dist/list/ListViewControls.d.ts +2 -2
  314. package/dist/list/ListViewControls.js +372 -0
  315. package/dist/list/RowContextMenu.js +350 -0
  316. package/dist/list/UserHoverCard.js +117 -0
  317. package/dist/list/index.d.ts +9 -9
  318. package/dist/list/index.js +11 -2
  319. package/dist/list/listSearch.js +137 -0
  320. package/dist/list/listView.js +210 -0
  321. package/dist/list/textEntry.js +10 -0
  322. package/dist/logviewer/LogViewer.d.ts +1 -1
  323. package/dist/logviewer/LogViewer.js +750 -0
  324. package/dist/logviewer/index.d.ts +1 -0
  325. package/dist/logviewer/index.js +3 -742
  326. package/dist/logviewer/segments.d.ts +9 -0
  327. package/dist/logviewer/segments.js +80 -0
  328. package/dist/styles/fonts.css +4 -4
  329. package/dist/styles/theme.css +6 -4
  330. package/dist/styles.css +1 -1
  331. package/dist/theme/index.js +2 -2
  332. package/package.json +38 -14
  333. package/LICENSE +0 -202
  334. package/dist/chunks/Monaco-C1POOdVv.js +0 -1046
  335. package/dist/chunks/PageHeader-l49p1nM9.js +0 -690
  336. package/dist/chunks/SwitchToggle-CHPP8uZQ.js +0 -755
  337. package/dist/chunks/editor-9_uVtUyy.js +0 -347
  338. package/dist/chunks/list-qrojZZNv.js +0 -1411
  339. package/dist/editor/usesGitlab.d.ts +0 -97
  340. package/dist/form/Fields/BucketReadOnlyField.d.ts +0 -7
  341. package/dist/form/types/workflow-schema.d.ts +0 -1145
  342. package/dist/form/utils/pwResolver.d.ts +0 -18
  343. package/dist/{chunks/MarkdownImpl-Bq4QhrtA.js → components/MarkdownImpl.js} +1 -1
  344. /package/dist/{workflow → graph}/AnnotationBanner.d.ts +0 -0
  345. /package/dist/{workflow → graph}/Collapse.d.ts +0 -0
  346. /package/dist/{workflow → graph}/DependencyGraph.d.ts +0 -0
  347. /package/dist/{workflow → graph}/JobSummary.d.ts +0 -0
  348. /package/dist/{workflow → graph}/Reveal.d.ts +0 -0
  349. /package/dist/{workflow → graph}/TreeView.d.ts +0 -0
@@ -1,2445 +1,18 @@
1
- import { l as useSlots, u as useStrings } from "../chunks/Provider-Ghua9PkN.js";
2
- import { AngleRightIcon, EyeIcon, EyeOffIcon, SuccessCheckmark, TrashIcon } from "../icons.js";
3
- import { n as TOOLTIP_ID, r as TooltipInfo } from "../chunks/Tooltip-Crxo_eU_.js";
4
- import { c as isCategory, n as StatusDot } from "../chunks/StatusBadge-C1Blx_cW.js";
5
- import { a as UncontrolledCollapsiblePanel, c as Dropdown, n as SectionHeader, o as MultiSelectDropdown, r as ControlledCollapsiblePanel, s as CustomListbox, t as SwitchToggle } from "../chunks/SwitchToggle-CHPP8uZQ.js";
6
- import { t as Loader } from "../chunks/Loader-BXoXTZGc.js";
7
- import { n as Table } from "../chunks/Table-4OHjdKdO.js";
8
- import { a as initializeValues, c as findSecondaryOption, d as useFieldLabelledByProps, f as useFieldRequired, i as impureSetValueFromPath, l as FieldProvider, n as enforceOneMustBeTrue, o as resolvedFlag, r as flattenGroups, s as applySecondaryField, t as FieldWrapper, u as useFieldControlProps } from "../chunks/FieldWrapper-Bef01EWO.js";
9
- import cx from "classnames";
10
- import React, { Suspense, createContext, memo, useCallback, useContext, useEffect, useId, useMemo, useRef, useState } from "react";
11
- import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
12
- import deepEqual from "fast-deep-equal";
13
- import { Duration } from "luxon";
14
- import { callWhenParserInitialized, convertToDynamicForm, extractInputDeps, initializeParseStringsOfObj, parseStringsOfObj, parseStringsOfObjReady } from "@parallelworks/workflow-parser";
15
- import { Field, Form, Formik, getIn, useField, useFormikContext } from "formik";
16
- import { NumericFormat } from "react-number-format";
17
- //#region src/form/utils/getValueUsingPath.ts
18
- function getValueUsingPath(obj, path) {
19
- if (typeof path !== "string" || !path) return;
20
- const keys = path.split(".");
21
- let current = obj;
22
- for (const key of keys) {
23
- if (!current || typeof current !== "object") return;
24
- const container = current;
25
- if (key.includes("[")) {
26
- const [arrayKey = "", indexStr = ""] = key.split("[");
27
- const index = parseInt(indexStr.slice(0, -1), 10);
28
- const list = container[arrayKey];
29
- if (!Array.isArray(list) || isNaN(index)) return;
30
- current = list[index];
31
- } else {
32
- if (!(key in container)) return;
33
- current = container[key];
34
- }
35
- }
36
- return current;
37
- }
38
- //#endregion
39
- //#region src/form/utils/checkShowIfObject.ts
40
- function checkShowIfObject(showIfObject, formValues, index) {
41
- const valueList = [];
42
- for (let [key, value] of Object.entries(showIfObject)) {
43
- if (index !== void 0) key = key.replace("[index]", `[${index}]`);
44
- const dependsOnValue = getValueUsingPath(formValues, key);
45
- if (typeof value === "boolean" || typeof value === "string" || typeof value === "number") valueList.push(dependsOnValue !== null && dependsOnValue !== void 0 && dependsOnValue.toString() === value.toString());
46
- else if (Array.isArray(value)) valueList.push(value.includes(dependsOnValue));
47
- }
48
- return valueList;
49
- }
50
- //#endregion
51
- //#region src/form/utils/getParentValue.ts
52
- function getParentValue(index, targetField, values, dependentPath, field) {
53
- let parentValue;
54
- if (typeof targetField === "object" && targetField !== null && !Array.isArray(targetField)) parentValue = !checkShowIfObject(targetField, values, index).includes(false);
55
- else if (field.depends_on) {
56
- dependentPath = field.depends_on;
57
- if (index !== void 0) dependentPath = field.depends_on.replace("[index]", `[${index}]`);
58
- const dependsOnValue = getValueUsingPath(values, dependentPath);
59
- const dependsOnText = dependsOnValue === null || dependsOnValue === void 0 ? void 0 : String(dependsOnValue);
60
- if (Array.isArray(targetField)) parentValue = targetField.includes(dependsOnValue) || targetField.includes(dependsOnText);
61
- else if (typeof targetField === "string") parentValue = dependsOnText !== void 0 && targetField.includes(dependsOnText);
62
- else parentValue = targetField === dependsOnValue;
63
- } else {
64
- dependentPath = typeof targetField === "string" ? targetField : "";
65
- if (index !== void 0 && typeof targetField === "string") dependentPath = targetField.replace("[index]", `[${index}]`);
66
- parentValue = getValueUsingPath(values, dependentPath);
67
- }
68
- return parentValue;
69
- }
70
- //#endregion
71
- //#region src/form/useParsedOpts.ts
72
- var EMPTY_VARS = {};
73
- var EMPTY_KEYS = [];
74
- /**
75
- * useMemo won't work here — Formik values changes on every keystroke, so this hook extracts which inputs.* keys the schema references and re-parses only when those change. Defaults `userVars`/`secretKeys` to empty — callers in React scope should pass them from `useUserVars()` so the parser leaves `${{ var.SECRET }}` literal during template eval (otherwise the recursive substitution turns `"https://${{ var.PAT }}@host/"` into `"https://@host/"`).
76
- */
77
- function useParsedOpts(rawOptions, values, organizationVariables, userVars = EMPTY_VARS, secretKeys = EMPTY_KEYS, remoteVars = EMPTY_VARS) {
78
- const c = useRef({
79
- rawOptions: null,
80
- orgVars: void 0,
81
- userVars: null,
82
- secretKeys: null,
83
- remoteVars: null,
84
- converted: {},
85
- inputDeps: /* @__PURE__ */ new Set(),
86
- hasExpressions: false,
87
- relevantSnapshot: {},
88
- result: null
89
- }).current;
90
- if (rawOptions !== c.rawOptions) {
91
- c.rawOptions = rawOptions;
92
- c.converted = convertToDynamicForm(rawOptions);
93
- const { inputDeps, hasExpressions } = extractInputDeps(c.converted);
94
- c.inputDeps = inputDeps;
95
- c.hasExpressions = hasExpressions;
96
- c.result = null;
97
- }
98
- if (organizationVariables !== c.orgVars || userVars !== c.userVars || secretKeys !== c.secretKeys || remoteVars !== c.remoteVars) {
99
- c.orgVars = organizationVariables;
100
- c.userVars = userVars;
101
- c.secretKeys = secretKeys;
102
- c.remoteVars = remoteVars;
103
- c.result = null;
104
- }
105
- if (c.result !== null) {
106
- if (c.inputDeps.size === 0) return c.result;
107
- let inputsChanged = false;
108
- for (const key of c.inputDeps) if (!deepEqual(values[key], c.relevantSnapshot[key])) {
109
- inputsChanged = true;
110
- break;
111
- }
112
- if (!inputsChanged) return c.result;
113
- }
114
- const parsed = parseStringsOfObj({
115
- inputs: values,
116
- obj: c.converted,
117
- orgVars: organizationVariables,
118
- userVars,
119
- secretKeys,
120
- remoteVars
121
- });
122
- c.result = parsed;
123
- c.relevantSnapshot = {};
124
- for (const key of c.inputDeps) c.relevantSnapshot[key] = values[key];
125
- return parsed;
126
- }
127
- //#endregion
128
- //#region src/form/usePrevious.ts
129
- function usePrevious(value) {
130
- const ref = useRef(void 0);
131
- useEffect(() => {
132
- ref.current = value;
133
- });
134
- return ref.current;
135
- }
136
- //#endregion
137
- //#region src/form/Wizard/WizardNavigation.tsx
138
- function WizardNavigation({ currentStep, stepOrder, canGoToNext, canGoBack, isCurrentStepValid, nextLabel, prevLabel, submitLabel = "Submit", onNext, onPrevious, onSubmit }) {
139
- const [isLoading, setIsLoading] = useState(false);
140
- const [error, setError] = useState(null);
141
- const isLastStep = stepOrder.indexOf(currentStep) === stepOrder.length - 1;
142
- const handleNext = async () => {
143
- setIsLoading(true);
144
- setError(null);
145
- try {
146
- if (!await onNext()) setError("Please fix errors before proceeding");
147
- } catch (err) {
148
- setError(err instanceof Error ? err.message : "An error occurred");
149
- } finally {
150
- setIsLoading(false);
151
- }
152
- };
153
- const handleSubmit = async () => {
154
- setIsLoading(true);
155
- setError(null);
156
- try {
157
- await onSubmit();
158
- } catch (err) {
159
- setError(err instanceof Error ? err.message : "An error occurred");
160
- } finally {
161
- setIsLoading(false);
162
- }
163
- };
164
- return /* @__PURE__ */ jsxs("div", { children: [error && /* @__PURE__ */ jsx("div", {
165
- className: "mb-4 p-3 bg-red-50 border border-red-200 rounded text-sm text-red-700",
166
- children: error
167
- }), /* @__PURE__ */ jsxs("div", {
168
- className: "flex gap-3 justify-between pt-4",
169
- children: [/* @__PURE__ */ jsx("button", {
170
- type: "button",
171
- onClick: onPrevious,
172
- disabled: !canGoBack || isLoading,
173
- className: "px-6 py-3 rounded-lg font-medium transition-all duration-200 border flex items-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed",
174
- style: {
175
- color: "var(--theme-app)",
176
- borderColor: "var(--theme-border)",
177
- backgroundColor: "var(--theme-app-bg)"
178
- },
179
- onMouseEnter: (e) => {
180
- if (!(!canGoBack || isLoading)) e.currentTarget.style.backgroundColor = "var(--theme-hover)";
181
- },
182
- onMouseLeave: (e) => {
183
- e.currentTarget.style.backgroundColor = "var(--theme-app-bg)";
184
- },
185
- "aria-label": "Go to previous step",
186
- children: prevLabel || "← Previous"
187
- }), isLastStep ? /* @__PURE__ */ jsxs("button", {
188
- type: "button",
189
- onClick: handleSubmit,
190
- disabled: !isCurrentStepValid || isLoading,
191
- className: "px-8 py-3 rounded-lg font-medium transition-all duration-200 text-white flex items-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed shadow-md hover:shadow-lg",
192
- style: { backgroundColor: "var(--theme-element)" },
193
- onMouseEnter: (e) => {
194
- if (!(!isCurrentStepValid || isLoading)) e.currentTarget.style.filter = "brightness(0.9)";
195
- },
196
- onMouseLeave: (e) => {
197
- e.currentTarget.style.filter = "brightness(1)";
198
- },
199
- "aria-label": submitLabel,
200
- children: [isLoading && /* @__PURE__ */ jsx(Loader, {
201
- size: 16,
202
- full: false
203
- }), submitLabel]
204
- }) : /* @__PURE__ */ jsxs("button", {
205
- type: "button",
206
- onClick: handleNext,
207
- disabled: !canGoToNext || isLoading,
208
- className: "px-8 py-3 rounded-lg font-medium transition-all duration-200 text-white flex items-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed shadow-md hover:shadow-lg",
209
- style: { backgroundColor: "var(--theme-element)" },
210
- onMouseEnter: (e) => {
211
- if (!(!canGoToNext || isLoading)) e.currentTarget.style.filter = "brightness(0.9)";
212
- },
213
- onMouseLeave: (e) => {
214
- e.currentTarget.style.filter = "brightness(1)";
215
- },
216
- "aria-label": "Go to next step",
217
- children: [isLoading && /* @__PURE__ */ jsx(Loader, {
218
- size: 16,
219
- full: false
220
- }), nextLabel || "Next →"]
221
- })]
222
- })] });
223
- }
224
- //#endregion
225
- //#region src/form/Wizard/WizardStepContent.tsx
226
- function WizardStepContent({ currentStep, stepConfig, flatten = true, values, onValuesChange, setFormDirty, setFieldValue, setFieldTouched, ...fieldsProps }) {
227
- if (!stepConfig?.options) return null;
228
- return /* @__PURE__ */ jsxs("div", {
229
- className: "mb-4 w-full",
230
- children: [
231
- stepConfig.title && /* @__PURE__ */ jsx("h2", {
232
- className: "text-2xl font-bold mb-2",
233
- style: { color: "var(--theme-app)" },
234
- children: stepConfig.title
235
- }),
236
- stepConfig.description && /* @__PURE__ */ jsx("p", {
237
- className: "mb-4 leading-relaxed",
238
- style: { color: "var(--theme-muted-text-color)" },
239
- children: stepConfig.description
240
- }),
241
- /* @__PURE__ */ jsx(FieldsFromOptions, {
242
- options: stepConfig.options,
243
- values,
244
- setFormDirty,
245
- setFieldValue,
246
- setFieldTouched,
247
- ...flatten === false ? { parentInfo: {
248
- parentName: currentStep,
249
- fieldNamePrefix: `${currentStep}.`
250
- } } : {},
251
- ...fieldsProps
252
- }, currentStep)
253
- ]
254
- });
255
- }
256
- //#endregion
257
- //#region src/form/Wizard/WizardStepIndicator.tsx
258
- function WizardStepIndicator({ stepOrder, currentStep, steps, visitedSteps, invalidSteps, onStepClick, allowJump = false, hideStepNumbers = false }) {
259
- const currentIndex = useMemo(() => stepOrder.indexOf(currentStep), [stepOrder, currentStep]);
260
- const getStepStatus = (stepKey, index) => {
261
- if (stepKey === currentStep) return "active";
262
- if (index < currentIndex) return "completed";
263
- return "upcoming";
264
- };
265
- const handleStepClick = (stepKey) => {
266
- if (!allowJump || !visitedSteps.has(stepKey)) return;
267
- onStepClick?.(stepKey);
268
- };
269
- const isClickable = (stepKey) => {
270
- return allowJump && visitedSteps.has(stepKey) && stepKey !== currentStep;
271
- };
272
- return /* @__PURE__ */ jsx("div", {
273
- className: "flex items-start justify-between w-full mb-2 gap-4 py-2",
274
- children: stepOrder.map((stepKey, index) => {
275
- const stepConfig = steps[stepKey];
276
- const status = getStepStatus(stepKey, index);
277
- const isInvalid = invalidSteps.has(stepKey);
278
- const isClickable_ = isClickable(stepKey);
279
- const isDotActive = status === "active";
280
- const isDotCompleted = status === "completed";
281
- const isDotUpcoming = status === "upcoming";
282
- return /* @__PURE__ */ jsxs("div", {
283
- className: "flex flex-col items-center flex-1 relative",
284
- children: [
285
- /* @__PURE__ */ jsxs("button", {
286
- type: "button",
287
- onClick: () => handleStepClick(stepKey),
288
- disabled: !isClickable_,
289
- className: cx("w-12 h-12 rounded-full border-3 flex items-center justify-center font-bold text-base transition-all duration-300 flex-shrink-0 relative", {
290
- "cursor-pointer": isClickable_,
291
- "cursor-not-allowed": !isClickable_
292
- }, {
293
- "text-white": isDotActive,
294
- "bg-green-500 border-green-600 text-white shadow-md": isDotCompleted && !isInvalid,
295
- "bg-(--theme-border) theme-border theme-muted-text shadow-sm": isDotUpcoming && !isInvalid,
296
- "bg-(--theme-muted-panel-bg) theme-border theme-muted-text shadow-sm": !isDotActive && !isDotCompleted && !isDotUpcoming && !isInvalid,
297
- "bg-red-50 border-red-500 text-red-600 shadow-sm": isInvalid && status !== "active"
298
- }),
299
- style: isDotActive ? {
300
- backgroundColor: "var(--theme-element)",
301
- borderColor: "var(--theme-element)",
302
- color: "var(--theme-element-text)",
303
- boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)",
304
- zIndex: 10
305
- } : { zIndex: 10 },
306
- "aria-label": `Step ${index + 1}: ${stepConfig?.title}`,
307
- title: stepConfig?.title,
308
- children: [!hideStepNumbers && /* @__PURE__ */ jsx("span", {
309
- className: cx({ hidden: status === "completed" }),
310
- children: index + 1
311
- }), status === "completed" && /* @__PURE__ */ jsx("svg", {
312
- "aria-hidden": "true",
313
- className: "w-4 h-4",
314
- fill: "none",
315
- stroke: "currentColor",
316
- viewBox: "0 0 24 24",
317
- children: /* @__PURE__ */ jsx("path", {
318
- strokeLinecap: "round",
319
- strokeLinejoin: "round",
320
- strokeWidth: 3,
321
- d: "M5 13l4 4L19 7"
322
- })
323
- })]
324
- }),
325
- index < stepOrder.length - 1 && /* @__PURE__ */ jsx("div", {
326
- className: cx("absolute h-1 transition-all duration-300", {
327
- "bg-green-500": index < currentIndex,
328
- "bg-(--theme-border)": index >= currentIndex,
329
- "opacity-25": index >= currentIndex
330
- }),
331
- style: {
332
- top: "24px",
333
- left: "calc(50% + 24px)",
334
- right: "-50%",
335
- boxShadow: index < currentIndex ? "0 1px 2px rgba(34, 197, 94, 0.2)" : "none"
336
- },
337
- "aria-hidden": "true"
338
- }),
339
- /* @__PURE__ */ jsxs("div", {
340
- className: "mt-2 text-center",
341
- children: [/* @__PURE__ */ jsx("div", {
342
- className: cx("text-sm text-center transition-colors duration-300 max-w-30", {
343
- "font-bold text-green-600": isDotCompleted && !isInvalid,
344
- "font-bold text-red-600": isInvalid && status !== "active",
345
- "font-semibold theme-muted-text": !isDotActive && !isDotCompleted && !isInvalid,
346
- "font-bold text-white": isDotActive
347
- }),
348
- style: isDotActive ? { color: "var(--theme-element)" } : {},
349
- children: stepConfig?.title
350
- }), stepConfig?.description && /* @__PURE__ */ jsx("div", {
351
- className: "text-xs theme-muted-text mt-1 text-center max-w-30",
352
- children: stepConfig.description
353
- })]
354
- })
355
- ]
356
- }, stepKey);
357
- })
358
- });
359
- }
360
- //#endregion
361
- //#region src/form/Wizard/useWizardState.ts
362
- function useWizardState({ stepOrder, steps, validateStep, initialStep }) {
363
- const firstStep = initialStep || stepOrder[0] || "";
364
- const [currentStep, setCurrentStep] = useState(firstStep);
365
- const [visitedSteps, setVisitedSteps] = useState(new Set([firstStep]));
366
- const [invalidSteps, setInvalidSteps] = useState(/* @__PURE__ */ new Set());
367
- const currentStepIndex = stepOrder.indexOf(currentStep);
368
- const isLastStep = currentStepIndex === stepOrder.length - 1;
369
- const canGoBack = currentStepIndex > 0;
370
- /**
371
- * Mark step as visited
372
- */
373
- const markStepVisited = useCallback((stepKey) => {
374
- setVisitedSteps((prev) => new Set([...prev, stepKey]));
375
- }, []);
376
- /**
377
- * Mark step as invalid
378
- */
379
- const markStepInvalid = useCallback((stepKey, invalid) => {
380
- setInvalidSteps((prev) => {
381
- const next = new Set(prev);
382
- if (invalid) next.add(stepKey);
383
- else next.delete(stepKey);
384
- return next;
385
- });
386
- }, []);
387
- const goToNext = useCallback(async () => {
388
- if (isLastStep) return false;
389
- if (steps[currentStep]?.validateOnNext !== false) {
390
- if (!await validateStep(currentStep)) {
391
- markStepInvalid(currentStep, true);
392
- return false;
393
- }
394
- }
395
- markStepInvalid(currentStep, false);
396
- const nextStepKey = stepOrder[currentStepIndex + 1];
397
- setCurrentStep(nextStepKey ?? "");
398
- markStepVisited(nextStepKey ?? "");
399
- return true;
400
- }, [
401
- currentStep,
402
- currentStepIndex,
403
- isLastStep,
404
- steps,
405
- stepOrder,
406
- validateStep,
407
- markStepInvalid,
408
- markStepVisited
409
- ]);
410
- const goToPrevious = useCallback(() => {
411
- if (!canGoBack) return;
412
- const prevStepKey = stepOrder[currentStepIndex - 1];
413
- setCurrentStep(prevStepKey ?? "");
414
- }, [
415
- canGoBack,
416
- currentStepIndex,
417
- stepOrder
418
- ]);
419
- const jumpToStep = useCallback((stepKey) => {
420
- if (!stepOrder.includes(stepKey)) return false;
421
- if (!visitedSteps.has(stepKey) && stepKey !== currentStep) return false;
422
- setCurrentStep(stepKey);
423
- return true;
424
- }, [
425
- stepOrder,
426
- visitedSteps,
427
- currentStep
428
- ]);
429
- /**
430
- * Reset wizard to initial state
431
- */
432
- const reset = useCallback(() => {
433
- setCurrentStep(firstStep);
434
- setVisitedSteps(new Set([firstStep]));
435
- setInvalidSteps(/* @__PURE__ */ new Set());
436
- }, [firstStep]);
437
- /**
438
- * Get current wizard state
439
- */
440
- const getState = useCallback(() => {
441
- return {
442
- currentStep,
443
- visitedSteps,
444
- invalidSteps,
445
- stepOrder,
446
- totalSteps: stepOrder.length,
447
- isLastStep,
448
- canProceedToNext: !isLastStep,
449
- canGoBack
450
- };
451
- }, [
452
- currentStep,
453
- visitedSteps,
454
- invalidSteps,
455
- stepOrder,
456
- isLastStep,
457
- canGoBack
458
- ]);
459
- return {
460
- currentStep,
461
- visitedSteps,
462
- invalidSteps,
463
- isLastStep,
464
- canGoBack,
465
- currentStepIndex,
466
- totalSteps: stepOrder.length,
467
- goToNext,
468
- goToPrevious,
469
- jumpToStep,
470
- reset,
471
- markStepVisited,
472
- markStepInvalid,
473
- getState
474
- };
475
- }
476
- //#endregion
477
- //#region src/form/Wizard/WizardContainer.tsx
478
- function hasNestedError(stepErrors, fieldName) {
479
- return typeof stepErrors === "object" && stepErrors !== null && fieldName in stepErrors;
480
- }
481
- function WizardContainer({ wizardConfig, values, onChange, onSubmit, className = "", labelPosition = "left", missingFields = [], spaceCompact = false, workflowForm = false, setFieldValue, setFieldTouched }) {
482
- const { validateForm, setTouched, touched } = useFormikContext();
483
- const { config, steps, stepOrder } = wizardConfig;
484
- const { currentStep, visitedSteps, invalidSteps, isLastStep, canGoBack, goToNext, goToPrevious, jumpToStep } = useWizardState({
485
- stepOrder,
486
- steps,
487
- validateStep: useCallback(async (stepKey) => {
488
- const stepConfig = steps[stepKey];
489
- if (!stepConfig?.options) return true;
490
- const stepFieldNames = Object.keys(stepConfig.options);
491
- const shouldFlatten = config.flatten !== false;
492
- const errors = await validateForm();
493
- const hasStepErrors = shouldFlatten ? stepFieldNames.some((fieldName) => errors[fieldName]) : stepFieldNames.some((fieldName) => hasNestedError(errors[stepKey], fieldName));
494
- if (hasStepErrors) {
495
- const touchedFields = stepFieldNames.reduce((acc, fieldName) => {
496
- acc[fieldName] = true;
497
- return acc;
498
- }, {});
499
- if (shouldFlatten) setTouched({
500
- ...touched,
501
- ...touchedFields
502
- }, false);
503
- else setTouched({
504
- ...touched,
505
- [stepKey]: touchedFields
506
- }, false);
507
- }
508
- return !hasStepErrors;
509
- }, [
510
- steps,
511
- validateForm,
512
- setTouched,
513
- touched
514
- ])
515
- });
516
- const currentStepConfig = steps[currentStep];
517
- const config_ = config.navigation || {};
518
- const handleGoToNext = useCallback(async () => {
519
- const success = await goToNext();
520
- if (success) onChange?.(values);
521
- return success;
522
- }, [
523
- goToNext,
524
- values,
525
- onChange
526
- ]);
527
- const handleJumpToStep = useCallback((stepKey) => {
528
- if (!config_.allowJump || !visitedSteps.has(stepKey)) return false;
529
- return jumpToStep(stepKey);
530
- }, [
531
- jumpToStep,
532
- config_.allowJump,
533
- visitedSteps
534
- ]);
535
- const handleSubmit = useCallback(async () => {
536
- if (!isLastStep) return;
537
- const errors = await validateForm();
538
- if (Object.keys(errors).length > 0) {
539
- const touchedFields = Object.keys(values).reduce((acc, fieldName) => {
540
- acc[fieldName] = true;
541
- return acc;
542
- }, {});
543
- setTouched({
544
- ...touched,
545
- ...touchedFields
546
- }, false);
547
- throw new Error("Please fix errors before submitting");
548
- }
549
- await onSubmit?.(values);
550
- }, [
551
- isLastStep,
552
- validateForm,
553
- values,
554
- setTouched,
555
- touched,
556
- onSubmit
557
- ]);
558
- return /* @__PURE__ */ jsxs("div", {
559
- className,
560
- children: [
561
- config_.showSteps !== false && /* @__PURE__ */ jsx(WizardStepIndicator, {
562
- stepOrder,
563
- currentStep,
564
- steps,
565
- visitedSteps,
566
- invalidSteps,
567
- onStepClick: handleJumpToStep,
568
- allowJump: config_.allowJump,
569
- hideStepNumbers: config_.hideStepNumbers
570
- }),
571
- currentStepConfig && /* @__PURE__ */ jsx("div", {
572
- className: "mb-4",
573
- children: /* @__PURE__ */ jsx(WizardStepContent, {
574
- currentStep,
575
- stepConfig: currentStepConfig,
576
- flatten: config.flatten,
577
- values,
578
- onValuesChange: onChange,
579
- labelPosition,
580
- missingFields,
581
- spaceCompact,
582
- setFormDirty: () => onChange?.(values),
583
- workflowForm,
584
- setFieldValue,
585
- setFieldTouched
586
- })
587
- }),
588
- /* @__PURE__ */ jsx(WizardNavigation, {
589
- currentStep,
590
- stepOrder,
591
- canGoToNext: !isLastStep,
592
- canGoBack,
593
- isCurrentStepValid: !invalidSteps.has(currentStep),
594
- nextLabel: currentStepConfig?.nextLabel,
595
- prevLabel: currentStepConfig?.prevLabel,
596
- submitLabel: config.submitLabel || "Execute",
597
- onNext: handleGoToNext,
598
- onPrevious: goToPrevious,
599
- onSubmit: handleSubmit
600
- })
601
- ]
602
- });
603
- }
604
- //#endregion
605
- //#region src/form/Wizard/utils.ts
606
- function isWizardMeta(value) {
607
- return typeof value === "object" && value !== null && "wizard" in value;
608
- }
609
- function isStepField(field) {
610
- return typeof field === "object" && field !== null && "type" in field && field.type === "step";
611
- }
612
- function parseWizardConfig(options) {
613
- const meta = options["$meta"];
614
- const wizardConfig = isWizardMeta(meta) ? meta.wizard : void 0;
615
- if (!wizardConfig || wizardConfig.mode !== "wizard") return null;
616
- const steps = {};
617
- const stepOrder = [];
618
- const otherFields = {};
619
- Object.entries(options).forEach(([key, field]) => {
620
- if (key === "$meta") return;
621
- if (isStepField(field)) {
622
- steps[key] = field;
623
- stepOrder.push(key);
624
- } else otherFields[key] = field;
625
- });
626
- if (stepOrder.length === 0) {
627
- console.warn("Wizard mode enabled but no steps found");
628
- return null;
629
- }
630
- return {
631
- config: wizardConfig,
632
- steps,
633
- stepOrder,
634
- otherFields
635
- };
636
- }
637
- //#endregion
638
- //#region src/form/SpecialTypes/FormikCustomDropdown.tsx
639
- function toComparableOptions(options) {
640
- return options?.map((option) => {
641
- if (typeof option === "string") return option;
642
- if (isCategory(option)) return {
643
- category: option.category,
644
- options: option.options.map((o) => {
645
- const base = {
646
- label: o.label,
647
- value: o.value,
648
- disabled: o.disabled
649
- };
650
- return o.description !== void 0 ? {
651
- ...base,
652
- description: o.description
653
- } : base;
654
- })
655
- };
656
- const base = {
657
- label: option.label,
658
- value: option.value,
659
- disabled: option.disabled
660
- };
661
- return option.description !== void 0 ? {
662
- ...base,
663
- description: option.description
664
- } : base;
665
- });
666
- }
667
- function arePropsEqual(prev, next) {
668
- for (const key of Object.keys(next)) if (key === "options") {
669
- if (!deepEqual(toComparableOptions(prev.options), toComparableOptions(next.options))) return false;
670
- } else if (prev[key] !== next[key]) return false;
671
- return true;
672
- }
673
- var FormikCustomDropdown_default = memo(function FormikCustomDropdown({ name, ariaLabel, options, setFormDirty, onChange, parentValue, className, secondaryField, disabled = false, loading = false, invalid = false, autoselect, value, allowCustomValue = false, customValueLabel, placeholder, currentValue, setFieldValue, setFieldTouched }) {
674
- const required = useFieldRequired();
675
- const controlProps = useFieldControlProps();
676
- const prevParentRef = useRef(parentValue);
677
- const handleChange = (val) => {
678
- if (secondaryField && typeof options[0] !== "string") {
679
- const option = findSecondaryOption(options, val);
680
- if (option) applySecondaryField(secondaryField, option.secondaryValue, setFieldValue);
681
- }
682
- if (onChange) onChange(val);
683
- setFieldValue(name, val);
684
- setFieldTouched(name, true);
685
- setFormDirty(true);
686
- };
687
- useEffect(() => {
688
- prevParentRef.current = parentValue;
689
- });
690
- const prevOptions = useRef(null);
691
- useEffect(() => {
692
- if (deepEqual(prevOptions.current, toComparableOptions(options))) return;
693
- if (prevOptions.current === null && currentValue !== void 0 && currentValue !== null) {
694
- prevOptions.current = toComparableOptions(options);
695
- return;
696
- }
697
- prevOptions.current = toComparableOptions(options);
698
- if (autoselect === false || !options?.length) return;
699
- if (autoselect === void 0) {
700
- if (options.length && isCategory(options[0])) {
701
- let optionCount = 0;
702
- for (const category of options) {
703
- if (isCategory(category)) optionCount += category.options.length;
704
- if (optionCount > 1) return;
705
- }
706
- } else if (options.length !== 1) return;
707
- }
708
- if (!options?.find((option) => {
709
- if (typeof option === "string") return option === currentValue;
710
- else if (isCategory(option)) return option.options.some((subOption) => {
711
- return subOption.value === currentValue;
712
- });
713
- return option.value === currentValue;
714
- }) || autoselect) {
715
- let val;
716
- for (const option of options) if (typeof option === "string") {
717
- val = option;
718
- break;
719
- } else if (isCategory(option)) {
720
- const enabled = option.options.find((o) => !o.disabled);
721
- if (enabled) {
722
- val = enabled.value;
723
- break;
724
- }
725
- } else if (!option.disabled) {
726
- val = option.value;
727
- break;
728
- }
729
- if (val !== void 0) handleChange(val);
730
- }
731
- }, [
732
- parentValue,
733
- options,
734
- autoselect
735
- ]);
736
- return /* @__PURE__ */ jsx(Dropdown, {
737
- ...controlProps,
738
- textBoxClassName: cx(className, invalid && "invalid"),
739
- ariaLabel,
740
- required,
741
- onChange: (val) => handleChange(val),
742
- value: options?.length === 0 && !allowCustomValue ? value : currentValue,
743
- options,
744
- placeholder,
745
- disabled,
746
- loading,
747
- allowCustomValue,
748
- customValueLabel
749
- });
750
- }, arePropsEqual);
751
- //#endregion
752
- //#region src/form/SpecialTypes/FormikCustomInput.tsx
753
- var DEBOUNCE_MS$2 = 500;
754
- function FormikCustomInput({ name, type, step, placeholder, onChange, disabled, min, max, sanitize, invalid = false, lowercase = false, minLength, maxLength, defaultValue = "", value }) {
755
- const [field] = useField(name);
756
- const required = useFieldRequired();
757
- const controlProps = useFieldControlProps();
758
- const { setFieldValue, setFieldTouched } = useFormikContext();
759
- const [showPassword, setShowPassword] = useState(false);
760
- const isPassword = type === "password";
761
- const [localValue, setLocalValue] = useState(value ?? field.value);
762
- const isFocused = useRef(false);
763
- const debounceRef = useRef(null);
764
- useEffect(() => {
765
- if (!isFocused.current) setLocalValue(value ?? field.value);
766
- }, [field.value, value]);
767
- const sanitizeName = useCallback((val) => {
768
- if (sanitize === void 0) return lowercase ? val.toLowerCase() : val;
769
- try {
770
- const regexp = new RegExp(sanitize);
771
- if (lowercase) val = val.toLowerCase();
772
- return val.replace(regexp, "");
773
- } catch {
774
- console.error("Invalid regex for sanitization:", sanitize);
775
- }
776
- return val;
777
- }, [sanitize, lowercase]);
778
- const flushToFormik = useCallback((val) => {
779
- if (onChange) onChange(val);
780
- setFieldTouched(name, true);
781
- setFieldValue(name, val);
782
- }, [
783
- name,
784
- onChange,
785
- setFieldTouched,
786
- setFieldValue
787
- ]);
788
- const handleChange = (e) => {
789
- const text = sanitize ? sanitizeName(e.target.value) : e.target.value;
790
- let val = text;
791
- if (type === "number") {
792
- const parsed = parseFloat(text);
793
- val = isNaN(parsed) ? void 0 : parsed;
794
- }
795
- if (type === "password" && val === "") val = defaultValue;
796
- setLocalValue(val);
797
- clearTimeout(debounceRef.current ?? void 0);
798
- debounceRef.current = setTimeout(() => flushToFormik(val), DEBOUNCE_MS$2);
799
- };
800
- const handleFocus = () => {
801
- isFocused.current = true;
802
- };
803
- const handleBlur = () => {
804
- isFocused.current = false;
805
- clearTimeout(debounceRef.current ?? void 0);
806
- const current = localValue;
807
- if (current === void 0 || current === null) return;
808
- const text = current.toString();
809
- let val = text;
810
- if (type === "number") {
811
- const parsed = parseFloat(text);
812
- if (Number.isNaN(parsed)) val = void 0;
813
- else if (min !== void 0 && parsed < min) val = min;
814
- else if (max !== void 0 && parsed > max) val = max;
815
- else val = parsed;
816
- }
817
- setLocalValue(val);
818
- flushToFormik(val);
819
- };
820
- const displayValue = isPassword && localValue === defaultValue ? "" : localValue ?? "";
821
- const resolvedType = isPassword ? showPassword ? "text" : "password" : type === "string" ? "text" : type;
822
- const input = /* @__PURE__ */ jsx("input", {
823
- ...controlProps,
824
- "data-1p-ignore": true,
825
- className: cx("w-full", disabled && "disabled", invalid && "invalid", isPassword && "pr-8"),
826
- name,
827
- value: displayValue,
828
- type: resolvedType,
829
- "aria-required": required || void 0,
830
- disabled,
831
- placeholder,
832
- minLength,
833
- maxLength,
834
- onChange: handleChange,
835
- onFocus: handleFocus,
836
- onBlur: handleBlur,
837
- min,
838
- max,
839
- step
840
- });
841
- if (!isPassword) return input;
842
- return /* @__PURE__ */ jsxs("div", {
843
- className: "relative w-full",
844
- children: [input, /* @__PURE__ */ jsx("button", {
845
- type: "button",
846
- className: "absolute right-2 top-1/2 -translate-y-1/2 theme-muted-text",
847
- onClick: () => setShowPassword((prev) => !prev),
848
- disabled,
849
- tabIndex: -1,
850
- "aria-label": showPassword ? "Hide value" : "Show value",
851
- children: showPassword ? /* @__PURE__ */ jsx(EyeOffIcon, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(EyeIcon, { className: "h-4 w-4" })
852
- })]
853
- });
854
- }
855
- //#endregion
856
- //#region src/form/SpecialTypes/FormikNumericInput.tsx
857
- var DEBOUNCE_MS$1 = 500;
858
- function FormikNumericInput({ name, placeholder, onChange, disabled, min, max, invalid = false }) {
859
- const [field] = useField(name);
860
- const required = useFieldRequired();
861
- const controlProps = useFieldControlProps();
862
- const { setFieldValue, setFieldTouched } = useFormikContext();
863
- const [localValue, setLocalValue] = useState(field.value);
864
- const isFocused = useRef(false);
865
- const debounceRef = useRef(null);
866
- useEffect(() => {
867
- if (!isFocused.current) setLocalValue(field.value);
868
- }, [field.value]);
869
- const flush = (val) => {
870
- onChange?.(val);
871
- setFieldTouched(name, true);
872
- setFieldValue(name, val);
873
- };
874
- const handleBlur = () => {
875
- isFocused.current = false;
876
- clearTimeout(debounceRef.current ?? void 0);
877
- let val = localValue;
878
- if (val !== void 0) {
879
- if (min !== void 0 && val < min) val = min;
880
- if (max !== void 0 && val > max) val = max;
881
- }
882
- setLocalValue(val);
883
- flush(val);
884
- };
885
- return /* @__PURE__ */ jsx(NumericFormat, {
886
- ...controlProps,
887
- "data-1p-ignore": true,
888
- className: cx("w-full", disabled && "disabled", invalid && "invalid"),
889
- name,
890
- ...localValue === void 0 ? {} : { value: localValue },
891
- "aria-required": required || void 0,
892
- thousandSeparator: ",",
893
- decimalScale: 20,
894
- allowNegative: true,
895
- disabled,
896
- placeholder,
897
- onValueChange: (v) => {
898
- setLocalValue(v.floatValue);
899
- clearTimeout(debounceRef.current ?? void 0);
900
- debounceRef.current = setTimeout(() => flush(v.floatValue), DEBOUNCE_MS$1);
901
- },
902
- onFocus: () => {
903
- isFocused.current = true;
904
- },
905
- onBlur: handleBlur
906
- });
907
- }
908
- //#endregion
909
- //#region src/form/utils/getPlaceholder.ts
910
- /** `placeholder=""` (empty string) intentionally overrides the default; undefined does not. */
911
- function getPlaceholder(placeholder, defaultValue) {
912
- if (placeholder !== void 0) return placeholder;
913
- if (defaultValue && (typeof defaultValue === "string" || typeof defaultValue === "number")) return defaultValue.toString();
914
- return "";
915
- }
916
- //#endregion
917
- //#region src/form/Fields/StringField.tsx
918
- var StringField_default = memo(function StringField(props) {
919
- const { field, label, labelPosition, missing, disabled, onChange, tooltipComponent, spaceCompact, currentValue } = props;
920
- const value = field.prefillDefault ? currentValue : void 0;
921
- return /* @__PURE__ */ jsx(FieldWrapper, {
922
- optional: field.optional,
923
- label,
924
- tooltipComponent,
925
- description: field.description,
926
- labelPosition,
927
- spaceCompact,
928
- children: /* @__PURE__ */ jsx(FormikCustomInput, {
929
- invalid: missing,
930
- name: field.name,
931
- type: field.type,
932
- lowercase: field.lowercase,
933
- placeholder: field.type === "password" && field.placeholder === void 0 ? "•".repeat((field.default ?? "").toString().length > 0 ? 16 : 0) : getPlaceholder(field.placeholder, field.default),
934
- disabled,
935
- step: field.step,
936
- min: field.min,
937
- max: field.max,
938
- minLength: field.minLength,
939
- maxLength: field.maxLength,
940
- sanitize: field.sanitize,
941
- defaultValue: field.default,
942
- value,
943
- ...onChange && { onChange }
944
- }, field.name)
945
- });
946
- });
947
- //#endregion
948
- //#region src/form/Fields/NumberField.tsx
949
- function NumberField(props) {
950
- const { field, label, labelPosition, missing, disabled, onChange, tooltipComponent, spaceCompact } = props;
951
- const [fieldState] = useField(field.name);
952
- const { setFieldValue } = useFormikContext();
953
- useEffect(() => {
954
- if (field.prefillDefault && field.default !== void 0 && (fieldState.value === void 0 || fieldState.value === null || fieldState.value === "")) setFieldValue(field.name, field.default);
955
- }, [
956
- field.prefillDefault,
957
- field.default,
958
- field.name,
959
- fieldState.value,
960
- setFieldValue
961
- ]);
962
- return /* @__PURE__ */ jsx(FieldWrapper, {
963
- optional: field.optional,
964
- label,
965
- tooltipComponent,
966
- labelPosition,
967
- spaceCompact,
968
- children: field.thousandsSeparator ? /* @__PURE__ */ jsx(FormikNumericInput, {
969
- invalid: missing,
970
- name: field.name,
971
- placeholder: getPlaceholder(field.placeholder, field.default),
972
- disabled,
973
- min: field.min,
974
- max: field.max,
975
- ...onChange && { onChange }
976
- }, field.name) : /* @__PURE__ */ jsx(FormikCustomInput, {
977
- invalid: missing,
978
- name: field.name,
979
- type: "number",
980
- placeholder: getPlaceholder(field.placeholder, field.default),
981
- disabled,
982
- step: field.step,
983
- min: field.min,
984
- max: field.max,
985
- defaultValue: field.default,
986
- ...onChange && { onChange }
987
- }, field.name)
988
- });
989
- }
990
- //#endregion
991
- //#region src/form/Fields/BooleanField.tsx
992
- function BooleanField(props) {
993
- const { field, label, labelPosition, missing, disabled, onChange, tooltipComponent, spaceCompact, setFormDirty } = props;
994
- const [fieldState] = useField(field.name);
995
- const { setFieldValue, setFieldTouched } = useFormikContext();
996
- const handleChange = (val) => {
997
- if (disabled) return;
998
- if (onChange) onChange(val);
999
- setFieldTouched(field.name, true);
1000
- setFieldValue(field.name, val);
1001
- setFormDirty(true);
1002
- };
1003
- const value = fieldState.value;
1004
- const options = field.options ? {
1005
- onOption: String(field.options.onOption ?? true),
1006
- offOption: String(field.options.offOption ?? false)
1007
- } : void 0;
1008
- const displayLabel = (option) => option.charAt(0).toUpperCase() + option.slice(1).toLowerCase();
1009
- return /* @__PURE__ */ jsx(FieldWrapper, {
1010
- optional: true,
1011
- label,
1012
- tooltipComponent: /* @__PURE__ */ jsx("div", { children: tooltipComponent }),
1013
- description: field.description,
1014
- labelPosition,
1015
- spaceCompact,
1016
- children: ({ id, describedBy }) => options ? /* @__PURE__ */ jsx(SwitchToggle, {
1017
- id,
1018
- "aria-describedby": describedBy,
1019
- onChange: (on) => handleChange(on ? options.onOption : options.offOption),
1020
- value: value === true || value === options.onOption,
1021
- yesLabel: displayLabel(options.onOption),
1022
- noLabel: displayLabel(options.offOption),
1023
- disabled,
1024
- invalid: missing
1025
- }) : /* @__PURE__ */ jsx(SwitchToggle, {
1026
- id,
1027
- "aria-describedby": describedBy,
1028
- datatip: field.disabled_message,
1029
- onChange: handleChange,
1030
- value: value === true || value === "true",
1031
- disabled,
1032
- invalid: missing
1033
- })
1034
- });
1035
- }
1036
- //#endregion
1037
- //#region src/form/Fields/DropdownField.tsx
1038
- function DropdownField(props) {
1039
- const { field, label, labelPosition, missing, disabled, onChange, tooltipComponent, spaceCompact, setFieldValue, setFieldTouched, currentValue } = props;
1040
- let parentValue = field.parentValue;
1041
- let options = Array.isArray(field.options) && field.options || [];
1042
- if (field.depends_on) {
1043
- parentValue = field.depends_on;
1044
- if (!Array.isArray(field.options)) options = field.options[parentValue] ?? [];
1045
- }
1046
- const secondaryField = Array.isArray(field.secondaryField) ? field.secondaryField[0] : field.secondaryField;
1047
- return /* @__PURE__ */ jsx(FieldWrapper, {
1048
- optional: field.optional,
1049
- label,
1050
- tooltipComponent,
1051
- description: field.description,
1052
- labelPosition,
1053
- spaceCompact,
1054
- children: /* @__PURE__ */ jsx("div", {
1055
- className: "grow min-w-0",
1056
- children: /* @__PURE__ */ jsx(FormikCustomDropdown_default, {
1057
- invalid: missing,
1058
- className: "w-full",
1059
- name: field.name,
1060
- ariaLabel: label,
1061
- options,
1062
- setFormDirty: props.setFormDirty,
1063
- parentValue,
1064
- onChange,
1065
- secondaryField,
1066
- disabled,
1067
- resetOnChange: field.resetOnChange ? field.name : "",
1068
- loading: false,
1069
- autoselect: field.autoselect,
1070
- allowCustomValue: field.allowCustomValue,
1071
- customValueLabel: field.customValueLabel,
1072
- placeholder: getPlaceholder(field.placeholder, typeof field.default === "string" ? field.default : void 0),
1073
- currentValue,
1074
- setFieldValue,
1075
- setFieldTouched
1076
- }, field.name)
1077
- })
1078
- });
1079
- }
1080
- //#endregion
1081
- //#region src/form/Fields/TextAreaField.tsx
1082
- function TextAreaField(props) {
1083
- const { field, label, labelPosition, missing, disabled, onChange, tooltipComponent, spaceCompact } = props;
1084
- return /* @__PURE__ */ jsx(FieldWrapper, {
1085
- optional: field.optional,
1086
- label,
1087
- tooltipComponent,
1088
- description: field.description,
1089
- labelPosition,
1090
- spaceCompact,
1091
- children: ({ id, describedBy }) => /* @__PURE__ */ jsx(Field, {
1092
- className: cx("theme-input h-[82px] w-full rounded leading-4 overflow-y-scroll", disabled && "disabled", missing && "border-red-600 focus:border-red-600 focus:ring-red-500"),
1093
- as: "textarea",
1094
- id,
1095
- "aria-describedby": describedBy,
1096
- "aria-required": field.optional ? void 0 : true,
1097
- name: field.name,
1098
- type: "search",
1099
- placeholder: getPlaceholder(field.placeholder, field.default),
1100
- disabled,
1101
- ...onChange && { onChange }
1102
- }, field.name)
1103
- });
1104
- }
1105
- //#endregion
1106
- //#region src/form/Fields/RangeField.tsx
1107
- function RangeField(props) {
1108
- const { field, label, labelPosition, disabled, onChange, tooltipComponent, spaceCompact, setFormDirty } = props;
1109
- const [fieldState] = useField(field.name);
1110
- const { setFieldValue, setFieldTouched } = useFormikContext();
1111
- const handleChange = (e) => {
1112
- let val = e.target.valueAsNumber;
1113
- if (!isNaN(val)) {
1114
- val = Math.max(field.min, Math.min(field.max, val));
1115
- setFieldValue(field.name, val);
1116
- if (onChange) onChange(val);
1117
- setFieldTouched(field.name, true);
1118
- setFormDirty(true);
1119
- }
1120
- };
1121
- const seedValue = field.default ?? field.min;
1122
- if ((fieldState.value === void 0 || fieldState.value === null) && seedValue !== void 0) setFieldValue(field.name, seedValue);
1123
- const currentValue = fieldState.value !== void 0 ? fieldState.value : seedValue;
1124
- return /* @__PURE__ */ jsx(FieldWrapper, {
1125
- optional: field.optional,
1126
- label,
1127
- tooltipComponent,
1128
- labelPosition,
1129
- spaceCompact,
1130
- children: ({ id, labelId }) => /* @__PURE__ */ jsxs("div", {
1131
- className: "flex items-center",
1132
- children: [/* @__PURE__ */ jsx(Field, {
1133
- className: cx("theme-input h-7 w-1/3 mr-5 text-center rounded border", disabled && "disabled"),
1134
- id,
1135
- type: "number",
1136
- value: currentValue,
1137
- min: field.min,
1138
- max: field.max,
1139
- step: field.step || 1,
1140
- disabled,
1141
- onChange: handleChange
1142
- }), /* @__PURE__ */ jsx(Field, {
1143
- className: cx("rounded w-full", disabled && "disabled"),
1144
- name: field.name,
1145
- value: currentValue,
1146
- type: "range",
1147
- "aria-labelledby": labelId,
1148
- "aria-required": field.optional ? void 0 : true,
1149
- min: field.min,
1150
- max: field.max,
1151
- step: field.step || 1,
1152
- disabled,
1153
- onChange: handleChange
1154
- })]
1155
- })
1156
- });
1157
- }
1158
- //#endregion
1159
- //#region src/form/utils/slurmDuration.ts
1160
- var WITH_DAYS = /^(\d+)-(\d{1,2})(?::(\d{1,2})(?::(\d{1,2}))?)?$/;
1161
- var WITHOUT_DAYS = /^(\d+):(\d{1,2}):(\d{1,2})$/;
1162
- function parseSlurmDuration(text) {
1163
- const trimmed = text.trim();
1164
- let days = 0;
1165
- let hours;
1166
- let minutes;
1167
- let seconds;
1168
- const withDays = trimmed.match(WITH_DAYS);
1169
- if (withDays) {
1170
- days = Number(withDays[1]);
1171
- hours = Number(withDays[2]);
1172
- minutes = Number(withDays[3] ?? 0);
1173
- seconds = Number(withDays[4] ?? 0);
1174
- if (hours > 23) return null;
1175
- } else {
1176
- const withoutDays = trimmed.match(WITHOUT_DAYS);
1177
- if (!withoutDays) return null;
1178
- hours = Number(withoutDays[1]);
1179
- minutes = Number(withoutDays[2]);
1180
- seconds = Number(withoutDays[3]);
1181
- }
1182
- if (minutes > 59 || seconds > 59) return null;
1183
- return days * 86400 + hours * 3600 + minutes * 60 + seconds;
1184
- }
1185
- function formatSecondsAsSlurmDuration(totalSeconds) {
1186
- if (!Number.isFinite(totalSeconds) || totalSeconds < 0) return "";
1187
- const parts = Duration.fromObject({ seconds: Math.floor(totalSeconds) }).shiftTo("days", "hours", "minutes", "seconds");
1188
- const pad = (n) => String(n).padStart(2, "0");
1189
- const time = `${pad(parts.hours)}:${pad(parts.minutes)}:${pad(parts.seconds)}`;
1190
- return parts.days > 0 ? `${parts.days}-${time}` : time;
1191
- }
1192
- var FLEX_MAX_DURATION_RANGE = {
1193
- min: 600,
1194
- max: 604800
1195
- };
1196
- var FLEX_WAIT_TIME_RANGE = {
1197
- min: 30,
1198
- max: 604800
1199
- };
1200
- var STANDARD_MAX_DURATION_RANGE = {
1201
- min: 30,
1202
- max: 10368e3
1203
- };
1204
- var DURATION_PATH = /^partitions\[\d+\]\.(maxDuration|flexStartWaitTime|suspendTime)$/;
1205
- function collectPartitionDurationIssues(partitions, invalidDurationPaths) {
1206
- const issues = new Set(invalidDurationPaths.filter((path) => DURATION_PATH.test(path)));
1207
- (partitions ?? []).forEach((partition, i) => {
1208
- const p = partition;
1209
- const check = (fieldName, range, required) => {
1210
- const raw = p?.[fieldName];
1211
- if (raw === void 0 || raw === null || raw === "") {
1212
- if (required) issues.add(`partitions[${i}].${fieldName}`);
1213
- return;
1214
- }
1215
- const value = Number(raw);
1216
- if (!Number.isFinite(value) || value < range.min || value > range.max) issues.add(`partitions[${i}].${fieldName}`);
1217
- };
1218
- if (p?.provisioningMode === "flex") {
1219
- check("maxDuration", FLEX_MAX_DURATION_RANGE, true);
1220
- check("flexStartWaitTime", FLEX_WAIT_TIME_RANGE, true);
1221
- } else check("maxDuration", STANDARD_MAX_DURATION_RANGE, false);
1222
- });
1223
- return [...issues];
1224
- }
1225
- //#endregion
1226
- //#region src/form/Fields/durationValidity.ts
1227
- var invalidPaths = /* @__PURE__ */ new Set();
1228
- function setDurationValidity(path, valid) {
1229
- if (valid) invalidPaths.delete(path);
1230
- else invalidPaths.add(path);
1231
- }
1232
- function getInvalidDurationPaths() {
1233
- return [...invalidPaths];
1234
- }
1235
- //#endregion
1236
- //#region src/form/Fields/DurationField.tsx
1237
- var DEBOUNCE_MS = 500;
1238
- var isNumericString = (value) => typeof value === "string" && /^\d+$/.test(value.trim());
1239
- var toDisplay = (value) => {
1240
- if (typeof value === "number") return formatSecondsAsSlurmDuration(value);
1241
- if (isNumericString(value)) return formatSecondsAsSlurmDuration(Number(value));
1242
- return "";
1243
- };
1244
- var DurationField_default = memo(function DurationField({ field, label, labelPosition, missing, disabled, onChange, tooltipComponent, spaceCompact, setFormDirty, currentValue, setFieldValue, setFieldTouched }) {
1245
- const { form: t } = useStrings();
1246
- const name = field.name;
1247
- const min = field.min !== void 0 ? Number(field.min) : Number.NEGATIVE_INFINITY;
1248
- const max = field.max !== void 0 ? Number(field.max) : Number.POSITIVE_INFINITY;
1249
- const disableValue = field.disableValue;
1250
- const hasDisableCheckbox = disableValue !== void 0 && field.disableLabel !== void 0;
1251
- const isSentinel = hasDisableCheckbox && currentValue !== "" && Number(currentValue) === disableValue;
1252
- const [text, setText] = useState(() => toDisplay(currentValue));
1253
- const focusedRef = useRef(false);
1254
- const debounceRef = useRef(null);
1255
- useEffect(() => {
1256
- if (isSentinel) {
1257
- if (typeof currentValue !== "number") setFieldValue?.(name, disableValue);
1258
- } else if (isNumericString(currentValue)) setFieldValue?.(name, Number(currentValue));
1259
- else if ((currentValue === void 0 || currentValue === null || currentValue === "") && typeof field.default === "number") setFieldValue?.(name, field.default);
1260
- }, []);
1261
- useEffect(() => {
1262
- if (!focusedRef.current) setText(toDisplay(currentValue));
1263
- }, [currentValue]);
1264
- const trimmed = text.trim();
1265
- const parsed = trimmed === "" ? null : parseSlurmDuration(trimmed);
1266
- let error = null;
1267
- if (!isSentinel && trimmed !== "") {
1268
- if (parsed === null) error = t.invalidDuration;
1269
- else if (parsed < min || parsed > max) error = Number.isFinite(max) ? t.durationOutOfRange(formatSecondsAsSlurmDuration(Math.max(min, 0)), formatSecondsAsSlurmDuration(max)) : t.durationBelowMin(formatSecondsAsSlurmDuration(Math.max(min, 0)));
1270
- }
1271
- useEffect(() => {
1272
- setDurationValidity(name, error === null);
1273
- }, [name, error]);
1274
- useEffect(() => () => {
1275
- setDurationValidity(name, true);
1276
- clearTimeout(debounceRef.current ?? void 0);
1277
- }, [name]);
1278
- const flushToFormik = (val) => {
1279
- setFieldValue?.(name, val);
1280
- onChange?.(val);
1281
- setFieldTouched?.(name, true);
1282
- };
1283
- const handleDisableToggle = (checked) => {
1284
- clearTimeout(debounceRef.current ?? void 0);
1285
- setFormDirty(true);
1286
- if (checked) {
1287
- setText("");
1288
- flushToFormik(disableValue);
1289
- } else flushToFormik(typeof field.default === "number" ? field.default : "");
1290
- };
1291
- const handleChange = (e) => {
1292
- const raw = e.target.value;
1293
- setText(raw);
1294
- setFormDirty(true);
1295
- clearTimeout(debounceRef.current ?? void 0);
1296
- const rawTrimmed = raw.trim();
1297
- const value = rawTrimmed === "" ? "" : parseSlurmDuration(rawTrimmed);
1298
- if (value === "" || typeof value === "number" && value >= min && value <= max) debounceRef.current = setTimeout(() => flushToFormik(value), DEBOUNCE_MS);
1299
- };
1300
- const handleFocus = () => {
1301
- focusedRef.current = true;
1302
- };
1303
- const handleBlur = () => {
1304
- focusedRef.current = false;
1305
- clearTimeout(debounceRef.current ?? void 0);
1306
- if (trimmed === "") {
1307
- setText("");
1308
- flushToFormik("");
1309
- } else if (parsed !== null && parsed >= min && parsed <= max) {
1310
- setText(formatSecondsAsSlurmDuration(parsed));
1311
- flushToFormik(parsed);
1312
- }
1313
- };
1314
- return /* @__PURE__ */ jsx(FieldWrapper, {
1315
- optional: field.optional ?? false,
1316
- label,
1317
- tooltipComponent,
1318
- labelPosition,
1319
- spaceCompact: spaceCompact ?? false,
1320
- ...field.description !== void 0 && { description: field.description },
1321
- children: ({ id, describedBy }) => /* @__PURE__ */ jsxs(Fragment$1, { children: [
1322
- !isSentinel && /* @__PURE__ */ jsx("input", {
1323
- "data-1p-ignore": true,
1324
- className: cx("w-full", disabled && "disabled", (missing || error !== null) && "invalid"),
1325
- id,
1326
- "aria-describedby": describedBy,
1327
- name,
1328
- value: text,
1329
- type: "text",
1330
- "aria-required": field.optional ? void 0 : true,
1331
- disabled,
1332
- placeholder: field.placeholder ?? "DD-HH:MM:SS",
1333
- onChange: handleChange,
1334
- onFocus: handleFocus,
1335
- onBlur: handleBlur
1336
- }),
1337
- error && /* @__PURE__ */ jsx("div", {
1338
- className: "text-xs text-red-500 mt-1",
1339
- children: error
1340
- }),
1341
- hasDisableCheckbox && /* @__PURE__ */ jsxs("label", {
1342
- className: cx("flex items-center gap-2 mt-1", disabled ? "cursor-not-allowed opacity-60" : "cursor-pointer"),
1343
- children: [/* @__PURE__ */ jsx("input", {
1344
- type: "checkbox",
1345
- checked: isSentinel,
1346
- disabled,
1347
- onChange: (e) => handleDisableToggle(e.target.checked),
1348
- className: cx("h-4 w-4 accent-(--theme-link)", disabled ? "cursor-not-allowed" : "cursor-pointer")
1349
- }), /* @__PURE__ */ jsx("span", {
1350
- className: "theme-text",
1351
- children: field.disableLabel
1352
- })]
1353
- })
1354
- ] })
1355
- });
1356
- });
1357
- //#endregion
1358
- //#region src/form/Fields/RadioField.tsx
1359
- function RadioField(props) {
1360
- const { field, label, labelPosition, missing, disabled, onChange, tooltipComponent, spaceCompact, setFormDirty } = props;
1361
- const uid = useId();
1362
- const { setFieldValue, setFieldTouched } = useFormikContext();
1363
- const handleChange = (val) => {
1364
- if (disabled) return;
1365
- if (onChange) onChange(val);
1366
- setFieldTouched(field.name, true);
1367
- setFieldValue(field.name, val);
1368
- setFormDirty(true);
1369
- };
1370
- return /* @__PURE__ */ jsx(FieldWrapper, {
1371
- optional: field.optional,
1372
- label,
1373
- tooltipComponent,
1374
- labelPosition,
1375
- spaceCompact,
1376
- children: ({ labelId, describedBy }) => /* @__PURE__ */ jsx("div", {
1377
- role: "radiogroup",
1378
- "aria-labelledby": labelId,
1379
- "aria-describedby": describedBy,
1380
- "aria-required": field.optional ? void 0 : true,
1381
- className: cx("flex", field.optionLabelPosition === "top" ? "flex-col" : "flex-row"),
1382
- children: field.options?.map((option) => /* @__PURE__ */ jsxs("label", {
1383
- htmlFor: `${uid}-${option}`,
1384
- className: "capitalize",
1385
- children: [/* @__PURE__ */ jsx(Field, {
1386
- id: `${uid}-${option}`,
1387
- className: cx("theme-input mr-1", disabled && "disabled", missing && "invalid"),
1388
- name: field.name,
1389
- type: "radio",
1390
- value: option,
1391
- disabled,
1392
- onChange: (e) => {
1393
- handleChange(e.target.value);
1394
- }
1395
- }), option]
1396
- }, field.name + option))
1397
- })
1398
- });
1399
- }
1400
- //#endregion
1401
- //#region src/form/Fields/LabelField.tsx
1402
- function LabelField(props) {
1403
- const { field, label } = props;
1404
- return /* @__PURE__ */ jsx("div", {
1405
- className: "flex items-center w-full",
1406
- children: /* @__PURE__ */ jsx("h3", {
1407
- style: field.size ? {
1408
- fontSize: field.size.toString() + "px",
1409
- whiteSpace: "pre-wrap"
1410
- } : { whiteSpace: "pre-wrap" },
1411
- className: cx("relative", "text-md", field.bold !== false ? "font-bold" : ""),
1412
- children: field.text && field.text.toString().replace(/(\r)/g, "").replace(/(\\n)/g, "\n") || label
1413
- })
1414
- });
1415
- }
1416
- //#endregion
1417
- //#region src/form/Fields/ColorField.tsx
1418
- function ColorField(props) {
1419
- const { field, label, labelPosition, onChange, tooltipComponent, spaceCompact } = props;
1420
- const [fieldState] = useField(field.name);
1421
- const { setFieldValue, setFieldTouched } = useFormikContext();
1422
- const handleChange = (e) => {
1423
- const val = e.target.value;
1424
- if (onChange) onChange(val);
1425
- setFieldTouched(field.name, true);
1426
- setFieldValue(field.name, val);
1427
- };
1428
- return /* @__PURE__ */ jsx(FieldWrapper, {
1429
- optional: field.optional,
1430
- label,
1431
- tooltipComponent,
1432
- labelPosition,
1433
- spaceCompact,
1434
- children: ({ id }) => /* @__PURE__ */ jsx(Field, {
1435
- className: "theme-input h-8 w-18 rounded border border-neutral-400 placeholder:text-xs",
1436
- id,
1437
- name: field.name,
1438
- value: fieldState.value || "#fbbf24",
1439
- type: "color",
1440
- "aria-required": field.optional ? void 0 : true,
1441
- onChange: handleChange
1442
- })
1443
- });
1444
- }
1445
- //#endregion
1446
- //#region src/form/Fields/MultiDropdownField.tsx
1447
- function MultiDropdownField({ field, label, tooltipComponent, labelPosition, spaceCompact, disabled, missing, setFormDirty, onChange }) {
1448
- let parentValue = field.parentValue;
1449
- let options = field.options;
1450
- if (field.depends_on) {
1451
- parentValue = field.depends_on;
1452
- if (!Array.isArray(field.options)) options = field.options[parentValue] || [];
1453
- }
1454
- return /* @__PURE__ */ jsx(FieldWrapper, {
1455
- optional: field.optional,
1456
- label,
1457
- tooltipComponent,
1458
- labelPosition,
1459
- spaceCompact,
1460
- children: ({ id }) => disabled ? /* @__PURE__ */ jsx(Field, {
1461
- className: "theme-input h-7 w-full rounded border placeholder:text-xs",
1462
- id,
1463
- type: "search",
1464
- name: field.name,
1465
- disabled: true,
1466
- onChange
1467
- }, field.name) : /* @__PURE__ */ jsx(MultiSelectionDropdown, {
1468
- className: "themee-input h-7 w-full rounded border flex items-center px-3 py-2",
1469
- name: field.name,
1470
- label: field.label || label,
1471
- options: Array.isArray(options) ? options : [],
1472
- setFormDirty,
1473
- parentValue,
1474
- onChange,
1475
- secondaryField: typeof field.secondaryField === "string" ? field.secondaryField : void 0,
1476
- invalid: missing
1477
- }, field.name)
1478
- });
1479
- }
1480
- //#endregion
1481
- //#region src/form/Fields/CheckboxGroupField.tsx
1482
- function impliedFrom(values, implies = {}) {
1483
- const result = /* @__PURE__ */ new Set();
1484
- for (const value of values) for (const target of implies[value] ?? []) result.add(target);
1485
- return result;
1486
- }
1487
- function union(manual, forced) {
1488
- return [...manual, ...[...forced].filter((v) => !manual.includes(v))];
1489
- }
1490
- function CheckboxGroupField({ field, label, labelPosition, missing, disabled, onChange, tooltipComponent, spaceCompact, setFormDirty }) {
1491
- const [fieldState] = useField(field.name);
1492
- const { setFieldValue, setFieldTouched } = useFormikContext();
1493
- const implies = field.implies;
1494
- const [manual, setManual] = useState(() => {
1495
- const saved = fieldState.value || [];
1496
- const impliedBySaved = impliedFrom(saved, implies);
1497
- return saved.filter((v) => !impliedBySaved.has(v));
1498
- });
1499
- const forced = impliedFrom(manual, implies);
1500
- const handleToggle = (value) => {
1501
- if (disabled) return;
1502
- const nextManual = manual.includes(value) ? manual.filter((v) => v !== value) : [...manual, value];
1503
- const next = union(nextManual, impliedFrom(nextManual, implies));
1504
- setManual(nextManual);
1505
- setFieldTouched(field.name, true);
1506
- setFieldValue(field.name, next);
1507
- if (onChange) onChange(next);
1508
- setFormDirty(true);
1509
- };
1510
- return /* @__PURE__ */ jsx(FieldWrapper, {
1511
- optional: field.optional,
1512
- label,
1513
- tooltipComponent,
1514
- labelPosition,
1515
- spaceCompact,
1516
- children: ({ labelId, describedBy }) => /* @__PURE__ */ jsx("fieldset", {
1517
- "aria-labelledby": labelId,
1518
- "aria-describedby": describedBy,
1519
- className: "flex min-w-0 max-w-lg flex-col gap-1",
1520
- children: field.options?.map((option) => {
1521
- const locked = forced.has(option.value);
1522
- const optionDisabled = disabled || locked;
1523
- return /* @__PURE__ */ jsxs("label", {
1524
- className: cx("-mx-2 flex items-start gap-2.5 rounded-md px-2 py-1.5 transition-colors", optionDisabled ? "cursor-not-allowed opacity-60" : "cursor-pointer hover:bg-(--theme-hover) has-[:focus-visible]:bg-(--theme-hover)"),
1525
- children: [/* @__PURE__ */ jsx("input", {
1526
- type: "checkbox",
1527
- checked: manual.includes(option.value) || locked,
1528
- disabled: optionDisabled,
1529
- onChange: () => handleToggle(option.value),
1530
- className: cx("mt-px h-4 w-4 rounded accent-(--theme-link)", optionDisabled ? "cursor-not-allowed" : "cursor-pointer", missing && "invalid")
1531
- }), /* @__PURE__ */ jsxs("div", {
1532
- className: "flex min-w-0 flex-col",
1533
- children: [/* @__PURE__ */ jsx("span", {
1534
- className: "font-medium leading-tight theme-text",
1535
- children: option.label
1536
- }), option.description && /* @__PURE__ */ jsx("span", {
1537
- className: "mt-0.5 text-xs leading-snug theme-muted-text",
1538
- children: option.description
1539
- })]
1540
- })]
1541
- }, field.name + option.value);
1542
- })
1543
- })
1544
- });
1545
- }
1546
- //#endregion
1547
- //#region src/form/Fields/ObjectField.tsx
1548
- function ObjectField({ field, label, tooltipComponent, missingFields, setFormDirty, workflowForm, labelPosition, spaceCompact }) {
1549
- const [open, setOpen] = useState(field.collapsed !== void 0 ? !field.collapsed : true);
1550
- const { values, setFieldValue, setFieldTouched } = useFormikContext();
1551
- const meta = resolveMetaOverrides(field.options, labelPosition, spaceCompact ?? false);
1552
- const HeaderElement = field.noCollapse ? React.Fragment : ControlledCollapsiblePanel;
1553
- return /* @__PURE__ */ jsxs("div", {
1554
- className: "flex flex-col w-full",
1555
- children: [field.noCollapse && /* @__PURE__ */ jsx("div", {
1556
- className: "flex items-center w-full mb-[15px]",
1557
- children: /* @__PURE__ */ jsxs("h3", {
1558
- className: "flex items-center gap-1 text-md font-bold",
1559
- children: [
1560
- label,
1561
- " ",
1562
- tooltipComponent
1563
- ]
1564
- })
1565
- }), /* @__PURE__ */ jsx(HeaderElement, {
1566
- open,
1567
- setOpen,
1568
- title: /* @__PURE__ */ jsx(GroupHeader, {
1569
- open,
1570
- setOpen,
1571
- title: field.label || label
1572
- }),
1573
- children: /* @__PURE__ */ jsx("div", {
1574
- className: "w-full",
1575
- children: /* @__PURE__ */ jsx(FieldsFromOptions, {
1576
- options: field.options,
1577
- values,
1578
- setFormDirty,
1579
- setFieldValue,
1580
- setFieldTouched,
1581
- missingFields,
1582
- workflowForm,
1583
- labelPosition: meta.labelPosition,
1584
- spaceCompact: meta.spaceCompact,
1585
- parentInfo: {
1586
- parentName: field.name,
1587
- fieldNamePrefix: `${field.name}.`
1588
- }
1589
- })
1590
- })
1591
- })]
1592
- });
1593
- }
1594
- //#endregion
1595
- //#region src/form/Fields/StepField.tsx
1596
- function StepField(_props) {
1597
- return null;
1598
- }
1599
- //#endregion
1600
- //#region src/form/Fields/BucketReadOnlyField.tsx
1601
- function BucketReadOnlyField({ field, disabled, missing, labelPosition, setFormDirty }) {
1602
- const { form: t } = useStrings();
1603
- const [fieldState] = useField(field.name);
1604
- const { setFieldTouched, setFieldValue, values } = useFormikContext();
1605
- const accessFieldName = field.name?.replace(/readOnly$/, "bucketAccessWritable");
1606
- const forced = fieldState.value === true && !!accessFieldName && getIn(values, accessFieldName) === false;
1607
- return /* @__PURE__ */ jsx(FieldWrapper, {
1608
- optional: true,
1609
- label: t.readOnly,
1610
- description: t.readOnlyDescription,
1611
- labelPosition,
1612
- children: ({ id, describedBy }) => /* @__PURE__ */ jsxs("div", {
1613
- className: "flex flex-col gap-2",
1614
- children: [/* @__PURE__ */ jsx(SwitchToggle, {
1615
- id,
1616
- "aria-describedby": describedBy,
1617
- onChange: (value) => {
1618
- if (disabled) return;
1619
- setFieldTouched(field.name, true);
1620
- setFieldValue(field.name, value);
1621
- setFormDirty(true);
1622
- },
1623
- value: fieldState.value === true,
1624
- disabled,
1625
- invalid: missing,
1626
- yesLabel: t.readOnly,
1627
- noLabel: t.readWrite
1628
- }), /* @__PURE__ */ jsx("p", {
1629
- className: cx("theme-muted-text max-w-[65ch]", !forced && "hidden"),
1630
- children: t.readOnlyForced
1631
- })]
1632
- })
1633
- });
1634
- }
1635
- //#endregion
1636
- //#region src/form/fieldRegistry.tsx
1637
- var CORE_FIELD_COMPONENTS = {
1638
- string: StringField_default,
1639
- password: StringField_default,
1640
- number: NumberField,
1641
- boolean: BooleanField,
1642
- dropdown: DropdownField,
1643
- textarea: TextAreaField,
1644
- range: RangeField,
1645
- duration: DurationField_default,
1646
- radio: RadioField,
1647
- label: LabelField,
1648
- color: ColorField,
1649
- "multi-dropdown": MultiDropdownField,
1650
- "checkbox-group": CheckboxGroupField,
1651
- object: ObjectField,
1652
- step: StepField,
1653
- "bucket-read-only": BucketReadOnlyField
1654
- };
1655
- var FieldRegistryContext = createContext({});
1656
- /** Returns null for field types no registry knows. Per-form `fields` win over
1657
- * the provider's `slots.formFields`, which win over the core registry. */
1658
- function Registry(props) {
1659
- const injected = useContext(FieldRegistryContext);
1660
- const { formFields } = useSlots();
1661
- const Component = injected[props.field.type] ?? formFields?.[props.field.type] ?? CORE_FIELD_COMPONENTS[props.field.type];
1662
- if (!Component) return null;
1663
- return /* @__PURE__ */ jsx(Suspense, {
1664
- fallback: null,
1665
- children: /* @__PURE__ */ jsx(Component, { ...props })
1666
- });
1667
- }
1668
- //#endregion
1669
- //#region src/form/Form.tsx
1670
- function asSchemaField(value) {
1671
- return value && typeof value === "object" ? value : null;
1672
- }
1673
- function asRecord(value) {
1674
- return value && typeof value === "object" && !Array.isArray(value) ? value : {};
1675
- }
1676
- function flagValue(value, fallback) {
1677
- return value && (typeof value === "string" || typeof value === "boolean") ? value : fallback;
1678
- }
1679
- /** Applies `$meta` overrides for `labelPosition` / `spaceCompact`. */
1680
- function resolveMetaOverrides(options, labelPosition, spaceCompact) {
1681
- const meta = options?.["$meta"];
1682
- return {
1683
- labelPosition: meta?.labelPosition ?? labelPosition,
1684
- spaceCompact: meta?.spaceCompact ?? spaceCompact
1685
- };
1686
- }
1687
- /** Cheap signature of every list's length; gates the expensive collect walk. */
1688
- function listLengthsSignature(options, values = {}, prefix = "") {
1689
- if (!options || typeof options !== "object") return "";
1690
- const parts = [];
1691
- for (const [fieldName, rawField] of Object.entries(options)) {
1692
- const field = asSchemaField(rawField);
1693
- if (fieldName === "$meta" || !field) continue;
1694
- const fullName = prefix ? `${prefix}${fieldName}` : fieldName;
1695
- if (field.type === "list") {
1696
- const items = values?.[fieldName];
1697
- const len = Array.isArray(items) ? items.length : 0;
1698
- parts.push(`${fullName}:${len}`);
1699
- const template = field.template || field.options;
1700
- if (template && Array.isArray(items)) for (let i = 0; i < items.length; i++) parts.push(listLengthsSignature(template, asRecord(items[i]), `${fullName}[${i}].`));
1701
- } else if (field.type === "group") {
1702
- const groupItems = field.items || field.options;
1703
- if (groupItems) parts.push(listLengthsSignature(groupItems, values, prefix));
1704
- } else if (field.type === "object") {
1705
- const groupItems = field.items || field.options;
1706
- if (groupItems) {
1707
- const nestedValues = asRecord(values?.[fieldName]);
1708
- parts.push(listLengthsSignature(groupItems, nestedValues, `${fullName}.`));
1709
- }
1710
- }
1711
- }
1712
- return parts.join("|");
1713
- }
1714
- /** Collects every field with a `default` for the form-level sync to track. */
1715
- function collectFieldsWithDefaults(options, values = {}, prefix = "") {
1716
- const result = [];
1717
- if (!options || typeof options !== "object") return result;
1718
- for (const [fieldName, rawField] of Object.entries(options)) {
1719
- const field = asSchemaField(rawField);
1720
- if (!field) continue;
1721
- if (fieldName === "$meta") continue;
1722
- const fullName = prefix ? `${prefix}${fieldName}` : fieldName;
1723
- if (field.type === "group") {
1724
- const groupItems = field.items || field.options;
1725
- if (groupItems) result.push(...collectFieldsWithDefaults(groupItems, values, prefix));
1726
- continue;
1727
- }
1728
- if (field.type === "object") {
1729
- const groupItems = field.items || field.options;
1730
- if (groupItems) {
1731
- const nestedValues = asRecord(values?.[fieldName]);
1732
- result.push(...collectFieldsWithDefaults(groupItems, nestedValues, `${fullName}.`));
1733
- }
1734
- continue;
1735
- }
1736
- if (field.type === "list") {
1737
- const listTemplate = field.template || field.options;
1738
- const listValues = values?.[fieldName];
1739
- if (listTemplate && Array.isArray(listValues)) for (let i = 0; i < listValues.length; i++) {
1740
- const itemValues = asRecord(listValues[i]);
1741
- result.push(...collectFieldsWithDefaults(listTemplate, itemValues, `${fullName}[${i}].`));
1742
- }
1743
- continue;
1744
- }
1745
- if (resolvedFlag(field.hidden) === true && field.ignore !== void 0 && resolvedFlag(field.ignore) !== false) continue;
1746
- if (field.default !== void 0) result.push({
1747
- name: fullName,
1748
- defaultValue: field.default,
1749
- ...field.secondaryField ? { hasSecondary: true } : {}
1750
- });
1751
- }
1752
- return result;
1753
- }
1754
- function defaultReferencesField(defaultValue, fieldPath) {
1755
- if (typeof defaultValue !== "string" || !defaultValue.includes("${{")) return false;
1756
- const escaped = fieldPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1757
- return new RegExp(`\\binputs\\.${escaped}(?![\\w.[-])`).test(defaultValue);
1758
- }
1759
- function findSelfReferencingFieldNames(schema, values = {}) {
1760
- return collectFieldsWithDefaults(schema, values).filter(({ name, defaultValue }) => defaultReferencesField(defaultValue, name)).map(({ name }) => name);
1761
- }
1762
- /** Resolves schema defaults into form values as dependencies change. Store-agnostic so the legacy
1763
- * form (Formik) and the cluster wizard (its own store) share one implementation. */
1764
- function useDynamicDefaultsSync({ options, values, setValue, isTouched }) {
1765
- const listSig = useMemo(() => listLengthsSignature(options, values), [options, values]);
1766
- const fieldsWithDefaults = useMemo(() => collectFieldsWithDefaults(options, values).map((f) => {
1767
- if (typeof f.defaultValue !== "string" || !f.defaultValue.includes("${{")) return f;
1768
- const lastRowIndex = [...f.name.matchAll(/\[(\d+)\]/g)].at(-1)?.[1];
1769
- const index = lastRowIndex === void 0 ? -1 : Number(lastRowIndex);
1770
- const parsed = parseStringsOfObj({
1771
- inputs: values,
1772
- obj: { v: f.defaultValue },
1773
- orgVars: {},
1774
- index
1775
- });
1776
- const resolved = parsed && typeof parsed === "object" && "v" in parsed ? parsed.v : f.defaultValue;
1777
- return {
1778
- ...f,
1779
- defaultValue: resolved
1780
- };
1781
- }), [options, listSig]);
1782
- const prevDefaultsRef = useRef(/* @__PURE__ */ new Map());
1783
- const synthTouchedRef = useRef(/* @__PURE__ */ new Set());
1784
- const prevListLensRef = useRef(/* @__PURE__ */ new Map());
1785
- const fieldsWithDefaultsRef = useRef(null);
1786
- if (!deepEqual(fieldsWithDefaultsRef.current, fieldsWithDefaults)) {
1787
- fieldsWithDefaultsRef.current = fieldsWithDefaults;
1788
- const currentNames = new Set(fieldsWithDefaults.map((f) => f.name));
1789
- for (const name of prevDefaultsRef.current.keys()) if (!currentNames.has(name)) {
1790
- prevDefaultsRef.current.delete(name);
1791
- synthTouchedRef.current.delete(name);
1792
- }
1793
- const listLens = /* @__PURE__ */ new Map();
1794
- for (const part of listSig.split("|")) {
1795
- const sep = part.lastIndexOf(":");
1796
- if (sep > 0) listLens.set(part.slice(0, sep), Number(part.slice(sep + 1)));
1797
- }
1798
- for (const [list, prevLen] of prevListLensRef.current) {
1799
- const nowLen = listLens.get(list);
1800
- if (nowLen !== void 0 && nowLen < prevLen) {
1801
- for (const name of [...prevDefaultsRef.current.keys()]) if (name.startsWith(`${list}[`)) prevDefaultsRef.current.delete(name);
1802
- for (const name of [...synthTouchedRef.current]) if (name.startsWith(`${list}[`)) synthTouchedRef.current.delete(name);
1803
- }
1804
- }
1805
- prevListLensRef.current = listLens;
1806
- for (const { name, defaultValue, hasSecondary } of fieldsWithDefaults) {
1807
- if (hasSecondary) continue;
1808
- if (synthTouchedRef.current.has(name)) continue;
1809
- if (isTouched(name)) continue;
1810
- if (typeof defaultValue === "string" && defaultValue.includes("${{")) continue;
1811
- const wasSeen = prevDefaultsRef.current.has(name);
1812
- const serializedDefault = JSON.stringify(defaultValue ?? null);
1813
- const prevDefault = prevDefaultsRef.current.get(name);
1814
- prevDefaultsRef.current.set(name, serializedDefault);
1815
- if (!wasSeen) {
1816
- const rawValue = getValueUsingPath(values, name);
1817
- const currVal = JSON.stringify(rawValue);
1818
- const trimmedValue = typeof rawValue === "string" ? rawValue.trim() : "";
1819
- const unresolvedValue = trimmedValue.startsWith("${{") && trimmedValue.endsWith("}}");
1820
- if (!unresolvedValue && currVal !== void 0 && currVal !== serializedDefault) {
1821
- synthTouchedRef.current.add(name);
1822
- continue;
1823
- }
1824
- if (!unresolvedValue) continue;
1825
- }
1826
- if (prevDefault === serializedDefault) continue;
1827
- if (defaultValue === null || defaultValue === void 0) continue;
1828
- if (JSON.stringify(getValueUsingPath(values, name)) !== serializedDefault) setValue(name, defaultValue);
1829
- }
1830
- }
1831
- }
1832
- function DynamicDefaultsSync({ options }) {
1833
- const { setFieldValue, getFieldMeta, values } = useFormikContext();
1834
- useDynamicDefaultsSync({
1835
- options,
1836
- values,
1837
- setValue: setFieldValue,
1838
- isTouched: (name) => getFieldMeta(name).touched
1839
- });
1840
- return null;
1841
- }
1842
- function GroupHeader({ children, open, setOpen, title }) {
1843
- return /* @__PURE__ */ jsxs(SectionHeader, {
1844
- className: "flex items-center justify-between pb-1 mb-2 border-b",
1845
- children: [/* @__PURE__ */ jsxs("button", {
1846
- type: "button",
1847
- "aria-expanded": open,
1848
- className: cx("w-full flex items-center text-left transform ease-in transition cursor-pointer"),
1849
- onClick: () => setOpen((open) => !open),
1850
- children: [/* @__PURE__ */ jsx(AngleRightIcon, { className: cx(open && "transform rotate-90 ", "px-1 text-[1.25rem] transition ease-in ") }), /* @__PURE__ */ jsx("span", { children: title })]
1851
- }), children]
1852
- });
1853
- }
1854
- function deleteFieldByPath(obj, path) {
1855
- if (!path) return;
1856
- const parts = path.replaceAll("[", ".").replaceAll("]", "").split(".");
1857
- const fieldToRemove = parts.pop();
1858
- if (!fieldToRemove) {
1859
- console.error("Invalid path");
1860
- return;
1861
- }
1862
- const parent = parts.reduce((acc, curr) => {
1863
- if (acc && typeof acc === "object") return acc[curr];
1864
- return {};
1865
- }, obj);
1866
- if (parent && typeof parent === "object" && Object.prototype.hasOwnProperty.call(parent, fieldToRemove)) delete parent[fieldToRemove];
1867
- }
1868
- function getClosestParentIndex(parentPath) {
1869
- for (let i = parentPath.length - 1; i >= 0; i--) if (parentPath[i] === "]") {
1870
- let j = i - 1;
1871
- while (j >= 0 && parentPath[j] !== "[") j--;
1872
- return parseInt(parentPath.slice(j + 1, i));
1873
- }
1874
- }
1875
- function getInstanceTypesForForm(instances) {
1876
- return Object.entries(instances).map(([name, instance]) => {
1877
- const flexLabel = instance.isFlex ? ", Flex" : "";
1878
- const localDisk = instance.localDiskSizeGb ? ` , ${instance.localDiskSizeGb} GB Local Disk` : "";
1879
- return {
1880
- value: name,
1881
- label: `${name} (${instance.vcpus} vCPUs, ${instance.memoryGb} GB Memory, ${instance.arch}${localDisk}${flexLabel})`,
1882
- secondaryValue: [
1883
- instance.arch,
1884
- !!instance.acceleratedNetworking,
1885
- !!instance.acceleratedNetworking,
1886
- !!instance.localDiskSizeGb,
1887
- !!instance.isFlex
1888
- ]
1889
- };
1890
- });
1891
- }
1892
- function StatusBlock({ status }) {
1893
- return /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx("div", {
1894
- "data-tooltip-id": TOOLTIP_ID,
1895
- "data-tooltip-content": status ? "Storage has been attached." : "Attaching the storage to cluster...",
1896
- children: status ? /* @__PURE__ */ jsx(SuccessCheckmark, {}) : /* @__PURE__ */ jsx(Loader, {
1897
- size: 13,
1898
- full: false
1899
- })
1900
- }) });
1901
- }
1902
- function MultiSelectionDropdown({ name, label = "", invalid = false, options: initialOptions = [], setFormDirty, onChange, parentValue }) {
1903
- const [field] = useField(name);
1904
- const required = useFieldRequired();
1905
- const controlProps = useFieldControlProps();
1906
- const { setFieldValue } = useFormikContext();
1907
- const options = useMemo(() => {
1908
- return initialOptions.map((option) => {
1909
- const op = typeof option === "string" ? {
1910
- label: option,
1911
- value: option,
1912
- selected: false
1913
- } : { ...option };
1914
- if (field.value?.includes(op.value)) op.selected = true;
1915
- return op;
1916
- });
1917
- }, [initialOptions, field.value]);
1918
- const handleOptionSelected = (selectedValue) => {
1919
- let selected = [];
1920
- if (field.value) selected = [...field.value];
1921
- selected.push(selectedValue);
1922
- setFieldValue(name, selected);
1923
- if (onChange) onChange(options);
1924
- setFormDirty(true);
1925
- };
1926
- const t = useStrings().common;
1927
- const handleRemoveOption = (key) => {
1928
- setFieldValue(name, [...field.value.filter((item) => item !== key)]);
1929
- if (onChange) onChange(options);
1930
- setFormDirty(true);
1931
- };
1932
- const prevParent = usePrevious(parentValue);
1933
- useEffect(() => {
1934
- if (prevParent && parentValue && prevParent !== parentValue) setFieldValue(name, []);
1935
- }, [parentValue]);
1936
- return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(CustomListbox, {
1937
- ...controlProps,
1938
- options,
1939
- required,
1940
- type: label.toLowerCase(),
1941
- handleOptionSelected,
1942
- className: "mb-3",
1943
- invalid,
1944
- closeOnSelect: false
1945
- }), /* @__PURE__ */ jsx(Table, { children: field.value && field.value.length > 0 ? options.filter((option) => option.selected).map((option, index) => {
1946
- return /* @__PURE__ */ jsxs("tr", { children: [/* @__PURE__ */ jsx(Table.Item, {
1947
- className: "w-full",
1948
- children: option?.label
1949
- }), /* @__PURE__ */ jsx(Table.Item, { children: /* @__PURE__ */ jsx("button", {
1950
- type: "button",
1951
- "aria-label": t.remove,
1952
- className: "inline-flex bg-transparent p-0 cursor-pointer",
1953
- onClick: () => handleRemoveOption(option.value),
1954
- children: /* @__PURE__ */ jsx(TrashIcon, { className: "link mr-4" })
1955
- }) })] }, index);
1956
- }) : /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsxs(Table.Item, { children: [
1957
- "No ",
1958
- label !== "" ? label.toLowerCase() : "options",
1959
- " selected"
1960
- ] }) }) })] });
1961
- }
1962
- var InputField = React.memo(function InputField({ label, field, setFormDirty, onChange, disabled = false, labelPosition = "left", missingFields = [], computeOn, spaceCompact = false, workflowForm = false, setFieldValue, setFieldTouched, values, currentValue }) {
1963
- const prevField = useRef(field);
1964
- const fieldObj = useMemo(() => {
1965
- if (deepEqual(prevField.current, field)) return prevField.current;
1966
- prevField.current = field;
1967
- return { ...field };
1968
- }, [field]);
1969
- const tooltipComponent = useMemo(() => {
1970
- if (!fieldObj.tooltip) return null;
1971
- return /* @__PURE__ */ jsx(TooltipInfo, {
1972
- className: "pr-2",
1973
- place: "top",
1974
- text: typeof fieldObj.tooltip === "string" ? fieldObj.tooltip.replace(/\r/g, "") : fieldObj.tooltip.filter(Boolean).join("\n")
1975
- });
1976
- }, [fieldObj.tooltip]);
1977
- return /* @__PURE__ */ jsx(Registry, {
1978
- field: fieldObj,
1979
- label,
1980
- labelPosition,
1981
- missing: missingFields.includes(fieldObj.name ?? ""),
1982
- missingFields,
1983
- values,
1984
- setFormDirty,
1985
- onChange,
1986
- disabled,
1987
- computeOn,
1988
- spaceCompact,
1989
- tooltipComponent,
1990
- workflowForm,
1991
- setFieldTouched,
1992
- setFieldValue,
1993
- currentValue
1994
- });
1995
- }, (prev, next) => {
1996
- if (!deepEqual(prev.field, next.field)) return false;
1997
- for (const key of Object.keys(prev)) {
1998
- if (key === "field" || key === "values") continue;
1999
- if (prev[key] !== next[key]) return false;
2000
- }
2001
- return true;
2002
- });
2003
- function FormikStateBridge({ onChange }) {
2004
- const { values } = useFormikContext();
2005
- const prevEmitted = useRef(null);
2006
- useEffect(() => {
2007
- const { editorKeyPostfix: _, ...next } = values;
2008
- if (prevEmitted.current !== null && prevEmitted.current !== void 0 && deepEqual(prevEmitted.current, next)) return;
2009
- prevEmitted.current = next;
2010
- onChange(next);
2011
- }, [values, onChange]);
2012
- return null;
2013
- }
2014
- function DirtyStateBridge({ setFormDirty }) {
2015
- const { dirty } = useFormikContext();
2016
- useEffect(() => {
2017
- setFormDirty(dirty);
2018
- }, [dirty, setFormDirty]);
2019
- return null;
2020
- }
2021
- /** Three-tier cache: no expressions → raw pass-through; input-dep expressions → re-parse on dep change; org-var/path expressions → parse once. */
2022
- function useFieldParse(optionsField, values, arrayIndex) {
2023
- const c = useRef({
2024
- lastOptField: null,
2025
- convertedField: null,
2026
- inputDeps: /* @__PURE__ */ new Set(),
2027
- hasExpressions: false,
2028
- lastRelevant: {},
2029
- lastIndex: void 0,
2030
- result: null
2031
- }).current;
2032
- if (optionsField !== c.lastOptField) {
2033
- c.lastOptField = optionsField;
2034
- c.convertedField = convertToDynamicForm(asRecord(optionsField));
2035
- const { inputDeps, hasExpressions } = extractInputDeps(c.convertedField);
2036
- c.inputDeps = inputDeps;
2037
- c.hasExpressions = hasExpressions;
2038
- c.result = null;
2039
- }
2040
- if (!c.hasExpressions) return optionsField;
2041
- if (c.inputDeps.size === 0) {
2042
- if (c.result !== null) return c.result;
2043
- }
2044
- if (c.result !== null) {
2045
- let changed = arrayIndex !== c.lastIndex;
2046
- if (!changed) {
2047
- for (const key of c.inputDeps) if (!deepEqual(values[key], c.lastRelevant[key])) {
2048
- changed = true;
2049
- break;
2050
- }
2051
- }
2052
- if (!changed) return c.result;
2053
- }
2054
- c.result = parseStringsOfObj({
2055
- inputs: values,
2056
- obj: c.convertedField,
2057
- orgVars: {},
2058
- index: arrayIndex
2059
- });
2060
- c.lastIndex = arrayIndex;
2061
- c.lastRelevant = {};
2062
- for (const key of c.inputDeps) c.lastRelevant[key] = values[key];
2063
- return c.result;
2064
- }
2065
- var FormField = React.memo(function FormField({ id, optionsField, fieldName, values, setFormDirty, setFieldValue, setFieldTouched, parentInfo, labelPosition = "left", missingFields = [], spaceCompact = false, workflowForm = false }) {
2066
- const parsedField = useFieldParse(optionsField, values, parentInfo?.arrayIndex);
2067
- const field = asSchemaField(parentInfo ? parsedField : optionsField);
2068
- if (field === null) return null;
2069
- const fieldNamePrefix = parentInfo?.fieldNamePrefix || "";
2070
- const fieldObj = {
2071
- ...field,
2072
- label: field.label || fieldName.charAt(0).toUpperCase() + fieldName.slice(1),
2073
- name: fieldNamePrefix + fieldName,
2074
- optional: resolvedFlag(field.optional) ?? field.default !== void 0
2075
- };
2076
- if (!fieldObj["name"] || fieldName === "v3") return null;
2077
- if (field.hidden === true || !field.type) {
2078
- if (resolvedFlag(field.ignore) && !workflowForm) deleteFieldByPath(values, fieldObj["name"]);
2079
- return null;
2080
- }
2081
- if (!field.type && field.options) fieldObj["type"] = "dropdown";
2082
- let onChange;
2083
- if (field.one_must_be_true && parentInfo?.onChange) {
2084
- const parentOnChange = parentInfo.onChange;
2085
- const parentArrayIndex = parentInfo.arrayIndex ?? 0;
2086
- const fieldOptions = fieldObj["options"];
2087
- onChange = (val) => parentOnChange(fieldName, parentArrayIndex, flagValue(fieldOptions?.["offOption"], false), flagValue(fieldOptions?.["onOption"], true), val);
2088
- }
2089
- if (field.show_if !== void 0 || field.show_if_not !== void 0) {
2090
- let index;
2091
- if (parentInfo) if (!parentInfo?.arrayIndex) index = getClosestParentIndex(parentInfo.fieldNamePrefix);
2092
- else index = parentInfo.arrayIndex;
2093
- let shouldShowField = true;
2094
- if (field.show_if !== void 0) shouldShowField = !!getParentValue(index, field.show_if, values, "", field);
2095
- if (shouldShowField && field.show_if_not !== void 0) shouldShowField = !getParentValue(index, field.show_if_not, values, "", field);
2096
- if (!shouldShowField) {
2097
- if (!workflowForm) deleteFieldByPath(values, fieldObj["name"]);
2098
- return null;
2099
- }
2100
- }
2101
- if (field.depends_on) {
2102
- if (parentInfo?.arrayIndex !== void 0) fieldObj["depends_on"] = field.depends_on.replace("[index]", `[${parentInfo.arrayIndex}]`);
2103
- }
2104
- if (field.enable_if && field.disabled === false) {
2105
- if (typeof field.enable_if === "string") {
2106
- let path = field.enable_if;
2107
- if (parentInfo?.arrayIndex !== void 0) path = field.enable_if.replace("[index]", `[${parentInfo.arrayIndex}]`);
2108
- if (getValueUsingPath(values, path) === false) fieldObj["disabled"] = true;
2109
- }
2110
- }
2111
- if (field.type === "group") {
2112
- const hideHeader = field.noCollapse;
2113
- const HeaderElement = hideHeader ? React.Fragment : UncontrolledCollapsiblePanel;
2114
- const meta = resolveMetaOverrides(field.options, labelPosition, spaceCompact);
2115
- return /* @__PURE__ */ jsx("div", {
2116
- className: "flex flex-col w-full",
2117
- children: /* @__PURE__ */ jsx(HeaderElement, {
2118
- ...!hideHeader && {
2119
- title: (open, setOpen) => /* @__PURE__ */ jsx(GroupHeader, {
2120
- open,
2121
- setOpen,
2122
- title: field.label
2123
- }),
2124
- initialState: field.collapsed !== void 0 ? !field.collapsed : true
2125
- },
2126
- children: /* @__PURE__ */ jsx("div", {
2127
- className: "w-full",
2128
- children: /* @__PURE__ */ jsx(FieldsFromOptions, {
2129
- options: field.options ?? {},
2130
- setFormDirty,
2131
- setFieldValue,
2132
- setFieldTouched,
2133
- values,
2134
- parentInfo,
2135
- workflowForm,
2136
- labelPosition: meta.labelPosition,
2137
- spaceCompact: meta.spaceCompact,
2138
- missingFields
2139
- })
2140
- })
2141
- })
2142
- }, fieldObj["name"]);
2143
- }
2144
- if (typeof field.type === "object" && field.depends_on) {
2145
- const parentValue = getValueUsingPath(values, field.depends_on);
2146
- const resolvedType = field.type[String(parentValue)];
2147
- if (!resolvedType) return null;
2148
- fieldObj["type"] = resolvedType;
2149
- }
2150
- if (field.type === "range" && field.depends_on) {
2151
- const parentValue = getValueUsingPath(values, field.depends_on);
2152
- const { min, max } = field;
2153
- if (typeof min === "object" && typeof max === "object") {
2154
- fieldObj["min"] = min[String(parentValue)];
2155
- fieldObj["max"] = max[String(parentValue)];
2156
- }
2157
- }
2158
- if (field.resetOnChange) {
2159
- const parentValue = getValueUsingPath(values, field.resetOnChange);
2160
- const prevParentValue = field.prevParentValue;
2161
- if (prevParentValue !== void 0 && prevParentValue !== parentValue) deleteFieldByPath(values, fieldObj["name"]);
2162
- field.prevParentValue = parentValue;
2163
- }
2164
- if (field.secondaryField) if (Array.isArray(field.secondaryField)) fieldObj["secondaryField"] = field.secondaryField.map((secondaryField) => fieldNamePrefix + secondaryField);
2165
- else fieldObj["secondaryField"] = fieldNamePrefix + field.secondaryField;
2166
- return /* @__PURE__ */ jsx("div", {
2167
- className: cx("flex w-full", labelPosition === "left" ? "mb-[15px]" : "mb-[5px]"),
2168
- children: /* @__PURE__ */ jsx(InputField, {
2169
- field: fieldObj,
2170
- label: fieldObj["label"],
2171
- setFormDirty,
2172
- setFieldValue,
2173
- setFieldTouched,
2174
- values,
2175
- currentValue: getValueUsingPath(values, fieldObj["name"]),
2176
- onChange,
2177
- disabled: Boolean(fieldObj["disabled"]),
2178
- computeOn: parentInfo?.computeOn || fieldObj["computeOn"],
2179
- labelPosition,
2180
- missingFields,
2181
- spaceCompact,
2182
- workflowForm
2183
- })
2184
- }, `field-${id}`);
2185
- }, (prev, next) => {
2186
- if (!deepEqual(prev.optionsField, next.optionsField)) return false;
2187
- if (prev.values !== next.values) return false;
2188
- if (!deepEqual(prev.parentInfo, next.parentInfo)) return false;
2189
- if (!deepEqual(prev.missingFields, next.missingFields)) return false;
2190
- if (prev.labelPosition !== next.labelPosition) return false;
2191
- if (prev.spaceCompact !== next.spaceCompact) return false;
2192
- if (prev.workflowForm !== next.workflowForm) return false;
2193
- return true;
2194
- });
2195
- function FieldsFromOptions({ options = {}, values, setFormDirty, setFieldValue, setFieldTouched, parentInfo, labelPosition = "left", missingFields = [], spaceCompact = false, workflowForm = false }) {
2196
- return Object.keys(options).map((fieldName, idx) => /* @__PURE__ */ jsx(FormField, {
2197
- id: idx,
2198
- optionsField: options[fieldName],
2199
- fieldName,
2200
- values,
2201
- setFormDirty,
2202
- setFieldValue,
2203
- setFieldTouched,
2204
- parentInfo,
2205
- labelPosition,
2206
- missingFields,
2207
- spaceCompact,
2208
- workflowForm
2209
- }, idx));
2210
- }
2211
- var noop = () => {};
2212
- function DynamicFormContent({ options, setValues, setFormDirty, labelPosition, missingFields, spaceCompact, organizationVariables, remoteVars, workflowForm, onSubmit, submitLabel, values, contextKey }) {
2213
- const { setFieldValue, setFieldTouched } = useFormikContext();
2214
- const opts = useParsedOpts(options, values, organizationVariables, void 0, void 0, remoteVars);
2215
- const handleChange = useCallback((state) => {
2216
- if (setValues) setValues(state);
2217
- }, [setValues]);
2218
- const wizardConfig = parseWizardConfig(opts);
2219
- return /* @__PURE__ */ jsxs(Fragment$1, { children: [
2220
- /* @__PURE__ */ jsx(DirtyStateBridge, { setFormDirty }),
2221
- /* @__PURE__ */ jsx(FormikStateBridge, { onChange: handleChange }),
2222
- /* @__PURE__ */ jsx(DynamicDefaultsSync, { options: opts }, contextKey ?? ""),
2223
- wizardConfig !== null && wizardConfig ? /* @__PURE__ */ jsx(WizardContainer, {
2224
- wizardConfig,
2225
- values,
2226
- onChange: handleChange,
2227
- onSubmit,
2228
- className: "w-full",
2229
- ...resolveMetaOverrides(options, labelPosition, spaceCompact),
2230
- missingFields,
2231
- workflowForm,
2232
- setFieldValue,
2233
- setFieldTouched
2234
- }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(FieldsFromOptions, {
2235
- ...resolveMetaOverrides(options, labelPosition, spaceCompact),
2236
- options: opts,
2237
- values,
2238
- setFormDirty,
2239
- setFieldValue,
2240
- setFieldTouched,
2241
- missingFields,
2242
- workflowForm
2243
- }), onSubmit !== void 0 && !workflowForm && /* @__PURE__ */ jsx("div", {
2244
- className: "mt-4",
2245
- children: /* @__PURE__ */ jsx("input", {
2246
- type: "submit",
2247
- className: "btn btn-info",
2248
- value: submitLabel
2249
- })
2250
- })] })
2251
- ] });
2252
- }
2253
- var NO_FIELDS = {};
2254
- function DynamicForm({ formJSONs: options = {}, reinitialize = false, initialValues, setValues, setFormDirty = noop, formikRef, labelPosition = "left", missingFields = [], spaceCompact = false, organizationVariables, remoteVars, needsOrganizationVariables = false, onSubmit = void 0, submitLabel = "Submit", className = "w-full", workflowForm = false, contextKey, skipValueParse = false, fields = NO_FIELDS }) {
2255
- const [parseReady, setParseReady] = useState(parseStringsOfObjReady);
2256
- useEffect(() => {
2257
- initializeParseStringsOfObj();
2258
- return callWhenParserInitialized(setParseReady);
2259
- }, []);
2260
- if (!parseReady) return /* @__PURE__ */ jsx(Loader, {});
2261
- const parsedInitVals = skipValueParse ? initialValues : initialValues && parseStringsOfObj({
2262
- inputs: initialValues,
2263
- obj: initialValues,
2264
- orgVars: organizationVariables,
2265
- remoteVars
2266
- });
2267
- return /* @__PURE__ */ jsx(FieldRegistryContext.Provider, {
2268
- value: fields,
2269
- children: /* @__PURE__ */ jsx("div", {
2270
- className,
2271
- children: (!needsOrganizationVariables || organizationVariables) && initialValues && /* @__PURE__ */ jsx(Formik, {
2272
- enableReinitialize: reinitialize,
2273
- initialValues: parsedInitVals,
2274
- onSubmit: onSubmit ?? (() => {}),
2275
- onReset: () => {
2276
- setFormDirty(false);
2277
- },
2278
- ...formikRef ? { innerRef: formikRef } : {},
2279
- children: ({ values }) => /* @__PURE__ */ jsx(Form, {
2280
- className: "w-full text-[13px]",
2281
- children: /* @__PURE__ */ jsx(DynamicFormContent, {
2282
- values,
2283
- options,
2284
- setValues,
2285
- setFormDirty,
2286
- labelPosition,
2287
- missingFields,
2288
- spaceCompact,
2289
- organizationVariables,
2290
- remoteVars,
2291
- workflowForm,
2292
- onSubmit,
2293
- submitLabel,
2294
- contextKey
2295
- })
2296
- })
2297
- })
2298
- })
2299
- });
2300
- }
2301
- //#endregion
2302
- //#region src/form/SpecialTypes/DropdownWrapper.tsx
2303
- function DropdownWrapper({ options, onChange, fieldObj, setFormDirty, invalid = false, loading = false, disabled = void 0, allowCustomValue = void 0, currentValue, setFieldValue, setFieldTouched }) {
2304
- return /* @__PURE__ */ jsx(FormikCustomDropdown_default, {
2305
- invalid,
2306
- className: "w-full",
2307
- onChange,
2308
- loading,
2309
- setFormDirty,
2310
- options,
2311
- disabled: disabled ?? Boolean(fieldObj.disabled),
2312
- placeholder: getPlaceholder(fieldObj.placeholder, typeof fieldObj.default === "string" ? fieldObj.default : void 0),
2313
- name: fieldObj.name ?? "",
2314
- ariaLabel: fieldObj.label,
2315
- parentValue: fieldObj.parentValue,
2316
- secondaryField: fieldObj.secondaryField,
2317
- resetOnChange: fieldObj.resetOnChange,
2318
- value: fieldObj.value,
2319
- autoselect: fieldObj.autoselect === void 0 ? void 0 : Boolean(fieldObj.autoselect),
2320
- allowCustomValue: allowCustomValue ?? fieldObj.allowCustomValue,
2321
- customValueLabel: fieldObj.customValueLabel,
2322
- currentValue,
2323
- setFieldValue,
2324
- setFieldTouched
2325
- });
2326
- }
2327
- //#endregion
2328
- //#region src/form/SpecialTypes/FormikMultiSelectDropdown.tsx
2329
- function FormikMultiSelectDropdown({ name, options, setFormDirty, onChange, disabled = false, loading = false, invalid = false, placeholder, compareBy }) {
2330
- const [field] = useField(name);
2331
- const required = useFieldRequired();
2332
- const labelledByProps = useFieldLabelledByProps();
2333
- const { setFieldValue, setFieldTouched } = useFormikContext();
2334
- const handleChange = (next) => {
2335
- setFieldValue(name, next);
2336
- setFieldTouched(name, true);
2337
- setFormDirty(true);
2338
- onChange?.(next);
2339
- };
2340
- return /* @__PURE__ */ jsx(MultiSelectDropdown, {
2341
- ...labelledByProps,
2342
- textBoxClassName: invalid ? "invalid" : void 0,
2343
- options,
2344
- required,
2345
- value: Array.isArray(field.value) ? field.value : [],
2346
- onChange: handleChange,
2347
- disabled,
2348
- loading,
2349
- placeholder,
2350
- compareBy
2351
- });
2352
- }
2353
- //#endregion
2354
- //#region src/form/utils/pwResolver.ts
2355
- function resolvePwString(raw, options, fallbackUsername, opts) {
2356
- let inputForm = null;
2357
- let owner;
2358
- let name;
2359
- if (typeof raw === "string" && raw.startsWith("pw://")) {
2360
- const parts = raw.slice(5).split("/");
2361
- if (parts.length === 1 && parts[0]) {
2362
- inputForm = "shorthand";
2363
- name = parts[0];
2364
- owner = fallbackUsername;
2365
- } else if (parts.length === 2 && parts[0] && parts[1]) {
2366
- inputForm = "full";
2367
- owner = parts[0];
2368
- name = parts[1];
2369
- }
2370
- } else if (raw !== null && typeof raw === "object" && !Array.isArray(raw)) {
2371
- const obj = raw;
2372
- if (typeof obj["uri"] === "string" && obj["uri"].startsWith("pw://")) {
2373
- const parts = obj["uri"].slice(5).split("/");
2374
- if (parts.length === 1 && parts[0]) {
2375
- inputForm = "shorthand";
2376
- name = parts[0];
2377
- owner = fallbackUsername;
2378
- } else if (parts.length === 2 && parts[0] && parts[1]) {
2379
- inputForm = "full";
2380
- owner = parts[0];
2381
- name = parts[1];
2382
- }
2383
- } else if (typeof obj["name"] === "string") {
2384
- name = obj["name"];
2385
- const ownerVal = typeof obj["user"] === "string" ? obj["user"] : typeof obj["namespace"] === "string" ? obj["namespace"] : void 0;
2386
- if (obj["type"] === "managed-cluster" || !ownerVal) {
2387
- inputForm = "shorthand";
2388
- owner = fallbackUsername;
2389
- } else {
2390
- inputForm = "full";
2391
- owner = ownerVal;
2392
- }
2393
- } else if (typeof obj["id"] === "string" && obj["id"]) {
2394
- inputForm = "shorthand";
2395
- name = obj["id"];
2396
- owner = fallbackUsername;
2397
- }
2398
- }
2399
- if (!inputForm || !name || !owner) return raw;
2400
- const special = opts.specialCase?.(name, owner);
2401
- if (special !== void 0) return special;
2402
- const find = (pred) => {
2403
- for (const category of options) for (const opt of category.options) if (opt.value && pred(opt.value)) return opt.value;
2404
- };
2405
- if (inputForm === "full") return find((v) => v.name === name && v[opts.ownerKey] === owner) ?? raw;
2406
- const managed = find((v) => v.name === name && v.type === "managed-cluster");
2407
- if (managed) return managed;
2408
- const ownedByCaller = find((v) => v.name === name && v[opts.ownerKey] === fallbackUsername);
2409
- if (ownedByCaller) return ownedByCaller;
2410
- const sharedWithCaller = find((v) => v.name === name && typeof v[opts.ownerKey] === "string" && v[opts.ownerKey] !== fallbackUsername);
2411
- if (sharedWithCaller) return sharedWithCaller;
2412
- return raw;
2413
- }
2414
- function resolvePwValue(currentValue, options, fallbackUsername, opts) {
2415
- if (Array.isArray(currentValue)) {
2416
- let changed = false;
2417
- const next = currentValue.map((v) => {
2418
- const resolved = resolvePwString(v, options, fallbackUsername, opts);
2419
- if (resolved !== v) changed = true;
2420
- return resolved;
2421
- });
2422
- return changed ? next : currentValue;
2423
- }
2424
- return resolvePwString(currentValue, options, fallbackUsername, opts);
2425
- }
2426
- //#endregion
2427
- //#region src/form/Fields/ResourceOptionIcon.tsx
2428
- function ResourceOptionIcon({ imageUrl, fallbackIcon, alt, variant = "success", dotSize = 6 }) {
2429
- return /* @__PURE__ */ jsxs("span", {
2430
- className: "flex items-center gap-1.5 shrink-0",
2431
- children: [/* @__PURE__ */ jsx(StatusDot, {
2432
- variant,
2433
- size: dotSize
2434
- }), /* @__PURE__ */ jsx("img", {
2435
- src: imageUrl || fallbackIcon,
2436
- alt,
2437
- className: "h-4 w-4 object-contain",
2438
- onError: (e) => {
2439
- e.currentTarget.src = fallbackIcon;
2440
- }
2441
- })]
2442
- });
2443
- }
2444
- //#endregion
2445
- export { DropdownWrapper, DynamicDefaultsSync, DynamicForm, FLEX_MAX_DURATION_RANGE, FLEX_WAIT_TIME_RANGE, FieldProvider, FieldRegistryContext, FieldWrapper, FieldsFromOptions, FormikCustomDropdown_default as FormikCustomDropdown, FormikCustomInput, FormikMultiSelectDropdown, FormikNumericInput, GroupHeader, MultiSelectionDropdown, NumberField, Registry, ResourceOptionIcon, STANDARD_MAX_DURATION_RANGE, StatusBlock, applySecondaryField, collectFieldsWithDefaults, collectPartitionDurationIssues, enforceOneMustBeTrue, findSecondaryOption, findSelfReferencingFieldNames, flattenGroups, formatSecondsAsSlurmDuration, getInstanceTypesForForm, getInvalidDurationPaths, getPlaceholder, impureSetValueFromPath, initializeValues, parseSlurmDuration, resolveMetaOverrides, resolvePwString, resolvePwValue, resolvedFlag, setDurationValidity, useDynamicDefaultsSync, useFieldControlProps, useFieldLabelledByProps, useFieldRequired, useParsedOpts };
1
+ import { FieldProvider, useFieldControlProps, useFieldLabelledByProps, useFieldRequired } from "./fieldContext.js";
2
+ import { applySecondaryField, findSecondaryOption } from "./utils/secondaryField.js";
3
+ import { enforceOneMustBeTrue, flattenGroups, impureSetValueFromPath, initializeValues, resolvedFlag } from "./lib.js";
4
+ import { FieldWrapper } from "./FieldWrapper.js";
5
+ import { getInvalidDurationPaths, setDurationValidity } from "./Fields/durationValidity.js";
6
+ import FormikCustomDropdown_default from "./SpecialTypes/FormikCustomDropdown.js";
7
+ import FormikCustomInput from "./SpecialTypes/FormikCustomInput.js";
8
+ import FormikNumericInput from "./SpecialTypes/FormikNumericInput.js";
9
+ import { getPlaceholder } from "./utils/getPlaceholder.js";
10
+ import NumberField from "./Fields/NumberField.js";
11
+ import { ResourceOptionIcon } from "./Fields/ResourceOptionIcon.js";
12
+ import { FLEX_MAX_DURATION_RANGE, FLEX_WAIT_TIME_RANGE, STANDARD_MAX_DURATION_RANGE, collectPartitionDurationIssues, formatDuration, parseDuration } from "./utils/duration.js";
13
+ import { FieldRegistryContext, Registry } from "./fieldRegistry.js";
14
+ import { useParsedOpts } from "./useParsedOpts.js";
15
+ import { DynamicDefaultsSync, DynamicForm, FieldsFromOptions, GroupHeader, MultiSelectionDropdown, collectFieldsWithDefaults, findSelfReferencingFieldNames, resolveMetaOverrides, useDynamicDefaultsSync } from "./Form.js";
16
+ import DropdownWrapper from "./SpecialTypes/DropdownWrapper.js";
17
+ import FormikMultiSelectDropdown from "./SpecialTypes/FormikMultiSelectDropdown.js";
18
+ export { DropdownWrapper, DynamicDefaultsSync, DynamicForm, FLEX_MAX_DURATION_RANGE, FLEX_WAIT_TIME_RANGE, FieldProvider, FieldRegistryContext, FieldWrapper, FieldsFromOptions, FormikCustomDropdown_default as FormikCustomDropdown, FormikCustomInput, FormikMultiSelectDropdown, FormikNumericInput, GroupHeader, MultiSelectionDropdown, NumberField, Registry, ResourceOptionIcon, STANDARD_MAX_DURATION_RANGE, applySecondaryField, collectFieldsWithDefaults, collectPartitionDurationIssues, enforceOneMustBeTrue, findSecondaryOption, findSelfReferencingFieldNames, flattenGroups, formatDuration, getInvalidDurationPaths, getPlaceholder, impureSetValueFromPath, initializeValues, parseDuration, resolveMetaOverrides, resolvedFlag, setDurationValidity, useDynamicDefaultsSync, useFieldControlProps, useFieldLabelledByProps, useFieldRequired, useParsedOpts };