@overmap-ai/forms 1.0.4-form-editor-revamp.0 → 1.0.4
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/dist/builder/FieldBuilder.d.ts +2 -2
- package/dist/builder/FieldSectionWithActions.d.ts +0 -1
- package/dist/builder/FieldWithActions.d.ts +0 -3
- package/dist/forms.js +1953 -1374
- package/dist/forms.js.map +1 -0
- package/dist/forms.umd.cjs +2667 -1
- package/dist/forms.umd.cjs.map +1 -0
- package/dist/style.css +26 -1
- package/package.json +1 -1
package/dist/forms.js
CHANGED
|
@@ -1,31 +1,37 @@
|
|
|
1
|
-
var
|
|
2
|
-
var
|
|
3
|
-
var
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
this
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __publicField = (obj, key, value) => {
|
|
4
|
+
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
5
|
+
return value;
|
|
6
|
+
};
|
|
7
|
+
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
8
|
+
import { Flex, Text, useSeverityColor, Checkbox, CheckCircledIcon, TextField as TextField$1, FontFamilyIcon, CalendarIcon, InputIcon, TextArea, RowsIcon, Select, Box, IconButton, PlusIcon, Badge, Cross1Icon, ListBulletIcon, DropdownMenuIcon, MultiSelect, CheckboxIcon, Card, Heading, Button, UploadIcon, ButtonList, divButtonProps, StarFilledIcon, StarIcon, QuestionMarkCircledIcon, PersonIcon, Tooltip, Avatar, Separator, Dialog, Pencil1Icon, TrashIcon, CopyIcon, DragHandleDots2Icon, DropdownMenu, DotsVerticalIcon, useAlertDialog, Em, Strong, useToast, Tabs } from "@overmap-ai/blocks";
|
|
9
|
+
import { useField, useFormikContext, useFormik, FormikProvider } from "formik";
|
|
10
|
+
import React, { useMemo, memo, useCallback, useState, useEffect, useRef, forwardRef, useReducer } from "react";
|
|
11
|
+
import { DragDropContext, Droppable, Draggable } from "@hello-pangea/dnd";
|
|
12
|
+
import { slugify, useAppSelector, selectFormRevision, useSDK, selectSubmissionAttachments, selectFilteredUserForms, selectUserFormMapping, selectOrganization, selectUser, selectNumberOfUserForms, selectCurrentUser, classNames, isToday, getLocalDateString, selectLatestFormRevision, useFileSrc, selectSubmissionsForForm } from "@overmap-ai/core";
|
|
13
|
+
import get from "lodash.get";
|
|
14
|
+
import set from "lodash.set";
|
|
15
|
+
class BaseFormElement {
|
|
16
|
+
constructor(options) {
|
|
17
|
+
__publicField(this, "type");
|
|
18
|
+
__publicField(this, "identifier");
|
|
19
|
+
__publicField(this, "description");
|
|
20
|
+
const { description: description2 = null, identifier, type } = options;
|
|
21
|
+
this.identifier = identifier;
|
|
22
|
+
this.description = description2;
|
|
23
|
+
this.type = type;
|
|
19
24
|
}
|
|
20
25
|
getId() {
|
|
21
26
|
return this.identifier;
|
|
22
27
|
}
|
|
23
|
-
static deserialize(
|
|
28
|
+
static deserialize(_data) {
|
|
24
29
|
throw new Error(`${this.name} must implement deserialize.`);
|
|
25
30
|
}
|
|
26
31
|
_serialize() {
|
|
27
|
-
if (!this.identifier)
|
|
32
|
+
if (!this.identifier) {
|
|
28
33
|
throw new Error("Field identifier must be set before serializing.");
|
|
34
|
+
}
|
|
29
35
|
return {
|
|
30
36
|
type: this.type,
|
|
31
37
|
identifier: this.identifier,
|
|
@@ -33,46 +39,51 @@ class Et {
|
|
|
33
39
|
};
|
|
34
40
|
}
|
|
35
41
|
}
|
|
36
|
-
class
|
|
37
|
-
constructor(
|
|
38
|
-
const { label
|
|
39
|
-
super(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
class BaseField extends BaseFormElement {
|
|
43
|
+
constructor(options) {
|
|
44
|
+
const { label, required, fieldValidators = [], formValidators = [], ...base } = options;
|
|
45
|
+
super(base);
|
|
46
|
+
__publicField(this, "required");
|
|
47
|
+
__publicField(this, "formValidators");
|
|
48
|
+
__publicField(this, "fieldValidators");
|
|
49
|
+
__publicField(this, "label");
|
|
44
50
|
/**
|
|
45
51
|
* By default, validation doesn't execute on `onChange` events when editing fields
|
|
46
52
|
* until the field has been `touched`. This can be overridden by setting this to `false`
|
|
47
53
|
* if you want to validate on every `onChange` event. This is important for fields like booleans
|
|
48
54
|
* which don't have a `onBlur` event (which is used to set the `touched` state).
|
|
49
55
|
*/
|
|
50
|
-
|
|
51
|
-
this.label =
|
|
56
|
+
__publicField(this, "onlyValidateAfterTouched", true);
|
|
57
|
+
this.label = label;
|
|
58
|
+
this.required = required;
|
|
59
|
+
this.fieldValidators = fieldValidators;
|
|
60
|
+
this.formValidators = formValidators;
|
|
52
61
|
}
|
|
53
62
|
static getFieldCreationSchema() {
|
|
54
63
|
return [];
|
|
55
64
|
}
|
|
56
|
-
isBlank(
|
|
57
|
-
return
|
|
65
|
+
isBlank(value) {
|
|
66
|
+
return value === null || value === void 0 || value === "";
|
|
58
67
|
}
|
|
59
|
-
getValueFromChangeEvent(
|
|
60
|
-
return
|
|
68
|
+
getValueFromChangeEvent(event) {
|
|
69
|
+
return event.target.value;
|
|
61
70
|
}
|
|
62
|
-
getError(
|
|
63
|
-
if (this.required && this.isBlank(
|
|
71
|
+
getError(value, allValues) {
|
|
72
|
+
if (this.required && this.isBlank(value)) {
|
|
64
73
|
return "This field is required.";
|
|
65
|
-
for (const n of this.getFieldValidators()) {
|
|
66
|
-
const s = n(e);
|
|
67
|
-
if (s)
|
|
68
|
-
return s;
|
|
69
74
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
+
for (const validator of this.getFieldValidators()) {
|
|
76
|
+
const error = validator(value);
|
|
77
|
+
if (error)
|
|
78
|
+
return error;
|
|
79
|
+
}
|
|
80
|
+
if (allValues) {
|
|
81
|
+
for (const validator of this.getFormValidators()) {
|
|
82
|
+
const error = validator(value, allValues);
|
|
83
|
+
if (error)
|
|
84
|
+
return error;
|
|
75
85
|
}
|
|
86
|
+
}
|
|
76
87
|
}
|
|
77
88
|
// TODO: We can probably combine _serialize and serialize.
|
|
78
89
|
_serialize() {
|
|
@@ -89,168 +100,218 @@ class O extends Et {
|
|
|
89
100
|
return [...this.formValidators];
|
|
90
101
|
}
|
|
91
102
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
103
|
+
__publicField(BaseField, "fieldTypeName");
|
|
104
|
+
__publicField(BaseField, "fieldTypeDescription");
|
|
105
|
+
const description$1 = "_description_17zed_1";
|
|
106
|
+
const styles$3 = {
|
|
107
|
+
description: description$1
|
|
108
|
+
};
|
|
109
|
+
const InputWithLabel = (props) => {
|
|
110
|
+
const { label, children, severity, inputId, labelId, flexProps } = props;
|
|
111
|
+
return /* @__PURE__ */ jsx(Flex, { direction: "column", gap: "1", asChild: true, ...flexProps, children: /* @__PURE__ */ jsxs("label", { htmlFor: inputId, children: [
|
|
112
|
+
/* @__PURE__ */ jsx(Text, { severity, id: labelId, children: label }),
|
|
113
|
+
children
|
|
100
114
|
] }) });
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
115
|
+
};
|
|
116
|
+
const InputWithLabelAndHelpText = (props) => {
|
|
117
|
+
const { helpText, children, severity } = props;
|
|
118
|
+
return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: "1", children: [
|
|
119
|
+
children,
|
|
120
|
+
/* @__PURE__ */ jsx(Flex, { direction: "column", children: /* @__PURE__ */ jsx(Text, { size: "1", severity, className: styles$3.description, children: helpText }) })
|
|
106
121
|
] });
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
122
|
+
};
|
|
123
|
+
const useFormikInput = (props) => {
|
|
124
|
+
const { id, field, formId: formId2, ...rest } = props;
|
|
125
|
+
const [fieldProps, meta, helpers] = useField(field.getId());
|
|
126
|
+
const { touched } = meta;
|
|
127
|
+
const helpText = meta.error ?? field.description;
|
|
128
|
+
const severity = meta.error ? "danger" : void 0;
|
|
129
|
+
const inputId = id ?? `${formId2}-${field.getId()}-input`;
|
|
130
|
+
const labelId = `${inputId}-label`;
|
|
131
|
+
const label = field.required ? `${field.label} *` : field.label;
|
|
132
|
+
const fieldPropsWithValidation = useMemo(() => {
|
|
133
|
+
const handleChange = (e) => {
|
|
134
|
+
const value = field.getValueFromChangeEvent(e);
|
|
135
|
+
helpers.setValue(value, false).then();
|
|
136
|
+
if (touched || !field.onlyValidateAfterTouched) {
|
|
137
|
+
helpers.setError(field.getError(value));
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
const handleBlur = (e) => {
|
|
141
|
+
helpers.setTouched(true, false).then();
|
|
142
|
+
helpers.setError(field.getError(field.getValueFromChangeEvent(e)));
|
|
143
|
+
};
|
|
144
|
+
return {
|
|
145
|
+
...fieldProps,
|
|
146
|
+
onChange: handleChange,
|
|
147
|
+
onBlur: handleBlur
|
|
148
|
+
};
|
|
149
|
+
}, [field, fieldProps, helpers, touched]);
|
|
118
150
|
return [
|
|
119
151
|
{
|
|
120
|
-
helpText
|
|
121
|
-
severity
|
|
122
|
-
inputId
|
|
123
|
-
labelId
|
|
124
|
-
label
|
|
125
|
-
fieldProps:
|
|
126
|
-
helpers
|
|
127
|
-
field
|
|
152
|
+
helpText,
|
|
153
|
+
severity,
|
|
154
|
+
inputId,
|
|
155
|
+
labelId,
|
|
156
|
+
label,
|
|
157
|
+
fieldProps: fieldPropsWithValidation,
|
|
158
|
+
helpers,
|
|
159
|
+
field
|
|
128
160
|
},
|
|
129
|
-
{ ...
|
|
161
|
+
{ ...rest, "aria-labelledby": labelId }
|
|
130
162
|
];
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
163
|
+
};
|
|
164
|
+
const truthyValues = [true, "true"];
|
|
165
|
+
const BooleanInput = memo(function BooleanInput2(props) {
|
|
166
|
+
const [{ inputId, labelId, severity, helpText, label, fieldProps }, rest] = useFormikInput(props);
|
|
167
|
+
const color = useSeverityColor(severity);
|
|
168
|
+
const value = truthyValues.includes(fieldProps.value);
|
|
169
|
+
return /* @__PURE__ */ jsx(InputWithLabelAndHelpText, { helpText, severity, children: /* @__PURE__ */ jsx(
|
|
170
|
+
InputWithLabel,
|
|
135
171
|
{
|
|
136
|
-
severity
|
|
137
|
-
inputId
|
|
138
|
-
labelId
|
|
139
|
-
label
|
|
172
|
+
severity,
|
|
173
|
+
inputId,
|
|
174
|
+
labelId,
|
|
175
|
+
label,
|
|
140
176
|
flexProps: { direction: "row-reverse", justify: "end", align: "center", gap: "2" },
|
|
141
|
-
children: /* @__PURE__ */
|
|
142
|
-
|
|
177
|
+
children: /* @__PURE__ */ jsx(
|
|
178
|
+
Checkbox,
|
|
143
179
|
{
|
|
144
|
-
...
|
|
145
|
-
...
|
|
146
|
-
id:
|
|
147
|
-
color
|
|
148
|
-
value:
|
|
149
|
-
checked:
|
|
150
|
-
onCheckedChange:
|
|
180
|
+
...rest,
|
|
181
|
+
...fieldProps,
|
|
182
|
+
id: inputId,
|
|
183
|
+
color,
|
|
184
|
+
value: value.toString(),
|
|
185
|
+
checked: value,
|
|
186
|
+
onCheckedChange: fieldProps.onChange,
|
|
151
187
|
onChange: void 0,
|
|
152
188
|
onBlur: void 0
|
|
153
189
|
}
|
|
154
190
|
)
|
|
155
191
|
}
|
|
156
192
|
) });
|
|
157
|
-
})
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
193
|
+
});
|
|
194
|
+
const _BooleanField = class _BooleanField extends BaseField {
|
|
195
|
+
constructor(options) {
|
|
196
|
+
super({ ...options, type: "boolean" });
|
|
197
|
+
__publicField(this, "onlyValidateAfterTouched", false);
|
|
161
198
|
}
|
|
162
199
|
// if a BooleanField is required, `false` is considered blank
|
|
163
|
-
isBlank(
|
|
164
|
-
return this.required && !
|
|
200
|
+
isBlank(value) {
|
|
201
|
+
return this.required && !value;
|
|
165
202
|
}
|
|
166
|
-
getValueFromChangeEvent(
|
|
167
|
-
|
|
203
|
+
getValueFromChangeEvent(event) {
|
|
204
|
+
if (typeof event === "boolean")
|
|
205
|
+
return event;
|
|
206
|
+
return event.target.checked;
|
|
168
207
|
}
|
|
169
208
|
serialize() {
|
|
170
209
|
return super._serialize();
|
|
171
210
|
}
|
|
172
|
-
static deserialize(
|
|
173
|
-
if (
|
|
211
|
+
static deserialize(data) {
|
|
212
|
+
if (data.type !== "boolean")
|
|
174
213
|
throw new Error("Type mismatch.");
|
|
175
|
-
return new
|
|
214
|
+
return new _BooleanField(data);
|
|
176
215
|
}
|
|
177
|
-
getInput(
|
|
178
|
-
return /* @__PURE__ */
|
|
216
|
+
getInput(props) {
|
|
217
|
+
return /* @__PURE__ */ jsx(BooleanInput, { ...props, field: this });
|
|
179
218
|
}
|
|
180
219
|
};
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
220
|
+
__publicField(_BooleanField, "fieldTypeName", "Checkbox");
|
|
221
|
+
__publicField(_BooleanField, "fieldTypeDescription", "Perfect for both optional and required yes/no questions.");
|
|
222
|
+
__publicField(_BooleanField, "Icon", CheckCircledIcon);
|
|
223
|
+
let BooleanField = _BooleanField;
|
|
224
|
+
const NumberInput$1 = memo(function NumberInput2(props) {
|
|
225
|
+
const [{ inputId, labelId, severity, helpText, label, fieldProps, field }, rest] = useFormikInput(props);
|
|
226
|
+
const color = useSeverityColor(severity);
|
|
227
|
+
return /* @__PURE__ */ jsx(InputWithLabelAndHelpText, { helpText, severity, children: /* @__PURE__ */ jsx(InputWithLabel, { severity, inputId, labelId, label, children: /* @__PURE__ */ jsx(
|
|
228
|
+
TextField$1.Input,
|
|
187
229
|
{
|
|
188
|
-
...
|
|
189
|
-
...
|
|
230
|
+
...rest,
|
|
231
|
+
...fieldProps,
|
|
190
232
|
type: "number",
|
|
191
|
-
id:
|
|
192
|
-
min:
|
|
193
|
-
max:
|
|
194
|
-
step:
|
|
195
|
-
color
|
|
233
|
+
id: inputId,
|
|
234
|
+
min: field.minimum,
|
|
235
|
+
max: field.maximum,
|
|
236
|
+
step: field.integers ? 1 : 0.1,
|
|
237
|
+
color
|
|
196
238
|
}
|
|
197
239
|
) }) });
|
|
198
|
-
})
|
|
199
|
-
|
|
240
|
+
});
|
|
241
|
+
const _NumberField = class _NumberField extends BaseField {
|
|
242
|
+
constructor(options) {
|
|
200
243
|
const {
|
|
201
|
-
minimum
|
|
202
|
-
maximum
|
|
203
|
-
integers
|
|
204
|
-
...
|
|
205
|
-
} =
|
|
206
|
-
super({ ...
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
this.minimum =
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
244
|
+
minimum = Number.MIN_SAFE_INTEGER,
|
|
245
|
+
maximum = Number.MAX_SAFE_INTEGER,
|
|
246
|
+
integers = false,
|
|
247
|
+
...base
|
|
248
|
+
} = options;
|
|
249
|
+
super({ ...base, type: "number" });
|
|
250
|
+
__publicField(this, "minimum");
|
|
251
|
+
__publicField(this, "maximum");
|
|
252
|
+
__publicField(this, "integers");
|
|
253
|
+
this.minimum = minimum;
|
|
254
|
+
this.maximum = maximum;
|
|
255
|
+
this.integers = integers;
|
|
256
|
+
}
|
|
257
|
+
getValueFromChangeEvent(event) {
|
|
258
|
+
const number = Number.parseFloat(event.target.value);
|
|
259
|
+
if (Number.isNaN(number))
|
|
260
|
+
return "";
|
|
261
|
+
return number;
|
|
215
262
|
}
|
|
216
263
|
static getFieldCreationSchema() {
|
|
217
264
|
return [
|
|
218
|
-
new
|
|
265
|
+
new _NumberField({
|
|
219
266
|
label: "Minimum",
|
|
220
267
|
description: "Minimum value",
|
|
221
|
-
integers:
|
|
222
|
-
required:
|
|
268
|
+
integers: true,
|
|
269
|
+
required: false,
|
|
223
270
|
identifier: "minimum",
|
|
224
271
|
formValidators: [this._validateMin]
|
|
225
272
|
}),
|
|
226
|
-
new
|
|
273
|
+
new _NumberField({
|
|
227
274
|
label: "Maximum",
|
|
228
275
|
description: "Maximum value",
|
|
229
|
-
integers:
|
|
230
|
-
required:
|
|
276
|
+
integers: true,
|
|
277
|
+
required: false,
|
|
231
278
|
identifier: "maximum",
|
|
232
279
|
formValidators: [this._validateMax]
|
|
233
280
|
}),
|
|
234
|
-
new
|
|
281
|
+
new BooleanField({
|
|
235
282
|
label: "Integers",
|
|
236
283
|
description: "Whole numbers only",
|
|
237
|
-
required:
|
|
284
|
+
required: false,
|
|
238
285
|
identifier: "integers"
|
|
239
286
|
})
|
|
240
287
|
];
|
|
241
288
|
}
|
|
242
289
|
getFieldValidators() {
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
290
|
+
const validators = super.getFieldValidators();
|
|
291
|
+
const min = this.minimum;
|
|
292
|
+
const max = this.maximum;
|
|
293
|
+
if (typeof min === "number") {
|
|
294
|
+
validators.push((value) => {
|
|
295
|
+
if (typeof value === "number" && value < min) {
|
|
296
|
+
return `Must be at least ${this.minimum}.`;
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
if (typeof max === "number") {
|
|
301
|
+
validators.push((value) => {
|
|
302
|
+
if (typeof value === "number" && value > max) {
|
|
303
|
+
return `Must be at most ${this.maximum}.`;
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
if (this.integers) {
|
|
308
|
+
validators.push((value) => {
|
|
309
|
+
if (typeof value === "number" && !Number.isInteger(value)) {
|
|
310
|
+
return "Must be a whole number.";
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
return validators;
|
|
254
315
|
}
|
|
255
316
|
serialize() {
|
|
256
317
|
return {
|
|
@@ -260,90 +321,121 @@ const _i = S(function(i) {
|
|
|
260
321
|
integers: this.integers
|
|
261
322
|
};
|
|
262
323
|
}
|
|
263
|
-
static deserialize(
|
|
264
|
-
if (
|
|
324
|
+
static deserialize(data) {
|
|
325
|
+
if (data.type !== "number")
|
|
265
326
|
throw new Error("Type mismatch.");
|
|
266
|
-
return new
|
|
327
|
+
return new _NumberField(data);
|
|
267
328
|
}
|
|
268
|
-
getInput(
|
|
269
|
-
return /* @__PURE__ */
|
|
329
|
+
getInput(props) {
|
|
330
|
+
return /* @__PURE__ */ jsx(NumberInput$1, { field: this, ...props });
|
|
270
331
|
}
|
|
271
332
|
};
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
333
|
+
__publicField(_NumberField, "fieldTypeName", "Number");
|
|
334
|
+
__publicField(_NumberField, "fieldTypeDescription", "Allows specifying a number within a given range.");
|
|
335
|
+
__publicField(_NumberField, "Icon", FontFamilyIcon);
|
|
336
|
+
__publicField(_NumberField, "_validateMin", (value, allValues) => {
|
|
337
|
+
if (typeof allValues.maximum === "number" && typeof value === "number" && allValues.maximum < value) {
|
|
338
|
+
return "Minimum cannot be greater than minimum.";
|
|
339
|
+
}
|
|
340
|
+
return null;
|
|
341
|
+
});
|
|
342
|
+
__publicField(_NumberField, "_validateMax", (value, allValues) => {
|
|
343
|
+
if (typeof allValues.minimum === "number" && typeof value === "number" && allValues.minimum > value) {
|
|
344
|
+
return "Maximum cannot be less than minimum.";
|
|
345
|
+
}
|
|
346
|
+
return null;
|
|
347
|
+
});
|
|
348
|
+
let NumberField = _NumberField;
|
|
349
|
+
const DateInput = memo(function DateInput2(props) {
|
|
350
|
+
const [{ inputId, labelId, severity, helpText, label, fieldProps }, rest] = useFormikInput(props);
|
|
351
|
+
const color = useSeverityColor(severity);
|
|
352
|
+
const value = fieldProps.value ? fieldProps.value.split("T")[0] : "";
|
|
353
|
+
return /* @__PURE__ */ jsx(InputWithLabelAndHelpText, { helpText, severity, children: /* @__PURE__ */ jsx(InputWithLabel, { severity, inputId, labelId, label, children: /* @__PURE__ */ jsx(TextField$1.Input, { ...rest, ...fieldProps, type: "date", id: inputId, color, value }) }) });
|
|
354
|
+
});
|
|
355
|
+
const _DateField = class _DateField extends BaseField {
|
|
356
|
+
constructor(options) {
|
|
357
|
+
super({ ...options, type: "date" });
|
|
358
|
+
__publicField(this, "onlyValidateAfterTouched", false);
|
|
281
359
|
}
|
|
282
360
|
serialize() {
|
|
283
361
|
return super._serialize();
|
|
284
362
|
}
|
|
285
|
-
getValueFromChangeEvent(
|
|
286
|
-
return new Date(
|
|
363
|
+
getValueFromChangeEvent(event) {
|
|
364
|
+
return new Date(event.target.value).toISOString();
|
|
287
365
|
}
|
|
288
|
-
static deserialize(
|
|
289
|
-
if (
|
|
366
|
+
static deserialize(data) {
|
|
367
|
+
if (data.type !== "date")
|
|
290
368
|
throw new Error("Type mismatch.");
|
|
291
|
-
return new
|
|
369
|
+
return new _DateField(data);
|
|
292
370
|
}
|
|
293
|
-
getInput(
|
|
294
|
-
return /* @__PURE__ */
|
|
371
|
+
getInput(props) {
|
|
372
|
+
return /* @__PURE__ */ jsx(DateInput, { field: this, ...props });
|
|
295
373
|
}
|
|
296
374
|
};
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
375
|
+
__publicField(_DateField, "fieldTypeName", "Date");
|
|
376
|
+
__publicField(_DateField, "fieldTypeDescription", "Allows specifying a date.");
|
|
377
|
+
__publicField(_DateField, "Icon", CalendarIcon);
|
|
378
|
+
let DateField = _DateField;
|
|
379
|
+
class StringOrTextField extends BaseField {
|
|
380
|
+
constructor(options) {
|
|
381
|
+
const { minLength, maxLength = 5e3, ...base } = options;
|
|
382
|
+
super(base);
|
|
383
|
+
__publicField(this, "minLength");
|
|
384
|
+
__publicField(this, "maxLength");
|
|
385
|
+
this.minLength = minLength ? Math.max(minLength, 0) : void 0;
|
|
386
|
+
this.maxLength = maxLength ? Math.max(maxLength, 0) : 5e3;
|
|
306
387
|
}
|
|
307
388
|
static getFieldCreationSchema() {
|
|
308
389
|
return [
|
|
309
390
|
// min, max
|
|
310
|
-
new
|
|
391
|
+
new NumberField({
|
|
311
392
|
label: "Minimum length",
|
|
312
393
|
description: "Minimum number of characters",
|
|
313
|
-
required:
|
|
394
|
+
required: false,
|
|
314
395
|
identifier: "minimum_length",
|
|
315
396
|
minimum: 0,
|
|
316
397
|
maximum: 100,
|
|
317
398
|
formValidators: [this._validateMin],
|
|
318
|
-
integers:
|
|
399
|
+
integers: true
|
|
319
400
|
}),
|
|
320
|
-
new
|
|
401
|
+
new NumberField({
|
|
321
402
|
label: "Maximum length",
|
|
322
403
|
description: "Maximum number of characters",
|
|
323
|
-
required:
|
|
404
|
+
required: false,
|
|
324
405
|
identifier: "maximum_length",
|
|
325
406
|
minimum: 1,
|
|
326
407
|
maximum: 5e3,
|
|
327
408
|
// TODO: depends on short vs long text
|
|
328
409
|
formValidators: [this._validateMax],
|
|
329
410
|
// TODO: default: 500 (see: "Short text fields can hold up to 500 characters on a single line.")
|
|
330
|
-
integers:
|
|
411
|
+
integers: true
|
|
331
412
|
})
|
|
332
413
|
];
|
|
333
414
|
}
|
|
334
415
|
getFieldValidators() {
|
|
335
|
-
const
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
416
|
+
const validators = super.getFieldValidators();
|
|
417
|
+
if (this.minLength) {
|
|
418
|
+
validators.push((value) => {
|
|
419
|
+
if (this.minLength && (!value || value.length < this.minLength)) {
|
|
420
|
+
if (!this.required && !value)
|
|
421
|
+
return null;
|
|
422
|
+
return `Minimum ${this.minLength} character(s).`;
|
|
423
|
+
}
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
if (this.maxLength) {
|
|
427
|
+
validators.push((value) => {
|
|
428
|
+
if (typeof value === "string" && this.maxLength && value.length > this.maxLength) {
|
|
429
|
+
return `Maximum ${this.maxLength} character(s).`;
|
|
430
|
+
}
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
return validators;
|
|
343
434
|
}
|
|
344
435
|
_serialize() {
|
|
345
|
-
if (!this.identifier)
|
|
436
|
+
if (!this.identifier) {
|
|
346
437
|
throw new Error("Field identifier must be set before serializing.");
|
|
438
|
+
}
|
|
347
439
|
return {
|
|
348
440
|
...super._serialize(),
|
|
349
441
|
minimum_length: this.minLength,
|
|
@@ -355,301 +447,405 @@ class ze extends O {
|
|
|
355
447
|
* This function validates that the value given for "minimum length" (when creating a new field) is less than or
|
|
356
448
|
* equal to the value given for "maximum length".
|
|
357
449
|
*/
|
|
358
|
-
|
|
450
|
+
__publicField(StringOrTextField, "_validateMin", (value, allValues) => {
|
|
451
|
+
if (typeof allValues.maximum_length === "number" && typeof value === "number" && allValues.maximum_length < value) {
|
|
452
|
+
return "Minimum cannot be greater than maximum.";
|
|
453
|
+
}
|
|
454
|
+
return null;
|
|
455
|
+
});
|
|
456
|
+
/**
|
|
359
457
|
* This function validates that the value given for "maximum length" (when creating a new field) is greater than or
|
|
360
458
|
* equal to the value given for "minimum length".
|
|
361
459
|
*/
|
|
362
|
-
|
|
363
|
-
if (typeof
|
|
460
|
+
__publicField(StringOrTextField, "_validateMax", (value, allValues) => {
|
|
461
|
+
if (typeof value !== "number")
|
|
462
|
+
return null;
|
|
463
|
+
const { minimum_length: minimumLength } = allValues;
|
|
464
|
+
if (typeof minimumLength !== "number") {
|
|
364
465
|
return null;
|
|
365
|
-
|
|
366
|
-
|
|
466
|
+
}
|
|
467
|
+
if (minimumLength > value) {
|
|
468
|
+
return "Maximum cannot be less than minimum.";
|
|
469
|
+
}
|
|
470
|
+
return null;
|
|
471
|
+
});
|
|
472
|
+
const StringInput = memo(function StringInput2(props) {
|
|
473
|
+
const [{ inputId, labelId, severity, helpText, label, fieldProps, field }, rest] = useFormikInput(props);
|
|
474
|
+
const color = useSeverityColor(severity);
|
|
475
|
+
return /* @__PURE__ */ jsx(InputWithLabelAndHelpText, { helpText, severity, children: /* @__PURE__ */ jsx(InputWithLabel, { severity, inputId, labelId, label, children: /* @__PURE__ */ jsx(TextField$1.Input, { ...rest, ...fieldProps, type: field.inputType, id: inputId, color }) }) });
|
|
367
476
|
});
|
|
368
|
-
const
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
this.inputType = t;
|
|
477
|
+
const _StringField = class _StringField extends StringOrTextField {
|
|
478
|
+
constructor(options) {
|
|
479
|
+
const { inputType = "text", ...rest } = options;
|
|
480
|
+
const maxLength = options.maxLength ? Math.min(500, options.maxLength) : 500;
|
|
481
|
+
const minLength = options.minLength ? Math.min(options.minLength, maxLength) : void 0;
|
|
482
|
+
super({ ...rest, maxLength, minLength, type: "string" });
|
|
483
|
+
__publicField(this, "inputType");
|
|
484
|
+
this.inputType = inputType;
|
|
377
485
|
}
|
|
378
486
|
serialize() {
|
|
379
487
|
return { ...super._serialize(), input_type: this.inputType };
|
|
380
488
|
}
|
|
381
|
-
static deserialize(
|
|
382
|
-
if (
|
|
489
|
+
static deserialize(data) {
|
|
490
|
+
if (data.type !== "string")
|
|
383
491
|
throw new Error("Type mismatch.");
|
|
384
|
-
const { maximum_length
|
|
385
|
-
return new
|
|
492
|
+
const { maximum_length, minimum_length, input_type, ...rest } = data;
|
|
493
|
+
return new _StringField({ ...rest, maxLength: maximum_length, minLength: minimum_length, inputType: input_type });
|
|
386
494
|
}
|
|
387
|
-
getInput(
|
|
388
|
-
return /* @__PURE__ */
|
|
495
|
+
getInput(props) {
|
|
496
|
+
return /* @__PURE__ */ jsx(StringInput, { field: this, ...props });
|
|
389
497
|
}
|
|
390
498
|
};
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
499
|
+
__publicField(_StringField, "fieldTypeName", "Short Text");
|
|
500
|
+
__publicField(_StringField, "fieldTypeDescription", "Short text fields can hold up to 500 characters on a single line.");
|
|
501
|
+
__publicField(_StringField, "Icon", InputIcon);
|
|
502
|
+
let StringField = _StringField;
|
|
503
|
+
const TextInput = memo(function TextInput2(props) {
|
|
504
|
+
const [{ inputId, labelId, severity, helpText, label, fieldProps }, rest] = useFormikInput(props);
|
|
505
|
+
return /* @__PURE__ */ jsx(InputWithLabelAndHelpText, { helpText, severity, children: /* @__PURE__ */ jsx(InputWithLabel, { severity, inputId, labelId, label, children: /* @__PURE__ */ jsx(TextArea, { ...rest, ...fieldProps, resize: "vertical", id: inputId, severity }) }) });
|
|
506
|
+
});
|
|
507
|
+
const _TextField = class _TextField extends StringOrTextField {
|
|
508
|
+
constructor(options) {
|
|
509
|
+
const maxLength = options.maxLength ? Math.min(5e3, options.maxLength) : 5e3;
|
|
510
|
+
const minLength = options.minLength ? Math.min(options.minLength, maxLength) : void 0;
|
|
511
|
+
super({ ...options, maxLength, minLength, type: "text" });
|
|
400
512
|
}
|
|
401
513
|
serialize() {
|
|
402
514
|
return super._serialize();
|
|
403
515
|
}
|
|
404
|
-
static deserialize(
|
|
405
|
-
if (
|
|
516
|
+
static deserialize(data) {
|
|
517
|
+
if (data.type !== "text")
|
|
406
518
|
throw new Error("Type mismatch.");
|
|
407
|
-
const { maximum_length
|
|
408
|
-
return new
|
|
519
|
+
const { maximum_length, minimum_length, ...rest } = data;
|
|
520
|
+
return new _TextField({ ...rest, maxLength: maximum_length, minLength: minimum_length });
|
|
409
521
|
}
|
|
410
|
-
getInput(
|
|
411
|
-
return /* @__PURE__ */
|
|
522
|
+
getInput(props) {
|
|
523
|
+
return /* @__PURE__ */ jsx(TextInput, { field: this, ...props });
|
|
412
524
|
}
|
|
413
525
|
};
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
526
|
+
__publicField(_TextField, "fieldTypeName", "Paragraph");
|
|
527
|
+
__publicField(_TextField, "fieldTypeDescription", "Paragraph fields can hold up to 5000 characters and can have multiple lines.");
|
|
528
|
+
__publicField(_TextField, "Icon", RowsIcon);
|
|
529
|
+
let TextField = _TextField;
|
|
530
|
+
const SelectInput = memo(function SelectInput2(props) {
|
|
531
|
+
const [{ inputId, labelId, severity, helpText, label, fieldProps, field }, rest] = useFormikInput(props);
|
|
532
|
+
const { onChange, onBlur } = fieldProps;
|
|
533
|
+
const options = useMemo(
|
|
534
|
+
() => field.options.map((option) => ({ value: option.value, itemContent: option.label })),
|
|
535
|
+
[field.options]
|
|
536
|
+
);
|
|
537
|
+
const handleChange = useCallback(
|
|
538
|
+
(value) => {
|
|
539
|
+
onChange(value);
|
|
540
|
+
onBlur(value);
|
|
423
541
|
},
|
|
424
|
-
[
|
|
542
|
+
[onChange, onBlur]
|
|
425
543
|
);
|
|
426
|
-
return /* @__PURE__ */
|
|
427
|
-
|
|
544
|
+
return /* @__PURE__ */ jsx(InputWithLabelAndHelpText, { helpText, severity, children: /* @__PURE__ */ jsx(InputWithLabel, { severity, inputId, labelId, label, children: /* @__PURE__ */ jsx(
|
|
545
|
+
Select,
|
|
428
546
|
{
|
|
429
|
-
items:
|
|
430
|
-
...
|
|
431
|
-
onValueChange:
|
|
547
|
+
items: options,
|
|
548
|
+
...fieldProps,
|
|
549
|
+
onValueChange: handleChange,
|
|
432
550
|
placeholder: "Select one...",
|
|
433
|
-
id:
|
|
434
|
-
severity
|
|
435
|
-
...
|
|
551
|
+
id: inputId,
|
|
552
|
+
severity,
|
|
553
|
+
...rest
|
|
436
554
|
}
|
|
437
555
|
) }) });
|
|
438
|
-
})
|
|
556
|
+
});
|
|
557
|
+
const emptySection = (id = "", fields = []) => ({
|
|
439
558
|
type: "section",
|
|
440
|
-
fields
|
|
441
|
-
identifier:
|
|
559
|
+
fields,
|
|
560
|
+
identifier: id,
|
|
442
561
|
label: null,
|
|
443
562
|
condition: null,
|
|
444
|
-
conditional:
|
|
445
|
-
})
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
563
|
+
conditional: false
|
|
564
|
+
});
|
|
565
|
+
const wrapRootFieldsWithFieldSection = (revision) => {
|
|
566
|
+
if (!revision)
|
|
567
|
+
return void 0;
|
|
568
|
+
const fields = revision.fields;
|
|
569
|
+
let pending = [];
|
|
570
|
+
const sections = [];
|
|
571
|
+
for (const field of fields) {
|
|
572
|
+
if (field.type === "section") {
|
|
573
|
+
if (pending.length > 0) {
|
|
574
|
+
sections.push(emptySection(`AUTO_section-${fields.indexOf(field)}`, pending));
|
|
575
|
+
pending = [];
|
|
576
|
+
}
|
|
577
|
+
sections.push(field);
|
|
578
|
+
} else {
|
|
579
|
+
pending.push(field);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
if (pending.length > 0) {
|
|
583
|
+
sections.push(emptySection("AUTO_section-last", pending));
|
|
584
|
+
}
|
|
585
|
+
return { ...revision, fields: sections, description: revision.description ?? "" };
|
|
454
586
|
};
|
|
455
|
-
function
|
|
456
|
-
const
|
|
457
|
-
|
|
587
|
+
function reorder(list, source, destination) {
|
|
588
|
+
const result = Array.from(list);
|
|
589
|
+
const [removed] = result.splice(source, 1);
|
|
590
|
+
if (!removed)
|
|
458
591
|
throw new Error("Could not find field to reorder.");
|
|
459
|
-
|
|
592
|
+
result.splice(destination, 0, removed);
|
|
593
|
+
return result;
|
|
460
594
|
}
|
|
461
|
-
function
|
|
462
|
-
const
|
|
463
|
-
|
|
595
|
+
function replace(list, index, value) {
|
|
596
|
+
const result = Array.from(list);
|
|
597
|
+
result[index] = value;
|
|
598
|
+
return result;
|
|
464
599
|
}
|
|
465
|
-
function
|
|
466
|
-
const
|
|
467
|
-
|
|
600
|
+
function insert(list, index, value) {
|
|
601
|
+
const result = Array.from(list ?? []);
|
|
602
|
+
result.splice(index, 0, value);
|
|
603
|
+
return result;
|
|
468
604
|
}
|
|
469
|
-
function
|
|
470
|
-
const
|
|
471
|
-
|
|
605
|
+
function remove(list, index) {
|
|
606
|
+
const result = Array.from(list);
|
|
607
|
+
result.splice(index, 1);
|
|
608
|
+
return result;
|
|
472
609
|
}
|
|
473
|
-
const
|
|
474
|
-
if (typeof
|
|
475
|
-
return
|
|
476
|
-
const
|
|
477
|
-
return `${
|
|
478
|
-
}
|
|
479
|
-
|
|
610
|
+
const makeIdentifier = (existing, label) => {
|
|
611
|
+
if (typeof existing === "string" && existing.length > 0)
|
|
612
|
+
return existing;
|
|
613
|
+
const now = /* @__PURE__ */ new Date();
|
|
614
|
+
return `${slugify(label)}-${now.getTime()}`;
|
|
615
|
+
};
|
|
616
|
+
const findFieldByIdentifier = (fields, identifier) => {
|
|
617
|
+
if (!identifier)
|
|
480
618
|
return null;
|
|
481
|
-
for (const
|
|
482
|
-
if (
|
|
483
|
-
const
|
|
484
|
-
if (
|
|
485
|
-
return
|
|
486
|
-
} else if (
|
|
487
|
-
return
|
|
619
|
+
for (const field of fields) {
|
|
620
|
+
if (field.type === "section") {
|
|
621
|
+
const result = findFieldByIdentifier(field.fields, identifier);
|
|
622
|
+
if (result)
|
|
623
|
+
return result;
|
|
624
|
+
} else if (field.identifier === identifier) {
|
|
625
|
+
return field;
|
|
626
|
+
}
|
|
627
|
+
}
|
|
488
628
|
return null;
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
629
|
+
};
|
|
630
|
+
const makeConditionalSourceFields = (sections, index) => {
|
|
631
|
+
return sections.filter((_, i) => i < index).flatMap((field) => field.fields);
|
|
632
|
+
};
|
|
633
|
+
const getTakenFieldLabels = (fields) => {
|
|
634
|
+
return fields.flatMap(
|
|
635
|
+
(field) => field.type === "section" ? [...field.fields.map((f) => f.label), field.label] : field.label
|
|
636
|
+
).filter((id) => id !== null);
|
|
637
|
+
};
|
|
638
|
+
const incrementFieldLabel = (label, takenLabels) => {
|
|
639
|
+
let count = 1;
|
|
640
|
+
let newLabel = `${label} (${count})`;
|
|
641
|
+
while (takenLabels.includes(newLabel)) {
|
|
642
|
+
newLabel = `${label} (${++count})`;
|
|
643
|
+
}
|
|
644
|
+
return newLabel;
|
|
645
|
+
};
|
|
646
|
+
const MultiStringInput = memo(function MultiStringInput2(props) {
|
|
647
|
+
const [{ inputId, labelId, severity, helpText, label, fieldProps }, rest] = useFormikInput(props);
|
|
648
|
+
const color = useSeverityColor(severity);
|
|
649
|
+
const value = useMemo(() => Array.isArray(fieldProps.value) ? fieldProps.value : [], [fieldProps.value]);
|
|
650
|
+
const { onChange, onBlur } = fieldProps;
|
|
651
|
+
const droppableId = `${inputId}-droppable`;
|
|
652
|
+
const { disabled } = rest;
|
|
653
|
+
const [intermediateValue, setIntermediateValue] = useState("");
|
|
654
|
+
const [internalError, setInternalError] = useState("");
|
|
655
|
+
const updatedHelpText = internalError || helpText;
|
|
656
|
+
const updatedColor = internalError ? "red" : color;
|
|
657
|
+
const setValueAndTouched = useCallback(
|
|
658
|
+
(newValue) => {
|
|
659
|
+
onChange(newValue);
|
|
660
|
+
onBlur(newValue);
|
|
500
661
|
},
|
|
501
|
-
[
|
|
502
|
-
)
|
|
503
|
-
|
|
504
|
-
|
|
662
|
+
[onChange, onBlur]
|
|
663
|
+
);
|
|
664
|
+
const handleChange = useCallback(
|
|
665
|
+
(e) => {
|
|
666
|
+
if (value.findIndex((option) => option.value === e.target.value.trim()) >= 0) {
|
|
667
|
+
setInternalError("All options must be unique");
|
|
668
|
+
} else if (!e.target.value) {
|
|
669
|
+
setInternalError("Option cannot be empty");
|
|
670
|
+
} else {
|
|
671
|
+
setInternalError("");
|
|
672
|
+
}
|
|
673
|
+
setIntermediateValue(e.target.value);
|
|
505
674
|
},
|
|
506
|
-
[
|
|
507
|
-
)
|
|
508
|
-
|
|
675
|
+
[setIntermediateValue, value]
|
|
676
|
+
);
|
|
677
|
+
const addOption = useCallback(() => {
|
|
678
|
+
if (internalError)
|
|
509
679
|
return;
|
|
510
|
-
if (!
|
|
511
|
-
return
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
(
|
|
516
|
-
|
|
680
|
+
if (!intermediateValue.trim()) {
|
|
681
|
+
return setInternalError("Option cannot be empty");
|
|
682
|
+
}
|
|
683
|
+
const trimmedValue = intermediateValue.trim();
|
|
684
|
+
setValueAndTouched([...value, { value: trimmedValue, label: trimmedValue }]);
|
|
685
|
+
setIntermediateValue("");
|
|
686
|
+
}, [intermediateValue, internalError, setValueAndTouched, value]);
|
|
687
|
+
const handleKeyDown = useCallback(
|
|
688
|
+
(e) => {
|
|
689
|
+
if (e.key === "Enter") {
|
|
690
|
+
e.preventDefault();
|
|
691
|
+
addOption();
|
|
692
|
+
}
|
|
517
693
|
},
|
|
518
|
-
[
|
|
519
|
-
)
|
|
520
|
-
|
|
521
|
-
|
|
694
|
+
[addOption]
|
|
695
|
+
);
|
|
696
|
+
const handleDeleteOption = useCallback(
|
|
697
|
+
(index) => {
|
|
698
|
+
setValueAndTouched(remove(value, index));
|
|
522
699
|
},
|
|
523
|
-
[
|
|
524
|
-
)
|
|
525
|
-
|
|
526
|
-
|
|
700
|
+
[value, setValueAndTouched]
|
|
701
|
+
);
|
|
702
|
+
const handleDragEnd = useCallback(
|
|
703
|
+
(result) => {
|
|
704
|
+
if (!result.destination)
|
|
527
705
|
return;
|
|
528
|
-
const
|
|
529
|
-
|
|
706
|
+
const sourceIndex = result.source.index;
|
|
707
|
+
const destinationIndex = result.destination.index;
|
|
708
|
+
setValueAndTouched(reorder(value, sourceIndex, destinationIndex));
|
|
530
709
|
},
|
|
531
|
-
[
|
|
710
|
+
[setValueAndTouched, value]
|
|
532
711
|
);
|
|
533
|
-
return /* @__PURE__ */
|
|
534
|
-
/* @__PURE__ */
|
|
535
|
-
/* @__PURE__ */
|
|
536
|
-
|
|
712
|
+
return /* @__PURE__ */ jsx(DragDropContext, { onDragEnd: handleDragEnd, children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: "2", children: [
|
|
713
|
+
/* @__PURE__ */ jsx(InputWithLabelAndHelpText, { helpText: updatedHelpText, severity, children: /* @__PURE__ */ jsx(InputWithLabel, { severity, inputId, labelId, label, children: (!disabled || value.length === 0) && /* @__PURE__ */ jsxs(Flex, { gap: "2", children: [
|
|
714
|
+
/* @__PURE__ */ jsx(Box, { grow: "1", children: /* @__PURE__ */ jsx(
|
|
715
|
+
TextField$1.Input,
|
|
537
716
|
{
|
|
538
717
|
placeholder: "Press enter to add a new option",
|
|
539
|
-
...
|
|
540
|
-
...
|
|
541
|
-
value:
|
|
542
|
-
onChange:
|
|
543
|
-
onKeyDown:
|
|
544
|
-
id:
|
|
545
|
-
color:
|
|
718
|
+
...rest,
|
|
719
|
+
...fieldProps,
|
|
720
|
+
value: intermediateValue,
|
|
721
|
+
onChange: handleChange,
|
|
722
|
+
onKeyDown: handleKeyDown,
|
|
723
|
+
id: inputId,
|
|
724
|
+
color: updatedColor,
|
|
546
725
|
onBlur: void 0
|
|
547
726
|
}
|
|
548
727
|
) }),
|
|
549
|
-
/* @__PURE__ */
|
|
550
|
-
|
|
728
|
+
/* @__PURE__ */ jsx(
|
|
729
|
+
IconButton,
|
|
551
730
|
{
|
|
552
731
|
type: "button",
|
|
553
732
|
"aria-label": "Add option",
|
|
554
|
-
disabled: !!
|
|
555
|
-
onClick:
|
|
556
|
-
children: /* @__PURE__ */
|
|
733
|
+
disabled: !!internalError || disabled,
|
|
734
|
+
onClick: addOption,
|
|
735
|
+
children: /* @__PURE__ */ jsx(PlusIcon, {})
|
|
557
736
|
}
|
|
558
737
|
)
|
|
559
738
|
] }) }) }),
|
|
560
|
-
/* @__PURE__ */
|
|
561
|
-
|
|
562
|
-
|
|
739
|
+
/* @__PURE__ */ jsx(Droppable, { droppableId, children: (droppableProvided) => /* @__PURE__ */ jsxs(Flex, { ...droppableProvided.droppableProps, ref: droppableProvided.innerRef, direction: "column", children: [
|
|
740
|
+
value.map((option, index) => /* @__PURE__ */ jsx(
|
|
741
|
+
Draggable,
|
|
563
742
|
{
|
|
564
|
-
draggableId: `${
|
|
565
|
-
index
|
|
566
|
-
isDragDisabled:
|
|
567
|
-
children: ({ draggableProps
|
|
568
|
-
|
|
743
|
+
draggableId: `${option.value}-draggable`,
|
|
744
|
+
index,
|
|
745
|
+
isDragDisabled: disabled,
|
|
746
|
+
children: ({ draggableProps, dragHandleProps, innerRef }) => /* @__PURE__ */ jsx(
|
|
747
|
+
Flex,
|
|
569
748
|
{
|
|
570
|
-
...
|
|
571
|
-
...
|
|
572
|
-
ref:
|
|
749
|
+
...dragHandleProps,
|
|
750
|
+
...draggableProps,
|
|
751
|
+
ref: innerRef,
|
|
573
752
|
gap: "2",
|
|
574
753
|
align: "center",
|
|
575
754
|
justify: "between",
|
|
576
755
|
mb: "1",
|
|
577
|
-
asChild:
|
|
578
|
-
children: /* @__PURE__ */
|
|
579
|
-
/* @__PURE__ */
|
|
580
|
-
/* @__PURE__ */
|
|
581
|
-
|
|
756
|
+
asChild: true,
|
|
757
|
+
children: /* @__PURE__ */ jsxs(Badge, { color: "gray", size: "2", children: [
|
|
758
|
+
/* @__PURE__ */ jsx("span", { children: option.label }),
|
|
759
|
+
/* @__PURE__ */ jsx(
|
|
760
|
+
IconButton,
|
|
582
761
|
{
|
|
583
762
|
size: "small",
|
|
584
763
|
variant: "ghost",
|
|
585
764
|
type: "button",
|
|
586
765
|
"aria-label": "Delete option",
|
|
587
766
|
severity: "info",
|
|
588
|
-
disabled
|
|
589
|
-
onClick: () =>
|
|
590
|
-
children: /* @__PURE__ */
|
|
767
|
+
disabled,
|
|
768
|
+
onClick: () => handleDeleteOption(index),
|
|
769
|
+
children: /* @__PURE__ */ jsx(Cross1Icon, {})
|
|
591
770
|
}
|
|
592
771
|
)
|
|
593
772
|
] })
|
|
594
773
|
}
|
|
595
774
|
)
|
|
596
775
|
},
|
|
597
|
-
|
|
776
|
+
option.value
|
|
598
777
|
)),
|
|
599
|
-
|
|
778
|
+
droppableProvided.placeholder
|
|
600
779
|
] }) })
|
|
601
780
|
] }) });
|
|
602
|
-
})
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
this
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
781
|
+
});
|
|
782
|
+
const _MultiStringField = class _MultiStringField extends BaseField {
|
|
783
|
+
constructor(options) {
|
|
784
|
+
const { minimum_length, maximum_length, ...rest } = options;
|
|
785
|
+
super({ ...rest, type: "multi-string" });
|
|
786
|
+
__publicField(this, "minLength");
|
|
787
|
+
__publicField(this, "maxLength");
|
|
788
|
+
__publicField(this, "onlyValidateAfterTouched", false);
|
|
789
|
+
this.minLength = minimum_length ?? 0;
|
|
790
|
+
this.maxLength = maximum_length ?? Infinity;
|
|
791
|
+
}
|
|
792
|
+
getValueFromChangeEvent(event) {
|
|
793
|
+
if (Array.isArray(event))
|
|
794
|
+
return event;
|
|
614
795
|
throw new Error("Expected an array.");
|
|
615
796
|
}
|
|
616
|
-
getInput(
|
|
617
|
-
return /* @__PURE__ */
|
|
797
|
+
getInput(props) {
|
|
798
|
+
return /* @__PURE__ */ jsx(MultiStringInput, { field: this, ...props });
|
|
618
799
|
}
|
|
619
800
|
serialize() {
|
|
620
801
|
return { ...super._serialize(), minimum_length: this.minLength, maximum_length: this.maxLength };
|
|
621
802
|
}
|
|
622
|
-
isBlank(
|
|
623
|
-
return super.isBlank(
|
|
803
|
+
isBlank(value) {
|
|
804
|
+
return super.isBlank(value) || value.length === 0;
|
|
624
805
|
}
|
|
625
806
|
getFieldValidators() {
|
|
626
|
-
const
|
|
627
|
-
|
|
628
|
-
if (Array.isArray(
|
|
807
|
+
const validators = super.getFieldValidators();
|
|
808
|
+
validators.push((value) => {
|
|
809
|
+
if (Array.isArray(value) && value.length < this.minLength) {
|
|
629
810
|
return `Must have at least ${this.minLength} options.`;
|
|
630
|
-
|
|
631
|
-
|
|
811
|
+
}
|
|
812
|
+
});
|
|
813
|
+
validators.push((value) => {
|
|
814
|
+
if (Array.isArray(value) && value.length > this.maxLength) {
|
|
632
815
|
return `Must have at most ${this.maxLength} options.`;
|
|
633
|
-
|
|
816
|
+
}
|
|
817
|
+
});
|
|
818
|
+
return validators;
|
|
634
819
|
}
|
|
635
|
-
static deserialize(
|
|
636
|
-
if (
|
|
820
|
+
static deserialize(data) {
|
|
821
|
+
if (data.type !== "multi-string")
|
|
637
822
|
throw new Error("Type mismatch.");
|
|
638
|
-
return new
|
|
823
|
+
return new _MultiStringField(data);
|
|
639
824
|
}
|
|
640
825
|
};
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
this
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
826
|
+
__publicField(_MultiStringField, "fieldTypeName", "Multi-string");
|
|
827
|
+
__publicField(_MultiStringField, "fieldTypeDescription", "Allows the user to provide multiple unique strings.");
|
|
828
|
+
__publicField(_MultiStringField, "Icon", ListBulletIcon);
|
|
829
|
+
let MultiStringField = _MultiStringField;
|
|
830
|
+
class BaseSelectField extends BaseField {
|
|
831
|
+
constructor(options) {
|
|
832
|
+
super(options);
|
|
833
|
+
__publicField(this, "options");
|
|
834
|
+
__publicField(this, "onlyValidateAfterTouched", false);
|
|
835
|
+
const encounteredIds = /* @__PURE__ */ new Set();
|
|
836
|
+
this.options = options.options.map((option) => {
|
|
837
|
+
if (typeof option === "string") {
|
|
838
|
+
option = { label: option, value: option };
|
|
839
|
+
}
|
|
840
|
+
encounteredIds.add(option.label);
|
|
841
|
+
return option;
|
|
842
|
+
});
|
|
843
|
+
if (encounteredIds.size !== options.options.length) {
|
|
844
|
+
console.error(
|
|
845
|
+
`${options.options.length - encounteredIds.size} duplicate identifiers found in options. This may cause unexpected behavior. Options:`,
|
|
846
|
+
options.options
|
|
847
|
+
);
|
|
848
|
+
}
|
|
653
849
|
}
|
|
654
850
|
_serialize() {
|
|
655
851
|
return {
|
|
@@ -659,10 +855,10 @@ class Vt extends O {
|
|
|
659
855
|
}
|
|
660
856
|
static getFieldCreationSchema() {
|
|
661
857
|
return [
|
|
662
|
-
new
|
|
858
|
+
new MultiStringField({
|
|
663
859
|
label: "Options",
|
|
664
860
|
description: "List possible options for the user to select from.",
|
|
665
|
-
required:
|
|
861
|
+
required: true,
|
|
666
862
|
identifier: "options",
|
|
667
863
|
minimum_length: 2,
|
|
668
864
|
maximum_length: 20
|
|
@@ -670,193 +866,247 @@ class Vt extends O {
|
|
|
670
866
|
];
|
|
671
867
|
}
|
|
672
868
|
}
|
|
673
|
-
const
|
|
674
|
-
constructor(
|
|
675
|
-
super({ ...
|
|
869
|
+
const _SelectField = class _SelectField extends BaseSelectField {
|
|
870
|
+
constructor(options) {
|
|
871
|
+
super({ ...options, type: "select" });
|
|
676
872
|
}
|
|
677
|
-
getValueFromChangeEvent(
|
|
678
|
-
|
|
873
|
+
getValueFromChangeEvent(event) {
|
|
874
|
+
if (typeof event === "string")
|
|
875
|
+
return event;
|
|
876
|
+
return event.target.value;
|
|
679
877
|
}
|
|
680
878
|
serialize() {
|
|
681
879
|
return super._serialize();
|
|
682
880
|
}
|
|
683
|
-
static deserialize(
|
|
684
|
-
if (
|
|
881
|
+
static deserialize(data) {
|
|
882
|
+
if (data.type !== "select")
|
|
685
883
|
throw new Error("Type mismatch.");
|
|
686
|
-
return new
|
|
884
|
+
return new _SelectField(data);
|
|
687
885
|
}
|
|
688
|
-
getInput(
|
|
689
|
-
return /* @__PURE__ */
|
|
886
|
+
getInput(props) {
|
|
887
|
+
return /* @__PURE__ */ jsx(SelectInput, { field: this, ...props });
|
|
690
888
|
}
|
|
691
889
|
};
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
890
|
+
__publicField(_SelectField, "fieldTypeName", "Dropdown");
|
|
891
|
+
__publicField(_SelectField, "fieldTypeDescription", "Allows the user to select a single option from a list of options.");
|
|
892
|
+
__publicField(_SelectField, "Icon", DropdownMenuIcon);
|
|
893
|
+
let SelectField = _SelectField;
|
|
894
|
+
const parseValueToArray = (value) => {
|
|
895
|
+
if (!value)
|
|
896
|
+
return [];
|
|
897
|
+
if (Array.isArray(value))
|
|
898
|
+
return value;
|
|
899
|
+
return [value];
|
|
900
|
+
};
|
|
901
|
+
const MultiSelectInput = memo(function MultiSelectInput2(props) {
|
|
902
|
+
const [{ inputId, labelId, severity, helpText, label, fieldProps, field }, rest] = useFormikInput(props);
|
|
903
|
+
const { onChange, onBlur } = fieldProps;
|
|
904
|
+
const value = useMemo(() => parseValueToArray(fieldProps.value), [fieldProps.value]);
|
|
905
|
+
const handleChange = useCallback(
|
|
906
|
+
(value2) => {
|
|
907
|
+
onChange(value2);
|
|
908
|
+
onBlur(value2);
|
|
698
909
|
},
|
|
699
|
-
[
|
|
910
|
+
[onChange, onBlur]
|
|
700
911
|
);
|
|
701
|
-
return /* @__PURE__ */
|
|
702
|
-
|
|
912
|
+
return /* @__PURE__ */ jsx(InputWithLabelAndHelpText, { helpText, severity, children: /* @__PURE__ */ jsx(InputWithLabel, { severity, inputId, labelId, label, children: /* @__PURE__ */ jsx(
|
|
913
|
+
MultiSelect,
|
|
703
914
|
{
|
|
704
|
-
value
|
|
705
|
-
onValueChange:
|
|
706
|
-
options:
|
|
707
|
-
name:
|
|
915
|
+
value,
|
|
916
|
+
onValueChange: handleChange,
|
|
917
|
+
options: field.options,
|
|
918
|
+
name: fieldProps.name,
|
|
708
919
|
placeholder: "Select one or more...",
|
|
709
|
-
id:
|
|
710
|
-
severity
|
|
711
|
-
...
|
|
920
|
+
id: inputId,
|
|
921
|
+
severity,
|
|
922
|
+
...rest
|
|
712
923
|
}
|
|
713
924
|
) }) });
|
|
714
|
-
})
|
|
715
|
-
|
|
716
|
-
|
|
925
|
+
});
|
|
926
|
+
const _MultiSelectField = class _MultiSelectField extends BaseSelectField {
|
|
927
|
+
constructor(options) {
|
|
928
|
+
super({ ...options, type: "multi-select" });
|
|
717
929
|
}
|
|
718
|
-
getValueFromChangeEvent(
|
|
719
|
-
if (Array.isArray(
|
|
720
|
-
return
|
|
930
|
+
getValueFromChangeEvent(event) {
|
|
931
|
+
if (Array.isArray(event))
|
|
932
|
+
return event;
|
|
721
933
|
throw new Error("Expected an array.");
|
|
722
934
|
}
|
|
723
|
-
isBlank(
|
|
724
|
-
return super.isBlank(
|
|
935
|
+
isBlank(value) {
|
|
936
|
+
return super.isBlank(value) || value.length === 0;
|
|
725
937
|
}
|
|
726
938
|
serialize() {
|
|
727
939
|
return super._serialize();
|
|
728
940
|
}
|
|
729
|
-
static deserialize(
|
|
730
|
-
if (
|
|
941
|
+
static deserialize(data) {
|
|
942
|
+
if (data.type !== "multi-select")
|
|
731
943
|
throw new Error("Type mismatch.");
|
|
732
|
-
return new
|
|
944
|
+
return new _MultiSelectField(data);
|
|
733
945
|
}
|
|
734
|
-
getInput(
|
|
735
|
-
return /* @__PURE__ */
|
|
946
|
+
getInput(props) {
|
|
947
|
+
return /* @__PURE__ */ jsx(MultiSelectInput, { field: this, ...props });
|
|
736
948
|
}
|
|
737
949
|
};
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
950
|
+
__publicField(_MultiSelectField, "fieldTypeName", "Multi-select");
|
|
951
|
+
__publicField(_MultiSelectField, "fieldTypeDescription", "Allows the user to select a multiple options from a list of options.");
|
|
952
|
+
__publicField(_MultiSelectField, "Icon", CheckboxIcon);
|
|
953
|
+
let MultiSelectField = _MultiSelectField;
|
|
954
|
+
const FieldInputCloner = memo(function FieldInputCloner2({ field, ...props }) {
|
|
955
|
+
const [{ value: identifier }] = useField(field.options.clonedFieldIdentifier);
|
|
956
|
+
const deserializedField = useMemo(() => {
|
|
957
|
+
const options = field.options.getFieldToClone(identifier);
|
|
958
|
+
if (!options)
|
|
959
|
+
return null;
|
|
960
|
+
return deserialize(options);
|
|
961
|
+
}, [field.options, identifier]);
|
|
962
|
+
return useFieldInput(deserializedField, props);
|
|
746
963
|
});
|
|
747
|
-
class
|
|
748
|
-
constructor(
|
|
749
|
-
super({ ...
|
|
750
|
-
|
|
964
|
+
class CustomField extends BaseField {
|
|
965
|
+
constructor(options, Component) {
|
|
966
|
+
super({ ...options, type: "custom" });
|
|
967
|
+
__publicField(this, "Component");
|
|
751
968
|
// identifier of the field whose value is the label of the field to re-render
|
|
752
|
-
|
|
753
|
-
this.options =
|
|
969
|
+
__publicField(this, "options");
|
|
970
|
+
this.options = options;
|
|
971
|
+
this.Component = Component;
|
|
754
972
|
}
|
|
755
973
|
serialize() {
|
|
756
974
|
throw new Error("Serializing only supported for public input types.");
|
|
757
975
|
}
|
|
758
|
-
getInput(
|
|
759
|
-
const
|
|
760
|
-
return /* @__PURE__ */
|
|
976
|
+
getInput(props) {
|
|
977
|
+
const CustomInput = this.Component;
|
|
978
|
+
return /* @__PURE__ */ jsx(CustomInput, { field: this, ...props });
|
|
761
979
|
}
|
|
762
980
|
}
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
981
|
+
__publicField(CustomField, "fieldTypeName", "Custom");
|
|
982
|
+
__publicField(CustomField, "fieldTypeDescription", "Allows re-rendering of field already in the form");
|
|
983
|
+
class FieldInputClonerField extends CustomField {
|
|
984
|
+
constructor(options) {
|
|
985
|
+
super(options, FieldInputCloner);
|
|
767
986
|
}
|
|
768
987
|
}
|
|
769
|
-
const
|
|
770
|
-
const { field:
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
988
|
+
const FieldSectionLayout = memo(function FieldSectionLayout2(props) {
|
|
989
|
+
const { field: section, ...rest } = props;
|
|
990
|
+
const { label, description: description2, fields, condition } = section;
|
|
991
|
+
const { values, setFieldValue } = useFormikContext();
|
|
992
|
+
const conditionValue = (condition == null ? void 0 : condition.identifier) ? get(values, condition.identifier) : void 0;
|
|
993
|
+
const conditionMet = useMemo(() => isConditionMet(condition, conditionValue), [condition, conditionValue]);
|
|
994
|
+
useEffect(() => {
|
|
995
|
+
if (!conditionMet) {
|
|
996
|
+
for (const childField of fields) {
|
|
997
|
+
setFieldValue(childField.getId(), "").then();
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
}, [conditionMet, fields, setFieldValue]);
|
|
1001
|
+
const inputs = useFieldInputs(fields, rest);
|
|
1002
|
+
if (!conditionMet) {
|
|
1003
|
+
return null;
|
|
1004
|
+
}
|
|
1005
|
+
if (!label) {
|
|
1006
|
+
return inputs;
|
|
1007
|
+
}
|
|
1008
|
+
return /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: "3", children: [
|
|
1009
|
+
/* @__PURE__ */ jsxs(Flex, { direction: "column", children: [
|
|
1010
|
+
/* @__PURE__ */ jsx(Heading, { as: "h3", size: "3", children: label }),
|
|
1011
|
+
/* @__PURE__ */ jsx(Text, { className: styles$3.description, children: description2 })
|
|
781
1012
|
] }),
|
|
782
|
-
|
|
783
|
-
] }) })
|
|
784
|
-
})
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
this
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
1013
|
+
inputs
|
|
1014
|
+
] }) });
|
|
1015
|
+
});
|
|
1016
|
+
const _FieldSection = class _FieldSection extends BaseFormElement {
|
|
1017
|
+
constructor(options) {
|
|
1018
|
+
const { label = null, fields, condition = null, conditional, ...base } = options;
|
|
1019
|
+
super({ ...base, type: "section" });
|
|
1020
|
+
__publicField(this, "label");
|
|
1021
|
+
__publicField(this, "fields");
|
|
1022
|
+
__publicField(this, "condition");
|
|
1023
|
+
this.fields = fields;
|
|
1024
|
+
this.condition = condition;
|
|
1025
|
+
this.label = label;
|
|
1026
|
+
if (conditional === false) {
|
|
1027
|
+
this.condition = null;
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
static getFieldCreationSchema(options) {
|
|
1031
|
+
if (options.length === 0)
|
|
1032
|
+
return [];
|
|
1033
|
+
return [
|
|
1034
|
+
new BooleanField({
|
|
796
1035
|
label: "Conditional",
|
|
797
1036
|
description: "Conditionally show or hide this section.",
|
|
798
1037
|
identifier: "conditional",
|
|
799
|
-
required:
|
|
1038
|
+
required: false
|
|
800
1039
|
}),
|
|
801
1040
|
// Declare a section that will hold options for the condition (if any).
|
|
802
|
-
new
|
|
1041
|
+
new _FieldSection({
|
|
803
1042
|
label: "Conditional settings",
|
|
804
1043
|
identifier: "conditional-settings",
|
|
805
1044
|
// This section will only be rendered if the above "Conditional" field is checked.
|
|
806
1045
|
condition: {
|
|
807
1046
|
identifier: "conditional",
|
|
808
|
-
value:
|
|
1047
|
+
value: true
|
|
809
1048
|
},
|
|
810
1049
|
// These are the options of the condition.
|
|
811
1050
|
fields: [
|
|
812
1051
|
// Declare a select field that will be used to select the field against which we will check the
|
|
813
1052
|
// condition. This must be selected before the next field is rendered.
|
|
814
|
-
new
|
|
1053
|
+
new SelectField({
|
|
815
1054
|
label: "Field",
|
|
816
1055
|
description: "The field to use for the condition.",
|
|
817
1056
|
// The options (for the field against which we will check the condition) are all the labels of
|
|
818
1057
|
// the fields in the previous section(s) (or fields declared before with no section) that
|
|
819
1058
|
// support conditions. We pass in both the label and the identifier of each supported field. The
|
|
820
1059
|
// identifier becomes the value of the option.
|
|
821
|
-
options:
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
1060
|
+
options: options.map((option) => {
|
|
1061
|
+
if (!option.label)
|
|
1062
|
+
return null;
|
|
1063
|
+
if (option.type === "upload")
|
|
1064
|
+
return null;
|
|
1065
|
+
return {
|
|
1066
|
+
label: option.label,
|
|
1067
|
+
value: option.identifier
|
|
1068
|
+
};
|
|
1069
|
+
}).filter((option) => !!option),
|
|
825
1070
|
identifier: "condition.identifier",
|
|
826
|
-
required:
|
|
1071
|
+
required: true
|
|
827
1072
|
}),
|
|
828
1073
|
// Declare a custom field that will be used to input a value for the condition. The value of the
|
|
829
1074
|
// conditional field selected in the previous step must be equal to the value the user inputs into
|
|
830
1075
|
// this field for the section to be rendered.
|
|
831
|
-
new
|
|
1076
|
+
new FieldInputClonerField({
|
|
832
1077
|
label: "Value",
|
|
833
1078
|
identifier: "condition.value",
|
|
834
|
-
required:
|
|
1079
|
+
required: true,
|
|
835
1080
|
clonedFieldIdentifier: "condition.identifier",
|
|
836
|
-
getFieldToClone(
|
|
837
|
-
if (!
|
|
1081
|
+
getFieldToClone(identifier) {
|
|
1082
|
+
if (!identifier) {
|
|
838
1083
|
return null;
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
1084
|
+
}
|
|
1085
|
+
const option = options.find((option2) => option2.identifier === identifier);
|
|
1086
|
+
if (!option) {
|
|
1087
|
+
console.error("Could not find field with identifier", identifier);
|
|
1088
|
+
return null;
|
|
1089
|
+
}
|
|
1090
|
+
return {
|
|
1091
|
+
...option,
|
|
842
1092
|
// Override some options to make it make sense in the context and to make it work with the framework.
|
|
843
1093
|
label: "Value",
|
|
844
1094
|
identifier: "condition.value",
|
|
845
1095
|
description: "The value to compare against.",
|
|
846
|
-
required:
|
|
847
|
-
}
|
|
1096
|
+
required: option.type !== "boolean"
|
|
1097
|
+
};
|
|
848
1098
|
}
|
|
849
1099
|
})
|
|
850
1100
|
]
|
|
851
1101
|
})
|
|
852
1102
|
];
|
|
853
1103
|
}
|
|
854
|
-
static deserialize(
|
|
855
|
-
var
|
|
856
|
-
if (
|
|
1104
|
+
static deserialize(data) {
|
|
1105
|
+
var _a;
|
|
1106
|
+
if (data.type !== "section")
|
|
857
1107
|
throw new Error("Invalid type");
|
|
858
|
-
const
|
|
859
|
-
return new
|
|
1108
|
+
const fields = ((_a = data.fields) == null ? void 0 : _a.map(deserializeField)) ?? [];
|
|
1109
|
+
return new _FieldSection({ ...data, fields });
|
|
860
1110
|
}
|
|
861
1111
|
conditional() {
|
|
862
1112
|
return this.condition !== null;
|
|
@@ -867,144 +1117,199 @@ const Ki = S(function(i) {
|
|
|
867
1117
|
label: this.label,
|
|
868
1118
|
condition: this.condition,
|
|
869
1119
|
conditional: this.conditional(),
|
|
870
|
-
fields: this.fields.map((
|
|
1120
|
+
fields: this.fields.map((field) => field.serialize())
|
|
871
1121
|
};
|
|
872
1122
|
}
|
|
873
|
-
getErrors(
|
|
874
|
-
const
|
|
875
|
-
for (const
|
|
876
|
-
const
|
|
877
|
-
|
|
1123
|
+
getErrors(allValues) {
|
|
1124
|
+
const errors = {};
|
|
1125
|
+
for (const field of this.fields) {
|
|
1126
|
+
const id = field.getId();
|
|
1127
|
+
const error = field.getError(get(allValues, id), allValues);
|
|
1128
|
+
if (error) {
|
|
1129
|
+
set(errors, field.getId(), error);
|
|
1130
|
+
}
|
|
878
1131
|
}
|
|
879
|
-
return
|
|
1132
|
+
return errors;
|
|
880
1133
|
}
|
|
881
|
-
getInput(
|
|
882
|
-
return /* @__PURE__ */
|
|
1134
|
+
getInput(props) {
|
|
1135
|
+
return /* @__PURE__ */ jsx(FieldSectionLayout, { field: this, ...props });
|
|
883
1136
|
}
|
|
884
1137
|
};
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
1138
|
+
__publicField(_FieldSection, "fieldTypeName", "Section");
|
|
1139
|
+
__publicField(_FieldSection, "fieldTypeDescription", "Sections can be useful for grouping fields together. They can also be conditionally shown or hidden.");
|
|
1140
|
+
let FieldSection = _FieldSection;
|
|
1141
|
+
const previewImage = "_previewImage_1ig84_1";
|
|
1142
|
+
const styles$2 = {
|
|
1143
|
+
previewImage
|
|
1144
|
+
};
|
|
1145
|
+
const convertBytesToLargestUnit = (bytes) => {
|
|
1146
|
+
const units = ["byte", "kilobyte", "megabyte"];
|
|
1147
|
+
let sizeInUnit = bytes;
|
|
1148
|
+
let unitIndex = 0;
|
|
1149
|
+
while (sizeInUnit > 1024 && unitIndex < units.length - 1) {
|
|
1150
|
+
sizeInUnit /= 1024;
|
|
1151
|
+
unitIndex++;
|
|
1152
|
+
}
|
|
1153
|
+
const formatter = new Intl.NumberFormat([], { maximumFractionDigits: 2, style: "unit", unit: units[unitIndex] });
|
|
1154
|
+
return formatter.format(sizeInUnit);
|
|
1155
|
+
};
|
|
1156
|
+
const NumberInput = memo(function NumberInput22(props) {
|
|
1157
|
+
var _a;
|
|
1158
|
+
const [{ inputId, labelId, severity, helpText, label, fieldProps, field }, rest] = useFormikInput(props);
|
|
1159
|
+
const { onChange } = fieldProps;
|
|
1160
|
+
const color = useSeverityColor(severity);
|
|
1161
|
+
const input = useRef(null);
|
|
1162
|
+
const { value } = fieldProps;
|
|
1163
|
+
const updatedHelpText = useMemo(() => {
|
|
1164
|
+
if (helpText)
|
|
1165
|
+
return helpText;
|
|
1166
|
+
if (field.maxFileSize) {
|
|
1167
|
+
const size = convertBytesToLargestUnit(field.maxFileSize);
|
|
1168
|
+
return `Maximum file size: ${size}`;
|
|
1169
|
+
}
|
|
1170
|
+
return null;
|
|
1171
|
+
}, [field.maxFileSize, helpText]);
|
|
1172
|
+
const handleClick = useCallback(() => {
|
|
1173
|
+
var _a2;
|
|
1174
|
+
(_a2 = input.current) == null ? void 0 : _a2.click();
|
|
1175
|
+
}, []);
|
|
1176
|
+
const handleRemove = useCallback(
|
|
1177
|
+
(index) => {
|
|
1178
|
+
const files = [...value];
|
|
1179
|
+
files.splice(index, 1);
|
|
1180
|
+
const event = { target: { files } };
|
|
1181
|
+
onChange(event);
|
|
904
1182
|
},
|
|
905
|
-
[
|
|
906
|
-
)
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
1183
|
+
[value, onChange]
|
|
1184
|
+
);
|
|
1185
|
+
const multipleButtonText = value ? "Select new files" : "Select files";
|
|
1186
|
+
const singleButtonText = value ? "Select new file" : "Select a file";
|
|
1187
|
+
const buttonText = field.maxFiles > 1 ? multipleButtonText : singleButtonText;
|
|
1188
|
+
return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: "2", children: [
|
|
1189
|
+
/* @__PURE__ */ jsx(InputWithLabelAndHelpText, { helpText: updatedHelpText, severity, children: /* @__PURE__ */ jsxs(InputWithLabel, { severity, inputId, labelId, label, children: [
|
|
1190
|
+
/* @__PURE__ */ jsx(Flex, { direction: "row", gap: "2", children: /* @__PURE__ */ jsx(Box, { width: "max-content", asChild: true, children: /* @__PURE__ */ jsxs(Button, { ...rest, onClick: handleClick, children: [
|
|
1191
|
+
/* @__PURE__ */ jsx(UploadIcon, {}),
|
|
911
1192
|
" ",
|
|
912
|
-
|
|
1193
|
+
buttonText
|
|
913
1194
|
] }) }) }),
|
|
914
|
-
/* @__PURE__ */
|
|
1195
|
+
/* @__PURE__ */ jsx(
|
|
915
1196
|
"input",
|
|
916
1197
|
{
|
|
917
|
-
...
|
|
1198
|
+
...rest,
|
|
918
1199
|
type: "file",
|
|
919
|
-
ref:
|
|
920
|
-
id:
|
|
921
|
-
accept: (
|
|
922
|
-
multiple:
|
|
923
|
-
color
|
|
1200
|
+
ref: input,
|
|
1201
|
+
id: inputId,
|
|
1202
|
+
accept: (_a = field.extensions) == null ? void 0 : _a.join(","),
|
|
1203
|
+
multiple: field.maxFiles > 1,
|
|
1204
|
+
color,
|
|
924
1205
|
style: { display: "none" },
|
|
925
|
-
...
|
|
1206
|
+
...fieldProps,
|
|
926
1207
|
value: ""
|
|
927
1208
|
}
|
|
928
1209
|
)
|
|
929
1210
|
] }) }),
|
|
930
|
-
Array.isArray(
|
|
931
|
-
|
|
1211
|
+
Array.isArray(value) && value.length > 0 && /* @__PURE__ */ jsx(Flex, { direction: "column", gap: "2", children: value.map((file, index) => /* @__PURE__ */ jsx(
|
|
1212
|
+
DisplayFile,
|
|
932
1213
|
{
|
|
933
|
-
field
|
|
934
|
-
file
|
|
935
|
-
onRemove: () =>
|
|
936
|
-
disabled:
|
|
1214
|
+
field,
|
|
1215
|
+
file,
|
|
1216
|
+
onRemove: () => handleRemove(index),
|
|
1217
|
+
disabled: rest.disabled
|
|
937
1218
|
},
|
|
938
|
-
|
|
1219
|
+
index
|
|
939
1220
|
)) })
|
|
940
1221
|
] });
|
|
941
|
-
})
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
1222
|
+
});
|
|
1223
|
+
const DisplayFile = memo(function DisplayFile2({ file, field, onRemove, disabled }) {
|
|
1224
|
+
const [resolvedFile, setResolvedFile] = useState(null);
|
|
1225
|
+
const error = useMemo(() => resolvedFile && field.getError([resolvedFile]), [field, resolvedFile]);
|
|
1226
|
+
const { url, name, size } = useMemo(() => {
|
|
1227
|
+
let url2 = null;
|
|
1228
|
+
let name2;
|
|
1229
|
+
let size2;
|
|
1230
|
+
if (resolvedFile == null ? void 0 : resolvedFile.type.startsWith("image/")) {
|
|
1231
|
+
url2 = URL.createObjectURL(resolvedFile);
|
|
1232
|
+
}
|
|
1233
|
+
if (resolvedFile) {
|
|
1234
|
+
name2 = resolvedFile.name;
|
|
1235
|
+
size2 = convertBytesToLargestUnit(resolvedFile.size);
|
|
1236
|
+
} else {
|
|
1237
|
+
name2 = "Downloading...";
|
|
1238
|
+
size2 = "...";
|
|
1239
|
+
}
|
|
1240
|
+
return { url: url2, name: name2, size: size2 };
|
|
1241
|
+
}, [resolvedFile]);
|
|
1242
|
+
useEffect(() => {
|
|
1243
|
+
if (file instanceof Promise) {
|
|
1244
|
+
file.then(setResolvedFile);
|
|
1245
|
+
} else {
|
|
1246
|
+
setResolvedFile(file);
|
|
1247
|
+
}
|
|
1248
|
+
}, [file]);
|
|
1249
|
+
return /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsxs(Flex, { direction: { initial: "column", sm: "row" }, gap: "3", justify: "between", children: [
|
|
1250
|
+
/* @__PURE__ */ jsxs(Flex, { direction: "row", gap: "3", align: "center", grow: "1", shrink: "0", children: [
|
|
1251
|
+
/* @__PURE__ */ jsx(
|
|
1252
|
+
IconButton,
|
|
952
1253
|
{
|
|
953
1254
|
severity: "info",
|
|
954
1255
|
variant: "outline",
|
|
955
|
-
"aria-label": `Remove ${
|
|
956
|
-
disabled
|
|
957
|
-
onClick:
|
|
958
|
-
children: /* @__PURE__ */
|
|
1256
|
+
"aria-label": `Remove ${name}`,
|
|
1257
|
+
disabled,
|
|
1258
|
+
onClick: onRemove,
|
|
1259
|
+
children: /* @__PURE__ */ jsx(Cross1Icon, {})
|
|
959
1260
|
}
|
|
960
1261
|
),
|
|
961
|
-
/* @__PURE__ */
|
|
962
|
-
/* @__PURE__ */
|
|
963
|
-
/* @__PURE__ */
|
|
964
|
-
|
|
1262
|
+
/* @__PURE__ */ jsxs(Flex, { direction: "column", gap: "1", children: [
|
|
1263
|
+
/* @__PURE__ */ jsx(Text, { children: name }),
|
|
1264
|
+
/* @__PURE__ */ jsx(Text, { size: "1", children: size }),
|
|
1265
|
+
error && /* @__PURE__ */ jsx(Text, { size: "1", severity: "danger", children: error })
|
|
965
1266
|
] })
|
|
966
1267
|
] }),
|
|
967
|
-
|
|
1268
|
+
url && /* @__PURE__ */ jsx("img", { className: styles$2.previewImage, src: url, alt: name })
|
|
968
1269
|
] }) });
|
|
969
|
-
})
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
this
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
1270
|
+
});
|
|
1271
|
+
const largestSupportedSize = 50 * 1024 * 1024;
|
|
1272
|
+
const _UploadField = class _UploadField extends BaseField {
|
|
1273
|
+
constructor(options) {
|
|
1274
|
+
const { extensions, maximum_files, maximum_size, ...base } = options;
|
|
1275
|
+
super({ ...base, type: "upload" });
|
|
1276
|
+
__publicField(this, "extensions");
|
|
1277
|
+
__publicField(this, "maxFileSize");
|
|
1278
|
+
__publicField(this, "maxFiles");
|
|
1279
|
+
__publicField(this, "onlyValidateAfterTouched", false);
|
|
1280
|
+
this.maxFileSize = typeof maximum_size === "number" ? maximum_size : void 0;
|
|
1281
|
+
this.maxFiles = Math.max(typeof maximum_files === "number" ? maximum_files : 1, 1);
|
|
1282
|
+
this.extensions = extensions;
|
|
1283
|
+
}
|
|
1284
|
+
getValueFromChangeEvent(event) {
|
|
1285
|
+
return Array.from(event.target.files || []);
|
|
1286
|
+
}
|
|
1287
|
+
isBlank(value) {
|
|
1288
|
+
return super.isBlank(value) || value.length === 0;
|
|
984
1289
|
}
|
|
985
1290
|
static getFieldCreationSchema() {
|
|
986
1291
|
return [
|
|
987
|
-
new
|
|
1292
|
+
new NumberField({
|
|
988
1293
|
label: "How many files can be uploaded?",
|
|
989
1294
|
description: "By default, only one file can be uploaded.",
|
|
990
|
-
required:
|
|
1295
|
+
required: false,
|
|
991
1296
|
minimum: 1,
|
|
992
1297
|
maximum: 10,
|
|
993
1298
|
identifier: "maximum_files"
|
|
994
1299
|
}),
|
|
995
|
-
new
|
|
1300
|
+
new NumberField({
|
|
996
1301
|
label: "What is the maximum size of each file?",
|
|
997
1302
|
description: "Maximum file size in bytes.",
|
|
998
|
-
required:
|
|
1303
|
+
required: false,
|
|
999
1304
|
identifier: "maximum_size",
|
|
1000
1305
|
minimum: 1,
|
|
1001
|
-
maximum:
|
|
1002
|
-
integers:
|
|
1306
|
+
maximum: largestSupportedSize,
|
|
1307
|
+
integers: true
|
|
1003
1308
|
}),
|
|
1004
|
-
new
|
|
1309
|
+
new MultiSelectField({
|
|
1005
1310
|
label: "Accepted file types",
|
|
1006
1311
|
description: "Types of allowed files to upload. If left blank, all files will be accepted.",
|
|
1007
|
-
required:
|
|
1312
|
+
required: false,
|
|
1008
1313
|
identifier: "extensions",
|
|
1009
1314
|
options: [
|
|
1010
1315
|
{
|
|
@@ -1032,14 +1337,20 @@ const Yi = "_previewImage_1ig84_1", Qi = {
|
|
|
1032
1337
|
];
|
|
1033
1338
|
}
|
|
1034
1339
|
getFieldValidators() {
|
|
1035
|
-
const
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
})
|
|
1340
|
+
const validators = super.getFieldValidators();
|
|
1341
|
+
const maxFileSize = this.maxFileSize ?? largestSupportedSize;
|
|
1342
|
+
const maxFiles = this.maxFiles ?? 1;
|
|
1343
|
+
validators.push((value) => {
|
|
1344
|
+
if (value && value.some((file) => file.size > maxFileSize)) {
|
|
1345
|
+
return `Files must be at most ${convertBytesToLargestUnit(maxFileSize)}.`;
|
|
1346
|
+
}
|
|
1347
|
+
});
|
|
1348
|
+
validators.push((value) => {
|
|
1349
|
+
if (value && value.length > maxFiles) {
|
|
1350
|
+
return `You can only upload ${maxFiles} files.`;
|
|
1351
|
+
}
|
|
1352
|
+
});
|
|
1353
|
+
return validators;
|
|
1043
1354
|
}
|
|
1044
1355
|
serialize() {
|
|
1045
1356
|
return {
|
|
@@ -1049,1044 +1360,1312 @@ const Yi = "_previewImage_1ig84_1", Qi = {
|
|
|
1049
1360
|
maximum_files: this.maxFiles
|
|
1050
1361
|
};
|
|
1051
1362
|
}
|
|
1052
|
-
static deserialize(
|
|
1053
|
-
if (
|
|
1363
|
+
static deserialize(data) {
|
|
1364
|
+
if (data.type !== "upload")
|
|
1054
1365
|
throw new Error("Type mismatch.");
|
|
1055
|
-
return new
|
|
1366
|
+
return new _UploadField(data);
|
|
1056
1367
|
}
|
|
1057
|
-
getInput(
|
|
1058
|
-
return /* @__PURE__ */
|
|
1368
|
+
getInput(props) {
|
|
1369
|
+
return /* @__PURE__ */ jsx(NumberInput, { field: this, ...props });
|
|
1059
1370
|
}
|
|
1060
1371
|
};
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1372
|
+
__publicField(_UploadField, "fieldTypeName", "Upload");
|
|
1373
|
+
__publicField(_UploadField, "fieldTypeDescription", "Allows a file to be uploaded.");
|
|
1374
|
+
__publicField(_UploadField, "Icon", UploadIcon);
|
|
1375
|
+
let UploadField = _UploadField;
|
|
1376
|
+
const FieldTypeToClsMapping = {
|
|
1377
|
+
date: DateField,
|
|
1378
|
+
number: NumberField,
|
|
1379
|
+
boolean: BooleanField,
|
|
1380
|
+
select: SelectField,
|
|
1381
|
+
string: StringField,
|
|
1382
|
+
text: TextField,
|
|
1383
|
+
custom: CustomField,
|
|
1384
|
+
upload: UploadField,
|
|
1072
1385
|
// TODO: Underscore
|
|
1073
|
-
"multi-string":
|
|
1074
|
-
"multi-select":
|
|
1075
|
-
}
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1386
|
+
"multi-string": MultiStringField,
|
|
1387
|
+
"multi-select": MultiSelectField
|
|
1388
|
+
};
|
|
1389
|
+
const deserializeField = (serializedField) => {
|
|
1390
|
+
const fieldType = serializedField.type;
|
|
1391
|
+
const fieldCls = FieldTypeToClsMapping[fieldType];
|
|
1392
|
+
return fieldCls.deserialize(serializedField);
|
|
1393
|
+
};
|
|
1394
|
+
const deserialize = (serialized) => {
|
|
1395
|
+
if (serialized.type === "section") {
|
|
1396
|
+
return FieldSection.deserialize(serialized);
|
|
1397
|
+
}
|
|
1398
|
+
return deserializeField(serialized);
|
|
1399
|
+
};
|
|
1400
|
+
function formRevisionToSchema(formRevision, meta = {}) {
|
|
1401
|
+
const { readonly = false } = meta;
|
|
1081
1402
|
return {
|
|
1082
|
-
title:
|
|
1083
|
-
description:
|
|
1084
|
-
fields:
|
|
1085
|
-
meta: { readonly
|
|
1403
|
+
title: formRevision.title,
|
|
1404
|
+
description: formRevision.description,
|
|
1405
|
+
fields: formRevision.fields.map((serializedField) => deserialize(serializedField)),
|
|
1406
|
+
meta: { readonly }
|
|
1086
1407
|
};
|
|
1087
1408
|
}
|
|
1088
|
-
function
|
|
1089
|
-
|
|
1409
|
+
function valueIsFile(v) {
|
|
1410
|
+
if (Array.isArray(v) && v.some((v2) => v2 instanceof File || v2 instanceof Promise))
|
|
1411
|
+
return true;
|
|
1412
|
+
return false;
|
|
1090
1413
|
}
|
|
1091
|
-
function
|
|
1092
|
-
if (!
|
|
1093
|
-
return
|
|
1094
|
-
if (
|
|
1414
|
+
function isConditionMet(condition, value) {
|
|
1415
|
+
if (!condition)
|
|
1416
|
+
return true;
|
|
1417
|
+
if (valueIsFile(value) || valueIsFile(condition.value))
|
|
1095
1418
|
throw new Error("Conditions do not support file uploads");
|
|
1096
|
-
const
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1419
|
+
const valueAsPrimitive = Array.isArray(value) ? value.map((v) => typeof v === "string" ? v : v.value) : value;
|
|
1420
|
+
const valueToCompare = Array.isArray(condition.value) ? condition.value.map((v) => typeof v === "string" ? v : v.value) : condition.value;
|
|
1421
|
+
if (Array.isArray(valueToCompare) && Array.isArray(valueAsPrimitive)) {
|
|
1422
|
+
for (const v of valueToCompare) {
|
|
1423
|
+
if (!valueAsPrimitive.includes(v))
|
|
1424
|
+
return false;
|
|
1425
|
+
}
|
|
1426
|
+
return true;
|
|
1427
|
+
}
|
|
1428
|
+
return valueToCompare === value;
|
|
1104
1429
|
}
|
|
1105
|
-
const
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1430
|
+
const useFieldInput = (field, props) => {
|
|
1431
|
+
return useMemo(() => {
|
|
1432
|
+
if (!props || !field)
|
|
1433
|
+
return null;
|
|
1434
|
+
return field.getInput(props);
|
|
1435
|
+
}, [field, props]);
|
|
1436
|
+
};
|
|
1437
|
+
const useFieldInputs = (fields, props) => {
|
|
1438
|
+
const inputs = useMemo(() => {
|
|
1439
|
+
return fields.map((field) => {
|
|
1440
|
+
return /* @__PURE__ */ jsx("div", { children: field.getInput(props) }, field.getId());
|
|
1441
|
+
});
|
|
1442
|
+
}, [fields, props]);
|
|
1443
|
+
return /* @__PURE__ */ jsx(Flex, { direction: "column", gap: "2", children: inputs });
|
|
1444
|
+
};
|
|
1445
|
+
const hasKeys = (errors) => {
|
|
1446
|
+
return Object.keys(errors).length > 0;
|
|
1447
|
+
};
|
|
1448
|
+
const validateForm = async (schema, form) => {
|
|
1449
|
+
const errors = {};
|
|
1450
|
+
for (const field of schema.fields) {
|
|
1451
|
+
if (field instanceof FieldSection) {
|
|
1452
|
+
if (field.condition) {
|
|
1453
|
+
const { identifier } = field.condition;
|
|
1454
|
+
if (!isConditionMet(field.condition, get(form, identifier))) {
|
|
1115
1455
|
continue;
|
|
1456
|
+
}
|
|
1116
1457
|
}
|
|
1117
|
-
Object.assign(
|
|
1458
|
+
Object.assign(errors, field.getErrors(form));
|
|
1118
1459
|
} else {
|
|
1119
|
-
if (!(
|
|
1460
|
+
if (!(field instanceof BaseField)) {
|
|
1120
1461
|
throw new Error("Invalid field type");
|
|
1121
|
-
|
|
1122
|
-
|
|
1462
|
+
}
|
|
1463
|
+
const id = field.getId();
|
|
1464
|
+
const error = field.getError(get(form, id), form);
|
|
1465
|
+
if (error)
|
|
1466
|
+
set(errors, id, error);
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
if (hasKeys(errors))
|
|
1470
|
+
return errors;
|
|
1471
|
+
};
|
|
1472
|
+
const uncontrolledValues = [null, void 0];
|
|
1473
|
+
const initialFormValues = (fields, values) => {
|
|
1474
|
+
return fields.reduce((acc, field) => {
|
|
1475
|
+
if (field instanceof FieldSection) {
|
|
1476
|
+
return { ...acc, ...initialFormValues(field.fields, values) };
|
|
1123
1477
|
}
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
}
|
|
1478
|
+
if (uncontrolledValues.includes(get(acc, field.getId()))) {
|
|
1479
|
+
set(acc, field.getId(), "");
|
|
1480
|
+
}
|
|
1481
|
+
return acc;
|
|
1482
|
+
}, values);
|
|
1483
|
+
};
|
|
1484
|
+
const defaultHandleSubmit = () => {
|
|
1127
1485
|
throw new Error("onSubmit must be provided if form is not readonly.");
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1486
|
+
};
|
|
1487
|
+
const FormRenderer = memo(
|
|
1488
|
+
forwardRef((props, ref) => {
|
|
1130
1489
|
const {
|
|
1131
|
-
schema
|
|
1132
|
-
values
|
|
1133
|
-
onSubmit
|
|
1134
|
-
submitText
|
|
1135
|
-
cancelText
|
|
1136
|
-
onCancel
|
|
1137
|
-
onDirty
|
|
1490
|
+
schema,
|
|
1491
|
+
values = {},
|
|
1492
|
+
onSubmit = defaultHandleSubmit,
|
|
1493
|
+
submitText = "Submit",
|
|
1494
|
+
cancelText,
|
|
1495
|
+
onCancel,
|
|
1496
|
+
onDirty,
|
|
1138
1497
|
// if the title isn't provided, hide it by default
|
|
1139
|
-
hideTitle
|
|
1140
|
-
hideDescription
|
|
1141
|
-
className
|
|
1142
|
-
} =
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1498
|
+
hideTitle = !schema.title,
|
|
1499
|
+
hideDescription,
|
|
1500
|
+
className
|
|
1501
|
+
} = props;
|
|
1502
|
+
const { readonly } = schema.meta;
|
|
1503
|
+
const formId2 = useMemo(() => crypto.randomUUID(), []);
|
|
1504
|
+
const formik = useFormik({
|
|
1505
|
+
initialValues: initialFormValues(schema.fields, values),
|
|
1506
|
+
onSubmit,
|
|
1507
|
+
validate: (form) => validateForm(schema, form),
|
|
1146
1508
|
// only validate the entire form on submit
|
|
1147
|
-
validateOnBlur:
|
|
1148
|
-
validateOnChange:
|
|
1149
|
-
})
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1509
|
+
validateOnBlur: false,
|
|
1510
|
+
validateOnChange: false
|
|
1511
|
+
});
|
|
1512
|
+
const { dirty } = formik;
|
|
1513
|
+
const Title = useMemo(
|
|
1514
|
+
() => typeof schema.title === "string" ? /* @__PURE__ */ jsx(Heading, { children: schema.title }) : schema.title,
|
|
1515
|
+
[schema.title]
|
|
1516
|
+
);
|
|
1517
|
+
const Description = useMemo(
|
|
1518
|
+
() => typeof schema.description === "string" ? /* @__PURE__ */ jsx(Text, { className: styles$3.description, children: schema.description }) : schema.description,
|
|
1519
|
+
[schema.description]
|
|
1520
|
+
);
|
|
1521
|
+
const inputs = useFieldInputs(schema.fields, { formId: formId2, disabled: readonly });
|
|
1522
|
+
useEffect(() => {
|
|
1523
|
+
if (dirty && onDirty)
|
|
1524
|
+
onDirty();
|
|
1525
|
+
}, [dirty, onDirty]);
|
|
1526
|
+
return /* @__PURE__ */ jsx(FormikProvider, { value: formik, children: /* @__PURE__ */ jsx(Flex, { ref, direction: "column", gap: "2", className, asChild: true, children: /* @__PURE__ */ jsxs("form", { id: formId2, onSubmit: formik.handleSubmit, children: [
|
|
1527
|
+
!hideTitle && /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: "1", children: [
|
|
1528
|
+
Title,
|
|
1529
|
+
!hideDescription && Description
|
|
1162
1530
|
] }) }),
|
|
1163
|
-
|
|
1164
|
-
!
|
|
1165
|
-
|
|
1166
|
-
/* @__PURE__ */
|
|
1531
|
+
inputs,
|
|
1532
|
+
!readonly && /* @__PURE__ */ jsxs(Flex, { justify: "end", gap: "2", children: [
|
|
1533
|
+
cancelText && /* @__PURE__ */ jsx(Button, { type: "button", variant: "soft", onClick: onCancel, children: cancelText }),
|
|
1534
|
+
/* @__PURE__ */ jsx(Button, { type: "submit", disabled: !formik.isValid, children: submitText })
|
|
1167
1535
|
] })
|
|
1168
1536
|
] }) }) });
|
|
1169
1537
|
})
|
|
1170
|
-
)
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1538
|
+
);
|
|
1539
|
+
const FormSubmissionViewer = memo(
|
|
1540
|
+
forwardRef((props, ref) => {
|
|
1541
|
+
const { submission, showFormDescription = false, showFormTitle = true } = props;
|
|
1542
|
+
const revision = useAppSelector(selectFormRevision(submission.form_revision));
|
|
1543
|
+
const { sdk } = useSDK();
|
|
1544
|
+
if (!revision) {
|
|
1174
1545
|
throw new Error(
|
|
1175
|
-
`Could not find revision ${
|
|
1546
|
+
`Could not find revision ${submission.form_revision} for submission ${submission.offline_id}.`
|
|
1176
1547
|
);
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1548
|
+
}
|
|
1549
|
+
const schema = useMemo(() => {
|
|
1550
|
+
return formRevisionToSchema(revision, { readonly: true });
|
|
1551
|
+
}, [revision]);
|
|
1552
|
+
const submissionValuesWithAttachments = useMemo(() => {
|
|
1553
|
+
const attachments = selectSubmissionAttachments(submission.offline_id)(sdk.store.getState()) ?? [];
|
|
1554
|
+
const downloadedAttachments = {};
|
|
1555
|
+
for (const attachment of attachments) {
|
|
1556
|
+
const promise = sdk.files.fetchFileFromUrl(attachment.file, attachment.file_sha1, attachment.file_name).then((response) => {
|
|
1557
|
+
if (!response.success)
|
|
1558
|
+
throw new Error(`Failed to download attachment ${attachment.file_name}.`);
|
|
1559
|
+
return response.body;
|
|
1560
|
+
});
|
|
1561
|
+
const fieldAttachments = downloadedAttachments[attachment.field_identifier];
|
|
1562
|
+
if (fieldAttachments) {
|
|
1563
|
+
fieldAttachments.push(promise);
|
|
1564
|
+
} else {
|
|
1565
|
+
downloadedAttachments[attachment.field_identifier] = [promise];
|
|
1566
|
+
}
|
|
1186
1567
|
}
|
|
1187
|
-
return { ...
|
|
1188
|
-
}, [
|
|
1189
|
-
return /* @__PURE__ */
|
|
1190
|
-
|
|
1568
|
+
return { ...submission.values, ...downloadedAttachments };
|
|
1569
|
+
}, [sdk.files, sdk.store, submission.offline_id, submission.values]);
|
|
1570
|
+
return /* @__PURE__ */ jsx(
|
|
1571
|
+
FormRenderer,
|
|
1191
1572
|
{
|
|
1192
|
-
ref
|
|
1193
|
-
schema
|
|
1194
|
-
values:
|
|
1195
|
-
hideDescription: !
|
|
1196
|
-
hideTitle: !
|
|
1573
|
+
ref,
|
|
1574
|
+
schema,
|
|
1575
|
+
values: submissionValuesWithAttachments,
|
|
1576
|
+
hideDescription: !showFormDescription,
|
|
1577
|
+
hideTitle: !showFormTitle
|
|
1197
1578
|
}
|
|
1198
1579
|
);
|
|
1199
1580
|
})
|
|
1200
|
-
)
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1581
|
+
);
|
|
1582
|
+
const favoriteIcon = "_favoriteIcon_1bixi_1";
|
|
1583
|
+
const regularIcon = "_regularIcon_1bixi_9";
|
|
1584
|
+
const styles$1 = {
|
|
1585
|
+
favoriteIcon,
|
|
1586
|
+
regularIcon
|
|
1587
|
+
};
|
|
1588
|
+
const orgOptionPrefix = "organization:";
|
|
1589
|
+
const userOptionPrefix = "user:";
|
|
1590
|
+
const FormBrowser = memo(
|
|
1591
|
+
forwardRef((props, ref) => {
|
|
1592
|
+
const { maxResults = 20, ...entryProps } = props;
|
|
1593
|
+
const [filter, setFilter] = useState("");
|
|
1594
|
+
const [ownerFilter, setOwnerFilter] = useState("");
|
|
1595
|
+
const { sdk } = useSDK();
|
|
1596
|
+
const ownerFilterOptions = useMemo(() => {
|
|
1597
|
+
const ret = { maxResults, searchTerm: filter };
|
|
1598
|
+
if (ownerFilter) {
|
|
1599
|
+
if (ownerFilter.startsWith(orgOptionPrefix)) {
|
|
1600
|
+
ret.owner_organization = parseInt(ownerFilter.slice(orgOptionPrefix.length));
|
|
1601
|
+
} else if (ownerFilter.startsWith(userOptionPrefix)) {
|
|
1602
|
+
ret.owner_user = parseInt(ownerFilter.slice(userOptionPrefix.length));
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
return ret;
|
|
1606
|
+
}, [filter, maxResults, ownerFilter]);
|
|
1607
|
+
const userForms = useAppSelector(selectFilteredUserForms(ownerFilterOptions)) ?? [];
|
|
1608
|
+
const userFormMapping = useAppSelector(selectUserFormMapping);
|
|
1609
|
+
const handleToggleFavorite = useCallback(
|
|
1610
|
+
(form) => {
|
|
1611
|
+
if (form.favorite) {
|
|
1612
|
+
sdk.userForms.unfavorite(form.offline_id).then();
|
|
1613
|
+
} else {
|
|
1614
|
+
sdk.userForms.favorite(form.offline_id).then();
|
|
1615
|
+
}
|
|
1211
1616
|
},
|
|
1212
|
-
[
|
|
1213
|
-
)
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
const
|
|
1219
|
-
|
|
1617
|
+
[sdk]
|
|
1618
|
+
);
|
|
1619
|
+
const options = useMemo(() => {
|
|
1620
|
+
const state = sdk.store.getState();
|
|
1621
|
+
const accumulator = {};
|
|
1622
|
+
for (const form of Object.values(userFormMapping)) {
|
|
1623
|
+
const organization = selectOrganization(form.owner_organization || -1)(state);
|
|
1624
|
+
if (organization) {
|
|
1625
|
+
accumulator[`${orgOptionPrefix}${organization.id}`] = organization.name;
|
|
1626
|
+
}
|
|
1627
|
+
const user = selectUser(form.owner_user || -1)(state);
|
|
1628
|
+
if (user) {
|
|
1629
|
+
accumulator[`${userOptionPrefix}${user.id}`] = user.username;
|
|
1630
|
+
}
|
|
1220
1631
|
}
|
|
1221
|
-
return Object.entries(
|
|
1222
|
-
}, [
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1632
|
+
return Object.entries(accumulator).map(([value, label]) => ({ itemContent: label, value }));
|
|
1633
|
+
}, [userFormMapping, sdk.store]);
|
|
1634
|
+
const handleChange = useCallback((e) => {
|
|
1635
|
+
setFilter(e.currentTarget.value);
|
|
1636
|
+
}, []);
|
|
1637
|
+
const numberOfForms = useAppSelector(selectNumberOfUserForms) || 0;
|
|
1638
|
+
const numberOfHiddenForms = numberOfForms - userForms.length;
|
|
1639
|
+
const overflowMessage = userForms.length == maxResults && numberOfHiddenForms > 0 ? `Only the first ${maxResults} results are shown (${numberOfHiddenForms} hidden)` : numberOfHiddenForms > 0 && `${numberOfHiddenForms} hidden forms`;
|
|
1640
|
+
return /* @__PURE__ */ jsxs(Flex, { ref, direction: "column", gap: "2", children: [
|
|
1641
|
+
/* @__PURE__ */ jsxs(Flex, { gap: "2", grow: "1", children: [
|
|
1642
|
+
/* @__PURE__ */ jsx(Box, { grow: "1", asChild: true, children: /* @__PURE__ */ jsx(TextField$1.Root, { size: "3", children: /* @__PURE__ */ jsx(TextField$1.Input, { placeholder: "Filter", value: filter, onChange: handleChange }) }) }),
|
|
1643
|
+
/* @__PURE__ */ jsx(
|
|
1644
|
+
Select,
|
|
1230
1645
|
{
|
|
1231
|
-
items:
|
|
1232
|
-
value:
|
|
1233
|
-
onValueChange:
|
|
1646
|
+
items: options,
|
|
1647
|
+
value: ownerFilter,
|
|
1648
|
+
onValueChange: setOwnerFilter,
|
|
1234
1649
|
placeholder: "Owner",
|
|
1235
1650
|
size: "large"
|
|
1236
1651
|
}
|
|
1237
1652
|
)
|
|
1238
1653
|
] }),
|
|
1239
|
-
|
|
1240
|
-
|
|
1654
|
+
userForms.length > 0 && /* @__PURE__ */ jsx(ButtonList.Root, { children: userForms.map((form) => /* @__PURE__ */ jsx(
|
|
1655
|
+
FormBrowserEntry,
|
|
1241
1656
|
{
|
|
1242
|
-
...
|
|
1243
|
-
form
|
|
1244
|
-
handleToggleFavorite: () =>
|
|
1657
|
+
...entryProps,
|
|
1658
|
+
form,
|
|
1659
|
+
handleToggleFavorite: () => handleToggleFavorite(form)
|
|
1245
1660
|
},
|
|
1246
|
-
|
|
1661
|
+
form.offline_id
|
|
1247
1662
|
)) }),
|
|
1248
|
-
/* @__PURE__ */
|
|
1663
|
+
/* @__PURE__ */ jsx(Box, { px: "3", children: /* @__PURE__ */ jsx(Text, { size: "2", severity: "info", children: overflowMessage }) })
|
|
1249
1664
|
] });
|
|
1250
1665
|
})
|
|
1251
|
-
)
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1666
|
+
);
|
|
1667
|
+
const FormBrowserEntry = (props) => {
|
|
1668
|
+
var _a;
|
|
1669
|
+
const { form, onSelectForm, isFavoriteEditable, handleToggleFavorite } = props;
|
|
1670
|
+
const ownerOrganization = (_a = useAppSelector(selectOrganization(form.owner_organization || -1))) == null ? void 0 : _a.name;
|
|
1671
|
+
const ownerUser = useAppSelector(selectUser(form.owner_user || -1));
|
|
1672
|
+
const currentUserId = useAppSelector(selectCurrentUser).id;
|
|
1673
|
+
const ownedByCurrentUser = !!ownerUser && ownerUser.id === currentUserId;
|
|
1674
|
+
const owner = ownerOrganization ?? (ownedByCurrentUser ? "You" : ownerUser == null ? void 0 : ownerUser.username) ?? "Unknown";
|
|
1675
|
+
const handleFavoriteClick = useCallback(
|
|
1676
|
+
(e) => {
|
|
1677
|
+
e.stopPropagation();
|
|
1678
|
+
handleToggleFavorite();
|
|
1256
1679
|
},
|
|
1257
|
-
[
|
|
1258
|
-
)
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1680
|
+
[handleToggleFavorite]
|
|
1681
|
+
);
|
|
1682
|
+
const ret = /* @__PURE__ */ jsx(ButtonList.Item, { onClick: () => onSelectForm(form), asChild: true, children: /* @__PURE__ */ jsxs(Flex, { justify: "between", gap: "2", py: "2", px: "3", ...divButtonProps, children: [
|
|
1683
|
+
/* @__PURE__ */ jsxs(Flex, { grow: "1", align: "center", gap: "2", children: [
|
|
1684
|
+
/* @__PURE__ */ jsx(
|
|
1685
|
+
IconButton,
|
|
1262
1686
|
{
|
|
1263
|
-
className:
|
|
1687
|
+
className: classNames(form.favorite ? styles$1.favoriteIcon : styles$1.regularIcon),
|
|
1264
1688
|
variant: "ghost",
|
|
1265
|
-
onClick:
|
|
1266
|
-
"aria-label":
|
|
1267
|
-
disabled: !
|
|
1268
|
-
children:
|
|
1689
|
+
onClick: handleFavoriteClick,
|
|
1690
|
+
"aria-label": form.favorite ? "Favorite form" : "Standard form",
|
|
1691
|
+
disabled: !isFavoriteEditable,
|
|
1692
|
+
children: form.favorite ? /* @__PURE__ */ jsx(StarFilledIcon, {}) : /* @__PURE__ */ jsx(StarIcon, {})
|
|
1269
1693
|
}
|
|
1270
1694
|
),
|
|
1271
|
-
/* @__PURE__ */
|
|
1272
|
-
|
|
1695
|
+
/* @__PURE__ */ jsx(Text, { noWrap: true, children: form.latestRevision.title }),
|
|
1696
|
+
form.latestRevision.description && /* @__PURE__ */ jsx(QuestionMarkCircledIcon, {})
|
|
1273
1697
|
] }),
|
|
1274
|
-
|
|
1275
|
-
/* @__PURE__ */
|
|
1698
|
+
owner && /* @__PURE__ */ jsxs(Flex, { align: "center", gap: "2", children: [
|
|
1699
|
+
/* @__PURE__ */ jsx(PersonIcon, {}),
|
|
1276
1700
|
" ",
|
|
1277
|
-
|
|
1701
|
+
owner
|
|
1278
1702
|
] })
|
|
1279
1703
|
] }) });
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
}
|
|
1285
|
-
|
|
1286
|
-
|
|
1704
|
+
if (form.latestRevision.description) {
|
|
1705
|
+
return /* @__PURE__ */ jsx(Tooltip, { content: form.latestRevision.description, children: ret }, form.offline_id);
|
|
1706
|
+
}
|
|
1707
|
+
return ret;
|
|
1708
|
+
};
|
|
1709
|
+
const submissionsContainer = "_submissionsContainer_9iirt_1";
|
|
1710
|
+
const stopHorizontalOverflow = "_stopHorizontalOverflow_9iirt_6";
|
|
1711
|
+
const styles = {
|
|
1712
|
+
submissionsContainer,
|
|
1713
|
+
stopHorizontalOverflow
|
|
1714
|
+
};
|
|
1715
|
+
const FormSubmissionBrowserEntry = memo(function FormSubmissionBrowserEntry2(props) {
|
|
1716
|
+
var _a;
|
|
1717
|
+
const { submission, onSubmissionClick, compact, labelType, rowDecorator } = props;
|
|
1718
|
+
const currentUser = useAppSelector(selectCurrentUser);
|
|
1719
|
+
const createdBy = useAppSelector(selectUser("created_by" in submission ? submission.created_by : currentUser.id));
|
|
1720
|
+
const dateToUse = getCreatedAtOrSubmittedAtDate(submission);
|
|
1721
|
+
const formattedDateTime = isToday(dateToUse) ? dateToUse.toLocaleTimeString([], {
|
|
1287
1722
|
hour: "2-digit",
|
|
1288
1723
|
minute: "2-digit"
|
|
1289
|
-
}) :
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1724
|
+
}) : getLocalDateString(dateToUse);
|
|
1725
|
+
const revision = useAppSelector(selectFormRevision(submission.form_revision));
|
|
1726
|
+
if (!revision) {
|
|
1727
|
+
throw new Error(`Could not find revision ${submission.form_revision} for submission ${submission.offline_id}.`);
|
|
1728
|
+
}
|
|
1729
|
+
const latestRevisionNumber = (_a = useAppSelector(selectLatestFormRevision(revision.form))) == null ? void 0 : _a.revision;
|
|
1730
|
+
const creatorProfileSrc = useFileSrc({
|
|
1731
|
+
file: (createdBy == null ? void 0 : createdBy.profile.file) ?? null,
|
|
1732
|
+
fileSha1: (createdBy == null ? void 0 : createdBy.profile.file_sha1) ?? null
|
|
1733
|
+
});
|
|
1734
|
+
const creatorProfileFallback = (createdBy == null ? void 0 : createdBy.username.charAt(0).toUpperCase()) ?? "?";
|
|
1735
|
+
const isLatestRevision = revision.revision === latestRevisionNumber;
|
|
1736
|
+
const handleClick = React.useCallback(() => {
|
|
1737
|
+
if (onSubmissionClick) {
|
|
1738
|
+
onSubmissionClick({ submission });
|
|
1739
|
+
}
|
|
1740
|
+
}, [submission, onSubmissionClick]);
|
|
1741
|
+
const row = /* @__PURE__ */ jsx(ButtonList.Item, { onClick: handleClick, asChild: true, children: /* @__PURE__ */ jsxs(Flex, { grow: "1", width: "100%", p: "2", gap: "2", justify: "between", children: [
|
|
1742
|
+
/* @__PURE__ */ jsxs(Flex, { gap: "2", align: "center", className: styles.stopHorizontalOverflow, children: [
|
|
1743
|
+
/* @__PURE__ */ jsx(Avatar, { src: creatorProfileSrc, size: "1", fallback: creatorProfileFallback }),
|
|
1744
|
+
/* @__PURE__ */ jsx(Text, { size: "2", noWrap: true, children: labelType === "creator" ? (createdBy || currentUser).username : revision.title })
|
|
1301
1745
|
] }),
|
|
1302
|
-
/* @__PURE__ */
|
|
1303
|
-
!
|
|
1304
|
-
/* @__PURE__ */
|
|
1746
|
+
/* @__PURE__ */ jsxs(Flex, { gap: "2", align: "center", children: [
|
|
1747
|
+
!compact && (revision.revision ? /* @__PURE__ */ jsx(Badge, { variant: "soft", severity: isLatestRevision ? "primary" : "info", children: compact ? revision.revision.toString() : `Revision #${revision.revision}` }) : !!latestRevisionNumber && /* @__PURE__ */ jsx(Badge, { children: "Original" })),
|
|
1748
|
+
/* @__PURE__ */ jsx(Text, { size: "2", noWrap: true, children: formattedDateTime })
|
|
1305
1749
|
] })
|
|
1306
1750
|
] }) });
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
return
|
|
1311
|
-
}
|
|
1751
|
+
if (rowDecorator) {
|
|
1752
|
+
return rowDecorator(submission, row);
|
|
1753
|
+
}
|
|
1754
|
+
return row;
|
|
1755
|
+
});
|
|
1756
|
+
const getCreatedAtOrSubmittedAtDate = (submission) => {
|
|
1757
|
+
const date = "created_at" in submission ? submission.created_at : submission.submitted_at;
|
|
1758
|
+
return new Date(date);
|
|
1759
|
+
};
|
|
1760
|
+
const FormSubmissionBrowser = memo(function FormSubmissionBrowser2(props) {
|
|
1312
1761
|
const {
|
|
1313
|
-
formId:
|
|
1314
|
-
submissions:
|
|
1315
|
-
compact
|
|
1316
|
-
className
|
|
1317
|
-
after
|
|
1318
|
-
variant
|
|
1319
|
-
...
|
|
1320
|
-
} =
|
|
1321
|
-
if (!!
|
|
1762
|
+
formId: formId2,
|
|
1763
|
+
submissions: propSubmissions,
|
|
1764
|
+
compact = false,
|
|
1765
|
+
className,
|
|
1766
|
+
after,
|
|
1767
|
+
variant = "outline",
|
|
1768
|
+
...submissionEntryProps
|
|
1769
|
+
} = props;
|
|
1770
|
+
if (!!formId2 === !!propSubmissions) {
|
|
1322
1771
|
throw new Error("Either formId or submissions must be provided, but not both.");
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1772
|
+
}
|
|
1773
|
+
const submissions = useAppSelector(
|
|
1774
|
+
propSubmissions ? () => propSubmissions : selectSubmissionsForForm(formId2)
|
|
1775
|
+
);
|
|
1776
|
+
const sortedSubmissions = useMemo(
|
|
1777
|
+
() => submissions == null ? void 0 : submissions.sort((a, b) => {
|
|
1778
|
+
return getCreatedAtOrSubmittedAtDate(b).getTime() - getCreatedAtOrSubmittedAtDate(a).getTime();
|
|
1779
|
+
}),
|
|
1780
|
+
[submissions]
|
|
1328
1781
|
);
|
|
1329
|
-
return /* @__PURE__ */
|
|
1330
|
-
|
|
1782
|
+
return /* @__PURE__ */ jsx(
|
|
1783
|
+
ButtonList.Root,
|
|
1331
1784
|
{
|
|
1332
|
-
className:
|
|
1785
|
+
className: classNames(styles.submissionsContainer, className),
|
|
1333
1786
|
size: "small",
|
|
1334
|
-
variant
|
|
1335
|
-
before: !
|
|
1787
|
+
variant,
|
|
1788
|
+
before: !compact && /* @__PURE__ */ jsxs(Text, { severity: "info", children: [
|
|
1336
1789
|
"There are ",
|
|
1337
|
-
((
|
|
1790
|
+
((submissions == null ? void 0 : submissions.length) || 0).toString(),
|
|
1338
1791
|
" submissions of this form."
|
|
1339
1792
|
] }),
|
|
1340
|
-
after
|
|
1341
|
-
children:
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1793
|
+
after,
|
|
1794
|
+
children: sortedSubmissions == null ? void 0 : sortedSubmissions.map((submission, index) => {
|
|
1795
|
+
return /* @__PURE__ */ jsx(
|
|
1796
|
+
FormSubmissionBrowserEntry,
|
|
1797
|
+
{
|
|
1798
|
+
submission,
|
|
1799
|
+
compact,
|
|
1800
|
+
...submissionEntryProps
|
|
1801
|
+
},
|
|
1802
|
+
index
|
|
1803
|
+
);
|
|
1804
|
+
})
|
|
1350
1805
|
}
|
|
1351
1806
|
);
|
|
1352
|
-
})
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1807
|
+
});
|
|
1808
|
+
const PatchField = memo(function PatchField2(props) {
|
|
1809
|
+
const { name, render } = props;
|
|
1810
|
+
const { submitForm } = useFormikContext();
|
|
1811
|
+
const [fieldProps, _meta, helpers] = useField(name);
|
|
1812
|
+
return useMemo(() => {
|
|
1813
|
+
const setValue = (value) => helpers.setValue(value, false);
|
|
1814
|
+
return render({
|
|
1815
|
+
value: fieldProps.value,
|
|
1816
|
+
setValue,
|
|
1817
|
+
patchValue: submitForm
|
|
1360
1818
|
});
|
|
1361
|
-
}, [
|
|
1362
|
-
})
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1819
|
+
}, [submitForm, helpers, fieldProps.value, render]);
|
|
1820
|
+
});
|
|
1821
|
+
const PatchFormProvider = memo(
|
|
1822
|
+
forwardRef((props, ref) => {
|
|
1823
|
+
const { children, schema, values, onPatch, onError, ...rest } = props;
|
|
1824
|
+
const initialValues2 = useMemo(() => initialFormValues(schema.fields, values), [schema.fields, values]);
|
|
1825
|
+
const handlePatch = useCallback(
|
|
1826
|
+
(values2) => {
|
|
1827
|
+
const diff = {};
|
|
1828
|
+
for (const key in values2) {
|
|
1829
|
+
const value = values2[key];
|
|
1830
|
+
if (value !== initialValues2[key] && value !== void 0) {
|
|
1831
|
+
diff[key] = value;
|
|
1832
|
+
}
|
|
1370
1833
|
}
|
|
1371
|
-
|
|
1834
|
+
if (!hasKeys(diff))
|
|
1835
|
+
return;
|
|
1836
|
+
onPatch(diff);
|
|
1372
1837
|
},
|
|
1373
|
-
[
|
|
1374
|
-
)
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1838
|
+
[initialValues2, onPatch]
|
|
1839
|
+
);
|
|
1840
|
+
const validate = useCallback(
|
|
1841
|
+
async (form) => {
|
|
1842
|
+
const error = await validateForm(schema, form);
|
|
1843
|
+
if (error) {
|
|
1844
|
+
onError(error);
|
|
1845
|
+
}
|
|
1846
|
+
return error;
|
|
1378
1847
|
},
|
|
1379
|
-
[
|
|
1380
|
-
)
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1848
|
+
[schema, onError]
|
|
1849
|
+
);
|
|
1850
|
+
const formik = useFormik({
|
|
1851
|
+
initialValues: initialValues2,
|
|
1852
|
+
onSubmit: handlePatch,
|
|
1853
|
+
validate,
|
|
1384
1854
|
// only validate the entire form on submit
|
|
1385
|
-
validateOnBlur:
|
|
1386
|
-
validateOnChange:
|
|
1387
|
-
})
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1855
|
+
validateOnBlur: false,
|
|
1856
|
+
validateOnChange: false
|
|
1857
|
+
});
|
|
1858
|
+
const { errors, resetForm } = formik;
|
|
1859
|
+
useEffect(() => {
|
|
1860
|
+
if (hasKeys(errors)) {
|
|
1861
|
+
resetForm({ values: initialValues2, errors: {} });
|
|
1862
|
+
}
|
|
1863
|
+
}, [errors, initialValues2, resetForm]);
|
|
1864
|
+
return /* @__PURE__ */ jsx(FormikProvider, { value: formik, children: /* @__PURE__ */ jsx("form", { ...rest, ref, onSubmit: formik.handleSubmit, children }) });
|
|
1391
1865
|
})
|
|
1392
1866
|
);
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
viewBox: "0 0 15 15",
|
|
1407
|
-
fill: "none",
|
|
1408
|
-
xmlns: "http://www.w3.org/2000/svg"
|
|
1409
|
-
}, n, {
|
|
1410
|
-
ref: i
|
|
1411
|
-
}), ft("path", {
|
|
1412
|
-
d: "M12.8536 2.85355C13.0488 2.65829 13.0488 2.34171 12.8536 2.14645C12.6583 1.95118 12.3417 1.95118 12.1464 2.14645L7.5 6.79289L2.85355 2.14645C2.65829 1.95118 2.34171 1.95118 2.14645 2.14645C1.95118 2.34171 1.95118 2.65829 2.14645 2.85355L6.79289 7.5L2.14645 12.1464C1.95118 12.3417 1.95118 12.6583 2.14645 12.8536C2.34171 13.0488 2.65829 13.0488 2.85355 12.8536L7.5 8.20711L12.1464 12.8536C12.3417 13.0488 12.6583 13.0488 12.8536 12.8536C13.0488 12.6583 13.0488 12.3417 12.8536 12.1464L8.20711 7.5L12.8536 2.85355Z",
|
|
1413
|
-
fill: t,
|
|
1414
|
-
fillRule: "evenodd",
|
|
1415
|
-
clipRule: "evenodd"
|
|
1416
|
-
}));
|
|
1417
|
-
});
|
|
1418
|
-
const Nt = {
|
|
1419
|
-
...lt,
|
|
1420
|
-
section: X
|
|
1421
|
-
}, un = S(function(i) {
|
|
1422
|
-
const { field: e, setFieldType: t } = i, n = e.fieldTypeName, s = e.fieldTypeDescription, a = e.Icon;
|
|
1423
|
-
return /* @__PURE__ */ b(y, { gap: "4", align: "center", children: [
|
|
1424
|
-
/* @__PURE__ */ r(H, { type: "button", variant: "surface", onClick: t, style: { width: "135px" }, children: /* @__PURE__ */ b(y, { gap: "3", align: "center", grow: "1", children: [
|
|
1425
|
-
/* @__PURE__ */ r(a, {}),
|
|
1426
|
-
n
|
|
1867
|
+
const CompleteFieldTypeToClsMapping = {
|
|
1868
|
+
...FieldTypeToClsMapping,
|
|
1869
|
+
section: FieldSection
|
|
1870
|
+
};
|
|
1871
|
+
const FieldToChoose = memo(function FieldToChoose2(props) {
|
|
1872
|
+
const { field, setFieldType } = props;
|
|
1873
|
+
const typeName = field.fieldTypeName;
|
|
1874
|
+
const description2 = field.fieldTypeDescription;
|
|
1875
|
+
const Icon = field.Icon;
|
|
1876
|
+
return /* @__PURE__ */ jsxs(Flex, { gap: "4", align: "center", children: [
|
|
1877
|
+
/* @__PURE__ */ jsx(Button, { type: "button", variant: "surface", onClick: setFieldType, style: { width: "135px" }, children: /* @__PURE__ */ jsxs(Flex, { gap: "3", align: "center", grow: "1", children: [
|
|
1878
|
+
/* @__PURE__ */ jsx(Icon, {}),
|
|
1879
|
+
typeName
|
|
1427
1880
|
] }) }),
|
|
1428
|
-
/* @__PURE__ */
|
|
1881
|
+
/* @__PURE__ */ jsx(Text, { children: description2 })
|
|
1429
1882
|
] });
|
|
1430
|
-
})
|
|
1883
|
+
});
|
|
1884
|
+
const fieldsToChoose = [
|
|
1431
1885
|
["string", "text"],
|
|
1432
1886
|
["select", "multi-select", "upload"],
|
|
1433
1887
|
["boolean", "date", "number", "multi-string"]
|
|
1434
|
-
]
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1888
|
+
];
|
|
1889
|
+
const indexOfLastFieldGroup = fieldsToChoose.length - 1;
|
|
1890
|
+
const ChooseFieldToAdd = memo(function ChooseFieldToAdd2(props) {
|
|
1891
|
+
const { setFieldType } = props;
|
|
1892
|
+
return /* @__PURE__ */ jsx(Flex, { direction: "column", gap: "3", children: fieldsToChoose.map((fieldGroup, index) => /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: "3", children: [
|
|
1893
|
+
/* @__PURE__ */ jsx(Flex, { direction: "column", gap: "2", children: fieldGroup.map((identifier) => /* @__PURE__ */ jsx(
|
|
1894
|
+
FieldToChoose,
|
|
1439
1895
|
{
|
|
1440
|
-
field:
|
|
1441
|
-
setFieldType: () =>
|
|
1896
|
+
field: FieldTypeToClsMapping[identifier],
|
|
1897
|
+
setFieldType: () => setFieldType(identifier)
|
|
1442
1898
|
},
|
|
1443
|
-
|
|
1899
|
+
identifier
|
|
1444
1900
|
)) }),
|
|
1445
|
-
|
|
1446
|
-
] },
|
|
1447
|
-
})
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1901
|
+
index < indexOfLastFieldGroup && /* @__PURE__ */ jsx(Separator, { size: "4" })
|
|
1902
|
+
] }, index)) });
|
|
1903
|
+
});
|
|
1904
|
+
const validateNewFieldName = (takenLabels) => {
|
|
1905
|
+
return (value) => {
|
|
1906
|
+
if (!value || typeof value !== "string")
|
|
1907
|
+
return;
|
|
1908
|
+
if (takenLabels.includes(value.trim())) {
|
|
1909
|
+
return "This name is already taken.";
|
|
1910
|
+
}
|
|
1911
|
+
};
|
|
1912
|
+
};
|
|
1913
|
+
const commonFields = (takenLabels, type) => {
|
|
1914
|
+
const base = [
|
|
1915
|
+
new StringField({
|
|
1453
1916
|
label: "Label",
|
|
1454
|
-
required:
|
|
1917
|
+
required: true,
|
|
1455
1918
|
maxLength: 200,
|
|
1456
1919
|
identifier: "label",
|
|
1457
|
-
fieldValidators: [
|
|
1920
|
+
fieldValidators: [validateNewFieldName(takenLabels)]
|
|
1458
1921
|
}),
|
|
1459
|
-
new
|
|
1922
|
+
new TextField({
|
|
1460
1923
|
label: "Description",
|
|
1461
|
-
required:
|
|
1924
|
+
required: false,
|
|
1462
1925
|
maxLength: 1e3,
|
|
1463
1926
|
identifier: "description"
|
|
1464
1927
|
})
|
|
1465
1928
|
];
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1929
|
+
if (type === "section")
|
|
1930
|
+
return base;
|
|
1931
|
+
return [
|
|
1932
|
+
...base,
|
|
1933
|
+
new BooleanField({ label: "Required", description: null, required: false, identifier: "required" })
|
|
1469
1934
|
];
|
|
1470
|
-
}
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1935
|
+
};
|
|
1936
|
+
const FieldOptionsForm = memo(function FieldOptionsForm2(props) {
|
|
1937
|
+
const { fieldType, handleCancel, handleCreateField, defaultField, conditionalSourceFields } = props;
|
|
1938
|
+
const fieldCls = CompleteFieldTypeToClsMapping[fieldType];
|
|
1939
|
+
const formik = useFormikContext();
|
|
1940
|
+
const schema = useMemo(() => {
|
|
1941
|
+
const takenFieldLabels = getTakenFieldLabels(formik.values.fields).filter((id) => id !== (defaultField == null ? void 0 : defaultField.label));
|
|
1942
|
+
let fields = commonFields(takenFieldLabels, fieldType);
|
|
1943
|
+
if (fieldCls === FieldSection) {
|
|
1944
|
+
if (conditionalSourceFields === void 0) {
|
|
1476
1945
|
throw new Error("Conditional source fields must be provided when changing sections.");
|
|
1477
|
-
|
|
1946
|
+
}
|
|
1947
|
+
fields = fields.concat(fieldCls.getFieldCreationSchema(conditionalSourceFields));
|
|
1478
1948
|
} else {
|
|
1479
|
-
if (!(
|
|
1480
|
-
throw new Error(`Field must be an instance of BaseField. Got ${
|
|
1481
|
-
|
|
1949
|
+
if (!(fieldCls.prototype instanceof BaseField)) {
|
|
1950
|
+
throw new Error(`Field must be an instance of BaseField. Got ${fieldCls}.`);
|
|
1951
|
+
}
|
|
1952
|
+
fields = [...fields, ...fieldCls.getFieldCreationSchema()];
|
|
1482
1953
|
}
|
|
1483
1954
|
return {
|
|
1484
|
-
fields
|
|
1485
|
-
meta: { readonly:
|
|
1955
|
+
fields,
|
|
1956
|
+
meta: { readonly: false },
|
|
1486
1957
|
// using the dialog title as the form title
|
|
1487
1958
|
title: null
|
|
1488
1959
|
};
|
|
1489
|
-
}, [
|
|
1490
|
-
return /* @__PURE__ */
|
|
1491
|
-
|
|
1960
|
+
}, [formik.values.fields, fieldType, fieldCls, defaultField == null ? void 0 : defaultField.label, conditionalSourceFields]);
|
|
1961
|
+
return /* @__PURE__ */ jsx(
|
|
1962
|
+
FormRenderer,
|
|
1492
1963
|
{
|
|
1493
|
-
schema
|
|
1494
|
-
values:
|
|
1495
|
-
onSubmit:
|
|
1496
|
-
cancelText:
|
|
1497
|
-
onCancel:
|
|
1964
|
+
schema,
|
|
1965
|
+
values: defaultField,
|
|
1966
|
+
onSubmit: handleCreateField,
|
|
1967
|
+
cancelText: defaultField ? void 0 : "Back",
|
|
1968
|
+
onCancel: handleCancel
|
|
1498
1969
|
}
|
|
1499
1970
|
);
|
|
1500
|
-
})
|
|
1501
|
-
|
|
1502
|
-
|
|
1971
|
+
});
|
|
1972
|
+
const FieldBuilder = memo(function FieldBuilder2(props) {
|
|
1973
|
+
const { parentPath, index, children, initial, editing, conditionalSourceFields } = props;
|
|
1974
|
+
const [fieldType, setFieldType] = useState();
|
|
1975
|
+
const type = (initial == null ? void 0 : initial.type) ?? fieldType;
|
|
1976
|
+
const typeName = type ? CompleteFieldTypeToClsMapping[type].fieldTypeName : void 0;
|
|
1977
|
+
const { setFieldValue, values } = useFormikContext();
|
|
1978
|
+
if (editing && !initial)
|
|
1503
1979
|
throw new Error("Initial field must be provided if editing is true.");
|
|
1504
|
-
const
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1980
|
+
const showChooseField = !type && !editing && !initial;
|
|
1981
|
+
const title2 = showChooseField ? "Choose a field type" : `${typeName} settings`;
|
|
1982
|
+
const description2 = showChooseField ? "Select a field type to add to this section." : (typeName == null ? void 0 : typeName.toLowerCase()) === "section" ? "Customize your section" : `Customize your ${typeName == null ? void 0 : typeName.toLowerCase()} field.`;
|
|
1983
|
+
const handleCancel = useCallback(() => setFieldType(void 0), []);
|
|
1984
|
+
const handleCloseInterrupt = useCallback((confirmClose) => {
|
|
1985
|
+
setFieldType(void 0);
|
|
1986
|
+
confirmClose();
|
|
1987
|
+
}, []);
|
|
1988
|
+
const handleCreateField = useCallback(
|
|
1989
|
+
(form, closeDialog) => {
|
|
1990
|
+
const { label } = form;
|
|
1991
|
+
if (!type)
|
|
1510
1992
|
throw new Error("Field type must be selected before creating a field.");
|
|
1511
|
-
if (!
|
|
1993
|
+
if (!label || typeof label !== "string")
|
|
1512
1994
|
throw new Error("Label must be provided before creating a field.");
|
|
1513
|
-
const
|
|
1514
|
-
type
|
|
1515
|
-
...
|
|
1516
|
-
identifier:
|
|
1517
|
-
}).serialize()
|
|
1518
|
-
|
|
1995
|
+
const field = deserialize({
|
|
1996
|
+
type,
|
|
1997
|
+
...form,
|
|
1998
|
+
identifier: makeIdentifier(form.identifier, label)
|
|
1999
|
+
}).serialize();
|
|
2000
|
+
const parent = get(values, parentPath);
|
|
2001
|
+
if (parent === void 0) {
|
|
1519
2002
|
throw new Error("Parent path must point to an existing field.");
|
|
1520
|
-
|
|
1521
|
-
|
|
2003
|
+
}
|
|
2004
|
+
let newFields;
|
|
2005
|
+
if (!Array.isArray(parent))
|
|
1522
2006
|
throw new Error("Parent path must point to an array.");
|
|
1523
|
-
|
|
2007
|
+
if (editing) {
|
|
2008
|
+
newFields = replace(parent, index, field);
|
|
2009
|
+
} else {
|
|
2010
|
+
newFields = insert(parent, index, field);
|
|
2011
|
+
}
|
|
2012
|
+
setFieldValue(parentPath, newFields).then();
|
|
2013
|
+
closeDialog();
|
|
1524
2014
|
},
|
|
1525
|
-
[
|
|
2015
|
+
[editing, type, values, parentPath, setFieldValue, index]
|
|
1526
2016
|
);
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
] }),
|
|
1532
|
-
/* @__PURE__ */ r(V, { children: z }),
|
|
1533
|
-
g ? /* @__PURE__ */ r(mn, { setFieldType: d }) : /* @__PURE__ */ r(
|
|
1534
|
-
gn,
|
|
1535
|
-
{
|
|
1536
|
-
conditionalSourceFields: c,
|
|
1537
|
-
handleCancel: I,
|
|
1538
|
-
handleCreateField: (A) => k(A),
|
|
1539
|
-
fieldType: u,
|
|
1540
|
-
defaultField: s
|
|
2017
|
+
const dialogContent = useCallback(
|
|
2018
|
+
(closeDialog) => {
|
|
2019
|
+
if (showChooseField) {
|
|
2020
|
+
return /* @__PURE__ */ jsx(ChooseFieldToAdd, { setFieldType });
|
|
1541
2021
|
}
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
2022
|
+
return /* @__PURE__ */ jsx(
|
|
2023
|
+
FieldOptionsForm,
|
|
2024
|
+
{
|
|
2025
|
+
conditionalSourceFields,
|
|
2026
|
+
handleCancel,
|
|
2027
|
+
handleCreateField: (form) => handleCreateField(form, closeDialog),
|
|
2028
|
+
fieldType: type,
|
|
2029
|
+
defaultField: initial
|
|
2030
|
+
}
|
|
2031
|
+
);
|
|
2032
|
+
},
|
|
2033
|
+
[conditionalSourceFields, handleCancel, handleCreateField, initial, showChooseField, type]
|
|
2034
|
+
);
|
|
2035
|
+
return /* @__PURE__ */ jsx(Dialog, { onCloseInterrupt: handleCloseInterrupt, title: title2, description: description2, content: dialogContent, children });
|
|
2036
|
+
});
|
|
2037
|
+
const DefaultWrapper = ({ children }) => /* @__PURE__ */ jsx(Fragment, { children });
|
|
2038
|
+
const forMobile = (mobile, display) => ({
|
|
2039
|
+
initial: mobile ? display : "none",
|
|
2040
|
+
sm: mobile ? "none" : display
|
|
2041
|
+
});
|
|
2042
|
+
const FieldActions = memo(function FieldActions2(props) {
|
|
2043
|
+
const { remove: remove2, dragHandleProps, editProps, insertAfterProps, duplicateProps } = props;
|
|
2044
|
+
const actions = useMemo(
|
|
1549
2045
|
() => [
|
|
1550
2046
|
{
|
|
1551
|
-
Wrapper:
|
|
1552
|
-
wrapperProps:
|
|
1553
|
-
Icon:
|
|
2047
|
+
Wrapper: FieldBuilder,
|
|
2048
|
+
wrapperProps: editProps,
|
|
2049
|
+
Icon: Pencil1Icon,
|
|
1554
2050
|
text: "Edit"
|
|
1555
2051
|
},
|
|
1556
2052
|
{
|
|
1557
|
-
Icon:
|
|
2053
|
+
Icon: TrashIcon,
|
|
1558
2054
|
buttonProps: {
|
|
1559
|
-
onClick:
|
|
2055
|
+
onClick: remove2
|
|
1560
2056
|
},
|
|
1561
2057
|
text: "Delete"
|
|
1562
2058
|
},
|
|
1563
2059
|
{
|
|
1564
|
-
Wrapper:
|
|
1565
|
-
wrapperProps:
|
|
1566
|
-
Icon:
|
|
2060
|
+
Wrapper: FieldBuilder,
|
|
2061
|
+
wrapperProps: duplicateProps,
|
|
2062
|
+
Icon: CopyIcon,
|
|
1567
2063
|
text: "Duplicate"
|
|
1568
2064
|
},
|
|
1569
2065
|
{
|
|
1570
|
-
Wrapper:
|
|
1571
|
-
wrapperProps:
|
|
1572
|
-
Icon:
|
|
2066
|
+
Wrapper: FieldBuilder,
|
|
2067
|
+
wrapperProps: insertAfterProps,
|
|
2068
|
+
Icon: PlusIcon,
|
|
1573
2069
|
text: "Add after"
|
|
1574
2070
|
},
|
|
1575
2071
|
{
|
|
1576
2072
|
// Wrapping icon in a div so that the asChild turns the button into a div
|
|
1577
2073
|
// so that the drag handle props are not applied to the icon
|
|
1578
2074
|
// Note: b/c the <button> does not handle the space-press event correctly
|
|
1579
|
-
Icon: (
|
|
2075
|
+
Icon: (props2) => /* @__PURE__ */ jsx("div", { ...props2, children: /* @__PURE__ */ jsx(DragHandleDots2Icon, {}) }),
|
|
1580
2076
|
text: "Reorder",
|
|
1581
|
-
disableOnMobile:
|
|
1582
|
-
buttonProps: { ...
|
|
2077
|
+
disableOnMobile: true,
|
|
2078
|
+
buttonProps: { ...dragHandleProps, asChild: true }
|
|
1583
2079
|
}
|
|
1584
2080
|
],
|
|
1585
|
-
[
|
|
2081
|
+
[dragHandleProps, duplicateProps, editProps, insertAfterProps, remove2]
|
|
1586
2082
|
);
|
|
1587
|
-
return /* @__PURE__ */
|
|
1588
|
-
/* @__PURE__ */
|
|
1589
|
-
const
|
|
1590
|
-
return /* @__PURE__ */
|
|
2083
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2084
|
+
/* @__PURE__ */ jsx(Flex, { gap: "4", display: forMobile(false, "flex"), children: actions.map((Action) => {
|
|
2085
|
+
const Wrapper = Action.Wrapper ?? DefaultWrapper;
|
|
2086
|
+
return /* @__PURE__ */ jsx(Wrapper, { ...Action.wrapperProps, children: /* @__PURE__ */ jsx(IconButton, { type: "button", variant: "ghost", "aria-label": Action.text, ...Action.buttonProps, children: /* @__PURE__ */ jsx(Action.Icon, {}) }) }, Action.text);
|
|
1591
2087
|
}) }),
|
|
1592
|
-
/* @__PURE__ */
|
|
1593
|
-
|
|
2088
|
+
/* @__PURE__ */ jsx(Box, { display: forMobile(true, "block"), children: /* @__PURE__ */ jsx(
|
|
2089
|
+
DropdownMenu,
|
|
1594
2090
|
{
|
|
1595
|
-
trigger: /* @__PURE__ */
|
|
1596
|
-
closeOnSelect:
|
|
1597
|
-
items:
|
|
1598
|
-
var
|
|
1599
|
-
if (
|
|
2091
|
+
trigger: /* @__PURE__ */ jsx(IconButton, { variant: "ghost", "aria-label": "Actions menu", children: /* @__PURE__ */ jsx(DotsVerticalIcon, {}) }),
|
|
2092
|
+
closeOnSelect: false,
|
|
2093
|
+
items: actions.map((Action) => {
|
|
2094
|
+
var _a;
|
|
2095
|
+
if (Action.disableOnMobile)
|
|
1600
2096
|
return null;
|
|
1601
|
-
const
|
|
2097
|
+
const Wrapper = Action.Wrapper ?? DefaultWrapper;
|
|
1602
2098
|
return {
|
|
1603
|
-
...
|
|
1604
|
-
onSelect: (
|
|
1605
|
-
content: /* @__PURE__ */
|
|
1606
|
-
/* @__PURE__ */
|
|
1607
|
-
|
|
2099
|
+
...Action.buttonProps,
|
|
2100
|
+
onSelect: (_a = Action.buttonProps) == null ? void 0 : _a.onClick,
|
|
2101
|
+
content: /* @__PURE__ */ jsx(Wrapper, { ...Action.wrapperProps, children: /* @__PURE__ */ jsxs(Flex, { gap: "2", align: "center", children: [
|
|
2102
|
+
/* @__PURE__ */ jsx(Action.Icon, {}),
|
|
2103
|
+
Action.text
|
|
1608
2104
|
] }) })
|
|
1609
2105
|
};
|
|
1610
|
-
}).filter((
|
|
2106
|
+
}).filter((x) => x !== null)
|
|
1611
2107
|
}
|
|
1612
2108
|
) })
|
|
1613
2109
|
] });
|
|
1614
|
-
})
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
2110
|
+
});
|
|
2111
|
+
const formId = "form-builder";
|
|
2112
|
+
const FieldWithActions = memo(function FieldWithActions2(props) {
|
|
2113
|
+
const { field, index, sectionIndex, takenLabels, remove: remove2 } = props;
|
|
2114
|
+
const deserializedField = useMemo(() => deserialize(field), [field]);
|
|
2115
|
+
const input = useFieldInput(deserializedField, { formId, disabled: true });
|
|
2116
|
+
const duplicateField = useCallback(
|
|
2117
|
+
(field2) => {
|
|
2118
|
+
if (field2.label === null) {
|
|
2119
|
+
throw new Error(`Expected a label for field ${field2.identifier}`);
|
|
2120
|
+
}
|
|
2121
|
+
return { ...field2, label: incrementFieldLabel(field2.label, takenLabels), identifier: "" };
|
|
1620
2122
|
},
|
|
1621
|
-
[
|
|
1622
|
-
)
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
2123
|
+
[takenLabels]
|
|
2124
|
+
);
|
|
2125
|
+
const editFieldProps = useMemo(
|
|
2126
|
+
() => ({
|
|
2127
|
+
index,
|
|
2128
|
+
parentPath: `fields.${sectionIndex}.fields`,
|
|
2129
|
+
initial: field,
|
|
2130
|
+
editing: true
|
|
2131
|
+
}),
|
|
2132
|
+
[field, index, sectionIndex]
|
|
2133
|
+
);
|
|
2134
|
+
const duplicateFieldProps = useMemo(
|
|
1629
2135
|
() => ({
|
|
1630
|
-
parentPath: `fields.${
|
|
1631
|
-
index:
|
|
1632
|
-
initial:
|
|
1633
|
-
onComplete: () => l("display")
|
|
2136
|
+
parentPath: `fields.${sectionIndex}.fields`,
|
|
2137
|
+
index: index + 1,
|
|
2138
|
+
initial: duplicateField(field)
|
|
1634
2139
|
}),
|
|
1635
|
-
[
|
|
1636
|
-
)
|
|
2140
|
+
[duplicateField, field, index, sectionIndex]
|
|
2141
|
+
);
|
|
2142
|
+
const insertAfterProps = useMemo(
|
|
1637
2143
|
() => ({
|
|
1638
|
-
parentPath: `fields.${
|
|
1639
|
-
index:
|
|
1640
|
-
initial: void 0
|
|
1641
|
-
onComplete: () => l("display")
|
|
2144
|
+
parentPath: `fields.${sectionIndex}.fields`,
|
|
2145
|
+
index: index + 1,
|
|
2146
|
+
initial: void 0
|
|
1642
2147
|
}),
|
|
1643
|
-
[
|
|
2148
|
+
[index, sectionIndex]
|
|
1644
2149
|
);
|
|
1645
|
-
return
|
|
1646
|
-
|
|
2150
|
+
return /* @__PURE__ */ jsx(Draggable, { draggableId: field.identifier, index, children: (draggableProvided) => /* @__PURE__ */ jsx(
|
|
2151
|
+
Card,
|
|
1647
2152
|
{
|
|
1648
|
-
ref:
|
|
1649
|
-
...
|
|
1650
|
-
...
|
|
2153
|
+
ref: draggableProvided.innerRef,
|
|
2154
|
+
...draggableProvided.draggableProps,
|
|
2155
|
+
...draggableProvided.dragHandleProps,
|
|
1651
2156
|
mb: "4",
|
|
1652
|
-
children: /* @__PURE__ */
|
|
1653
|
-
|
|
1654
|
-
/* @__PURE__ */
|
|
1655
|
-
|
|
2157
|
+
children: /* @__PURE__ */ jsxs(Flex, { gap: "4", justify: "between", align: "center", children: [
|
|
2158
|
+
input,
|
|
2159
|
+
/* @__PURE__ */ jsx(
|
|
2160
|
+
FieldActions,
|
|
1656
2161
|
{
|
|
1657
|
-
remove:
|
|
1658
|
-
editProps:
|
|
1659
|
-
duplicateProps:
|
|
1660
|
-
insertAfterProps
|
|
1661
|
-
dragHandleProps:
|
|
2162
|
+
remove: remove2,
|
|
2163
|
+
editProps: editFieldProps,
|
|
2164
|
+
duplicateProps: duplicateFieldProps,
|
|
2165
|
+
insertAfterProps,
|
|
2166
|
+
dragHandleProps: draggableProvided.dragHandleProps
|
|
1662
2167
|
}
|
|
1663
2168
|
)
|
|
1664
2169
|
] })
|
|
1665
2170
|
}
|
|
1666
|
-
) })
|
|
1667
|
-
})
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
2171
|
+
) });
|
|
2172
|
+
});
|
|
2173
|
+
const FieldSectionWithActions = memo(function FieldSectionWithActions2(props) {
|
|
2174
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
2175
|
+
const { field, index: sectionIndex, dropState } = props;
|
|
2176
|
+
const isDropDisabled = (_a = dropState[field.identifier]) == null ? void 0 : _a.disabled;
|
|
2177
|
+
const { setFieldValue, values } = useFormikContext();
|
|
2178
|
+
const alertDialog = useAlertDialog();
|
|
2179
|
+
const takenFieldLabels = getTakenFieldLabels(values.fields);
|
|
2180
|
+
const removeSectionConditions = useCallback(
|
|
2181
|
+
(sectionsToUpdate, allSections) => {
|
|
2182
|
+
for (const section of sectionsToUpdate) {
|
|
2183
|
+
const sectionIndex2 = allSections.indexOf(section);
|
|
2184
|
+
setFieldValue(`fields.${sectionIndex2}.condition`, null).then();
|
|
2185
|
+
setFieldValue(`fields.${sectionIndex2}.conditional`, false).then();
|
|
1674
2186
|
}
|
|
1675
2187
|
},
|
|
1676
|
-
[
|
|
1677
|
-
)
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
2188
|
+
[setFieldValue]
|
|
2189
|
+
);
|
|
2190
|
+
const makeRemoveFieldAction = useCallback(
|
|
2191
|
+
(fieldIndex) => {
|
|
2192
|
+
var _a2;
|
|
2193
|
+
const removing = field.fields[fieldIndex];
|
|
2194
|
+
if (!removing)
|
|
1682
2195
|
throw new Error("Could not find field to remove.");
|
|
1683
|
-
const
|
|
1684
|
-
for (const
|
|
1685
|
-
((
|
|
2196
|
+
const sectionsWithMatchingCondition = [];
|
|
2197
|
+
for (const section of values.fields) {
|
|
2198
|
+
if (((_a2 = section.condition) == null ? void 0 : _a2.identifier) === removing.identifier) {
|
|
2199
|
+
sectionsWithMatchingCondition.push(section);
|
|
2200
|
+
}
|
|
2201
|
+
}
|
|
1686
2202
|
return {
|
|
1687
|
-
removing
|
|
1688
|
-
affectedSections:
|
|
1689
|
-
action: () =>
|
|
2203
|
+
removing,
|
|
2204
|
+
affectedSections: sectionsWithMatchingCondition,
|
|
2205
|
+
action: () => setFieldValue(`fields.${sectionIndex}.fields`, remove(field.fields, fieldIndex))
|
|
1690
2206
|
};
|
|
1691
2207
|
},
|
|
1692
|
-
[
|
|
1693
|
-
)
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
2208
|
+
[field.fields, values.fields, setFieldValue, sectionIndex]
|
|
2209
|
+
);
|
|
2210
|
+
const removeField = useCallback(
|
|
2211
|
+
(i) => {
|
|
2212
|
+
const { affectedSections, action, removing } = makeRemoveFieldAction(i);
|
|
2213
|
+
const cmd = () => {
|
|
2214
|
+
action().then();
|
|
2215
|
+
removeSectionConditions(affectedSections, values.fields);
|
|
1697
2216
|
};
|
|
1698
|
-
if (
|
|
1699
|
-
const
|
|
1700
|
-
return
|
|
2217
|
+
if (affectedSections.length > 0) {
|
|
2218
|
+
const labels = affectedSections.map((section) => section.label).join(", ");
|
|
2219
|
+
return alertDialog({
|
|
1701
2220
|
title: "Remove condition?",
|
|
1702
|
-
description: `${
|
|
2221
|
+
description: `${removing.label} is being used as a condition, deleting it will remove the condition from the ${labels} section(s).`,
|
|
1703
2222
|
severity: "danger",
|
|
1704
2223
|
actionText: "Remove",
|
|
1705
|
-
onAction:
|
|
2224
|
+
onAction: cmd
|
|
1706
2225
|
});
|
|
1707
2226
|
}
|
|
1708
|
-
|
|
2227
|
+
cmd();
|
|
1709
2228
|
},
|
|
1710
|
-
[
|
|
1711
|
-
)
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
2229
|
+
[makeRemoveFieldAction, removeSectionConditions, values.fields, alertDialog]
|
|
2230
|
+
);
|
|
2231
|
+
const removeSection = useCallback(() => {
|
|
2232
|
+
const fieldSideEffects = field.fields.map((_, i) => makeRemoveFieldAction(i));
|
|
2233
|
+
const affectedSections = fieldSideEffects.flatMap((sideEffect) => sideEffect.affectedSections);
|
|
2234
|
+
const title2 = affectedSections.length ? "Remove fields and conditions?" : "Remove fields?";
|
|
2235
|
+
const numFields = field.fields.length;
|
|
2236
|
+
const sectionLabels = affectedSections.map((section) => section.label).join(", ");
|
|
2237
|
+
const description2 = affectedSections.length ? `Deleting this section will remove the ${numFields} field(s) it contains and will remove the conditions from following sections: ${sectionLabels}` : `Deleting this section will remove the ${numFields} field(s) it contains.`;
|
|
2238
|
+
const updatedSections = remove(values.fields, sectionIndex);
|
|
2239
|
+
const cmd = () => setFieldValue("fields", updatedSections);
|
|
2240
|
+
if (affectedSections.length > 0) {
|
|
2241
|
+
return alertDialog({
|
|
2242
|
+
title: title2,
|
|
2243
|
+
description: description2,
|
|
1717
2244
|
severity: "danger",
|
|
1718
2245
|
actionText: "Remove",
|
|
1719
2246
|
onAction: () => {
|
|
1720
|
-
|
|
1721
|
-
|
|
2247
|
+
cmd().then(() => {
|
|
2248
|
+
removeSectionConditions(affectedSections, updatedSections);
|
|
1722
2249
|
});
|
|
1723
2250
|
}
|
|
1724
2251
|
});
|
|
1725
|
-
|
|
2252
|
+
}
|
|
2253
|
+
cmd().then();
|
|
1726
2254
|
}, [
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
])
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
2255
|
+
field.fields,
|
|
2256
|
+
values.fields,
|
|
2257
|
+
sectionIndex,
|
|
2258
|
+
makeRemoveFieldAction,
|
|
2259
|
+
setFieldValue,
|
|
2260
|
+
alertDialog,
|
|
2261
|
+
removeSectionConditions
|
|
2262
|
+
]);
|
|
2263
|
+
const duplicateSection = useCallback(
|
|
2264
|
+
(field2) => {
|
|
2265
|
+
if (field2.label === null) {
|
|
2266
|
+
throw new Error(`Expected a label for field ${field2.identifier}`);
|
|
2267
|
+
}
|
|
2268
|
+
const newSectionLabel = incrementFieldLabel(field2.label, takenFieldLabels);
|
|
2269
|
+
const newFields = field2.fields.map((f) => {
|
|
2270
|
+
const newLabel = incrementFieldLabel(f.label, takenFieldLabels);
|
|
1740
2271
|
return {
|
|
1741
|
-
...
|
|
1742
|
-
label:
|
|
1743
|
-
identifier:
|
|
2272
|
+
...f,
|
|
2273
|
+
label: newLabel,
|
|
2274
|
+
identifier: makeIdentifier(void 0, newLabel)
|
|
1744
2275
|
};
|
|
1745
2276
|
});
|
|
1746
|
-
return { ...
|
|
2277
|
+
return { ...field2, label: newSectionLabel, fields: newFields, identifier: "" };
|
|
1747
2278
|
},
|
|
1748
|
-
[
|
|
1749
|
-
)
|
|
1750
|
-
|
|
1751
|
-
const P = [...D];
|
|
1752
|
-
return P[F] = E, P;
|
|
1753
|
-
});
|
|
1754
|
-
}, []), w = x(
|
|
2279
|
+
[takenFieldLabels]
|
|
2280
|
+
);
|
|
2281
|
+
const editSectionProps = useMemo(
|
|
1755
2282
|
() => ({
|
|
1756
|
-
index:
|
|
2283
|
+
index: sectionIndex,
|
|
1757
2284
|
parentPath: "fields",
|
|
1758
|
-
initial:
|
|
1759
|
-
editing:
|
|
1760
|
-
conditionalSourceFields:
|
|
1761
|
-
onComplete: () => console.log("editsectionprops")
|
|
2285
|
+
initial: field,
|
|
2286
|
+
editing: true,
|
|
2287
|
+
conditionalSourceFields: makeConditionalSourceFields(values.fields, sectionIndex)
|
|
1762
2288
|
}),
|
|
1763
|
-
[
|
|
1764
|
-
)
|
|
2289
|
+
[field, sectionIndex, values.fields]
|
|
2290
|
+
);
|
|
2291
|
+
const insertSectionProps = useMemo(
|
|
1765
2292
|
() => ({
|
|
1766
|
-
index:
|
|
2293
|
+
index: sectionIndex + 1,
|
|
1767
2294
|
parentPath: "fields",
|
|
1768
|
-
initial:
|
|
1769
|
-
conditionalSourceFields:
|
|
1770
|
-
onComplete: () => console.log("insertsectionprops")
|
|
2295
|
+
initial: emptySection(),
|
|
2296
|
+
conditionalSourceFields: makeConditionalSourceFields(values.fields, sectionIndex + 1)
|
|
1771
2297
|
}),
|
|
1772
|
-
[
|
|
1773
|
-
)
|
|
2298
|
+
[sectionIndex, values.fields]
|
|
2299
|
+
);
|
|
2300
|
+
const insertFieldAtEndOfSection = useMemo(
|
|
1774
2301
|
() => ({
|
|
1775
|
-
parentPath: `fields.${
|
|
1776
|
-
index:
|
|
1777
|
-
initial: void 0
|
|
1778
|
-
onComplete: () => console.log("insertfieldatendofsectionprops")
|
|
2302
|
+
parentPath: `fields.${sectionIndex}.fields`,
|
|
2303
|
+
index: field.fields.length,
|
|
2304
|
+
initial: void 0
|
|
1779
2305
|
}),
|
|
1780
|
-
[
|
|
1781
|
-
)
|
|
2306
|
+
[field.fields.length, sectionIndex]
|
|
2307
|
+
);
|
|
2308
|
+
const duplicateSectionProps = useMemo(
|
|
1782
2309
|
() => ({
|
|
1783
|
-
index:
|
|
2310
|
+
index: sectionIndex + 1,
|
|
1784
2311
|
parentPath: "fields",
|
|
1785
|
-
initial:
|
|
1786
|
-
conditionalSourceFields:
|
|
1787
|
-
onComplete: () => console.log("duplicatesectionprops")
|
|
2312
|
+
initial: duplicateSection(field),
|
|
2313
|
+
conditionalSourceFields: makeConditionalSourceFields(values.fields, sectionIndex + 1)
|
|
1788
2314
|
}),
|
|
1789
|
-
[
|
|
1790
|
-
)
|
|
2315
|
+
[duplicateSection, field, sectionIndex, values.fields]
|
|
2316
|
+
);
|
|
2317
|
+
const conditionLabel = useMemo(
|
|
1791
2318
|
() => {
|
|
1792
|
-
var
|
|
1793
|
-
return (
|
|
2319
|
+
var _a2, _b2;
|
|
2320
|
+
return (_b2 = findFieldByIdentifier(values.fields, (_a2 = field.condition) == null ? void 0 : _a2.identifier)) == null ? void 0 : _b2.label;
|
|
1794
2321
|
},
|
|
1795
|
-
[(
|
|
1796
|
-
)
|
|
1797
|
-
|
|
2322
|
+
[(_b = field.condition) == null ? void 0 : _b.identifier, values.fields]
|
|
2323
|
+
);
|
|
2324
|
+
const conditionComparison = Array.isArray((_c = field.condition) == null ? void 0 : _c.value) ? "contains all of" : "equals";
|
|
2325
|
+
if (valueIsFile((_d = field.condition) == null ? void 0 : _d.value))
|
|
1798
2326
|
throw new Error("File values are not supported for conditions.");
|
|
1799
|
-
const
|
|
1800
|
-
return /* @__PURE__ */
|
|
1801
|
-
|
|
2327
|
+
const conditionValue = Array.isArray((_e = field.condition) == null ? void 0 : _e.value) ? (_f = field.condition) == null ? void 0 : _f.value.map((v) => typeof v === "string" ? v : v.label).join(", ") : (_g = field.condition) == null ? void 0 : _g.value.toString();
|
|
2328
|
+
return /* @__PURE__ */ jsx(Draggable, { draggableId: field.identifier, index: sectionIndex, children: (draggableProvided) => /* @__PURE__ */ jsx(
|
|
2329
|
+
Card,
|
|
1802
2330
|
{
|
|
1803
|
-
ref:
|
|
1804
|
-
...
|
|
1805
|
-
...
|
|
2331
|
+
ref: draggableProvided.innerRef,
|
|
2332
|
+
...draggableProvided.draggableProps,
|
|
2333
|
+
...draggableProvided.dragHandleProps,
|
|
1806
2334
|
mb: "4",
|
|
1807
|
-
children: /* @__PURE__ */
|
|
1808
|
-
/* @__PURE__ */
|
|
1809
|
-
/* @__PURE__ */
|
|
1810
|
-
/* @__PURE__ */
|
|
1811
|
-
/* @__PURE__ */
|
|
2335
|
+
children: /* @__PURE__ */ jsxs(Flex, { gap: "3", justify: "between", align: "center", children: [
|
|
2336
|
+
/* @__PURE__ */ jsxs(Flex, { direction: "column", gap: "2", grow: "1", children: [
|
|
2337
|
+
/* @__PURE__ */ jsxs(Flex, { direction: "column", children: [
|
|
2338
|
+
/* @__PURE__ */ jsx(Heading, { as: "h3", size: "3", children: field.label }),
|
|
2339
|
+
/* @__PURE__ */ jsx(Text, { className: styles$3.description, children: field.description })
|
|
1812
2340
|
] }),
|
|
1813
|
-
|
|
2341
|
+
field.condition && /* @__PURE__ */ jsx(Text, { size: "1", children: /* @__PURE__ */ jsxs(Em, { children: [
|
|
1814
2342
|
"Display only if ",
|
|
1815
|
-
/* @__PURE__ */
|
|
2343
|
+
/* @__PURE__ */ jsx(Strong, { children: conditionLabel }),
|
|
1816
2344
|
" ",
|
|
1817
|
-
|
|
2345
|
+
conditionComparison,
|
|
1818
2346
|
" ",
|
|
1819
|
-
/* @__PURE__ */
|
|
2347
|
+
/* @__PURE__ */ jsx(Strong, { children: conditionValue })
|
|
1820
2348
|
] }) }),
|
|
1821
|
-
/* @__PURE__ */
|
|
1822
|
-
|
|
2349
|
+
/* @__PURE__ */ jsx(Droppable, { droppableId: field.identifier, type: "SECTION", isDropDisabled, children: (droppableProvided) => /* @__PURE__ */ jsxs(
|
|
2350
|
+
Flex,
|
|
1823
2351
|
{
|
|
1824
|
-
ref:
|
|
1825
|
-
...
|
|
2352
|
+
ref: droppableProvided.innerRef,
|
|
2353
|
+
...droppableProvided.droppableProps,
|
|
1826
2354
|
direction: "column",
|
|
1827
2355
|
gap: "0",
|
|
1828
2356
|
children: [
|
|
1829
|
-
|
|
1830
|
-
|
|
2357
|
+
field.fields.map((child, i) => /* @__PURE__ */ jsx(
|
|
2358
|
+
FieldWithActions,
|
|
1831
2359
|
{
|
|
1832
|
-
field:
|
|
1833
|
-
index:
|
|
1834
|
-
sectionIndex
|
|
1835
|
-
remove: () =>
|
|
1836
|
-
takenLabels:
|
|
1837
|
-
mode: s[P] ?? "display",
|
|
1838
|
-
onModeChange: (_) => I(P, _)
|
|
2360
|
+
field: child,
|
|
2361
|
+
index: i,
|
|
2362
|
+
sectionIndex,
|
|
2363
|
+
remove: () => removeField(i),
|
|
2364
|
+
takenLabels: takenFieldLabels
|
|
1839
2365
|
},
|
|
1840
|
-
|
|
2366
|
+
child.identifier
|
|
1841
2367
|
)),
|
|
1842
|
-
|
|
1843
|
-
/* @__PURE__ */
|
|
1844
|
-
/* @__PURE__ */
|
|
2368
|
+
droppableProvided.placeholder,
|
|
2369
|
+
/* @__PURE__ */ jsx(FieldBuilder, { ...insertFieldAtEndOfSection, children: /* @__PURE__ */ jsxs(Button, { type: "button", variant: "outline", children: [
|
|
2370
|
+
/* @__PURE__ */ jsx(PlusIcon, {}),
|
|
1845
2371
|
" Add a field"
|
|
1846
2372
|
] }) })
|
|
1847
2373
|
]
|
|
1848
2374
|
}
|
|
1849
2375
|
) })
|
|
1850
2376
|
] }),
|
|
1851
|
-
/* @__PURE__ */
|
|
1852
|
-
|
|
2377
|
+
/* @__PURE__ */ jsx(
|
|
2378
|
+
FieldActions,
|
|
1853
2379
|
{
|
|
1854
|
-
remove:
|
|
1855
|
-
insertAfterProps:
|
|
1856
|
-
dragHandleProps:
|
|
1857
|
-
editProps:
|
|
1858
|
-
duplicateProps:
|
|
2380
|
+
remove: removeSection,
|
|
2381
|
+
insertAfterProps: insertSectionProps,
|
|
2382
|
+
dragHandleProps: draggableProvided.dragHandleProps,
|
|
2383
|
+
editProps: editSectionProps,
|
|
2384
|
+
duplicateProps: duplicateSectionProps
|
|
1859
2385
|
}
|
|
1860
2386
|
)
|
|
1861
2387
|
] })
|
|
1862
2388
|
}
|
|
1863
2389
|
) });
|
|
1864
|
-
})
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
2390
|
+
});
|
|
2391
|
+
const reducer = (state, action) => {
|
|
2392
|
+
var _a;
|
|
2393
|
+
const next = { ...state };
|
|
2394
|
+
switch (action.type) {
|
|
1868
2395
|
case "release":
|
|
1869
|
-
for (const
|
|
1870
|
-
|
|
1871
|
-
|
|
2396
|
+
for (const sectionId in next) {
|
|
2397
|
+
next[sectionId].disabled = false;
|
|
2398
|
+
}
|
|
2399
|
+
return next;
|
|
1872
2400
|
case "hold":
|
|
1873
|
-
for (const
|
|
1874
|
-
(
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
return i.state;
|
|
1878
|
-
}
|
|
1879
|
-
}, wn = (o, i) => {
|
|
1880
|
-
if (i)
|
|
1881
|
-
for (let e = 0; e < o.length; e++) {
|
|
1882
|
-
const t = o[e];
|
|
1883
|
-
if (t) {
|
|
1884
|
-
for (const n of t.fields)
|
|
1885
|
-
if (n.identifier === i)
|
|
1886
|
-
return e;
|
|
2401
|
+
for (const sectionId in next) {
|
|
2402
|
+
if ((_a = next[sectionId]) == null ? void 0 : _a.conditionFields.has(action.fieldId)) {
|
|
2403
|
+
next[sectionId].disabled = true;
|
|
2404
|
+
}
|
|
1887
2405
|
}
|
|
2406
|
+
return next;
|
|
2407
|
+
case "update":
|
|
2408
|
+
return action.state;
|
|
2409
|
+
}
|
|
2410
|
+
};
|
|
2411
|
+
const getConditionIndex = (fields, identifier) => {
|
|
2412
|
+
if (!identifier)
|
|
2413
|
+
return void 0;
|
|
2414
|
+
for (let i = 0; i < fields.length; i++) {
|
|
2415
|
+
const section = fields[i];
|
|
2416
|
+
if (!section)
|
|
2417
|
+
continue;
|
|
2418
|
+
for (const field of section.fields) {
|
|
2419
|
+
if (field.identifier === identifier)
|
|
2420
|
+
return i;
|
|
1888
2421
|
}
|
|
1889
|
-
}
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
2422
|
+
}
|
|
2423
|
+
};
|
|
2424
|
+
const initializer = (fields) => {
|
|
2425
|
+
var _a, _b, _c;
|
|
2426
|
+
const acc = {};
|
|
2427
|
+
for (let index = 0; index < fields.length; index++) {
|
|
2428
|
+
const field = fields[index];
|
|
2429
|
+
if (!field)
|
|
1895
2430
|
throw new Error("Field is undefined.");
|
|
1896
|
-
const
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
2431
|
+
const previousConditionFields = index > 0 ? (_a = acc[fields[index - 1].identifier]) == null ? void 0 : _a.conditionFields : void 0;
|
|
2432
|
+
const disabledFields = new Set(previousConditionFields);
|
|
2433
|
+
if ((_b = field.condition) == null ? void 0 : _b.identifier) {
|
|
2434
|
+
disabledFields.add(field.condition.identifier);
|
|
2435
|
+
}
|
|
2436
|
+
acc[field.identifier] = {
|
|
2437
|
+
disabled: false,
|
|
2438
|
+
conditionFields: disabledFields,
|
|
2439
|
+
conditionIndex: getConditionIndex(fields, (_c = field.condition) == null ? void 0 : _c.identifier),
|
|
2440
|
+
index,
|
|
2441
|
+
label: field.label
|
|
1903
2442
|
};
|
|
1904
2443
|
}
|
|
1905
|
-
return
|
|
1906
|
-
}
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
const
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
(
|
|
1919
|
-
|
|
1920
|
-
|
|
2444
|
+
return acc;
|
|
2445
|
+
};
|
|
2446
|
+
const findSection = (fields, sectionId) => {
|
|
2447
|
+
for (const [i, section] of Object.entries(fields)) {
|
|
2448
|
+
if (section.identifier === sectionId)
|
|
2449
|
+
return [section, i];
|
|
2450
|
+
}
|
|
2451
|
+
};
|
|
2452
|
+
const FieldsEditor = memo(function FieldsEditor2() {
|
|
2453
|
+
const { values, setFieldValue } = useFormikContext();
|
|
2454
|
+
const [dropState, dispatch] = useReducer(reducer, values.fields, initializer);
|
|
2455
|
+
const { showInfo } = useToast();
|
|
2456
|
+
useEffect(() => {
|
|
2457
|
+
dispatch({ type: "update", state: initializer(values.fields) });
|
|
2458
|
+
}, [dispatch, values.fields]);
|
|
2459
|
+
const handleDragStart = useCallback((start) => {
|
|
2460
|
+
if (start.type === "SECTION") {
|
|
2461
|
+
dispatch({ type: "hold", fieldId: start.draggableId });
|
|
2462
|
+
}
|
|
2463
|
+
}, []);
|
|
2464
|
+
const handleDragEnd = useCallback(
|
|
2465
|
+
(result) => {
|
|
2466
|
+
const { source, destination, type, reason, draggableId } = result;
|
|
2467
|
+
dispatch({ type: "release" });
|
|
2468
|
+
if (!destination || reason === "CANCEL")
|
|
1921
2469
|
return;
|
|
1922
|
-
if (
|
|
1923
|
-
const
|
|
1924
|
-
if (!
|
|
2470
|
+
if (type === "ROOT") {
|
|
2471
|
+
const state = dropState[draggableId];
|
|
2472
|
+
if (!state)
|
|
1925
2473
|
throw new Error("Could not find section context.");
|
|
1926
|
-
let
|
|
2474
|
+
let dest = typeof state.conditionIndex !== "undefined" ? (
|
|
1927
2475
|
// cannot move a section with a condition before the condition's field
|
|
1928
|
-
Math.max(
|
|
1929
|
-
) :
|
|
1930
|
-
for (const
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
2476
|
+
Math.max(state.conditionIndex + 1, destination.index)
|
|
2477
|
+
) : destination.index;
|
|
2478
|
+
for (const section of Object.values(dropState)) {
|
|
2479
|
+
if (section.conditionIndex === source.index) {
|
|
2480
|
+
dest = Math.min(dest, section.index - 1);
|
|
2481
|
+
}
|
|
2482
|
+
}
|
|
2483
|
+
if (dest != destination.index) {
|
|
2484
|
+
showInfo({
|
|
2485
|
+
title: "Reordered sections",
|
|
2486
|
+
description: "Sections with conditions must be below the fields they reference."
|
|
2487
|
+
});
|
|
2488
|
+
}
|
|
2489
|
+
return setFieldValue("fields", reorder(values.fields, source.index, dest));
|
|
1936
2490
|
}
|
|
1937
|
-
if (
|
|
2491
|
+
if (type !== "SECTION")
|
|
1938
2492
|
throw new Error("Unexpected droppable type.");
|
|
1939
|
-
const [
|
|
1940
|
-
|
|
2493
|
+
const [sourceSection, srcIndex] = findSection(values.fields, source.droppableId) ?? [];
|
|
2494
|
+
const [destinationSection, destIndex] = findSection(values.fields, destination.droppableId) ?? [];
|
|
2495
|
+
if (!(sourceSection == null ? void 0 : sourceSection.fields) || !destinationSection)
|
|
1941
2496
|
throw new Error("Could not find section with fields.");
|
|
1942
|
-
if (
|
|
1943
|
-
|
|
1944
|
-
`fields.${
|
|
1945
|
-
|
|
2497
|
+
if (sourceSection.identifier === destinationSection.identifier) {
|
|
2498
|
+
setFieldValue(
|
|
2499
|
+
`fields.${srcIndex}.fields`,
|
|
2500
|
+
reorder(sourceSection.fields, source.index, destination.index)
|
|
1946
2501
|
).then();
|
|
1947
|
-
else {
|
|
1948
|
-
const
|
|
1949
|
-
if (!
|
|
2502
|
+
} else {
|
|
2503
|
+
const removed = sourceSection.fields[source.index];
|
|
2504
|
+
if (!removed)
|
|
1950
2505
|
throw new Error("Could not find field to reorder.");
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
2506
|
+
setFieldValue(`fields.${srcIndex}.fields`, remove(sourceSection.fields, source.index)).then();
|
|
2507
|
+
setFieldValue(
|
|
2508
|
+
`fields.${destIndex}.fields`,
|
|
2509
|
+
insert(destinationSection.fields, destination.index, removed)
|
|
1954
2510
|
).then();
|
|
1955
2511
|
}
|
|
1956
2512
|
},
|
|
1957
|
-
[
|
|
1958
|
-
)
|
|
2513
|
+
[values.fields, setFieldValue, dropState, showInfo]
|
|
2514
|
+
);
|
|
2515
|
+
const makeFieldSectionProps = useMemo(
|
|
1959
2516
|
() => ({
|
|
1960
|
-
index:
|
|
2517
|
+
index: values.fields.length,
|
|
1961
2518
|
parentPath: "fields",
|
|
1962
|
-
initial:
|
|
1963
|
-
conditionalSourceFields:
|
|
1964
|
-
onComplete: () => console.log("makefieldsectionprops")
|
|
2519
|
+
initial: emptySection(),
|
|
2520
|
+
conditionalSourceFields: makeConditionalSourceFields(values.fields, values.fields.length)
|
|
1965
2521
|
}),
|
|
1966
|
-
[
|
|
2522
|
+
[values.fields]
|
|
1967
2523
|
);
|
|
1968
|
-
return /* @__PURE__ */
|
|
1969
|
-
|
|
2524
|
+
return /* @__PURE__ */ jsx(DragDropContext, { onDragStart: handleDragStart, onDragEnd: handleDragEnd, children: /* @__PURE__ */ jsx(Droppable, { droppableId: "droppable", type: "ROOT", children: (droppableProvided) => /* @__PURE__ */ jsxs(
|
|
2525
|
+
Flex,
|
|
1970
2526
|
{
|
|
1971
|
-
ref:
|
|
1972
|
-
...
|
|
2527
|
+
ref: droppableProvided.innerRef,
|
|
2528
|
+
...droppableProvided.droppableProps,
|
|
1973
2529
|
direction: "column",
|
|
1974
2530
|
gap: "0",
|
|
1975
2531
|
children: [
|
|
1976
|
-
|
|
1977
|
-
|
|
2532
|
+
values.fields.map((field, index) => /* @__PURE__ */ jsx(
|
|
2533
|
+
FieldSectionWithActions,
|
|
1978
2534
|
{
|
|
1979
|
-
field
|
|
1980
|
-
index
|
|
1981
|
-
dropState
|
|
2535
|
+
field,
|
|
2536
|
+
index,
|
|
2537
|
+
dropState
|
|
1982
2538
|
},
|
|
1983
|
-
|
|
2539
|
+
field.label
|
|
1984
2540
|
)),
|
|
1985
|
-
|
|
1986
|
-
/* @__PURE__ */
|
|
1987
|
-
/* @__PURE__ */
|
|
2541
|
+
droppableProvided.placeholder,
|
|
2542
|
+
/* @__PURE__ */ jsx(FieldBuilder, { ...makeFieldSectionProps, children: /* @__PURE__ */ jsxs(Button, { type: "button", variant: "outline", children: [
|
|
2543
|
+
/* @__PURE__ */ jsx(PlusIcon, {}),
|
|
1988
2544
|
" Add a section"
|
|
1989
2545
|
] }) })
|
|
1990
2546
|
]
|
|
1991
2547
|
}
|
|
1992
2548
|
) }) });
|
|
1993
|
-
})
|
|
2549
|
+
});
|
|
2550
|
+
const initialValues = {
|
|
1994
2551
|
title: "",
|
|
1995
2552
|
description: "",
|
|
1996
2553
|
fields: []
|
|
1997
|
-
}
|
|
2554
|
+
};
|
|
2555
|
+
const title = new StringField({
|
|
1998
2556
|
label: "Title",
|
|
1999
2557
|
minLength: 0,
|
|
2000
2558
|
maxLength: 100,
|
|
2001
|
-
required:
|
|
2559
|
+
required: true,
|
|
2002
2560
|
identifier: "title"
|
|
2003
|
-
})
|
|
2561
|
+
});
|
|
2562
|
+
const titleProps = { formId, placeholder: "Give your form a title." };
|
|
2563
|
+
const description = new TextField({
|
|
2004
2564
|
label: "Description",
|
|
2005
2565
|
minLength: 0,
|
|
2006
2566
|
maxLength: 1e3,
|
|
2007
|
-
required:
|
|
2567
|
+
required: false,
|
|
2008
2568
|
identifier: "description"
|
|
2009
|
-
})
|
|
2569
|
+
});
|
|
2570
|
+
const descriptionProps = { formId, placeholder: "Explain the purpose of this form." };
|
|
2571
|
+
const previewSubmit = () => {
|
|
2010
2572
|
alert("This is a form preview, your data will not be saved.");
|
|
2011
|
-
}
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2573
|
+
};
|
|
2574
|
+
const FormBuilder = memo(
|
|
2575
|
+
forwardRef((props, ref) => {
|
|
2576
|
+
const { onCancel, onSave, revision } = props;
|
|
2577
|
+
const defaultHeading = revision ? "Edit form" : "Create a new form";
|
|
2578
|
+
const { heading = defaultHeading } = props;
|
|
2579
|
+
const validate = useCallback((form) => {
|
|
2580
|
+
const errors = {};
|
|
2581
|
+
if (!form.title) {
|
|
2582
|
+
errors.title = "Title is required.";
|
|
2583
|
+
}
|
|
2584
|
+
if (!form.fields || form.fields.length === 0) {
|
|
2585
|
+
errors.fields = "At least one field is required.";
|
|
2586
|
+
}
|
|
2587
|
+
if (hasKeys(errors)) {
|
|
2588
|
+
return errors;
|
|
2589
|
+
}
|
|
2590
|
+
}, []);
|
|
2591
|
+
const formik = useFormik({
|
|
2592
|
+
initialValues: wrapRootFieldsWithFieldSection(revision) ?? initialValues,
|
|
2593
|
+
validate,
|
|
2594
|
+
onSubmit: (form) => onSave(form),
|
|
2021
2595
|
// only validate the entire for on submit
|
|
2022
|
-
validateOnChange:
|
|
2023
|
-
validateOnBlur:
|
|
2024
|
-
})
|
|
2025
|
-
|
|
2026
|
-
|
|
2596
|
+
validateOnChange: false,
|
|
2597
|
+
validateOnBlur: false
|
|
2598
|
+
});
|
|
2599
|
+
const previewSchema = useMemo(() => formRevisionToSchema(formik.values), [formik.values]);
|
|
2600
|
+
const titleInput = useFieldInput(title, titleProps);
|
|
2601
|
+
const descriptionInput = useFieldInput(description, descriptionProps);
|
|
2602
|
+
const FormBuilderHeading = useMemo(
|
|
2603
|
+
() => typeof heading === "object" ? heading : /* @__PURE__ */ jsx(Heading, { children: heading }),
|
|
2604
|
+
[heading]
|
|
2027
2605
|
);
|
|
2028
|
-
return /* @__PURE__ */
|
|
2029
|
-
/* @__PURE__ */
|
|
2030
|
-
/* @__PURE__ */
|
|
2031
|
-
/* @__PURE__ */
|
|
2606
|
+
return /* @__PURE__ */ jsx(Tabs.Root, { ref, defaultValue: "edit", children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: "2", children: [
|
|
2607
|
+
/* @__PURE__ */ jsxs(Tabs.List, { children: [
|
|
2608
|
+
/* @__PURE__ */ jsx(Tabs.Trigger, { value: "edit", children: "Edit" }),
|
|
2609
|
+
/* @__PURE__ */ jsx(Tabs.Trigger, { value: "preview", children: "Preview" })
|
|
2032
2610
|
] }),
|
|
2033
|
-
/* @__PURE__ */
|
|
2034
|
-
|
|
2035
|
-
/* @__PURE__ */
|
|
2611
|
+
/* @__PURE__ */ jsxs(Tabs.Content, { value: "edit", children: [
|
|
2612
|
+
FormBuilderHeading,
|
|
2613
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2036
2614
|
"Add a new form field by clicking a + button. Specify options for each field, then drag and drop to rearrange them. You can see what a submitted form might look like in the",
|
|
2037
2615
|
" ",
|
|
2038
|
-
/* @__PURE__ */
|
|
2616
|
+
/* @__PURE__ */ jsx("em", { children: "Preview" }),
|
|
2039
2617
|
" tab, but",
|
|
2040
2618
|
" ",
|
|
2041
|
-
/* @__PURE__ */
|
|
2619
|
+
/* @__PURE__ */ jsx("strong", { children: "field values entered on this page will not be saved." })
|
|
2042
2620
|
] }),
|
|
2043
|
-
/* @__PURE__ */
|
|
2044
|
-
/* @__PURE__ */
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
/* @__PURE__ */
|
|
2048
|
-
/* @__PURE__ */
|
|
2621
|
+
/* @__PURE__ */ jsx(Flex, { asChild: true, direction: "column", gap: "2", mt: "3", children: /* @__PURE__ */ jsxs("form", { id: formId, onSubmit: formik.handleSubmit, children: [
|
|
2622
|
+
/* @__PURE__ */ jsxs(FormikProvider, { value: formik, children: [
|
|
2623
|
+
titleInput,
|
|
2624
|
+
descriptionInput,
|
|
2625
|
+
/* @__PURE__ */ jsx(FieldsEditor, {}),
|
|
2626
|
+
/* @__PURE__ */ jsx(Text, { severity: "danger", size: "1", children: typeof formik.errors.fields === "string" && formik.errors.fields })
|
|
2049
2627
|
] }),
|
|
2050
|
-
/* @__PURE__ */
|
|
2051
|
-
/* @__PURE__ */
|
|
2052
|
-
/* @__PURE__ */
|
|
2628
|
+
/* @__PURE__ */ jsxs(Flex, { justify: "end", gap: "2", children: [
|
|
2629
|
+
/* @__PURE__ */ jsx(Button, { type: "button", variant: "soft", onClick: onCancel, children: "Cancel" }),
|
|
2630
|
+
/* @__PURE__ */ jsx(Button, { type: "submit", disabled: !formik.isValid, children: "Save" })
|
|
2053
2631
|
] })
|
|
2054
2632
|
] }) })
|
|
2055
2633
|
] }),
|
|
2056
|
-
/* @__PURE__ */
|
|
2634
|
+
/* @__PURE__ */ jsx(Tabs.Content, { value: "preview", children: /* @__PURE__ */ jsx(FormRenderer, { schema: previewSchema, onSubmit: previewSubmit }) })
|
|
2057
2635
|
] }) });
|
|
2058
2636
|
})
|
|
2059
2637
|
);
|
|
2060
2638
|
export {
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2639
|
+
BooleanField,
|
|
2640
|
+
BooleanInput,
|
|
2641
|
+
DateField,
|
|
2642
|
+
DateInput,
|
|
2643
|
+
FieldSection,
|
|
2644
|
+
FormBrowser,
|
|
2645
|
+
FormBuilder,
|
|
2646
|
+
FormRenderer,
|
|
2647
|
+
FormSubmissionBrowser,
|
|
2648
|
+
FormSubmissionViewer,
|
|
2649
|
+
MultiSelectField,
|
|
2650
|
+
MultiSelectInput,
|
|
2651
|
+
MultiStringField,
|
|
2652
|
+
MultiStringInput,
|
|
2653
|
+
NumberField,
|
|
2654
|
+
NumberInput$1 as NumberInput,
|
|
2655
|
+
PatchField,
|
|
2656
|
+
PatchFormProvider,
|
|
2657
|
+
SelectField,
|
|
2658
|
+
SelectInput,
|
|
2659
|
+
StringField,
|
|
2660
|
+
StringInput,
|
|
2661
|
+
TextField,
|
|
2662
|
+
TextInput,
|
|
2663
|
+
deserialize,
|
|
2664
|
+
deserializeField,
|
|
2665
|
+
formRevisionToSchema,
|
|
2666
|
+
isConditionMet,
|
|
2667
|
+
useFieldInput,
|
|
2668
|
+
useFieldInputs,
|
|
2669
|
+
valueIsFile
|
|
2092
2670
|
};
|
|
2671
|
+
//# sourceMappingURL=forms.js.map
|