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