@parallelworks/ui 0.41.0 → 0.42.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (280) hide show
  1. package/README.md +25 -0
  2. package/dist/ai/components/AllocationSelector.d.ts +3 -4
  3. package/dist/ai/components/AllocationSelector.js +37 -33
  4. package/dist/ai/components/AttachmentManager.js +7 -7
  5. package/dist/ai/components/AttachmentUpload.js +5 -5
  6. package/dist/ai/components/BranchNavigator.js +1 -1
  7. package/dist/ai/components/ChatEmptyState.js +2 -2
  8. package/dist/ai/components/ChatInput.js +10 -8
  9. package/dist/ai/components/ChatLayout.js +4 -5
  10. package/dist/ai/components/ChatMessage.js +12 -12
  11. package/dist/ai/components/ChatMessageList.js +9 -9
  12. package/dist/ai/components/ChatSidebar.js +8 -9
  13. package/dist/ai/components/ChatThread.js +4 -4
  14. package/dist/ai/components/ComposerChrome.d.ts +2 -2
  15. package/dist/ai/components/ComposerChrome.js +3 -6
  16. package/dist/ai/components/DragOverlay.js +2 -2
  17. package/dist/ai/components/ModelSelector.d.ts +5 -3
  18. package/dist/ai/components/ModelSelector.js +102 -339
  19. package/dist/ai/components/PastedText.js +11 -11
  20. package/dist/ai/components/ProviderIssueBanner.d.ts +2 -0
  21. package/dist/ai/components/ProviderIssueBanner.js +34 -16
  22. package/dist/ai/components/Reasoning.js +4 -4
  23. package/dist/ai/components/ShareDialog.js +43 -35
  24. package/dist/ai/components/SlashMenu.d.ts +12 -2
  25. package/dist/ai/components/SlashMenu.js +29 -13
  26. package/dist/ai/components/agent/ActivityLine.js +3 -3
  27. package/dist/ai/components/agent/AgentMessageParts.js +20 -20
  28. package/dist/ai/components/agent/PermissionPicker.js +13 -11
  29. package/dist/ai/components/conversationActions.d.ts +4 -6
  30. package/dist/ai/components/conversationActions.js +9 -13
  31. package/dist/ai/core/ChatProvider.js +1 -1
  32. package/dist/ai/core/config.js +1 -1
  33. package/dist/ai/core/providerIssues.d.ts +7 -0
  34. package/dist/ai/core/providerIssues.js +9 -1
  35. package/dist/ai/core/useKeyboardShortcuts.d.ts +4 -0
  36. package/dist/ai/core/useKeyboardShortcuts.js +8 -2
  37. package/dist/ai/index.d.ts +0 -1
  38. package/dist/ai/index.js +1 -2
  39. package/dist/ai/strings.d.ts +19 -0
  40. package/dist/ai/strings.js +18 -1
  41. package/dist/ai/ui/Avatar.js +5 -5
  42. package/dist/ai/ui/ConversationSidebar.d.ts +0 -1
  43. package/dist/ai/ui/ConversationSidebar.js +6 -7
  44. package/dist/ai/ui/Markdown.js +3 -3
  45. package/dist/ai/ui/Notice.js +6 -6
  46. package/dist/ai/ui/chip.d.ts +4 -0
  47. package/dist/ai/ui/chip.js +8 -0
  48. package/dist/ai/ui/sidebar.d.ts +6 -5
  49. package/dist/ai/ui/sidebar.js +39 -43
  50. package/dist/components/ActionMenu.d.ts +13 -0
  51. package/dist/components/ActionMenu.js +30 -0
  52. package/dist/components/Avatar.js +6 -6
  53. package/dist/components/Breadcrumbs.js +1 -1
  54. package/dist/components/Button.js +10 -10
  55. package/dist/components/Callout.js +2 -2
  56. package/dist/components/Card.js +3 -3
  57. package/dist/components/Combobox.d.ts +66 -0
  58. package/dist/components/Combobox.js +393 -0
  59. package/dist/components/ConfirmModal.js +11 -11
  60. package/dist/components/ContextMenu.d.ts +23 -0
  61. package/dist/components/ContextMenu.js +89 -0
  62. package/dist/components/CopyToClipboard.js +2 -2
  63. package/dist/components/CreateModal.d.ts +17 -36
  64. package/dist/components/CreateModal.js +97 -259
  65. package/dist/components/DescriptionList.js +8 -8
  66. package/dist/components/Filter.js +2 -2
  67. package/dist/components/FilterPill.js +2 -2
  68. package/dist/components/HoverCard.d.ts +14 -0
  69. package/dist/components/HoverCard.js +33 -0
  70. package/dist/components/IconButton.d.ts +5 -5
  71. package/dist/components/IconButton.js +18 -22
  72. package/dist/components/InfoTip.d.ts +15 -0
  73. package/dist/components/InfoTip.js +60 -0
  74. package/dist/components/Input.js +5 -5
  75. package/dist/components/LetterBadge.js +2 -2
  76. package/dist/components/Loader.js +3 -3
  77. package/dist/components/Markdown.js +1 -1
  78. package/dist/components/MarkdownImpl.js +2 -2
  79. package/dist/components/Menu.d.ts +33 -0
  80. package/dist/components/Menu.js +86 -0
  81. package/dist/components/MenuEntries.d.ts +56 -0
  82. package/dist/components/MenuEntries.js +123 -0
  83. package/dist/components/Modal.d.ts +29 -0
  84. package/dist/components/Modal.js +47 -0
  85. package/dist/components/Overview.js +11 -10
  86. package/dist/components/PageHeader.d.ts +10 -16
  87. package/dist/components/PageHeader.js +69 -95
  88. package/dist/components/Pagination.js +5 -5
  89. package/dist/components/Provider.d.ts +5 -11
  90. package/dist/components/Provider.js +11 -14
  91. package/dist/components/Radio.d.ts +8 -0
  92. package/dist/components/Radio.js +16 -0
  93. package/dist/components/SectionHeader.js +2 -2
  94. package/dist/components/SegmentedControl.d.ts +6 -5
  95. package/dist/components/SegmentedControl.js +34 -30
  96. package/dist/components/Select.d.ts +27 -0
  97. package/dist/components/Select.js +78 -0
  98. package/dist/components/SettingModeOption.d.ts +5 -8
  99. package/dist/components/SettingModeOption.js +10 -12
  100. package/dist/components/SettingRow.js +4 -4
  101. package/dist/components/SettingsCard.js +4 -4
  102. package/dist/components/SettingsGroup.js +2 -2
  103. package/dist/components/Sort.js +1 -1
  104. package/dist/components/StatusBadge.js +7 -7
  105. package/dist/components/StatusIcons.js +4 -4
  106. package/dist/components/Switch.d.ts +22 -0
  107. package/dist/components/Switch.js +28 -0
  108. package/dist/components/Table.js +11 -11
  109. package/dist/components/Tooltip.d.ts +24 -20
  110. package/dist/components/Tooltip.js +58 -76
  111. package/dist/components/floating.d.ts +14 -0
  112. package/dist/components/floating.js +21 -0
  113. package/dist/components/search.d.ts +2 -0
  114. package/dist/components/search.js +14 -0
  115. package/dist/editor/Editor.js +1 -1
  116. package/dist/editor/EditorField.js +16 -21
  117. package/dist/editor/FormCodePanel.js +19 -19
  118. package/dist/editor/Monaco.js +3 -3
  119. package/dist/file-explorer/FileExplorer.js +77 -81
  120. package/dist/file-explorer/FileExplorerContext.js +2 -2
  121. package/dist/file-explorer/FilePreview/CodePreviewEditor.js +1 -1
  122. package/dist/file-explorer/FilePreview/Inline.js +1 -1
  123. package/dist/file-explorer/FilePreview/StreamedBadge.js +5 -6
  124. package/dist/file-explorer/FilePreview/Viewer.js +8 -8
  125. package/dist/file-explorer/FilePreview/index.d.ts +1 -1
  126. package/dist/file-explorer/FilePreview/index.js +49 -83
  127. package/dist/file-explorer/FileRow.d.ts +4 -3
  128. package/dist/file-explorer/FileRow.js +32 -38
  129. package/dist/file-explorer/FileRowsWindow.d.ts +3 -2
  130. package/dist/file-explorer/FileRowsWindow.js +4 -4
  131. package/dist/file-explorer/FileTable.d.ts +3 -2
  132. package/dist/file-explorer/FileTable.js +3 -3
  133. package/dist/file-explorer/LoadMoreRow.js +1 -1
  134. package/dist/file-explorer/TreeView.js +11 -16
  135. package/dist/file-explorer/helpers.js +11 -12
  136. package/dist/file-explorer/toasts.js +11 -11
  137. package/dist/form/FieldGroup.d.ts +11 -0
  138. package/dist/form/FieldGroup.js +36 -0
  139. package/dist/form/FieldWrapper.js +4 -4
  140. package/dist/form/Fields/BooleanField.js +23 -33
  141. package/dist/form/Fields/CheckboxGroupField.d.ts +1 -1
  142. package/dist/form/Fields/CheckboxGroupField.js +9 -13
  143. package/dist/form/Fields/ColorField.d.ts +1 -2
  144. package/dist/form/Fields/ColorField.js +6 -11
  145. package/dist/form/Fields/DropdownField.d.ts +3 -13
  146. package/dist/form/Fields/DropdownField.js +40 -27
  147. package/dist/form/Fields/DurationField.d.ts +1 -2
  148. package/dist/form/Fields/DurationField.js +25 -32
  149. package/dist/form/Fields/LabelField.js +2 -2
  150. package/dist/form/Fields/MultiDropdownField.d.ts +6 -12
  151. package/dist/form/Fields/MultiDropdownField.js +31 -22
  152. package/dist/form/Fields/NumberField.d.ts +1 -1
  153. package/dist/form/Fields/NumberField.js +65 -35
  154. package/dist/form/Fields/ObjectField.d.ts +1 -1
  155. package/dist/form/Fields/ObjectField.js +12 -43
  156. package/dist/form/Fields/RadioField.d.ts +1 -1
  157. package/dist/form/Fields/RadioField.js +30 -41
  158. package/dist/form/Fields/RangeField.d.ts +1 -2
  159. package/dist/form/Fields/RangeField.js +23 -16
  160. package/dist/form/Fields/StringField.d.ts +1 -2
  161. package/dist/form/Fields/StringField.js +58 -26
  162. package/dist/form/Fields/TextAreaField.js +12 -10
  163. package/dist/form/Form.d.ts +30 -48
  164. package/dist/form/Form.js +306 -412
  165. package/dist/form/FormLayout.js +9 -9
  166. package/dist/form/Wizard/WizardContainer.d.ts +1 -4
  167. package/dist/form/Wizard/WizardContainer.js +40 -56
  168. package/dist/form/Wizard/WizardNavigation.js +1 -1
  169. package/dist/form/Wizard/WizardStepContent.d.ts +1 -5
  170. package/dist/form/Wizard/WizardStepContent.js +11 -16
  171. package/dist/form/Wizard/WizardStepIndicator.js +6 -6
  172. package/dist/form/Wizard/types.d.ts +0 -14
  173. package/dist/form/fieldRegistry.d.ts +1 -1
  174. package/dist/form/fieldRegistry.js +6 -6
  175. package/dist/form/formEditing.js +1 -1
  176. package/dist/form/index.d.ts +5 -4
  177. package/dist/form/index.js +6 -7
  178. package/dist/form/types/fieldComponentTypes.d.ts +8 -10
  179. package/dist/form/useChoiceField.d.ts +20 -0
  180. package/dist/form/useChoiceField.js +49 -0
  181. package/dist/form/useFieldValue.d.ts +7 -0
  182. package/dist/form/useFieldValue.js +20 -0
  183. package/dist/form/useParsedOpts.d.ts +3 -1
  184. package/dist/form/useParsedOpts.js +12 -17
  185. package/dist/form/utils/dependentOptions.d.ts +8 -4
  186. package/dist/form/utils/dependentOptions.js +25 -17
  187. package/dist/form/utils/fieldItems.d.ts +10 -0
  188. package/dist/form/utils/fieldItems.js +11 -0
  189. package/dist/form/utils/fieldPath.d.ts +10 -0
  190. package/dist/form/utils/fieldPath.js +20 -0
  191. package/dist/form/utils/shareEqual.d.ts +6 -0
  192. package/dist/form/utils/shareEqual.js +38 -0
  193. package/dist/graph/AnnotationBanner.js +8 -8
  194. package/dist/graph/Collapse.d.ts +1 -0
  195. package/dist/graph/Collapse.js +6 -27
  196. package/dist/graph/Connectors.js +5 -5
  197. package/dist/graph/DependencyGraph.js +20 -19
  198. package/dist/graph/GraphEditor.js +30 -41
  199. package/dist/graph/GraphEditorDialogs.js +17 -17
  200. package/dist/graph/GraphNode.js +1 -1
  201. package/dist/graph/InputDialog.js +19 -22
  202. package/dist/graph/InputsEditor.js +58 -66
  203. package/dist/graph/JobSummary.js +42 -39
  204. package/dist/graph/MatrixGroup.js +1 -1
  205. package/dist/graph/ProblemsButton.js +18 -19
  206. package/dist/graph/Reveal.js +1 -1
  207. package/dist/graph/ShortcutsButton.js +15 -18
  208. package/dist/graph/SuggestionInput.d.ts +5 -10
  209. package/dist/graph/SuggestionInput.js +14 -18
  210. package/dist/graph/ToolbarPopover.d.ts +11 -6
  211. package/dist/graph/ToolbarPopover.js +35 -67
  212. package/dist/graph/TreeView.js +24 -37
  213. package/dist/graph/WorkflowSettingsDialog.js +4 -4
  214. package/dist/graph/editorChrome.js +13 -12
  215. package/dist/graph/editorFields.js +29 -29
  216. package/dist/graph/editorPrimitives.d.ts +1 -1
  217. package/dist/graph/editorPrimitives.js +10 -7
  218. package/dist/graph/editorRows.js +103 -97
  219. package/dist/graph/fieldProblems.js +2 -2
  220. package/dist/graph/graphEditorState.d.ts +4 -3
  221. package/dist/graph/inputRefs.js +9 -9
  222. package/dist/graph/settingsViews.js +6 -7
  223. package/dist/graph/stepWith.js +6 -6
  224. package/dist/index.d.ts +14 -15
  225. package/dist/index.js +15 -14
  226. package/dist/list/ActionToolbar.d.ts +6 -14
  227. package/dist/list/ActionToolbar.js +48 -96
  228. package/dist/list/ListPage.d.ts +6 -12
  229. package/dist/list/ListPage.js +46 -29
  230. package/dist/list/ListPager.js +4 -4
  231. package/dist/list/ListTable.js +5 -5
  232. package/dist/list/ListViewControls.d.ts +5 -4
  233. package/dist/list/ListViewControls.js +177 -267
  234. package/dist/list/UserHoverCard.d.ts +3 -17
  235. package/dist/list/UserHoverCard.js +21 -45
  236. package/dist/list/index.d.ts +3 -4
  237. package/dist/list/index.js +3 -4
  238. package/dist/list/listSearch.js +12 -11
  239. package/dist/list/rowMenu.d.ts +63 -0
  240. package/dist/list/rowMenu.js +134 -0
  241. package/dist/logviewer/LogViewer.js +26 -26
  242. package/dist/styles.css +1 -1
  243. package/package.json +7 -13
  244. package/dist/ai/ui/Dropdown.d.ts +0 -31
  245. package/dist/ai/ui/Dropdown.js +0 -110
  246. package/dist/components/AddMenu.d.ts +0 -18
  247. package/dist/components/AddMenu.js +0 -46
  248. package/dist/components/BareModal.d.ts +0 -17
  249. package/dist/components/BareModal.js +0 -77
  250. package/dist/components/CollapsiblePanel.d.ts +0 -27
  251. package/dist/components/CollapsiblePanel.js +0 -77
  252. package/dist/components/Dropdown.d.ts +0 -28
  253. package/dist/components/Dropdown.js +0 -355
  254. package/dist/components/Listbox.d.ts +0 -21
  255. package/dist/components/Listbox.js +0 -119
  256. package/dist/components/MultiSelect.d.ts +0 -22
  257. package/dist/components/MultiSelect.js +0 -168
  258. package/dist/components/SwitchToggle.d.ts +0 -15
  259. package/dist/components/SwitchToggle.js +0 -57
  260. package/dist/components/SwitchToggleSmall.d.ts +0 -6
  261. package/dist/components/SwitchToggleSmall.js +0 -14
  262. package/dist/components/Toggle.d.ts +0 -6
  263. package/dist/components/Toggle.js +0 -20
  264. package/dist/components/dropdownUtils.d.ts +0 -37
  265. package/dist/components/dropdownUtils.js +0 -99
  266. package/dist/form/SpecialTypes/DropdownWrapper.d.ts +0 -25
  267. package/dist/form/SpecialTypes/DropdownWrapper.js +0 -28
  268. package/dist/form/SpecialTypes/FormikCustomDropdown.d.ts +0 -51
  269. package/dist/form/SpecialTypes/FormikCustomDropdown.js +0 -119
  270. package/dist/form/SpecialTypes/FormikCustomInput.d.ts +0 -19
  271. package/dist/form/SpecialTypes/FormikCustomInput.js +0 -121
  272. package/dist/form/SpecialTypes/FormikMultiSelectDropdown.d.ts +0 -13
  273. package/dist/form/SpecialTypes/FormikMultiSelectDropdown.js +0 -31
  274. package/dist/form/SpecialTypes/FormikNumericInput.d.ts +0 -9
  275. package/dist/form/SpecialTypes/FormikNumericInput.js +0 -60
  276. package/dist/form/SpecialTypes/index.d.ts +0 -3
  277. package/dist/list/HoverCard.d.ts +0 -24
  278. package/dist/list/HoverCard.js +0 -73
  279. package/dist/list/RowContextMenu.d.ts +0 -127
  280. package/dist/list/RowContextMenu.js +0 -515
package/dist/form/Form.js CHANGED
@@ -1,29 +1,24 @@
1
- import { useStrings, useWorkflowEngine } from "../components/Provider.js";
2
- import { BiSolidTrashAlt } from "../icons/bi/BiSolidTrashAlt.js";
3
- import { FaAngleRight } from "../icons/fa/FaAngleRight.js";
4
- import { TooltipInfo } from "../components/Tooltip.js";
5
- import { UncontrolledCollapsiblePanel } from "../components/CollapsiblePanel.js";
6
- import CustomListbox from "../components/Listbox.js";
1
+ import { useWorkflowEngine } from "../components/Provider.js";
2
+ import { InfoTip } from "../components/InfoTip.js";
7
3
  import Loader from "../components/Loader.js";
8
- import SectionHeader from "../components/SectionHeader.js";
9
- import { Table } from "../components/Table.js";
10
4
  import { parseWizardConfig } from "./Wizard/utils.js";
11
5
  import { inputWidth, resolvedFlag } from "./lib.js";
6
+ import { FieldGroup } from "./FieldGroup.js";
12
7
  import { resolveFormLayout } from "./layout.js";
13
8
  import { FormLayout, LayoutCSSContext } from "./FormLayout.js";
14
- import { useFieldControlProps, useFieldRequired } from "./fieldContext.js";
15
- import { getValueUsingPath } from "./utils/getValueUsingPath.js";
16
9
  import { FieldRegistryContext, Registry } from "./fieldRegistry.js";
17
10
  import { EditingScope, useFormEditing } from "./formEditing.js";
11
+ import { shareEqual } from "./utils/shareEqual.js";
18
12
  import { useParsedOpts } from "./useParsedOpts.js";
19
- import { usePrevious } from "./usePrevious.js";
13
+ import { atIndex, fieldPath, rowIndexOf } from "./utils/fieldPath.js";
14
+ import { getValueUsingPath } from "./utils/getValueUsingPath.js";
20
15
  import { getParentValue } from "./utils/getParentValue.js";
21
16
  import { WizardContainer } from "./Wizard/WizardContainer.js";
22
- import cx from "classnames";
23
- import React, { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
24
- import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
25
- import deepEqual from "fast-deep-equal";
26
- import { Form, Formik, useField, useFormikContext } from "formik";
17
+ import { clsx } from "clsx";
18
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
19
+ import { createContext, memo, useContext, useEffect, useEffectEvent, useLayoutEffect, useMemo, useRef, useState } from "react";
20
+ import { dequal } from "dequal";
21
+ import { FormProvider, get, useForm, useFormContext, useFormState, useWatch } from "react-hook-form";
27
22
  //#region src/form/Form.tsx
28
23
  function asSchemaField(value) {
29
24
  return value && typeof value === "object" ? value : null;
@@ -62,7 +57,7 @@ function listLengthsSignature(options, values = {}, prefix = "") {
62
57
  const len = Array.isArray(items) ? items.length : 0;
63
58
  parts.push(`${fullName}:${len}`);
64
59
  const template = field.template || field.options;
65
- if (template && Array.isArray(items)) for (let i = 0; i < items.length; i++) parts.push(listLengthsSignature(template, asRecord(items[i]), `${fullName}[${i}].`));
60
+ if (template && Array.isArray(items)) for (let i = 0; i < items.length; i++) parts.push(listLengthsSignature(template, asRecord(items[i]), `${fullName}.${i}.`));
66
61
  } else if (field.type === "group") {
67
62
  const groupItems = field.items || field.options;
68
63
  if (groupItems) parts.push(listLengthsSignature(groupItems, values, prefix));
@@ -76,7 +71,8 @@ function listLengthsSignature(options, values = {}, prefix = "") {
76
71
  }
77
72
  return parts.join("|");
78
73
  }
79
- /** Collects every field with a `default` for the form-level sync to track. */
74
+ /** Collects every field with a `default` for the form-level sync to track, named as the form store
75
+ * names it: a row's fields by `list.0.field`. */
80
76
  function collectFieldsWithDefaults(options, values = {}, prefix = "") {
81
77
  const result = [];
82
78
  if (!options || typeof options !== "object") return result;
@@ -102,7 +98,7 @@ function collectFieldsWithDefaults(options, values = {}, prefix = "") {
102
98
  const listValues = values?.[fieldName];
103
99
  if (listTemplate && Array.isArray(listValues)) for (let i = 0; i < listValues.length; i++) {
104
100
  const itemValues = asRecord(listValues[i]);
105
- result.push(...collectFieldsWithDefaults(listTemplate, itemValues, `${fullName}[${i}].`));
101
+ result.push(...collectFieldsWithDefaults(listTemplate, itemValues, `${fullName}.${i}.`));
106
102
  }
107
103
  continue;
108
104
  }
@@ -123,15 +119,14 @@ function defaultReferencesField(defaultValue, fieldPath) {
123
119
  function findSelfReferencingFieldNames(schema, values = {}) {
124
120
  return collectFieldsWithDefaults(schema, values).filter(({ name, defaultValue }) => defaultReferencesField(defaultValue, name)).map(({ name }) => name);
125
121
  }
126
- /** Resolves schema defaults into form values as dependencies change. Store-agnostic so the Formik
127
- * form and hosts with their own store share one implementation. */
122
+ /** Resolves schema defaults into form values as dependencies change. Store-agnostic so the form and
123
+ * hosts with their own store share one implementation. */
128
124
  function useDynamicDefaultsSync({ options, values, setValue, isTouched }) {
129
125
  const engine = useWorkflowEngine();
130
126
  const listSig = useMemo(() => listLengthsSignature(options, values), [options, values]);
131
127
  const fieldsWithDefaults = useMemo(() => collectFieldsWithDefaults(options, values).map((f) => {
132
128
  if (typeof f.defaultValue !== "string" || !f.defaultValue.includes("${{")) return f;
133
- const lastRowIndex = [...f.name.matchAll(/\[(\d+)\]/g)].at(-1)?.[1];
134
- const index = lastRowIndex === void 0 ? -1 : Number(lastRowIndex);
129
+ const index = rowIndexOf(f.name) ?? -1;
135
130
  const parsed = engine.evaluate({
136
131
  inputs: values,
137
132
  obj: { v: f.defaultValue },
@@ -144,11 +139,16 @@ function useDynamicDefaultsSync({ options, values, setValue, isTouched }) {
144
139
  defaultValue: resolved
145
140
  };
146
141
  }), [options, listSig]);
142
+ const pendingRef = useRef([]);
143
+ useEffect(() => {
144
+ const pending = pendingRef.current.splice(0);
145
+ for (const [name, value] of pending) setValue(name, value);
146
+ });
147
147
  const prevDefaultsRef = useRef(/* @__PURE__ */ new Map());
148
148
  const synthTouchedRef = useRef(/* @__PURE__ */ new Set());
149
149
  const prevListLensRef = useRef(/* @__PURE__ */ new Map());
150
150
  const fieldsWithDefaultsRef = useRef(null);
151
- if (!deepEqual(fieldsWithDefaultsRef.current, fieldsWithDefaults)) {
151
+ if (!dequal(fieldsWithDefaultsRef.current, fieldsWithDefaults)) {
152
152
  fieldsWithDefaultsRef.current = fieldsWithDefaults;
153
153
  const currentNames = new Set(fieldsWithDefaults.map((f) => f.name));
154
154
  for (const name of prevDefaultsRef.current.keys()) if (!currentNames.has(name)) {
@@ -163,8 +163,8 @@ function useDynamicDefaultsSync({ options, values, setValue, isTouched }) {
163
163
  for (const [list, prevLen] of prevListLensRef.current) {
164
164
  const nowLen = listLens.get(list);
165
165
  if (nowLen !== void 0 && nowLen < prevLen) {
166
- for (const name of [...prevDefaultsRef.current.keys()]) if (name.startsWith(`${list}[`)) prevDefaultsRef.current.delete(name);
167
- for (const name of [...synthTouchedRef.current]) if (name.startsWith(`${list}[`)) synthTouchedRef.current.delete(name);
166
+ for (const name of [...prevDefaultsRef.current.keys()]) if (name.startsWith(`${list}.`)) prevDefaultsRef.current.delete(name);
167
+ for (const name of [...synthTouchedRef.current]) if (name.startsWith(`${list}.`)) synthTouchedRef.current.delete(name);
168
168
  }
169
169
  }
170
170
  prevListLensRef.current = listLens;
@@ -190,367 +190,227 @@ function useDynamicDefaultsSync({ options, values, setValue, isTouched }) {
190
190
  }
191
191
  if (prevDefault === serializedDefault) continue;
192
192
  if (defaultValue === null || defaultValue === void 0) continue;
193
- if (JSON.stringify(getValueUsingPath(values, name)) !== serializedDefault) setValue(name, defaultValue);
193
+ if (JSON.stringify(getValueUsingPath(values, name)) !== serializedDefault) pendingRef.current.push([name, defaultValue]);
194
194
  }
195
195
  }
196
196
  }
197
197
  function DynamicDefaultsSync({ options }) {
198
- const { setFieldValue, getFieldMeta, values } = useFormikContext();
198
+ const { control, setValue, getFieldState } = useFormContext();
199
199
  useDynamicDefaultsSync({
200
200
  options,
201
- values,
202
- setValue: setFieldValue,
203
- isTouched: (name) => getFieldMeta(name).touched
201
+ values: useWatch({ control }),
202
+ setValue: (name, value) => setValue(name, value, { shouldDirty: true }),
203
+ isTouched: (name) => getFieldState(name).isTouched
204
204
  });
205
205
  return null;
206
206
  }
207
- function GroupHeader({ children, open, setOpen, title }) {
208
- return /* @__PURE__ */ jsxs(SectionHeader, {
209
- className: "flex items-center justify-between pb-1 mb-2 border-b",
210
- children: [/* @__PURE__ */ jsxs("button", {
211
- type: "button",
212
- "aria-expanded": open,
213
- "data-field-label": true,
214
- className: "w-full flex items-center text-left transform ease-in transition cursor-pointer",
215
- onClick: () => setOpen((open) => !open),
216
- children: [/* @__PURE__ */ jsx(FaAngleRight, { className: cx(open && "transform rotate-90 ", "px-1 text-[1.25rem] transition ease-in ") }), /* @__PURE__ */ jsx("span", { children: title })]
217
- }), children]
218
- });
219
- }
220
- function deleteFieldByPath(obj, path) {
221
- if (!path) return;
222
- const parts = path.replaceAll("[", ".").replaceAll("]", "").split(".");
223
- const fieldToRemove = parts.pop();
224
- if (!fieldToRemove) {
225
- console.error("Invalid path");
226
- return;
207
+ var FormOptionsContext = createContext({ workflowForm: false });
208
+ /** Parses a field's schema against the form's values, again only when an input it reads changes.
209
+ * Without expressions the schema is its own parse; with only org-var or path expressions it parses once. */
210
+ function useFieldParse(optionsField, arrayIndex) {
211
+ const engine = useWorkflowEngine();
212
+ const cache = useRef(null);
213
+ let c = cache.current;
214
+ if (c === null || c.optionsField !== optionsField) {
215
+ const converted = engine.convertInputs(asRecord(optionsField));
216
+ const { inputDeps, hasExpressions } = engine.inputDependencies(converted);
217
+ c = {
218
+ optionsField,
219
+ converted,
220
+ inputDeps: [...inputDeps],
221
+ hasExpressions,
222
+ key: null,
223
+ result: null
224
+ };
225
+ cache.current = c;
227
226
  }
228
- const parent = parts.reduce((acc, curr) => {
229
- if (acc && typeof acc === "object") return acc[curr];
230
- return {};
231
- }, obj);
232
- if (parent && typeof parent === "object" && Object.hasOwn(parent, fieldToRemove)) delete parent[fieldToRemove];
227
+ const parse = c;
228
+ return (values) => {
229
+ if (!parse.hasExpressions) return optionsField;
230
+ const key = JSON.stringify([arrayIndex, ...parse.inputDeps.map((dep) => values[dep])]);
231
+ if (key !== parse.key) {
232
+ parse.key = key;
233
+ parse.result = shareEqual(parse.result, engine.evaluate({
234
+ inputs: values,
235
+ obj: parse.converted,
236
+ orgVars: {},
237
+ index: arrayIndex
238
+ }));
239
+ }
240
+ return parse.result;
241
+ };
233
242
  }
234
- function getClosestParentIndex(parentPath) {
235
- for (let i = parentPath.length - 1; i >= 0; i--) if (parentPath[i] === "]") {
236
- let j = i - 1;
237
- while (j >= 0 && parentPath[j] !== "[") j--;
238
- return parseInt(parentPath.slice(j + 1, i), 10);
239
- }
243
+ /** Whether a field's `show_if` and `show_if_not` let it show. */
244
+ function shows(field, values, index) {
245
+ if (field.show_if !== void 0 && !getParentValue(index, field.show_if, values, field)) return false;
246
+ return field.show_if_not === void 0 || !getParentValue(index, field.show_if_not, values, field);
240
247
  }
241
- function MultiSelectionDropdown({ name, label = "", invalid = false, options: initialOptions = [], setFormDirty, onChange, parentValue }) {
242
- const [field] = useField(name);
243
- const required = useFieldRequired();
244
- const controlProps = useFieldControlProps();
245
- const { setFieldValue } = useFormikContext();
246
- const options = useMemo(() => initialOptions.map((option) => {
247
- const op = typeof option === "string" ? {
248
- label: option,
249
- value: option
250
- } : option;
251
- return {
252
- ...op,
253
- selected: !!field.value?.includes(op.value) || !!op.selected
254
- };
255
- }), [initialOptions, field.value]);
256
- const handleOptionSelected = (selectedValue) => {
257
- setFieldValue(name, [...field.value ?? [], selectedValue]);
258
- onChange?.(options);
259
- setFormDirty(true);
248
+ function readField(field, values, index, workflowForm) {
249
+ if (field === null) return {
250
+ hidden: true,
251
+ clears: false,
252
+ disabledByInput: false,
253
+ parentValue: void 0,
254
+ resetBy: void 0
260
255
  };
261
- const t = useStrings().common;
262
- const handleRemoveOption = (key) => {
263
- setFieldValue(name, field.value.filter((item) => item !== key));
264
- onChange?.(options);
265
- setFormDirty(true);
256
+ const markedHidden = field.hidden === true || !field.type;
257
+ const hidden = markedHidden || !shows(field, values, index);
258
+ const clears = hidden && !workflowForm && (!markedHidden || Boolean(resolvedFlag(field.ignore)));
259
+ const enableIf = typeof field.enable_if === "string" && field.disabled === false ? getValueUsingPath(values, atIndex(field.enable_if, index)) : void 0;
260
+ const typedByParent = field.depends_on !== void 0 && (typeof field.type === "object" || field.type === "range");
261
+ return {
262
+ hidden,
263
+ clears,
264
+ disabledByInput: enableIf === false,
265
+ parentValue: typedByParent ? getValueUsingPath(values, atIndex(field.depends_on ?? "", index)) : void 0,
266
+ resetBy: field.resetOnChange ? JSON.stringify(getValueUsingPath(values, field.resetOnChange)) : void 0
266
267
  };
267
- const prevParent = usePrevious(parentValue);
268
+ }
269
+ /**
270
+ * One field of the schema. It renders again only when what it reads changes: an input its expressions
271
+ * or conditions read, or whether the form marks it missing. Its own value is its input's business.
272
+ */
273
+ var FormField = memo(function FormField({ fieldName, optionsField, nested, fieldNamePrefix, arrayIndex, computeOn, onRowChange, labelPosition, spaceCompact }) {
274
+ const { control, getValues, setValue, unregister } = useFormContext();
275
+ const { workflowForm } = useContext(FormOptionsContext);
276
+ const editing = useFormEditing();
277
+ const parse = useFieldParse(optionsField, nested ? arrayIndex : void 0);
278
+ const index = nested ? arrayIndex ?? rowIndexOf(fieldNamePrefix) : void 0;
279
+ const instance = fieldNamePrefix + fieldName;
280
+ const name = fieldPath(instance);
281
+ const schemaOf = (values) => asSchemaField(nested ? parse(values) : optionsField);
282
+ useWatch({
283
+ control,
284
+ compute: (values) => {
285
+ const field = schemaOf(values);
286
+ const reads = readField(field, values, index, workflowForm);
287
+ return [
288
+ field,
289
+ JSON.stringify(reads),
290
+ reads.clears && getValueUsingPath(values, name) !== void 0
291
+ ];
292
+ }
293
+ });
294
+ const { errors } = useFormState({
295
+ control,
296
+ name,
297
+ exact: true
298
+ });
299
+ const values = getValues();
300
+ const field = schemaOf(values);
301
+ const reads = readField(field, values, index, workflowForm);
302
+ const holdsValue = reads.clears && getValueUsingPath(values, name) !== void 0;
268
303
  useEffect(() => {
269
- if (prevParent && parentValue && prevParent !== parentValue) setFieldValue(name, []);
304
+ if (holdsValue) unregister(name);
270
305
  }, [
271
- parentValue,
272
- prevParent,
306
+ holdsValue,
273
307
  name,
274
- setFieldValue
308
+ unregister
275
309
  ]);
276
- return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(CustomListbox, {
277
- ...controlProps,
278
- options,
279
- required,
280
- type: label.toLowerCase(),
281
- handleOptionSelected,
282
- className: "mb-3",
283
- invalid,
284
- closeOnSelect: false
285
- }), /* @__PURE__ */ jsx(Table, { children: field.value && field.value.length > 0 ? options.filter((option) => option.selected).map((option) => {
286
- return /* @__PURE__ */ jsxs("tr", { children: [/* @__PURE__ */ jsx(Table.Item, {
287
- className: "w-full",
288
- children: option.label
289
- }), /* @__PURE__ */ jsx(Table.Item, { children: /* @__PURE__ */ jsx("button", {
290
- type: "button",
291
- "aria-label": t.remove,
292
- className: "inline-flex bg-transparent p-0 cursor-pointer",
293
- onClick: () => handleRemoveOption(option.value),
294
- children: /* @__PURE__ */ jsx(BiSolidTrashAlt, { className: "link mr-4" })
295
- }) })] }, String(option.value));
296
- }) : /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsxs(Table.Item, { children: [
297
- "No ",
298
- label !== "" ? label.toLowerCase() : "options",
299
- " selected"
300
- ] }) }) })] });
301
- }
302
- var InputField = React.memo(function InputField({ label, field, setFormDirty, onChange, disabled = false, labelPosition = "left", missingFields = [], computeOn, spaceCompact = false, workflowForm = false, setFieldValue, setFieldTouched, values, currentValue }) {
303
- const prevField = useRef(field);
304
- const fieldObj = useMemo(() => {
305
- if (!deepEqual(prevField.current, field)) prevField.current = field;
306
- return prevField.current;
307
- }, [field]);
308
- const tooltipComponent = useMemo(() => {
309
- if (!fieldObj.tooltip) return null;
310
- return /* @__PURE__ */ jsx(TooltipInfo, {
311
- className: "pr-2",
312
- place: "top",
313
- text: typeof fieldObj.tooltip === "string" ? fieldObj.tooltip.replace(/\r/g, "") : fieldObj.tooltip.filter(Boolean).join("\n")
314
- });
315
- }, [fieldObj.tooltip]);
316
- const missing = missingFields.includes(fieldObj.name ?? "");
317
- return /* @__PURE__ */ jsx(Registry, {
318
- field: fieldObj,
319
- label,
320
- labelPosition,
321
- missing,
322
- missingFields,
323
- values,
324
- setFormDirty,
325
- onChange,
326
- disabled,
327
- computeOn,
328
- spaceCompact,
329
- tooltipComponent,
330
- workflowForm,
331
- setFieldTouched,
332
- setFieldValue,
333
- currentValue
334
- });
335
- }, (prev, next) => {
336
- if (!deepEqual(prev.field, next.field)) return false;
337
- for (const key of Object.keys(prev)) {
338
- if (key === "field" || key === "values") continue;
339
- if (prev[key] !== next[key]) return false;
340
- }
341
- return true;
342
- });
343
- function FormikStateBridge({ onChange }) {
344
- const { values } = useFormikContext();
345
- const prevEmitted = useRef(null);
346
- useEffect(() => {
347
- const { editorKeyPostfix: _, ...next } = values;
348
- if (prevEmitted.current !== null && deepEqual(prevEmitted.current, next)) return;
349
- prevEmitted.current = next;
350
- onChange(next);
351
- }, [values, onChange]);
352
- return null;
353
- }
354
- function DirtyStateBridge({ setFormDirty }) {
355
- const { dirty } = useFormikContext();
310
+ const resetFrom = useRef(reads.resetBy);
356
311
  useEffect(() => {
357
- setFormDirty(dirty);
358
- }, [dirty, setFormDirty]);
359
- return null;
360
- }
361
- /** Three-tier cache: no expressions → raw pass-through; input-dep expressions → re-parse on dep change; org-var/path expressions → parse once. */
362
- function useFieldParse(optionsField, values, arrayIndex) {
363
- const engine = useWorkflowEngine();
364
- const c = useRef({
365
- lastOptField: null,
366
- convertedField: null,
367
- inputDeps: /* @__PURE__ */ new Set(),
368
- hasExpressions: false,
369
- lastRelevant: {},
370
- lastIndex: void 0,
371
- result: null
372
- }).current;
373
- if (optionsField !== c.lastOptField) {
374
- c.lastOptField = optionsField;
375
- c.convertedField = engine.convertInputs(asRecord(optionsField));
376
- const { inputDeps, hasExpressions } = engine.inputDependencies(c.convertedField);
377
- c.inputDeps = inputDeps;
378
- c.hasExpressions = hasExpressions;
379
- c.result = null;
380
- }
381
- if (!c.hasExpressions) return optionsField;
382
- if (c.result !== null) {
383
- if (c.inputDeps.size === 0 || arrayIndex === c.lastIndex && [...c.inputDeps].every((key) => deepEqual(values[key], c.lastRelevant[key]))) return c.result;
384
- }
385
- c.result = engine.evaluate({
386
- inputs: values,
387
- obj: c.convertedField,
388
- orgVars: {},
389
- index: arrayIndex
390
- });
391
- c.lastIndex = arrayIndex;
392
- c.lastRelevant = {};
393
- for (const key of c.inputDeps) c.lastRelevant[key] = values[key];
394
- return c.result;
395
- }
396
- var FormField = React.memo(function FormField({ optionsField, fieldName, values, setFormDirty, setFieldValue, setFieldTouched, parentInfo, labelPosition = "left", missingFields = [], spaceCompact = false, workflowForm = false }) {
397
- const parsedField = useFieldParse(optionsField, values, parentInfo?.arrayIndex);
398
- const editing = useFormEditing();
312
+ if (resetFrom.current !== void 0 && resetFrom.current !== reads.resetBy) setValue(name, void 0);
313
+ resetFrom.current = reads.resetBy;
314
+ }, [
315
+ reads.resetBy,
316
+ name,
317
+ setValue
318
+ ]);
319
+ const tooltip = field?.tooltip;
320
+ const tooltipComponent = useMemo(() => tooltip ? /* @__PURE__ */ jsx(InfoTip, {
321
+ className: "pr-2",
322
+ text: typeof tooltip === "string" ? tooltip.replace(/\r/g, "") : tooltip.filter(Boolean).join("\n")
323
+ }) : null, [tooltip]);
399
324
  const path = editing ? [...editing.parent, fieldName] : null;
400
- const field = asSchemaField(parentInfo ? parsedField : optionsField);
401
- if (field === null) return null;
402
- const fieldNamePrefix = parentInfo?.fieldNamePrefix || "";
403
- const fieldObj = {
404
- ...field,
405
- label: field.label || fieldName.charAt(0).toUpperCase() + fieldName.slice(1),
406
- name: fieldNamePrefix + fieldName,
407
- optional: resolvedFlag(field.optional) ?? field.default !== void 0
408
- };
409
- if (!fieldObj.name || fieldName === "v3") return null;
410
- if (field.hidden === true || !field.type) {
411
- if (resolvedFlag(field.ignore) && !workflowForm) deleteFieldByPath(values, fieldObj.name);
412
- return editing && path && fieldName !== "$meta" ? /* @__PURE__ */ jsx(editing.Row, {
413
- path,
414
- instance: fieldObj.name,
415
- hidden: true
416
- }) : null;
417
- }
418
- let onChange;
419
- if (field.one_must_be_true && parentInfo?.onChange) {
420
- const parentOnChange = parentInfo.onChange;
421
- const parentArrayIndex = parentInfo.arrayIndex ?? 0;
422
- const fieldOptions = fieldObj.options;
423
- onChange = (val) => parentOnChange(fieldName, parentArrayIndex, flagValue(fieldOptions?.["offOption"], false), flagValue(fieldOptions?.["onOption"], true), val);
424
- }
425
- if (field.show_if !== void 0 || field.show_if_not !== void 0) {
426
- const index = parentInfo ? parentInfo.arrayIndex || getClosestParentIndex(parentInfo.fieldNamePrefix) : void 0;
427
- let shouldShowField = true;
428
- if (field.show_if !== void 0) shouldShowField = !!getParentValue(index, field.show_if, values, field);
429
- if (shouldShowField && field.show_if_not !== void 0) shouldShowField = !getParentValue(index, field.show_if_not, values, field);
430
- if (!shouldShowField) {
431
- if (!workflowForm) deleteFieldByPath(values, fieldObj.name);
432
- return editing && path ? /* @__PURE__ */ jsx(editing.Row, {
433
- path,
434
- instance: fieldObj.name,
435
- hidden: true
436
- }) : null;
437
- }
438
- }
439
- if (field.depends_on && parentInfo?.arrayIndex !== void 0) fieldObj.depends_on = field.depends_on.replace("[index]", `[${parentInfo.arrayIndex}]`);
440
- if (field.enable_if && field.disabled === false) {
441
- if (typeof field.enable_if === "string") {
442
- let path = field.enable_if;
443
- if (parentInfo?.arrayIndex !== void 0) path = field.enable_if.replace("[index]", `[${parentInfo.arrayIndex}]`);
444
- if (getValueUsingPath(values, path) === false) fieldObj.disabled = true;
445
- }
446
- }
325
+ if (field === null || fieldName === "v3") return null;
326
+ if (reads.hidden) return editing && path && fieldName !== "$meta" ? /* @__PURE__ */ jsx(editing.Row, {
327
+ path,
328
+ instance,
329
+ hidden: true
330
+ }) : null;
331
+ const parentInfo = nested ? {
332
+ fieldNamePrefix,
333
+ arrayIndex,
334
+ computeOn,
335
+ onChange: onRowChange
336
+ } : void 0;
447
337
  if (field.type === "group") {
448
- const hideHeader = field.noCollapse;
449
- const HeaderElement = hideHeader ? React.Fragment : UncontrolledCollapsiblePanel;
450
338
  const meta = resolveMetaOverrides(field.options, labelPosition, spaceCompact);
451
- const group = /* @__PURE__ */ jsx("div", {
452
- className: "flex flex-col w-full",
453
- children: /* @__PURE__ */ jsx(HeaderElement, {
454
- ...!hideHeader && {
455
- title: (open, setOpen) => /* @__PURE__ */ jsx(GroupHeader, {
456
- open,
457
- setOpen,
458
- title: field.label
459
- }),
460
- initialState: field.collapsed !== void 0 ? !field.collapsed : true
461
- },
462
- children: /* @__PURE__ */ jsx("div", {
463
- className: "w-full",
464
- children: /* @__PURE__ */ jsx(EditingScope, {
465
- editing,
466
- path,
467
- children: /* @__PURE__ */ jsx(FieldsFromOptions, {
468
- options: field.options ?? {},
469
- setFormDirty,
470
- setFieldValue,
471
- setFieldTouched,
472
- values,
473
- parentInfo,
474
- workflowForm,
475
- labelPosition: meta.labelPosition,
476
- spaceCompact: meta.spaceCompact,
477
- missingFields
478
- })
479
- })
339
+ const group = /* @__PURE__ */ jsx(FieldGroup, {
340
+ title: field.label,
341
+ collapsible: !field.noCollapse,
342
+ defaultOpen: !field.collapsed,
343
+ children: /* @__PURE__ */ jsx(EditingScope, {
344
+ editing,
345
+ path,
346
+ children: /* @__PURE__ */ jsx(FieldsFromOptions, {
347
+ options: field.options ?? {},
348
+ parentInfo,
349
+ labelPosition: meta.labelPosition,
350
+ spaceCompact: meta.spaceCompact
480
351
  })
481
352
  })
482
353
  });
483
354
  return editing && path ? /* @__PURE__ */ jsx(editing.Row, {
484
355
  path,
485
- instance: fieldObj.name,
356
+ instance,
486
357
  children: group
487
358
  }) : group;
488
359
  }
360
+ const fieldObj = {
361
+ ...field,
362
+ label: field.label || fieldName.charAt(0).toUpperCase() + fieldName.slice(1),
363
+ name,
364
+ optional: resolvedFlag(field.optional) ?? field.default !== void 0
365
+ };
366
+ if (field.depends_on) fieldObj.depends_on = fieldPath(atIndex(field.depends_on, index));
367
+ if (reads.disabledByInput) fieldObj.disabled = true;
489
368
  if (typeof field.type === "object" && field.depends_on) {
490
- const parentValue = getValueUsingPath(values, field.depends_on);
491
- const resolvedType = field.type[String(parentValue)];
369
+ const resolvedType = field.type[String(reads.parentValue)];
492
370
  if (!resolvedType) return null;
493
371
  fieldObj.type = resolvedType;
494
372
  }
495
373
  if (field.type === "range" && field.depends_on) {
496
- const parentValue = getValueUsingPath(values, field.depends_on);
497
374
  const { min, max } = field;
498
375
  if (typeof min === "object" && typeof max === "object") {
499
- fieldObj.min = min[String(parentValue)];
500
- fieldObj.max = max[String(parentValue)];
376
+ fieldObj.min = min[String(reads.parentValue)];
377
+ fieldObj.max = max[String(reads.parentValue)];
501
378
  }
502
379
  }
503
- if (field.resetOnChange) {
504
- const parentValue = getValueUsingPath(values, field.resetOnChange);
505
- const prevParentValue = field.prevParentValue;
506
- if (prevParentValue !== void 0 && prevParentValue !== parentValue) deleteFieldByPath(values, fieldObj.name);
507
- field.prevParentValue = parentValue;
508
- }
509
- if (field.secondaryField) {
510
- if (Array.isArray(field.secondaryField)) fieldObj.secondaryField = field.secondaryField.map((secondaryField) => fieldNamePrefix + secondaryField);
511
- else fieldObj.secondaryField = fieldNamePrefix + field.secondaryField;
380
+ if (field.secondaryField) fieldObj.secondaryField = Array.isArray(field.secondaryField) ? field.secondaryField.map((secondary) => fieldPath(fieldNamePrefix + secondary)) : fieldPath(fieldNamePrefix + field.secondaryField);
381
+ let onChange;
382
+ if (field.one_must_be_true && onRowChange) {
383
+ const fieldOptions = fieldObj.options;
384
+ onChange = (val) => onRowChange(fieldName, arrayIndex ?? 0, flagValue(fieldOptions?.["offOption"], false), flagValue(fieldOptions?.["onOption"], true), val);
512
385
  }
513
386
  const input = /* @__PURE__ */ jsx("div", {
514
- className: cx("flex w-full", labelPosition === "left" ? "mb-[15px]" : "mb-[5px]"),
515
- children: /* @__PURE__ */ jsx(InputField, {
387
+ className: clsx("flex w-full", labelPosition === "left" ? "mb-[15px]" : "mb-[5px]"),
388
+ children: /* @__PURE__ */ jsx(Registry, {
516
389
  field: fieldObj,
517
- label: fieldObj.label,
518
- setFormDirty,
519
- setFieldValue,
520
- setFieldTouched,
521
- values,
522
- currentValue: getValueUsingPath(values, fieldObj.name),
390
+ label: fieldObj.label ?? "",
391
+ labelPosition,
392
+ missing: get(errors, name) !== void 0,
523
393
  onChange,
524
394
  disabled: Boolean(fieldObj.disabled),
525
- computeOn: parentInfo?.computeOn || fieldObj.computeOn,
526
- labelPosition,
527
- missingFields,
395
+ computeOn: computeOn || fieldObj.computeOn,
528
396
  spaceCompact,
529
- workflowForm
397
+ tooltipComponent
530
398
  })
531
399
  });
532
400
  if (!editing || !path) return input;
533
401
  const holds = fieldObj.type === "object" || fieldObj.type === "list";
534
402
  return /* @__PURE__ */ jsx(editing.Row, {
535
403
  path,
536
- instance: fieldObj.name,
404
+ instance,
537
405
  children: /* @__PURE__ */ jsx(EditingScope, {
538
406
  editing,
539
407
  path: holds ? path : null,
540
408
  children: input
541
409
  })
542
410
  });
543
- }, (prev, next) => {
544
- if (!deepEqual(prev.optionsField, next.optionsField)) return false;
545
- if (prev.values !== next.values) return false;
546
- if (!deepEqual(prev.parentInfo, next.parentInfo)) return false;
547
- if (!deepEqual(prev.missingFields, next.missingFields)) return false;
548
- if (prev.labelPosition !== next.labelPosition) return false;
549
- if (prev.spaceCompact !== next.spaceCompact) return false;
550
- if (prev.workflowForm !== next.workflowForm) return false;
551
- return true;
552
411
  });
553
- function FieldsFromOptions({ options = {}, values, setFormDirty, setFieldValue, setFieldTouched, parentInfo, labelPosition = "left", missingFields = [], spaceCompact = false, workflowForm = false }) {
412
+ /** Renders a schema's fields. Inside another field, `parentInfo` says where their values sit. */
413
+ function FieldsFromOptions({ options = {}, parentInfo, labelPosition = "left", spaceCompact = false }) {
554
414
  const editing = useFormEditing();
555
415
  const chosen = useChosenLabelPosition(options);
556
416
  const wizard = parseWizardConfig(options);
@@ -558,16 +418,11 @@ function FieldsFromOptions({ options = {}, values, setFormDirty, setFieldValue,
558
418
  value: chosen,
559
419
  children: /* @__PURE__ */ jsx(WizardContainer, {
560
420
  wizardConfig: wizard,
561
- values,
562
421
  className: "w-full",
563
422
  labelPosition,
564
- missingFields,
565
423
  spaceCompact,
566
- workflowForm,
567
424
  nested: true,
568
- fieldNamePrefix: parentInfo?.fieldNamePrefix,
569
- setFieldValue,
570
- setFieldTouched
425
+ fieldNamePrefix: parentInfo?.fieldNamePrefix
571
426
  })
572
427
  });
573
428
  const names = Object.keys(options);
@@ -579,15 +434,13 @@ function FieldsFromOptions({ options = {}, values, setFormDirty, setFieldValue,
579
434
  const fieldOf = (fieldName, compactLabels = false) => /* @__PURE__ */ jsx(FormField, {
580
435
  optionsField: options[fieldName],
581
436
  fieldName,
582
- values,
583
- setFormDirty,
584
- setFieldValue,
585
- setFieldTouched,
586
- parentInfo,
437
+ nested: parentInfo !== void 0,
438
+ fieldNamePrefix: parentInfo?.fieldNamePrefix ?? "",
439
+ arrayIndex: parentInfo?.arrayIndex,
440
+ computeOn: parentInfo?.computeOn,
441
+ onRowChange: parentInfo?.onChange,
587
442
  labelPosition: compactLabels ? chosen ?? "top" : labelPosition,
588
- missingFields,
589
- spaceCompact,
590
- workflowForm
443
+ spaceCompact
591
444
  }, fieldName);
592
445
  const add = editing ? /* @__PURE__ */ jsx(editing.Add, { parent: editing.parent }) : null;
593
446
  const layout = resolveFormLayout(asRecord(options["$meta"])["layout"], names.filter((name) => !name.startsWith("$") && asSchemaField(options[name])));
@@ -629,10 +482,9 @@ function FieldsFromOptions({ options = {}, values, setFormDirty, setFieldValue,
629
482
  className: "order-2 w-full px-2",
630
483
  children: add
631
484
  })]
632
- }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [names.map((name) => fieldOf(name, void 0)), add] })
485
+ }) : /* @__PURE__ */ jsxs(Fragment, { children: [names.map((name) => fieldOf(name, void 0)), add] })
633
486
  });
634
487
  }
635
- var noop = () => {};
636
488
  function repeatsPage(field) {
637
489
  return field.type === "step" && field.multi === true;
638
490
  }
@@ -661,95 +513,137 @@ function widthVars(width, wide) {
661
513
  "--input-narrow": width?.endsWith("px") ? width : "100%"
662
514
  };
663
515
  }
664
- function DynamicFormContent({ options, setValues, setFormDirty, labelPosition, missingFields, spaceCompact, organizationVariables, remoteVars, workflowForm, onSubmit, submitLabel, values, contextKey }) {
665
- const { setFieldValue, setFieldTouched } = useFormikContext();
666
- const opts = useParsedOpts(options, values, organizationVariables, void 0, void 0, remoteVars);
667
- const handleChange = useCallback((state) => setValues?.(state), [setValues]);
516
+ /** The form's schema parsed against its values. The form renders again only when an input the schema
517
+ * reads changes, and a field the change leaves alone keeps its identity, so it doesn't render. */
518
+ function useSchema(options, organizationVariables, remoteVars) {
519
+ const engine = useWorkflowEngine();
520
+ const { control, getValues } = useFormContext();
521
+ const reads = useMemo(() => [...engine.inputDependencies(engine.convertInputs(options)).inputDeps], [engine, options]);
522
+ useWatch({
523
+ control,
524
+ compute: (values) => JSON.stringify(reads.map((key) => values[key]))
525
+ });
526
+ return useParsedOpts(options, getValues(), organizationVariables, void 0, void 0, remoteVars);
527
+ }
528
+ function DynamicFormContent({ options, labelPosition, spaceCompact, organizationVariables, remoteVars, onSubmit, submitLabel, contextKey }) {
529
+ const { workflowForm } = useContext(FormOptionsContext);
530
+ const opts = useSchema(options, organizationVariables, remoteVars);
668
531
  const wizardConfig = parseWizardConfig(opts);
669
532
  const meta = resolveMetaOverrides(options, labelPosition, spaceCompact);
670
533
  const chosen = useChosenLabelPosition(options);
671
534
  return /* @__PURE__ */ jsxs(ChosenLabelPosition.Provider, {
672
535
  value: chosen,
673
- children: [
674
- /* @__PURE__ */ jsx(DirtyStateBridge, { setFormDirty }),
675
- /* @__PURE__ */ jsx(FormikStateBridge, { onChange: handleChange }),
676
- /* @__PURE__ */ jsx(DynamicDefaultsSync, { options: opts }, contextKey ?? ""),
677
- wizardConfig ? /* @__PURE__ */ jsx(WizardContainer, {
678
- wizardConfig,
679
- values,
680
- onChange: handleChange,
681
- onSubmit,
682
- className: "w-full",
683
- ...meta,
684
- missingFields,
685
- workflowForm,
686
- setFieldValue,
687
- setFieldTouched
688
- }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(FieldsFromOptions, {
689
- ...meta,
690
- options: opts,
691
- values,
692
- setFormDirty,
693
- setFieldValue,
694
- setFieldTouched,
695
- missingFields,
696
- workflowForm
697
- }), onSubmit !== void 0 && !workflowForm && /* @__PURE__ */ jsx("div", {
698
- className: "mt-4",
699
- children: /* @__PURE__ */ jsx("input", {
700
- type: "submit",
701
- className: "btn btn-info",
702
- value: submitLabel
703
- })
704
- })] })
705
- ]
536
+ children: [/* @__PURE__ */ jsx(DynamicDefaultsSync, { options: opts }, contextKey ?? ""), wizardConfig ? /* @__PURE__ */ jsx(WizardContainer, {
537
+ wizardConfig,
538
+ onSubmit,
539
+ className: "w-full",
540
+ ...meta
541
+ }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(FieldsFromOptions, {
542
+ ...meta,
543
+ options: opts
544
+ }), onSubmit !== void 0 && !workflowForm && /* @__PURE__ */ jsx("div", {
545
+ className: "mt-4",
546
+ children: /* @__PURE__ */ jsx("input", {
547
+ type: "submit",
548
+ className: "btn btn-info",
549
+ value: submitLabel
550
+ })
551
+ })] })]
706
552
  });
707
553
  }
708
554
  var NO_FIELDS = {};
709
- function DynamicForm({ formJSONs: options = {}, reinitialize = false, initialValues, setValues, setFormDirty = noop, formikRef, labelPosition = "left", missingFields = [], spaceCompact = false, organizationVariables, remoteVars, needsOrganizationVariables = false, onSubmit, submitLabel = "Submit", className = "w-full", workflowForm = false, contextKey, skipValueParse = false, fields = NO_FIELDS, allowLayoutCSS = false }) {
555
+ var NO_MISSING = [];
556
+ /** A form drawn from a workflow's schema. */
557
+ function DynamicForm({ form, ...props }) {
558
+ return form ? /* @__PURE__ */ jsx(DynamicFormBody, {
559
+ ...props,
560
+ form
561
+ }) : /* @__PURE__ */ jsx(OwnForm, { ...props });
562
+ }
563
+ function OwnForm(props) {
564
+ const form = useForm();
565
+ return /* @__PURE__ */ jsx(DynamicFormBody, {
566
+ ...props,
567
+ form
568
+ });
569
+ }
570
+ function DynamicFormBody({ form, formJSONs: options = {}, reinitialize = false, initialValues, onValuesChange, labelPosition = "left", missingFields = NO_MISSING, spaceCompact = false, organizationVariables, remoteVars, needsOrganizationVariables = false, onSubmit, submitLabel = "Submit", className = "w-full", workflowForm = false, contextKey, skipValueParse = false, fields = NO_FIELDS, allowLayoutCSS = false }) {
710
571
  const engine = useWorkflowEngine();
711
572
  const [parseReady, setParseReady] = useState(() => engine.isReady());
712
573
  useEffect(() => {
713
574
  engine.init();
714
575
  return engine.onReady(setParseReady);
715
576
  }, [engine]);
716
- if (!parseReady) return /* @__PURE__ */ jsx(Loader, {});
717
- const parsedInitVals = skipValueParse ? initialValues : initialValues && engine.evaluate({
577
+ const ready = parseReady && (!needsOrganizationVariables || organizationVariables !== void 0);
578
+ const parsed = useMemo(() => !ready || !initialValues ? void 0 : skipValueParse ? initialValues : engine.evaluate({
718
579
  inputs: initialValues,
719
580
  obj: initialValues,
720
581
  orgVars: organizationVariables,
721
582
  remoteVars
583
+ }), [
584
+ ready,
585
+ initialValues,
586
+ skipValueParse,
587
+ engine,
588
+ organizationVariables,
589
+ remoteVars
590
+ ]);
591
+ const load = parsed === void 0 ? void 0 : reinitialize ? JSON.stringify(parsed) : "";
592
+ const [loaded, setLoaded] = useState();
593
+ useLayoutEffect(() => {
594
+ if (load !== void 0 && load !== loaded) {
595
+ form.reset(parsed);
596
+ setLoaded(load);
597
+ }
598
+ }, [
599
+ form,
600
+ load,
601
+ loaded,
602
+ parsed
603
+ ]);
604
+ const report = useEffectEvent((values) => {
605
+ const { editorKeyPostfix: _, ...rest } = values;
606
+ onValuesChange?.(rest);
722
607
  });
608
+ useEffect(() => form.subscribe({
609
+ formState: { values: true },
610
+ callback: ({ values }) => report(values)
611
+ }), [form]);
612
+ const formOptions = useMemo(() => ({ workflowForm }), [workflowForm]);
613
+ const missing = missingFields.join("\n");
614
+ useEffect(() => {
615
+ if (!missing || loaded === void 0) return;
616
+ const names = missing.split("\n").map(fieldPath);
617
+ for (const name of names) form.setError(name, { type: "required" });
618
+ return () => form.clearErrors(names);
619
+ }, [
620
+ form,
621
+ missing,
622
+ loaded
623
+ ]);
723
624
  return /* @__PURE__ */ jsx(FieldRegistryContext.Provider, {
724
625
  value: fields,
725
626
  children: /* @__PURE__ */ jsx(LayoutCSSContext.Provider, {
726
627
  value: allowLayoutCSS,
727
- children: /* @__PURE__ */ jsx("div", {
728
- className,
729
- children: (!needsOrganizationVariables || organizationVariables) && initialValues && /* @__PURE__ */ jsx(Formik, {
730
- enableReinitialize: reinitialize,
731
- initialValues: parsedInitVals,
732
- onSubmit: onSubmit ?? (() => {}),
733
- onReset: () => {
734
- setFormDirty(false);
735
- },
736
- ...formikRef ? { innerRef: formikRef } : {},
737
- children: ({ values }) => /* @__PURE__ */ jsx(Form, {
738
- className: "w-full text-[13px]",
739
- children: /* @__PURE__ */ jsx(DynamicFormContent, {
740
- values,
741
- options,
742
- setValues,
743
- setFormDirty,
744
- labelPosition,
745
- missingFields,
746
- spaceCompact,
747
- organizationVariables,
748
- remoteVars,
749
- workflowForm,
750
- onSubmit,
751
- submitLabel,
752
- contextKey
628
+ children: /* @__PURE__ */ jsx(FormOptionsContext.Provider, {
629
+ value: formOptions,
630
+ children: /* @__PURE__ */ jsx("div", {
631
+ className,
632
+ children: !parseReady ? /* @__PURE__ */ jsx(Loader, {}) : loaded !== void 0 && /* @__PURE__ */ jsx(FormProvider, {
633
+ ...form,
634
+ children: /* @__PURE__ */ jsx("form", {
635
+ className: "w-full text-[13px]",
636
+ onSubmit: form.handleSubmit((values) => onSubmit?.(values)),
637
+ children: /* @__PURE__ */ jsx(DynamicFormContent, {
638
+ options,
639
+ labelPosition,
640
+ spaceCompact,
641
+ organizationVariables,
642
+ remoteVars,
643
+ onSubmit,
644
+ submitLabel,
645
+ contextKey
646
+ })
753
647
  })
754
648
  })
755
649
  })
@@ -758,4 +652,4 @@ function DynamicForm({ formJSONs: options = {}, reinitialize = false, initialVal
758
652
  });
759
653
  }
760
654
  //#endregion
761
- export { DynamicDefaultsSync, DynamicForm, FieldsFromOptions, GroupHeader, MultiSelectionDropdown, collectFieldsWithDefaults, defaultReferencesField, findSelfReferencingFieldNames, listLengthsSignature, resolveMetaOverrides, useDynamicDefaultsSync };
655
+ export { DynamicDefaultsSync, DynamicForm, FieldsFromOptions, collectFieldsWithDefaults, defaultReferencesField, findSelfReferencingFieldNames, listLengthsSignature, resolveMetaOverrides, useDynamicDefaultsSync };