@parallelworks/ui 0.20.0 → 0.21.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 (154) hide show
  1. package/dist/ai/adapter/openai/createOpenAIStream.js +3 -2
  2. package/dist/ai/agent/diff.d.ts +0 -1
  3. package/dist/ai/agent/diff.js +3 -2
  4. package/dist/ai/agent/toolfmt.js +2 -3
  5. package/dist/ai/ai.css +5 -0
  6. package/dist/ai/components/AllocationSelector.js +3 -2
  7. package/dist/ai/components/AttachmentManager.js +1 -1
  8. package/dist/ai/components/BranchNavigator.js +4 -2
  9. package/dist/ai/components/ChatInput.d.ts +2 -5
  10. package/dist/ai/components/ChatInput.js +10 -7
  11. package/dist/ai/components/ChatMessage.js +7 -7
  12. package/dist/ai/components/ChatSidebar.d.ts +0 -1
  13. package/dist/ai/components/ChatSidebar.js +4 -6
  14. package/dist/ai/components/ChatThread.js +5 -8
  15. package/dist/ai/components/ModelSelector.js +3 -2
  16. package/dist/ai/components/ShareDialog.js +6 -3
  17. package/dist/ai/components/SlashMenu.d.ts +0 -1
  18. package/dist/ai/components/agent/ActivityLine.d.ts +0 -1
  19. package/dist/ai/components/agent/ActivityLine.js +2 -2
  20. package/dist/ai/components/agent/AgentMessageParts.d.ts +1 -26
  21. package/dist/ai/components/agent/AgentMessageParts.js +4 -5
  22. package/dist/ai/core/ChatProvider.d.ts +2 -6
  23. package/dist/ai/core/ChatProvider.js +42 -67
  24. package/dist/ai/core/KeyboardShortcutsProvider.js +1 -1
  25. package/dist/ai/core/chatReducer.d.ts +0 -6
  26. package/dist/ai/core/chatReducer.js +1 -15
  27. package/dist/ai/core/useKeyboardShortcuts.d.ts +3 -1
  28. package/dist/ai/core/useKeyboardShortcuts.js +9 -11
  29. package/dist/ai/core/useSSEStream.d.ts +0 -1
  30. package/dist/ai/core/useSSEStream.js +5 -17
  31. package/dist/ai/strings.d.ts +0 -1
  32. package/dist/ai/strings.js +0 -1
  33. package/dist/ai/types.d.ts +1 -1
  34. package/dist/ai/ui/Avatar.d.ts +1 -2
  35. package/dist/ai/ui/Avatar.js +7 -18
  36. package/dist/ai/ui/Markdown.d.ts +1 -3
  37. package/dist/ai/ui/Markdown.js +1 -2
  38. package/dist/ai/ui/sidebar.d.ts +0 -1
  39. package/dist/ai/ui/sidebar.js +3 -1
  40. package/dist/ai/utils.js +1 -1
  41. package/dist/components/Avatar.js +4 -2
  42. package/dist/components/BareModal.js +1 -5
  43. package/dist/components/Breadcrumbs.js +21 -14
  44. package/dist/components/CollapsiblePanel.d.ts +1 -3
  45. package/dist/components/CollapsiblePanel.js +5 -5
  46. package/dist/components/Dropdown.d.ts +0 -4
  47. package/dist/components/Dropdown.js +5 -6
  48. package/dist/components/Listbox.js +14 -18
  49. package/dist/components/MultiSelect.js +5 -8
  50. package/dist/components/Pagination.js +1 -1
  51. package/dist/components/Provider.d.ts +0 -8
  52. package/dist/components/SwitchToggle.js +3 -5
  53. package/dist/components/Table.d.ts +1 -3
  54. package/dist/components/dropdownUtils.js +5 -6
  55. package/dist/components/relativeTime.d.ts +0 -7
  56. package/dist/components/relativeTime.js +1 -1
  57. package/dist/duration.js +1 -1
  58. package/dist/editor/Editor.d.ts +1 -1
  59. package/dist/editor/Editor.js +1 -1
  60. package/dist/editor/EditorField.d.ts +0 -1
  61. package/dist/editor/EditorField.js +8 -8
  62. package/dist/editor/FormCodePanel.js +14 -20
  63. package/dist/editor/yamlLanguage.js +2 -2
  64. package/dist/file-explorer/FileExplorer.js +56 -59
  65. package/dist/file-explorer/FileExplorerContext.d.ts +1 -2
  66. package/dist/file-explorer/FileExplorerContext.js +92 -125
  67. package/dist/file-explorer/FilePreview/Inline.d.ts +1 -2
  68. package/dist/file-explorer/FilePreview/Inline.js +2 -2
  69. package/dist/file-explorer/FilePreview/Viewer.js +0 -4
  70. package/dist/file-explorer/FilePreview/index.d.ts +1 -2
  71. package/dist/file-explorer/FilePreview/index.js +3 -3
  72. package/dist/file-explorer/FilePreview/parse.d.ts +2 -1
  73. package/dist/file-explorer/FilePreview/useObjectBlobUrl.d.ts +5 -4
  74. package/dist/file-explorer/FilePreview/useObjectBlobUrl.js +23 -36
  75. package/dist/file-explorer/FilePreview/useObjectText.d.ts +1 -2
  76. package/dist/file-explorer/FilePreview/useObjectText.js +5 -7
  77. package/dist/file-explorer/FileRow.js +1 -1
  78. package/dist/file-explorer/FileRowsWindow.d.ts +5 -12
  79. package/dist/file-explorer/FileTable.d.ts +2 -6
  80. package/dist/file-explorer/LoadMoreRow.d.ts +8 -0
  81. package/dist/file-explorer/TreeView.d.ts +0 -1
  82. package/dist/file-explorer/TreeView.js +6 -6
  83. package/dist/file-explorer/helpers.js +2 -4
  84. package/dist/file-explorer/lib/fileExplorer.d.ts +0 -2
  85. package/dist/file-explorer/lib/types.d.ts +0 -1
  86. package/dist/file-explorer/lib/utils.js +15 -20
  87. package/dist/file-explorer/toasts.js +36 -34
  88. package/dist/form/Fields/BooleanField.js +5 -4
  89. package/dist/form/Fields/CheckboxGroupField.js +5 -4
  90. package/dist/form/Fields/ColorField.js +5 -4
  91. package/dist/form/Fields/DropdownField.d.ts +0 -1
  92. package/dist/form/Fields/DropdownField.js +10 -13
  93. package/dist/form/Fields/DurationField.js +2 -2
  94. package/dist/form/Fields/LabelField.js +5 -5
  95. package/dist/form/Fields/MultiDropdownField.d.ts +1 -1
  96. package/dist/form/Fields/MultiDropdownField.js +8 -11
  97. package/dist/form/Fields/NumberField.js +6 -5
  98. package/dist/form/Fields/ObjectField.d.ts +1 -2
  99. package/dist/form/Fields/ObjectField.js +22 -21
  100. package/dist/form/Fields/RadioField.js +4 -3
  101. package/dist/form/Fields/RangeField.js +7 -6
  102. package/dist/form/Fields/StringField.js +2 -1
  103. package/dist/form/Fields/TextAreaField.js +2 -1
  104. package/dist/form/Form.d.ts +7 -8
  105. package/dist/form/Form.js +52 -78
  106. package/dist/form/SpecialTypes/DropdownWrapper.d.ts +0 -2
  107. package/dist/form/SpecialTypes/DropdownWrapper.js +1 -3
  108. package/dist/form/SpecialTypes/FormikCustomDropdown.d.ts +1 -3
  109. package/dist/form/SpecialTypes/FormikCustomDropdown.js +4 -7
  110. package/dist/form/SpecialTypes/FormikCustomInput.js +13 -11
  111. package/dist/form/Wizard/WizardContainer.js +8 -25
  112. package/dist/form/Wizard/WizardNavigation.d.ts +1 -1
  113. package/dist/form/Wizard/WizardNavigation.js +5 -6
  114. package/dist/form/Wizard/WizardStepContent.d.ts +1 -1
  115. package/dist/form/Wizard/WizardStepContent.js +2 -2
  116. package/dist/form/Wizard/WizardStepIndicator.js +7 -17
  117. package/dist/form/Wizard/types.d.ts +4 -46
  118. package/dist/form/Wizard/useWizardState.d.ts +2 -10
  119. package/dist/form/Wizard/useWizardState.js +42 -92
  120. package/dist/form/Wizard/utils.js +6 -11
  121. package/dist/form/lib.js +28 -54
  122. package/dist/form/types/fieldTypes.d.ts +4 -5
  123. package/dist/form/utils/dependentOptions.d.ts +6 -0
  124. package/dist/form/utils/dependentOptions.js +21 -0
  125. package/dist/form/utils/getParentValue.d.ts +2 -1
  126. package/dist/form/utils/getParentValue.js +10 -15
  127. package/dist/form/utils/getValueUsingPath.js +1 -1
  128. package/dist/graph/AnnotationBanner.d.ts +1 -1
  129. package/dist/graph/AnnotationBanner.js +1 -1
  130. package/dist/graph/Collapse.d.ts +3 -3
  131. package/dist/graph/Collapse.js +3 -8
  132. package/dist/graph/DependencyGraph.d.ts +5 -4
  133. package/dist/graph/DependencyGraph.js +172 -222
  134. package/dist/graph/JobSummary.d.ts +10 -8
  135. package/dist/graph/JobSummary.js +13 -13
  136. package/dist/graph/MatrixGroup.d.ts +5 -17
  137. package/dist/graph/MatrixGroup.js +11 -22
  138. package/dist/graph/TreeView.d.ts +1 -3
  139. package/dist/graph/TreeView.js +104 -491
  140. package/dist/graph/types.d.ts +3 -2
  141. package/dist/graph/util.d.ts +5 -0
  142. package/dist/graph/util.js +18 -0
  143. package/dist/icons.d.ts +2 -2
  144. package/dist/icons.js +2 -2
  145. package/dist/index.d.ts +1 -1
  146. package/dist/list/ListViewControls.js +17 -35
  147. package/dist/list/RowContextMenu.js +7 -7
  148. package/dist/logviewer/LogViewer.js +7 -9
  149. package/dist/logviewer/segments.js +2 -2
  150. package/dist/styles/theme.css +1 -0
  151. package/dist/styles.css +1 -1
  152. package/dist/theme/index.js +5 -5
  153. package/package.json +28 -30
  154. package/dist/ai/ui/buttons.d.ts +0 -1
@@ -1,46 +1,36 @@
1
1
  import cx from "classnames";
2
- import { useMemo } from "react";
3
2
  import { jsx, jsxs } from "react/jsx-runtime";
4
3
  //#region src/form/Wizard/WizardStepIndicator.tsx
5
4
  function WizardStepIndicator({ stepOrder, currentStep, steps, visitedSteps, invalidSteps, onStepClick, allowJump = false, hideStepNumbers = false }) {
6
- const currentIndex = useMemo(() => stepOrder.indexOf(currentStep), [stepOrder, currentStep]);
5
+ const currentIndex = stepOrder.indexOf(currentStep);
7
6
  const getStepStatus = (stepKey, index) => {
8
7
  if (stepKey === currentStep) return "active";
9
8
  if (index < currentIndex) return "completed";
10
9
  return "upcoming";
11
10
  };
12
- const handleStepClick = (stepKey) => {
13
- if (!allowJump || !visitedSteps.has(stepKey)) return;
14
- onStepClick?.(stepKey);
15
- };
16
- const isClickable = (stepKey) => {
17
- return allowJump && visitedSteps.has(stepKey) && stepKey !== currentStep;
18
- };
19
11
  return /* @__PURE__ */ jsx("div", {
20
12
  className: "flex items-start justify-between w-full mb-2 gap-4 py-2",
21
13
  children: stepOrder.map((stepKey, index) => {
22
14
  const stepConfig = steps[stepKey];
23
15
  const status = getStepStatus(stepKey, index);
24
16
  const isInvalid = invalidSteps.has(stepKey);
25
- const isClickable_ = isClickable(stepKey);
17
+ const isClickable = allowJump && visitedSteps.has(stepKey) && stepKey !== currentStep;
26
18
  const isDotActive = status === "active";
27
19
  const isDotCompleted = status === "completed";
28
- const isDotUpcoming = status === "upcoming";
29
20
  return /* @__PURE__ */ jsxs("div", {
30
21
  className: "flex flex-col items-center flex-1 relative",
31
22
  children: [
32
23
  /* @__PURE__ */ jsxs("button", {
33
24
  type: "button",
34
- onClick: () => handleStepClick(stepKey),
35
- disabled: !isClickable_,
25
+ onClick: () => onStepClick(stepKey),
26
+ disabled: !isClickable,
36
27
  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", {
37
- "cursor-pointer": isClickable_,
38
- "cursor-not-allowed": !isClickable_
28
+ "cursor-pointer": isClickable,
29
+ "cursor-not-allowed": !isClickable
39
30
  }, {
40
31
  "text-white": isDotActive,
41
32
  "bg-green-500 border-green-600 text-white shadow-md": isDotCompleted && !isInvalid,
42
- "bg-(--theme-border) theme-border theme-muted-text shadow-sm": isDotUpcoming && !isInvalid,
43
- "bg-(--theme-muted-panel-bg) theme-border theme-muted-text shadow-sm": !isDotActive && !isDotCompleted && !isDotUpcoming && !isInvalid,
33
+ "bg-(--theme-border) theme-border theme-muted-text shadow-sm": status === "upcoming" && !isInvalid,
44
34
  "bg-red-50 border-red-500 text-red-600 shadow-sm": isInvalid && status !== "active"
45
35
  }),
46
36
  style: isDotActive ? {
@@ -17,8 +17,6 @@ export interface WizardConfig {
17
17
  navigation?: WizardNavigation;
18
18
  /** Label for the final submit button */
19
19
  submitLabel?: string;
20
- /** Use URL-based routing instead of component state */
21
- urlBased?: boolean;
22
20
  /** Flatten step fields into top-level inputs (default: true). Set false to preserve step keys as nested objects. */
23
21
  flatten?: boolean;
24
22
  }
@@ -33,39 +31,10 @@ export interface StepFieldConfig {
33
31
  options: DynamicFormSchema;
34
32
  /** Validate current step before proceeding to next (default: true) */
35
33
  validateOnNext?: boolean;
36
- /** Allow skipping this step without validation */
37
- canSkip?: boolean;
38
34
  /** Custom label for next button on this step */
39
35
  nextLabel?: string;
40
36
  /** Custom label for previous button on this step */
41
37
  prevLabel?: string;
42
- /** Optional tooltip text */
43
- tooltip?: string | string[];
44
- /** Hidden field support */
45
- hidden?: boolean;
46
- /** Disable this step */
47
- disabled?: boolean;
48
- }
49
- /**
50
- * Internal wizard state managed by useWizardState hook
51
- */
52
- export interface WizardState {
53
- /** Current step ID/key */
54
- currentStep: string;
55
- /** Set of step IDs that have been visited */
56
- visitedSteps: Set<string>;
57
- /** Set of step IDs that have validation errors */
58
- invalidSteps: Set<string>;
59
- /** List of all step keys in order */
60
- stepOrder: string[];
61
- /** Total number of steps */
62
- totalSteps: number;
63
- /** Whether the wizard is on the last step */
64
- isLastStep: boolean;
65
- /** Whether the wizard can proceed to next step */
66
- canProceedToNext: boolean;
67
- /** Whether user can go back */
68
- canGoBack: boolean;
69
38
  }
70
39
  /**
71
40
  * Extracted wizard configuration and step information
@@ -77,12 +46,7 @@ export interface ParsedWizardConfig {
77
46
  steps: Record<string, StepFieldConfig>;
78
47
  /** Ordered list of step keys */
79
48
  stepOrder: string[];
80
- /** Schema for non-step fields (should be none in pure wizard) */
81
- otherFields: Record<string, unknown>;
82
49
  }
83
- /**
84
- * Wizard navigation parameters
85
- */
86
50
  /**
87
51
  * Props for WizardContainer component
88
52
  */
@@ -120,8 +84,8 @@ export interface WizardStepIndicatorProps {
120
84
  visitedSteps: Set<string>;
121
85
  /** Set of invalid step keys */
122
86
  invalidSteps: Set<string>;
123
- /** Callback when step is clicked */
124
- onStepClick?: (stepKey: string) => boolean;
87
+ /** Callback when a step the user may jump to is clicked */
88
+ onStepClick: (stepKey: string) => boolean;
125
89
  /** Whether clicking completed steps is allowed */
126
90
  allowJump?: boolean | undefined;
127
91
  /** Hide step numbers */
@@ -131,12 +95,8 @@ export interface WizardStepIndicatorProps {
131
95
  * Props for WizardNavigation component
132
96
  */
133
97
  export interface WizardNavigationProps {
134
- /** Current step key */
135
- currentStep: string;
136
- /** List of step keys in order */
137
- stepOrder: string[];
138
- /** Whether user can go to next step */
139
- canGoToNext: boolean;
98
+ /** Whether the wizard is on its last step, which submits instead of going next */
99
+ isLastStep: boolean;
140
100
  /** Whether user can go back */
141
101
  canGoBack: boolean;
142
102
  /** Whether current step is valid */
@@ -164,8 +124,6 @@ export interface WizardStepContentProps {
164
124
  stepConfig: StepFieldConfig;
165
125
  /** Form values */
166
126
  values: Record<string, unknown>;
167
- /** Update form values */
168
- onValuesChange?: ((values: Record<string, unknown>) => void) | undefined;
169
127
  /** Label position for fields */
170
128
  labelPosition?: LabelPosition | undefined;
171
129
  /** Missing fields list */
@@ -1,4 +1,4 @@
1
- import type { StepFieldConfig, WizardState } from './types.js';
1
+ import type { StepFieldConfig } from './types.js';
2
2
  interface UseWizardStateProps {
3
3
  /** Ordered list of step keys */
4
4
  stepOrder: string[];
@@ -6,23 +6,15 @@ interface UseWizardStateProps {
6
6
  steps: Record<string, StepFieldConfig>;
7
7
  /** Function to validate current step */
8
8
  validateStep: (stepKey: string) => Promise<boolean>;
9
- /** Initial step to start with (default: first step) */
10
- initialStep?: string;
11
9
  }
12
- export declare function useWizardState({ stepOrder, steps, validateStep, initialStep, }: UseWizardStateProps): {
10
+ export declare function useWizardState({ stepOrder, steps, validateStep }: UseWizardStateProps): {
13
11
  currentStep: string;
14
12
  visitedSteps: Set<string>;
15
13
  invalidSteps: Set<string>;
16
14
  isLastStep: boolean;
17
15
  canGoBack: boolean;
18
- currentStepIndex: number;
19
- totalSteps: number;
20
16
  goToNext: () => Promise<boolean>;
21
17
  goToPrevious: () => void;
22
18
  jumpToStep: (stepKey: string) => boolean;
23
- reset: () => void;
24
- markStepVisited: (stepKey: string) => void;
25
- markStepInvalid: (stepKey: string, invalid: boolean) => void;
26
- getState: () => WizardState;
27
19
  };
28
20
  export {};
@@ -1,22 +1,13 @@
1
1
  import { useCallback, useState } from "react";
2
2
  //#region src/form/Wizard/useWizardState.ts
3
- function useWizardState({ stepOrder, steps, validateStep, initialStep }) {
4
- const firstStep = initialStep || stepOrder[0] || "";
3
+ function useWizardState({ stepOrder, steps, validateStep }) {
4
+ const firstStep = stepOrder[0] ?? "";
5
5
  const [currentStep, setCurrentStep] = useState(firstStep);
6
6
  const [visitedSteps, setVisitedSteps] = useState(/* @__PURE__ */ new Set([firstStep]));
7
7
  const [invalidSteps, setInvalidSteps] = useState(/* @__PURE__ */ new Set());
8
8
  const currentStepIndex = stepOrder.indexOf(currentStep);
9
9
  const isLastStep = currentStepIndex === stepOrder.length - 1;
10
10
  const canGoBack = currentStepIndex > 0;
11
- /**
12
- * Mark step as visited
13
- */
14
- const markStepVisited = useCallback((stepKey) => {
15
- setVisitedSteps((prev) => /* @__PURE__ */ new Set([...prev, stepKey]));
16
- }, []);
17
- /**
18
- * Mark step as invalid
19
- */
20
11
  const markStepInvalid = useCallback((stepKey, invalid) => {
21
12
  setInvalidSteps((prev) => {
22
13
  const next = new Set(prev);
@@ -25,93 +16,52 @@ function useWizardState({ stepOrder, steps, validateStep, initialStep }) {
25
16
  return next;
26
17
  });
27
18
  }, []);
28
- const goToNext = useCallback(async () => {
29
- if (isLastStep) return false;
30
- if (steps[currentStep]?.validateOnNext !== false) {
31
- if (!await validateStep(currentStep)) {
32
- markStepInvalid(currentStep, true);
33
- return false;
34
- }
35
- }
36
- markStepInvalid(currentStep, false);
37
- const nextStepKey = stepOrder[currentStepIndex + 1];
38
- setCurrentStep(nextStepKey ?? "");
39
- markStepVisited(nextStepKey ?? "");
40
- return true;
41
- }, [
42
- currentStep,
43
- currentStepIndex,
44
- isLastStep,
45
- steps,
46
- stepOrder,
47
- validateStep,
48
- markStepInvalid,
49
- markStepVisited
50
- ]);
51
- const goToPrevious = useCallback(() => {
52
- if (!canGoBack) return;
53
- const prevStepKey = stepOrder[currentStepIndex - 1];
54
- setCurrentStep(prevStepKey ?? "");
55
- }, [
56
- canGoBack,
57
- currentStepIndex,
58
- stepOrder
59
- ]);
60
- const jumpToStep = useCallback((stepKey) => {
61
- if (!stepOrder.includes(stepKey)) return false;
62
- if (!visitedSteps.has(stepKey) && stepKey !== currentStep) return false;
63
- setCurrentStep(stepKey);
64
- return true;
65
- }, [
66
- stepOrder,
67
- visitedSteps,
68
- currentStep
69
- ]);
70
- /**
71
- * Reset wizard to initial state
72
- */
73
- const reset = useCallback(() => {
74
- setCurrentStep(firstStep);
75
- setVisitedSteps(/* @__PURE__ */ new Set([firstStep]));
76
- setInvalidSteps(/* @__PURE__ */ new Set());
77
- }, [firstStep]);
78
- /**
79
- * Get current wizard state
80
- */
81
- const getState = useCallback(() => {
82
- return {
83
- currentStep,
84
- visitedSteps,
85
- invalidSteps,
86
- stepOrder,
87
- totalSteps: stepOrder.length,
88
- isLastStep,
89
- canProceedToNext: !isLastStep,
90
- canGoBack
91
- };
92
- }, [
93
- currentStep,
94
- visitedSteps,
95
- invalidSteps,
96
- stepOrder,
97
- isLastStep,
98
- canGoBack
99
- ]);
100
19
  return {
101
20
  currentStep,
102
21
  visitedSteps,
103
22
  invalidSteps,
104
23
  isLastStep,
105
24
  canGoBack,
106
- currentStepIndex,
107
- totalSteps: stepOrder.length,
108
- goToNext,
109
- goToPrevious,
110
- jumpToStep,
111
- reset,
112
- markStepVisited,
113
- markStepInvalid,
114
- getState
25
+ goToNext: useCallback(async () => {
26
+ if (isLastStep) return false;
27
+ if (steps[currentStep]?.validateOnNext !== false) {
28
+ if (!await validateStep(currentStep)) {
29
+ markStepInvalid(currentStep, true);
30
+ return false;
31
+ }
32
+ }
33
+ markStepInvalid(currentStep, false);
34
+ const nextStepKey = stepOrder[currentStepIndex + 1] ?? "";
35
+ setCurrentStep(nextStepKey);
36
+ setVisitedSteps((prev) => /* @__PURE__ */ new Set([...prev, nextStepKey]));
37
+ return true;
38
+ }, [
39
+ currentStep,
40
+ currentStepIndex,
41
+ isLastStep,
42
+ steps,
43
+ stepOrder,
44
+ validateStep,
45
+ markStepInvalid
46
+ ]),
47
+ goToPrevious: useCallback(() => {
48
+ if (!canGoBack) return;
49
+ setCurrentStep(stepOrder[currentStepIndex - 1] ?? "");
50
+ }, [
51
+ canGoBack,
52
+ currentStepIndex,
53
+ stepOrder
54
+ ]),
55
+ jumpToStep: useCallback((stepKey) => {
56
+ if (!stepOrder.includes(stepKey)) return false;
57
+ if (!visitedSteps.has(stepKey) && stepKey !== currentStep) return false;
58
+ setCurrentStep(stepKey);
59
+ return true;
60
+ }, [
61
+ stepOrder,
62
+ visitedSteps,
63
+ currentStep
64
+ ])
115
65
  };
116
66
  }
117
67
  //#endregion
@@ -8,17 +8,13 @@ function isStepField(field) {
8
8
  function parseWizardConfig(options) {
9
9
  const meta = options["$meta"];
10
10
  const wizardConfig = isWizardMeta(meta) ? meta.wizard : void 0;
11
- if (!wizardConfig || wizardConfig.mode !== "wizard") return null;
11
+ if (wizardConfig?.mode !== "wizard") return null;
12
12
  const steps = {};
13
13
  const stepOrder = [];
14
- const otherFields = {};
15
- Object.entries(options).forEach(([key, field]) => {
16
- if (key === "$meta") return;
17
- if (isStepField(field)) {
18
- steps[key] = field;
19
- stepOrder.push(key);
20
- } else otherFields[key] = field;
21
- });
14
+ for (const [key, field] of Object.entries(options)) if (key !== "$meta" && isStepField(field)) {
15
+ steps[key] = field;
16
+ stepOrder.push(key);
17
+ }
22
18
  if (stepOrder.length === 0) {
23
19
  console.warn("Wizard mode enabled but no steps found");
24
20
  return null;
@@ -26,8 +22,7 @@ function parseWizardConfig(options) {
26
22
  return {
27
23
  config: wizardConfig,
28
24
  steps,
29
- stepOrder,
30
- otherFields
25
+ stepOrder
31
26
  };
32
27
  }
33
28
  //#endregion
package/dist/form/lib.js CHANGED
@@ -23,56 +23,44 @@ function enforceOneMustBeTrue(items, options, promoteIndex) {
23
23
  const fieldSchema = options[fieldKey];
24
24
  const opts = typeof fieldSchema === "object" && fieldSchema && "options" in fieldSchema ? fieldSchema.options : void 0;
25
25
  const trueOption = typeof opts === "object" && opts && !Array.isArray(opts) && "onOption" in opts ? opts.onOption || true : true;
26
- if (!items.some((item) => item[fieldKey] === trueOption)) items[clampedIndex][fieldKey] = trueOption;
26
+ const hasTrue = items.some((item) => item[fieldKey] === trueOption);
27
+ const promoted = items[clampedIndex];
28
+ if (!hasTrue && promoted) promoted[fieldKey] = trueOption;
27
29
  }
28
30
  return items;
29
31
  }
30
32
  function flattenGroups(schema) {
31
33
  if (!schema || typeof schema !== "object") return {};
32
- const fields = schema;
33
- let flattenedFields = {};
34
- Object.keys(fields).forEach((key) => {
35
- const entry = fields[key];
36
- if (entry?.type === "group") {
37
- const flattenedGroup = flattenGroups(entry.options);
38
- flattenedFields = {
39
- ...flattenedFields,
40
- ...flattenedGroup
41
- };
42
- } else if (entry?.type === "object") {
43
- const flattenedGroup = flattenGroups(entry.options);
44
- flattenedFields = {
45
- ...flattenedFields,
46
- [key]: flattenedGroup
47
- };
48
- } else flattenedFields[key] = entry;
49
- });
50
- return flattenedFields;
34
+ const flattened = {};
35
+ for (const [key, entry] of Object.entries(schema)) if (entry?.type === "group") Object.assign(flattened, flattenGroups(entry.options));
36
+ else if (entry?.type === "object") flattened[key] = flattenGroups(entry.options);
37
+ else flattened[key] = entry;
38
+ return flattened;
51
39
  }
52
40
  /**Not a pure function. Edits 'obj' passed into function */
53
41
  function impureSetValueFromPath(obj, path, value) {
54
- const pathArray = path.split(".");
55
- if (pathArray.length === 1) {
56
- obj[pathArray[0]] = value;
57
- return;
58
- }
42
+ const parents = path.split(".");
43
+ const leaf = parents.pop() ?? "";
59
44
  let current = obj;
60
- for (let i = 0; i < pathArray.length - 1; i++) {
61
- const segment = pathArray[i];
45
+ for (const segment of parents) {
62
46
  const arrayMatch = segment.match(/^(.+)\[(\d+)\]$/);
63
47
  if (arrayMatch) {
64
- const key = arrayMatch[1];
65
- const index = Number(arrayMatch[2]);
48
+ const [, key = "", indexText] = arrayMatch;
49
+ const index = Number(indexText);
66
50
  if (!current[key]) current[key] = [];
67
51
  const list = current[key];
68
- if (!list[index]) list[index] = {};
69
- current = list[index];
52
+ let item = list[index];
53
+ if (!item) {
54
+ item = {};
55
+ list[index] = item;
56
+ }
57
+ current = item;
70
58
  } else {
71
59
  if (!current[segment]) current[segment] = {};
72
60
  current = current[segment];
73
61
  }
74
62
  }
75
- current[pathArray[pathArray.length - 1]] = value;
63
+ current[leaf] = value;
76
64
  }
77
65
  function initializeValues(options, srcData = {}, blankDefault) {
78
66
  if (!options || typeof options !== "object") return;
@@ -82,25 +70,13 @@ function initializeValues(options, srcData = {}, blankDefault) {
82
70
  return Object.keys(schema).reduce((acc, field) => {
83
71
  if (field.startsWith("$")) return acc;
84
72
  const fieldSchema = schema[field] ?? EMPTY_ENTRY;
85
- if (fieldSchema.type === "group") {
73
+ const flattenStep = fieldSchema.type === "step" && schema["$meta"]?.wizard?.flatten !== false;
74
+ if (fieldSchema.type === "group" || flattenStep) {
86
75
  const values = initializeValues(fieldSchema.options, data);
87
- if (!values) return acc;
88
- Object.keys(values).forEach((key) => {
89
- acc[key] = data[key] ?? values[key];
90
- });
91
- return acc;
92
- }
93
- if (fieldSchema.type === "step") {
94
- if (schema["$meta"]?.wizard?.flatten !== false) {
95
- const values = initializeValues(fieldSchema.options, data);
96
- if (!values) return acc;
97
- Object.keys(values).forEach((key) => {
98
- acc[key] = data[key] ?? values[key];
99
- });
100
- } else acc[field] = initializeValues(fieldSchema.options, asRecord(data[field]));
76
+ for (const key of Object.keys(values ?? {})) acc[key] = data[key] ?? values?.[key];
101
77
  return acc;
102
78
  }
103
- if (fieldSchema.type === "object") {
79
+ if (fieldSchema.type === "step" || fieldSchema.type === "object") {
104
80
  acc[field] = initializeValues(fieldSchema.options, asRecord(data[field]));
105
81
  return acc;
106
82
  }
@@ -110,14 +86,12 @@ function initializeValues(options, srcData = {}, blankDefault) {
110
86
  acc[field] = Array.isArray(listData) ? [...listData] : [];
111
87
  return acc;
112
88
  }
113
- const arr = [];
114
- if (Array.isArray(listData)) for (let i = 0; i < listData.length; i++) arr.push(initializeValues(fieldSchema.options, asRecord(listData[i])));
115
- acc[field] = arr;
89
+ acc[field] = Array.isArray(listData) ? listData.map((item) => initializeValues(fieldSchema.options, asRecord(item))) : [];
116
90
  return acc;
117
91
  }
118
92
  if (data[field] !== void 0) {
119
93
  acc[field] = data[field];
120
- if ((fieldSchema.type === "dropdown" || fieldSchema.type === "storage") && fieldSchema.secondaryField && fieldSchema.options && Array.isArray(fieldSchema.options)) {
94
+ if ((fieldSchema.type === "dropdown" || fieldSchema.type === "storage") && fieldSchema.secondaryField && Array.isArray(fieldSchema.options)) {
121
95
  const option = findSecondaryOption(fieldSchema.options, data[field]);
122
96
  applySecondaryField(fieldSchema.secondaryField, option?.secondaryValue, (secondaryField, secondaryValue) => {
123
97
  if (!(fieldSchema.type === "storage" && Object.hasOwn(options, secondaryField) && source[secondaryField] !== void 0)) impureSetValueFromPath(acc, secondaryField, secondaryValue);
@@ -135,8 +109,8 @@ function initializeValues(options, srcData = {}, blankDefault) {
135
109
  else acc[field] = false;
136
110
  }
137
111
  if (fieldSchema.type === "dropdown") {
138
- if (fieldSchema.options && Array.isArray(fieldSchema.options) && fieldSchema.options.length > 0) acc[field] = fieldSchema.options[0].value;
139
- if (fieldSchema.type === "dropdown" && fieldSchema.secondaryField && fieldSchema.options && Array.isArray(fieldSchema.options)) {
112
+ if (Array.isArray(fieldSchema.options) && fieldSchema.options.length > 0) acc[field] = fieldSchema.options[0].value;
113
+ if (fieldSchema.secondaryField && Array.isArray(fieldSchema.options)) {
140
114
  const option = fieldSchema.options.find((option) => option.value === data[field]);
141
115
  const secondaryField = Array.isArray(fieldSchema.secondaryField) ? fieldSchema.secondaryField : [fieldSchema.secondaryField];
142
116
  const secondaryValue = Array.isArray(option?.secondaryValue) ? option?.secondaryValue : [option?.secondaryValue];
@@ -15,7 +15,8 @@ export interface RuntimeFieldExtensions {
15
15
  one_must_be_true?: boolean;
16
16
  computeOn?: boolean;
17
17
  labelPosition?: LabelPosition;
18
- resetOnChange?: boolean;
18
+ /** Path of another field; when its value changes, this field's value is cleared. */
19
+ resetOnChange?: string;
19
20
  options?: unknown;
20
21
  }
21
22
  export interface BaseField extends RuntimeFieldExtensions {
@@ -36,14 +37,13 @@ interface WizardConfig {
36
37
  /** Navigation UI options */
37
38
  navigation?: {
38
39
  showSteps?: boolean;
39
- showProgress?: boolean;
40
40
  allowJump?: boolean;
41
41
  hideStepNumbers?: boolean;
42
42
  };
43
43
  /** Label for the final submit button */
44
44
  submitLabel?: string;
45
- /** Use URL-based routing instead of component state */
46
- urlBased?: boolean;
45
+ /** Flatten step fields into top-level inputs (default: true). Set false to keep each step's fields under its key. */
46
+ flatten?: boolean;
47
47
  }
48
48
  interface StepField extends BaseField {
49
49
  type: 'step';
@@ -51,7 +51,6 @@ interface StepField extends BaseField {
51
51
  description?: string;
52
52
  options: Record<string, AnyField>;
53
53
  validateOnNext?: boolean;
54
- canSkip?: boolean;
55
54
  nextLabel?: string;
56
55
  prevLabel?: string;
57
56
  }
@@ -0,0 +1,6 @@
1
+ /** Options for a field whose list is keyed by another field's value: with
2
+ * depends_on, the list follows whatever is currently chosen at that path. */
3
+ export declare function dependentOptions<T>(options: T[] | Record<string, T[]>, dependsOn: string | undefined, values: Record<string, unknown>): {
4
+ options: T[];
5
+ parentValue: unknown;
6
+ };
@@ -0,0 +1,21 @@
1
+ import { getValueUsingPath } from "./getValueUsingPath.js";
2
+ //#region src/form/utils/dependentOptions.ts
3
+ /** Options for a field whose list is keyed by another field's value: with
4
+ * depends_on, the list follows whatever is currently chosen at that path. */
5
+ function dependentOptions(options, dependsOn, values) {
6
+ if (Array.isArray(options)) return {
7
+ options,
8
+ parentValue: void 0
9
+ };
10
+ const parentValue = dependsOn ? getValueUsingPath(values, dependsOn) : void 0;
11
+ if (parentValue === void 0 || parentValue === null) return {
12
+ options: [],
13
+ parentValue
14
+ };
15
+ return {
16
+ options: options[String(parentValue)] ?? [],
17
+ parentValue
18
+ };
19
+ }
20
+ //#endregion
21
+ export { dependentOptions };
@@ -1,3 +1,4 @@
1
- export declare function getParentValue(index: number | undefined, targetField: unknown, values: object, dependentPath: string, field: {
1
+ /** `index` replaces a path's `[index]` placeholder when the field sits in a list row. */
2
+ export declare function getParentValue(index: number | undefined, targetField: unknown, values: object, field: {
2
3
  depends_on?: string | undefined;
3
4
  }): unknown;
@@ -1,23 +1,18 @@
1
1
  import { getValueUsingPath } from "./getValueUsingPath.js";
2
2
  import { checkShowIfObject } from "./checkShowIfObject.js";
3
3
  //#region src/form/utils/getParentValue.ts
4
- function getParentValue(index, targetField, values, dependentPath, field) {
5
- let parentValue;
6
- if (typeof targetField === "object" && targetField !== null && !Array.isArray(targetField)) parentValue = !checkShowIfObject(targetField, values, index).includes(false);
7
- else if (field.depends_on) {
8
- dependentPath = field.depends_on;
9
- if (index !== void 0) dependentPath = field.depends_on.replace("[index]", `[${index}]`);
10
- const dependsOnValue = getValueUsingPath(values, dependentPath);
4
+ /** `index` replaces a path's `[index]` placeholder when the field sits in a list row. */
5
+ function getParentValue(index, targetField, values, field) {
6
+ const atIndex = (path) => index === void 0 ? path : path.replace("[index]", `[${index}]`);
7
+ if (typeof targetField === "object" && targetField !== null && !Array.isArray(targetField)) return !checkShowIfObject(targetField, values, index).includes(false);
8
+ if (field.depends_on) {
9
+ const dependsOnValue = getValueUsingPath(values, atIndex(field.depends_on));
11
10
  const dependsOnText = dependsOnValue === null || dependsOnValue === void 0 ? void 0 : String(dependsOnValue);
12
- if (Array.isArray(targetField)) parentValue = targetField.includes(dependsOnValue) || targetField.includes(dependsOnText);
13
- else if (typeof targetField === "string") parentValue = dependsOnText !== void 0 && targetField.includes(dependsOnText);
14
- else parentValue = targetField === dependsOnValue;
15
- } else {
16
- dependentPath = typeof targetField === "string" ? targetField : "";
17
- if (index !== void 0 && typeof targetField === "string") dependentPath = targetField.replace("[index]", `[${index}]`);
18
- parentValue = getValueUsingPath(values, dependentPath);
11
+ if (Array.isArray(targetField)) return targetField.includes(dependsOnValue) || targetField.includes(dependsOnText);
12
+ if (typeof targetField === "string") return dependsOnText !== void 0 && targetField.includes(dependsOnText);
13
+ return targetField === dependsOnValue;
19
14
  }
20
- return parentValue;
15
+ return getValueUsingPath(values, typeof targetField === "string" ? atIndex(targetField) : "");
21
16
  }
22
17
  //#endregion
23
18
  export { getParentValue };
@@ -10,7 +10,7 @@ function getValueUsingPath(obj, path) {
10
10
  const [arrayKey = "", indexStr = ""] = key.split("[");
11
11
  const index = parseInt(indexStr.slice(0, -1), 10);
12
12
  const list = container[arrayKey];
13
- if (!Array.isArray(list) || isNaN(index)) return;
13
+ if (!Array.isArray(list) || Number.isNaN(index)) return;
14
14
  current = list[index];
15
15
  } else {
16
16
  if (!(key in container)) return;
@@ -1,6 +1,6 @@
1
1
  import type { WorkflowJob } from './types.js';
2
2
  interface AnnotationBannerProps {
3
- executedJobs: Record<string, WorkflowJob> | null | undefined;
3
+ executedJobs: Record<string, WorkflowJob>;
4
4
  }
5
5
  export declare function AnnotationBanner({ executedJobs }: AnnotationBannerProps): import("react").JSX.Element | null;
6
6
  export {};
@@ -48,6 +48,7 @@ function collectAnnotations(executedJobs, parentPath) {
48
48
  title: annotation.title,
49
49
  file: annotation.file,
50
50
  line: annotation.line,
51
+ id: annotation.id,
51
52
  jobName: fullJobName,
52
53
  stepName: step?.name,
53
54
  stepIndex
@@ -62,7 +63,6 @@ function collectAnnotations(executedJobs, parentPath) {
62
63
  }
63
64
  function AnnotationBanner({ executedJobs }) {
64
65
  const [expanded, setExpanded] = useState(false);
65
- if (!executedJobs) return null;
66
66
  const annotations = collectAnnotations(executedJobs);
67
67
  if (annotations.length === 0) return null;
68
68
  const counts = {
@@ -1,6 +1,6 @@
1
- import { type ReactNode } from 'react';
2
- export declare function Collapse({ expanded, children, releaseOverflow, }: {
1
+ import type { ReactNode } from 'react';
2
+ export declare function Collapse({ expanded, id, children, }: {
3
3
  expanded: boolean;
4
+ id?: string;
4
5
  children: ReactNode;
5
- releaseOverflow?: boolean;
6
6
  }): import("react").JSX.Element;