@overmap-ai/core 1.0.31-display-invited-projects.1 → 1.0.31-revamp-forms-builder.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/forms/builder/FieldActions.d.ts +8 -7
  2. package/dist/forms/builder/FieldBuilder.d.ts +5 -18
  3. package/dist/forms/builder/FieldSectionWithActions.d.ts +2 -2
  4. package/dist/forms/builder/FieldWithActions.d.ts +1 -1
  5. package/dist/forms/builder/FieldsEditor.d.ts +1 -1
  6. package/dist/forms/builder/FormBuilder.d.ts +1 -5
  7. package/dist/forms/builder/componentConstants.d.ts +8 -0
  8. package/dist/forms/builder/utils.d.ts +13 -1
  9. package/dist/forms/fields/BaseField/BaseField.d.ts +15 -5
  10. package/dist/forms/fields/BaseField/hooks.d.ts +2 -1
  11. package/dist/forms/fields/BaseField/layouts.d.ts +7 -2
  12. package/dist/forms/fields/BooleanField/BooleanField.d.ts +6 -0
  13. package/dist/forms/fields/CustomField/CustomField.d.ts +6 -0
  14. package/dist/forms/fields/DateField/DateField.d.ts +6 -0
  15. package/dist/forms/fields/FieldSection/FieldSection.d.ts +12 -6
  16. package/dist/forms/fields/MultiStringField/MultiStringField.d.ts +8 -0
  17. package/dist/forms/fields/NumberField/NumberField.d.ts +18 -3
  18. package/dist/forms/fields/SelectField/BaseSelectField.d.ts +4 -1
  19. package/dist/forms/fields/SelectField/MultiSelectField.d.ts +7 -0
  20. package/dist/forms/fields/SelectField/SelectField.d.ts +7 -0
  21. package/dist/forms/fields/StringOrTextFields/StringField/StringField.d.ts +8 -0
  22. package/dist/forms/fields/StringOrTextFields/StringOrTextField.d.ts +8 -5
  23. package/dist/forms/fields/StringOrTextFields/TextField/TextField.d.ts +7 -0
  24. package/dist/forms/fields/UploadField/UploadField.d.ts +16 -1
  25. package/dist/forms/fields/constants.d.ts +75 -0
  26. package/dist/forms/fields/typings.d.ts +4 -1
  27. package/dist/forms/fields/utils.d.ts +3 -1
  28. package/dist/forms/renderer/PatchForm/Field.d.ts +3 -1
  29. package/dist/forms/renderer/PatchForm/Provider.d.ts +2 -1
  30. package/dist/forms/utils.d.ts +2 -1
  31. package/dist/overmap-core.js +1489 -1153
  32. package/dist/overmap-core.js.map +1 -1
  33. package/dist/overmap-core.umd.cjs +1490 -1154
  34. package/dist/overmap-core.umd.cjs.map +1 -1
  35. package/dist/sdk/services/ProjectService.d.ts +0 -1
  36. package/dist/store/slices/projectSlice.d.ts +1 -5
  37. package/dist/style.css +2 -0
  38. package/dist/typings/models/projects.d.ts +0 -1
  39. package/package.json +2 -2
  40. package/dist/forms/builder/constants.d.ts +0 -1
@@ -8,7 +8,7 @@ var _a;
8
8
  import * as React from "react";
9
9
  import React__default, { useState, useEffect, useRef, memo, useMemo, forwardRef, createElement, useCallback, createContext, useContext, Children, isValidElement, cloneElement, Fragment, useLayoutEffect, useReducer } from "react";
10
10
  import { jsx, jsxs, Fragment as Fragment$1 } from "react/jsx-runtime";
11
- import { unsafeShowToast, AlertDialogProvider, ToastProvider, DefaultTheme, Flex, Text, useSeverityColor, Checkbox, TextArea, Select, IconButton, Badge, MultiSelect, Button, ButtonList, divButtonProps, Tooltip, Separator, useDiscardAlertDialog, Dialog, DropdownItemMenu, useAlertDialog, useToast } from "@overmap-ai/blocks";
11
+ import { unsafeShowToast, AlertDialogProvider, ToastProvider, DefaultTheme, Flex, Text, useSeverityColor, Checkbox, TextArea, Select, useToast, IconButton, Badge, MultiSelect, Button, ButtonList, divButtonProps, Tooltip, DropdownItemMenu, DropdownMenu, DropdownMenuItemGroup, DropdownMenuSubMenuGroup, Popover, Input, useAlertDialog } from "@overmap-ai/blocks";
12
12
  import { DepGraph } from "dependency-graph";
13
13
  import { offline as offline$1 } from "@redux-offline/redux-offline";
14
14
  import offlineConfig from "@redux-offline/redux-offline/lib/defaults";
@@ -23,9 +23,9 @@ import jwtDecode from "jwt-decode";
23
23
  import { RESET_STATE } from "@redux-offline/redux-offline/lib/constants";
24
24
  import { openDB } from "idb";
25
25
  import { useField, useFormikContext, useFormik, FormikProvider } from "formik";
26
+ import get from "lodash.get";
26
27
  import Linkify from "linkify-react";
27
28
  import { DragDropContext, Droppable, Draggable } from "@hello-pangea/dnd";
28
- import get from "lodash.get";
29
29
  import set from "lodash.set";
30
30
  import cloneDeep from "lodash.clonedeep";
31
31
  import { flushSync } from "react-dom";
@@ -375,7 +375,7 @@ function Item(fun, array) {
375
375
  Item.prototype.run = function() {
376
376
  this.fun.apply(null, this.array);
377
377
  };
378
- var title$1 = "browser";
378
+ var title = "browser";
379
379
  var platform = "browser";
380
380
  var browser = true;
381
381
  var env = {};
@@ -431,7 +431,7 @@ function uptime() {
431
431
  }
432
432
  var browser$1 = {
433
433
  nextTick,
434
- title: title$1,
434
+ title,
435
435
  browser,
436
436
  env,
437
437
  argv,
@@ -2688,13 +2688,6 @@ const projectSlice = createSlice({
2688
2688
  deleteProject: (state, action) => {
2689
2689
  delete state.projects[action.payload.id];
2690
2690
  state.recentProjectIds = state.recentProjectIds.filter((id) => id !== action.payload.id);
2691
- },
2692
- acceptProjectInvite: (state, action) => {
2693
- if (action.payload in state.projects) {
2694
- state.projects[action.payload].invited = false;
2695
- } else {
2696
- throw new Error("Accept project invite: user is not in this project");
2697
- }
2698
2691
  }
2699
2692
  }
2700
2693
  });
@@ -2704,8 +2697,7 @@ const {
2704
2697
  updateOrCreateProjects: addOrReplaceProjects,
2705
2698
  setActiveProjectId,
2706
2699
  setCreateProjectType,
2707
- deleteProject,
2708
- acceptProjectInvite
2700
+ deleteProject
2709
2701
  } = projectSlice.actions;
2710
2702
  const selectProjects = (state) => state.projectReducer.projects;
2711
2703
  const selectActiveProjectId = (state) => state.projectReducer.activeProjectId;
@@ -2729,10 +2721,6 @@ const selectProjectUsersAsMapping = createSelector(
2729
2721
  [selectProjectUsersIds, selectUsersAsMapping],
2730
2722
  (projectUserIds, users) => projectUserIds.reduce((accum, userId) => ({ ...accum, [userId]: users[userId] }), {})
2731
2723
  );
2732
- const selectProjectsWithAccess = createSelector(
2733
- [selectProjects],
2734
- (projects) => Object.values(projects).filter((project) => !project.invited)
2735
- );
2736
2724
  const selectSortedProjectUsers = createSelector(
2737
2725
  [selectCurrentUser, selectProjectUsersAsMapping, selectProjectAccessUserMapping],
2738
2726
  (currentUser, userMapping, projectAccessMapping) => {
@@ -4383,7 +4371,7 @@ class ComponentService extends BaseApiService {
4383
4371
  });
4384
4372
  return promise;
4385
4373
  }
4386
- async refreshStore(replace2) {
4374
+ async refreshStore(replace) {
4387
4375
  const { store } = this.client;
4388
4376
  const result = await this.enqueueRequest({
4389
4377
  description: "Get components",
@@ -4392,7 +4380,7 @@ class ComponentService extends BaseApiService {
4392
4380
  blockers: [],
4393
4381
  blocks: []
4394
4382
  });
4395
- if (replace2) {
4383
+ if (replace) {
4396
4384
  store.dispatch(setComponents(result));
4397
4385
  } else {
4398
4386
  store.dispatch(addComponentsInBatches(result));
@@ -4867,16 +4855,14 @@ class MainService extends BaseApiService {
4867
4855
  // Don't accept updateStore in ComponentService.list. Just return the offline objects and promise. Here, if
4868
4856
  // overwrite, use setComponents. Otherwise, use bulkAddComponents.
4869
4857
  async _processInitialData(data, overwrite) {
4870
- var _a2, _b, _c;
4858
+ var _a2, _b;
4871
4859
  const workspaces = {};
4872
4860
  const projects = [];
4873
4861
  const categories = [];
4874
4862
  const projectsData = data.projects;
4875
4863
  const { store } = this.client;
4876
- const oldProjectId = (_a2 = projectsData.find(
4877
- (projectData) => projectData.id === store.getState().projectReducer.activeProjectId && !projectData.invited
4878
- )) == null ? void 0 : _a2.id;
4879
- let currentProjectId = oldProjectId ?? ((_b = projectsData.find((projectData) => !projectData.invited)) == null ? void 0 : _b.id);
4864
+ const oldProjectId = store.getState().projectReducer.activeProjectId;
4865
+ let currentProjectId = oldProjectId ?? ((_a2 = projectsData[0]) == null ? void 0 : _a2.id);
4880
4866
  store.dispatch(setActiveProjectId(currentProjectId ?? null));
4881
4867
  let isProjectIdValid = false;
4882
4868
  for (const projectData of projectsData) {
@@ -4885,10 +4871,9 @@ class MainService extends BaseApiService {
4885
4871
  name: projectData.name,
4886
4872
  owner_organization: projectData.organization_owner,
4887
4873
  owner_user: projectData.user_owner,
4888
- bounds: projectData.bounds,
4889
- invited: projectData.invited || false
4874
+ bounds: projectData.bounds
4890
4875
  });
4891
- if (currentProjectId === projectData.id && !projectData.invited) {
4876
+ if (currentProjectId === projectData.id) {
4892
4877
  isProjectIdValid = true;
4893
4878
  for (const workspaceData of projectData.workspaces) {
4894
4879
  const workspace = { ...workspaceData, project: projectData.id };
@@ -4905,7 +4890,6 @@ class MainService extends BaseApiService {
4905
4890
  store.dispatch(setCurrentUser(data.user));
4906
4891
  const organizationsData = data.organizations;
4907
4892
  store.dispatch(setOrganizations(organizationsData));
4908
- const validProjects = projects.filter((project) => !project.invited);
4909
4893
  const firstOrg = organizationsData[0];
4910
4894
  const currProjObj = projects.find((project) => project.id === currentProjectId);
4911
4895
  const isOrgProject = !!(currProjObj == null ? void 0 : currProjObj.owner_organization);
@@ -4925,21 +4909,19 @@ class MainService extends BaseApiService {
4925
4909
  store.dispatch(addUsers(orgUsersResult));
4926
4910
  }
4927
4911
  if (!isProjectIdValid) {
4928
- if (validProjects.length !== 0) {
4929
- currentProjectId = validProjects[0].id;
4912
+ if (projects.length !== 0) {
4913
+ currentProjectId = projects[0].id;
4930
4914
  store.dispatch(setActiveProjectId(currentProjectId));
4931
- const projectData = projectsData.find((projectData2) => projectData2.id === currentProjectId);
4932
- if (projectData) {
4933
- for (const workspaceData of projectData.workspaces) {
4934
- const workspace = { ...workspaceData, project: projectData.id };
4935
- if (workspace.categories) {
4936
- for (const category of workspace.categories) {
4937
- categories.push(category);
4938
- }
4915
+ const projectData = projectsData[0];
4916
+ for (const workspaceData of projectData.workspaces) {
4917
+ const workspace = { ...workspaceData, project: projectData.id };
4918
+ if (workspace.categories) {
4919
+ for (const category of workspace.categories) {
4920
+ categories.push(category);
4939
4921
  }
4940
- delete workspace.categories;
4941
- workspaces[workspace.offline_id] = workspace;
4942
4922
  }
4923
+ delete workspace.categories;
4924
+ workspaces[workspace.offline_id] = workspace;
4943
4925
  }
4944
4926
  } else {
4945
4927
  currentProjectId = null;
@@ -4956,7 +4938,7 @@ class MainService extends BaseApiService {
4956
4938
  let currentWorkspaceId;
4957
4939
  const oldWorkspaceId = this.client.store.getState().workspaceReducer.activeWorkspaceId;
4958
4940
  if (overwrite || !oldWorkspaceId) {
4959
- currentWorkspaceId = (_c = Object.values(workspaces).at(0)) == null ? void 0 : _c.offline_id;
4941
+ currentWorkspaceId = (_b = Object.values(workspaces).at(0)) == null ? void 0 : _b.offline_id;
4960
4942
  } else {
4961
4943
  currentWorkspaceId = oldWorkspaceId;
4962
4944
  }
@@ -5267,17 +5249,6 @@ class ProjectService extends BaseApiService {
5267
5249
  blocks: []
5268
5250
  });
5269
5251
  }
5270
- async acceptInvite(projectId) {
5271
- return this.enqueueRequest({
5272
- description: "Accept project invite",
5273
- method: HttpMethod.PATCH,
5274
- url: `/projects/${projectId}/accept-invite/`,
5275
- blockers: [projectId.toString()],
5276
- blocks: [projectId.toString()]
5277
- }).then(() => {
5278
- this.client.store.dispatch(acceptProjectInvite(projectId));
5279
- });
5280
- }
5281
5252
  }
5282
5253
  class UserFormService extends BaseApiService {
5283
5254
  add(state, initialRevision, url, ownerUser, ownerOrganization) {
@@ -6082,6 +6053,11 @@ class BaseFormElement {
6082
6053
  };
6083
6054
  }
6084
6055
  }
6056
+ const emptyBaseField = {
6057
+ label: "",
6058
+ required: false,
6059
+ description: ""
6060
+ };
6085
6061
  class BaseField extends BaseFormElement {
6086
6062
  constructor(options) {
6087
6063
  const { label, required, fieldValidators = [], formValidators = [], ...base } = options;
@@ -6146,25 +6122,29 @@ class BaseField extends BaseFormElement {
6146
6122
  __publicField(BaseField, "fieldTypeName");
6147
6123
  __publicField(BaseField, "fieldTypeDescription");
6148
6124
  const description$1 = "_description_17zed_1";
6149
- const styles$4 = {
6125
+ const styles$5 = {
6150
6126
  description: description$1
6151
6127
  };
6152
6128
  const InputWithLabel = (props) => {
6153
- const { label, children, severity, inputId, labelId, flexProps } = props;
6129
+ const { label, children, size, severity, inputId, labelId, flexProps } = props;
6154
6130
  return /* @__PURE__ */ jsx(Flex, { direction: "column", gap: "1", asChild: true, ...flexProps, children: /* @__PURE__ */ jsxs("label", { htmlFor: inputId, children: [
6155
- /* @__PURE__ */ jsx(Text, { severity, id: labelId, children: label }),
6131
+ /* @__PURE__ */ jsx(Text, { size, severity, id: labelId, children: label }),
6156
6132
  children
6157
6133
  ] }) });
6158
6134
  };
6159
- const InputWithLabelAndHelpText = (props) => {
6135
+ const InputWithHelpText = (props) => {
6160
6136
  const { helpText, children, severity } = props;
6161
6137
  return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: "1", children: [
6162
6138
  children,
6163
- /* @__PURE__ */ jsx(Flex, { direction: "column", children: /* @__PURE__ */ jsx(Text, { size: "1", severity, className: styles$4.description, children: helpText }) })
6139
+ /* @__PURE__ */ jsx(Flex, { direction: "column", children: /* @__PURE__ */ jsx(Text, { size: "1", severity, className: styles$5.description, children: helpText }) })
6164
6140
  ] });
6165
6141
  };
6142
+ const InputWithLabelAndHelpText = (props) => {
6143
+ const { children, ...restProps } = props;
6144
+ return /* @__PURE__ */ jsx(InputWithHelpText, { ...restProps, children });
6145
+ };
6166
6146
  const useFormikInput = (props) => {
6167
- const { id, field, formId: formId2, ...rest } = props;
6147
+ const { id, field, formId: formId2, size, ...rest } = props;
6168
6148
  const [fieldProps, meta, helpers] = useField(field.getId());
6169
6149
  const { touched } = meta;
6170
6150
  const helpText = meta.error ?? field.description;
@@ -6193,6 +6173,7 @@ const useFormikInput = (props) => {
6193
6173
  return [
6194
6174
  {
6195
6175
  helpText,
6176
+ size,
6196
6177
  severity,
6197
6178
  inputId,
6198
6179
  labelId,
@@ -6206,12 +6187,13 @@ const useFormikInput = (props) => {
6206
6187
  };
6207
6188
  const truthyValues = [true, "true"];
6208
6189
  const BooleanInput = memo(function BooleanInput2(props) {
6209
- const [{ inputId, labelId, severity, helpText, label, fieldProps }, rest] = useFormikInput(props);
6190
+ const [{ inputId, labelId, size, severity, helpText, label, fieldProps }, rest] = useFormikInput(props);
6210
6191
  const color = useSeverityColor(severity);
6211
6192
  const value = truthyValues.includes(fieldProps.value);
6212
6193
  return /* @__PURE__ */ jsx(InputWithLabelAndHelpText, { helpText, severity, children: /* @__PURE__ */ jsx(
6213
6194
  InputWithLabel,
6214
6195
  {
6196
+ size,
6215
6197
  severity,
6216
6198
  inputId,
6217
6199
  labelId,
@@ -6227,6 +6209,7 @@ const BooleanInput = memo(function BooleanInput2(props) {
6227
6209
  value: value.toString(),
6228
6210
  checked: value,
6229
6211
  onCheckedChange: fieldProps.onChange,
6212
+ alwaysShow: true,
6230
6213
  onChange: void 0,
6231
6214
  onBlur: void 0
6232
6215
  }
@@ -6248,6 +6231,42 @@ function _objectWithoutPropertiesLoose(source, excluded) {
6248
6231
  }
6249
6232
  return target;
6250
6233
  }
6234
+ var _excluded$e = ["color"];
6235
+ var ArrowDownIcon = /* @__PURE__ */ forwardRef(function(_ref, forwardedRef) {
6236
+ var _ref$color = _ref.color, color = _ref$color === void 0 ? "currentColor" : _ref$color, props = _objectWithoutPropertiesLoose(_ref, _excluded$e);
6237
+ return createElement("svg", Object.assign({
6238
+ width: "15",
6239
+ height: "15",
6240
+ viewBox: "0 0 15 15",
6241
+ fill: "none",
6242
+ xmlns: "http://www.w3.org/2000/svg"
6243
+ }, props, {
6244
+ ref: forwardedRef
6245
+ }), createElement("path", {
6246
+ d: "M7.5 2C7.77614 2 8 2.22386 8 2.5L8 11.2929L11.1464 8.14645C11.3417 7.95118 11.6583 7.95118 11.8536 8.14645C12.0488 8.34171 12.0488 8.65829 11.8536 8.85355L7.85355 12.8536C7.75979 12.9473 7.63261 13 7.5 13C7.36739 13 7.24021 12.9473 7.14645 12.8536L3.14645 8.85355C2.95118 8.65829 2.95118 8.34171 3.14645 8.14645C3.34171 7.95118 3.65829 7.95118 3.85355 8.14645L7 11.2929L7 2.5C7 2.22386 7.22386 2 7.5 2Z",
6247
+ fill: color,
6248
+ fillRule: "evenodd",
6249
+ clipRule: "evenodd"
6250
+ }));
6251
+ });
6252
+ var _excluded$j = ["color"];
6253
+ var ArrowUpIcon = /* @__PURE__ */ forwardRef(function(_ref, forwardedRef) {
6254
+ var _ref$color = _ref.color, color = _ref$color === void 0 ? "currentColor" : _ref$color, props = _objectWithoutPropertiesLoose(_ref, _excluded$j);
6255
+ return createElement("svg", Object.assign({
6256
+ width: "15",
6257
+ height: "15",
6258
+ viewBox: "0 0 15 15",
6259
+ fill: "none",
6260
+ xmlns: "http://www.w3.org/2000/svg"
6261
+ }, props, {
6262
+ ref: forwardedRef
6263
+ }), createElement("path", {
6264
+ d: "M7.14645 2.14645C7.34171 1.95118 7.65829 1.95118 7.85355 2.14645L11.8536 6.14645C12.0488 6.34171 12.0488 6.65829 11.8536 6.85355C11.6583 7.04882 11.3417 7.04882 11.1464 6.85355L8 3.70711L8 12.5C8 12.7761 7.77614 13 7.5 13C7.22386 13 7 12.7761 7 12.5L7 3.70711L3.85355 6.85355C3.65829 7.04882 3.34171 7.04882 3.14645 6.85355C2.95118 6.65829 2.95118 6.34171 3.14645 6.14645L7.14645 2.14645Z",
6265
+ fill: color,
6266
+ fillRule: "evenodd",
6267
+ clipRule: "evenodd"
6268
+ }));
6269
+ });
6251
6270
  var _excluded$I = ["color"];
6252
6271
  var CalendarIcon = /* @__PURE__ */ forwardRef(function(_ref, forwardedRef) {
6253
6272
  var _ref$color = _ref.color, color = _ref$color === void 0 ? "currentColor" : _ref$color, props = _objectWithoutPropertiesLoose(_ref, _excluded$I);
@@ -6356,24 +6375,6 @@ var DotsVerticalIcon = /* @__PURE__ */ forwardRef(function(_ref, forwardedRef) {
6356
6375
  clipRule: "evenodd"
6357
6376
  }));
6358
6377
  });
6359
- var _excluded$1R = ["color"];
6360
- var DragHandleDots2Icon = /* @__PURE__ */ forwardRef(function(_ref, forwardedRef) {
6361
- var _ref$color = _ref.color, color = _ref$color === void 0 ? "currentColor" : _ref$color, props = _objectWithoutPropertiesLoose(_ref, _excluded$1R);
6362
- return createElement("svg", Object.assign({
6363
- width: "15",
6364
- height: "15",
6365
- viewBox: "0 0 15 15",
6366
- fill: "none",
6367
- xmlns: "http://www.w3.org/2000/svg"
6368
- }, props, {
6369
- ref: forwardedRef
6370
- }), createElement("path", {
6371
- d: "M5.5 4.625C6.12132 4.625 6.625 4.12132 6.625 3.5C6.625 2.87868 6.12132 2.375 5.5 2.375C4.87868 2.375 4.375 2.87868 4.375 3.5C4.375 4.12132 4.87868 4.625 5.5 4.625ZM9.5 4.625C10.1213 4.625 10.625 4.12132 10.625 3.5C10.625 2.87868 10.1213 2.375 9.5 2.375C8.87868 2.375 8.375 2.87868 8.375 3.5C8.375 4.12132 8.87868 4.625 9.5 4.625ZM10.625 7.5C10.625 8.12132 10.1213 8.625 9.5 8.625C8.87868 8.625 8.375 8.12132 8.375 7.5C8.375 6.87868 8.87868 6.375 9.5 6.375C10.1213 6.375 10.625 6.87868 10.625 7.5ZM5.5 8.625C6.12132 8.625 6.625 8.12132 6.625 7.5C6.625 6.87868 6.12132 6.375 5.5 6.375C4.87868 6.375 4.375 6.87868 4.375 7.5C4.375 8.12132 4.87868 8.625 5.5 8.625ZM10.625 11.5C10.625 12.1213 10.1213 12.625 9.5 12.625C8.87868 12.625 8.375 12.1213 8.375 11.5C8.375 10.8787 8.87868 10.375 9.5 10.375C10.1213 10.375 10.625 10.8787 10.625 11.5ZM5.5 12.625C6.12132 12.625 6.625 12.1213 6.625 11.5C6.625 10.8787 6.12132 10.375 5.5 10.375C4.87868 10.375 4.375 10.8787 4.375 11.5C4.375 12.1213 4.87868 12.625 5.5 12.625Z",
6372
- fill: color,
6373
- fillRule: "evenodd",
6374
- clipRule: "evenodd"
6375
- }));
6376
- });
6377
6378
  var _excluded$1W = ["color"];
6378
6379
  var DropdownMenuIcon = /* @__PURE__ */ forwardRef(function(_ref, forwardedRef) {
6379
6380
  var _ref$color = _ref.color, color = _ref$color === void 0 ? "currentColor" : _ref$color, props = _objectWithoutPropertiesLoose(_ref, _excluded$1W);
@@ -6408,9 +6409,9 @@ var FontFamilyIcon = /* @__PURE__ */ forwardRef(function(_ref, forwardedRef) {
6408
6409
  fill: color
6409
6410
  }));
6410
6411
  });
6411
- var _excluded$2E = ["color"];
6412
- var InputIcon = /* @__PURE__ */ forwardRef(function(_ref, forwardedRef) {
6413
- var _ref$color = _ref.color, color = _ref$color === void 0 ? "currentColor" : _ref$color, props = _objectWithoutPropertiesLoose(_ref, _excluded$2E);
6412
+ var _excluded$2l = ["color"];
6413
+ var GearIcon = /* @__PURE__ */ forwardRef(function(_ref, forwardedRef) {
6414
+ var _ref$color = _ref.color, color = _ref$color === void 0 ? "currentColor" : _ref$color, props = _objectWithoutPropertiesLoose(_ref, _excluded$2l);
6414
6415
  return createElement("svg", Object.assign({
6415
6416
  width: "15",
6416
6417
  height: "15",
@@ -6420,15 +6421,15 @@ var InputIcon = /* @__PURE__ */ forwardRef(function(_ref, forwardedRef) {
6420
6421
  }, props, {
6421
6422
  ref: forwardedRef
6422
6423
  }), createElement("path", {
6423
- d: "M6.5 1C6.22386 1 6 1.22386 6 1.5C6 1.77614 6.22386 2 6.5 2C7.12671 2 7.45718 2.20028 7.65563 2.47812C7.8781 2.78957 8 3.28837 8 4V11C8 11.7116 7.8781 12.2104 7.65563 12.5219C7.45718 12.7997 7.12671 13 6.5 13C6.22386 13 6 13.2239 6 13.5C6 13.7761 6.22386 14 6.5 14C7.37329 14 8.04282 13.7003 8.46937 13.1031C8.47976 13.0886 8.48997 13.0739 8.5 13.0591C8.51003 13.0739 8.52024 13.0886 8.53063 13.1031C8.95718 13.7003 9.62671 14 10.5 14C10.7761 14 11 13.7761 11 13.5C11 13.2239 10.7761 13 10.5 13C9.87329 13 9.54282 12.7997 9.34437 12.5219C9.1219 12.2104 9 11.7116 9 11V4C9 3.28837 9.1219 2.78957 9.34437 2.47812C9.54282 2.20028 9.87329 2 10.5 2C10.7761 2 11 1.77614 11 1.5C11 1.22386 10.7761 1 10.5 1C9.62671 1 8.95718 1.29972 8.53063 1.89688C8.52024 1.91143 8.51003 1.92611 8.5 1.9409C8.48997 1.92611 8.47976 1.91143 8.46937 1.89688C8.04282 1.29972 7.37329 1 6.5 1ZM14 5H11V4H14C14.5523 4 15 4.44772 15 5V10C15 10.5523 14.5523 11 14 11H11V10H14V5ZM6 4V5H1L1 10H6V11H1C0.447715 11 0 10.5523 0 10V5C0 4.44772 0.447715 4 1 4H6Z",
6424
+ d: "M7.07095 0.650238C6.67391 0.650238 6.32977 0.925096 6.24198 1.31231L6.0039 2.36247C5.6249 2.47269 5.26335 2.62363 4.92436 2.81013L4.01335 2.23585C3.67748 2.02413 3.23978 2.07312 2.95903 2.35386L2.35294 2.95996C2.0722 3.2407 2.0232 3.6784 2.23493 4.01427L2.80942 4.92561C2.62307 5.2645 2.47227 5.62594 2.36216 6.00481L1.31209 6.24287C0.924883 6.33065 0.650024 6.6748 0.650024 7.07183V7.92897C0.650024 8.32601 0.924883 8.67015 1.31209 8.75794L2.36228 8.99603C2.47246 9.375 2.62335 9.73652 2.80979 10.0755L2.2354 10.9867C2.02367 11.3225 2.07267 11.7602 2.35341 12.041L2.95951 12.6471C3.24025 12.9278 3.67795 12.9768 4.01382 12.7651L4.92506 12.1907C5.26384 12.377 5.62516 12.5278 6.0039 12.6379L6.24198 13.6881C6.32977 14.0753 6.67391 14.3502 7.07095 14.3502H7.92809C8.32512 14.3502 8.66927 14.0753 8.75705 13.6881L8.99505 12.6383C9.37411 12.5282 9.73573 12.3773 10.0748 12.1909L10.986 12.7653C11.3218 12.977 11.7595 12.928 12.0403 12.6473L12.6464 12.0412C12.9271 11.7604 12.9761 11.3227 12.7644 10.9869L12.1902 10.076C12.3768 9.73688 12.5278 9.37515 12.638 8.99596L13.6879 8.75794C14.0751 8.67015 14.35 8.32601 14.35 7.92897V7.07183C14.35 6.6748 14.0751 6.33065 13.6879 6.24287L12.6381 6.00488C12.528 5.62578 12.3771 5.26414 12.1906 4.92507L12.7648 4.01407C12.9766 3.6782 12.9276 3.2405 12.6468 2.95975L12.0407 2.35366C11.76 2.07292 11.3223 2.02392 10.9864 2.23565L10.0755 2.80989C9.73622 2.62328 9.37437 2.47229 8.99505 2.36209L8.75705 1.31231C8.66927 0.925096 8.32512 0.650238 7.92809 0.650238H7.07095ZM4.92053 3.81251C5.44724 3.44339 6.05665 3.18424 6.71543 3.06839L7.07095 1.50024H7.92809L8.28355 3.06816C8.94267 3.18387 9.5524 3.44302 10.0794 3.81224L11.4397 2.9547L12.0458 3.56079L11.1882 4.92117C11.5573 5.44798 11.8164 6.0575 11.9321 6.71638L13.5 7.07183V7.92897L11.932 8.28444C11.8162 8.94342 11.557 9.55301 11.1878 10.0798L12.0453 11.4402L11.4392 12.0462L10.0787 11.1886C9.55192 11.5576 8.94241 11.8166 8.28355 11.9323L7.92809 13.5002H7.07095L6.71543 11.932C6.0569 11.8162 5.44772 11.5572 4.92116 11.1883L3.56055 12.046L2.95445 11.4399L3.81213 10.0794C3.4431 9.55266 3.18403 8.94326 3.06825 8.2845L1.50002 7.92897V7.07183L3.06818 6.71632C3.18388 6.05765 3.44283 5.44833 3.81171 4.92165L2.95398 3.561L3.56008 2.95491L4.92053 3.81251ZM9.02496 7.50008C9.02496 8.34226 8.34223 9.02499 7.50005 9.02499C6.65786 9.02499 5.97513 8.34226 5.97513 7.50008C5.97513 6.65789 6.65786 5.97516 7.50005 5.97516C8.34223 5.97516 9.02496 6.65789 9.02496 7.50008ZM9.92496 7.50008C9.92496 8.83932 8.83929 9.92499 7.50005 9.92499C6.1608 9.92499 5.07513 8.83932 5.07513 7.50008C5.07513 6.16084 6.1608 5.07516 7.50005 5.07516C8.83929 5.07516 9.92496 6.16084 9.92496 7.50008Z",
6424
6425
  fill: color,
6425
6426
  fillRule: "evenodd",
6426
6427
  clipRule: "evenodd"
6427
6428
  }));
6428
6429
  });
6429
- var _excluded$2Z = ["color"];
6430
- var ListBulletIcon = /* @__PURE__ */ forwardRef(function(_ref, forwardedRef) {
6431
- var _ref$color = _ref.color, color = _ref$color === void 0 ? "currentColor" : _ref$color, props = _objectWithoutPropertiesLoose(_ref, _excluded$2Z);
6430
+ var _excluded$2E = ["color"];
6431
+ var InputIcon = /* @__PURE__ */ forwardRef(function(_ref, forwardedRef) {
6432
+ var _ref$color = _ref.color, color = _ref$color === void 0 ? "currentColor" : _ref$color, props = _objectWithoutPropertiesLoose(_ref, _excluded$2E);
6432
6433
  return createElement("svg", Object.assign({
6433
6434
  width: "15",
6434
6435
  height: "15",
@@ -6438,15 +6439,15 @@ var ListBulletIcon = /* @__PURE__ */ forwardRef(function(_ref, forwardedRef) {
6438
6439
  }, props, {
6439
6440
  ref: forwardedRef
6440
6441
  }), createElement("path", {
6441
- d: "M1.5 5.25C1.91421 5.25 2.25 4.91421 2.25 4.5C2.25 4.08579 1.91421 3.75 1.5 3.75C1.08579 3.75 0.75 4.08579 0.75 4.5C0.75 4.91421 1.08579 5.25 1.5 5.25ZM4 4.5C4 4.22386 4.22386 4 4.5 4H13.5C13.7761 4 14 4.22386 14 4.5C14 4.77614 13.7761 5 13.5 5H4.5C4.22386 5 4 4.77614 4 4.5ZM4.5 7C4.22386 7 4 7.22386 4 7.5C4 7.77614 4.22386 8 4.5 8H13.5C13.7761 8 14 7.77614 14 7.5C14 7.22386 13.7761 7 13.5 7H4.5ZM4.5 10C4.22386 10 4 10.2239 4 10.5C4 10.7761 4.22386 11 4.5 11H13.5C13.7761 11 14 10.7761 14 10.5C14 10.2239 13.7761 10 13.5 10H4.5ZM2.25 7.5C2.25 7.91421 1.91421 8.25 1.5 8.25C1.08579 8.25 0.75 7.91421 0.75 7.5C0.75 7.08579 1.08579 6.75 1.5 6.75C1.91421 6.75 2.25 7.08579 2.25 7.5ZM1.5 11.25C1.91421 11.25 2.25 10.9142 2.25 10.5C2.25 10.0858 1.91421 9.75 1.5 9.75C1.08579 9.75 0.75 10.0858 0.75 10.5C0.75 10.9142 1.08579 11.25 1.5 11.25Z",
6442
+ d: "M6.5 1C6.22386 1 6 1.22386 6 1.5C6 1.77614 6.22386 2 6.5 2C7.12671 2 7.45718 2.20028 7.65563 2.47812C7.8781 2.78957 8 3.28837 8 4V11C8 11.7116 7.8781 12.2104 7.65563 12.5219C7.45718 12.7997 7.12671 13 6.5 13C6.22386 13 6 13.2239 6 13.5C6 13.7761 6.22386 14 6.5 14C7.37329 14 8.04282 13.7003 8.46937 13.1031C8.47976 13.0886 8.48997 13.0739 8.5 13.0591C8.51003 13.0739 8.52024 13.0886 8.53063 13.1031C8.95718 13.7003 9.62671 14 10.5 14C10.7761 14 11 13.7761 11 13.5C11 13.2239 10.7761 13 10.5 13C9.87329 13 9.54282 12.7997 9.34437 12.5219C9.1219 12.2104 9 11.7116 9 11V4C9 3.28837 9.1219 2.78957 9.34437 2.47812C9.54282 2.20028 9.87329 2 10.5 2C10.7761 2 11 1.77614 11 1.5C11 1.22386 10.7761 1 10.5 1C9.62671 1 8.95718 1.29972 8.53063 1.89688C8.52024 1.91143 8.51003 1.92611 8.5 1.9409C8.48997 1.92611 8.47976 1.91143 8.46937 1.89688C8.04282 1.29972 7.37329 1 6.5 1ZM14 5H11V4H14C14.5523 4 15 4.44772 15 5V10C15 10.5523 14.5523 11 14 11H11V10H14V5ZM6 4V5H1L1 10H6V11H1C0.447715 11 0 10.5523 0 10V5C0 4.44772 0.447715 4 1 4H6Z",
6442
6443
  fill: color,
6443
6444
  fillRule: "evenodd",
6444
6445
  clipRule: "evenodd"
6445
6446
  }));
6446
6447
  });
6447
- var _excluded$3n = ["color"];
6448
- var Pencil1Icon = /* @__PURE__ */ forwardRef(function(_ref, forwardedRef) {
6449
- var _ref$color = _ref.color, color = _ref$color === void 0 ? "currentColor" : _ref$color, props = _objectWithoutPropertiesLoose(_ref, _excluded$3n);
6448
+ var _excluded$2Z = ["color"];
6449
+ var ListBulletIcon = /* @__PURE__ */ forwardRef(function(_ref, forwardedRef) {
6450
+ var _ref$color = _ref.color, color = _ref$color === void 0 ? "currentColor" : _ref$color, props = _objectWithoutPropertiesLoose(_ref, _excluded$2Z);
6450
6451
  return createElement("svg", Object.assign({
6451
6452
  width: "15",
6452
6453
  height: "15",
@@ -6456,7 +6457,7 @@ var Pencil1Icon = /* @__PURE__ */ forwardRef(function(_ref, forwardedRef) {
6456
6457
  }, props, {
6457
6458
  ref: forwardedRef
6458
6459
  }), createElement("path", {
6459
- d: "M11.8536 1.14645C11.6583 0.951184 11.3417 0.951184 11.1465 1.14645L3.71455 8.57836C3.62459 8.66832 3.55263 8.77461 3.50251 8.89155L2.04044 12.303C1.9599 12.491 2.00189 12.709 2.14646 12.8536C2.29103 12.9981 2.50905 13.0401 2.69697 12.9596L6.10847 11.4975C6.2254 11.4474 6.3317 11.3754 6.42166 11.2855L13.8536 3.85355C14.0488 3.65829 14.0488 3.34171 13.8536 3.14645L11.8536 1.14645ZM4.42166 9.28547L11.5 2.20711L12.7929 3.5L5.71455 10.5784L4.21924 11.2192L3.78081 10.7808L4.42166 9.28547Z",
6460
+ d: "M1.5 5.25C1.91421 5.25 2.25 4.91421 2.25 4.5C2.25 4.08579 1.91421 3.75 1.5 3.75C1.08579 3.75 0.75 4.08579 0.75 4.5C0.75 4.91421 1.08579 5.25 1.5 5.25ZM4 4.5C4 4.22386 4.22386 4 4.5 4H13.5C13.7761 4 14 4.22386 14 4.5C14 4.77614 13.7761 5 13.5 5H4.5C4.22386 5 4 4.77614 4 4.5ZM4.5 7C4.22386 7 4 7.22386 4 7.5C4 7.77614 4.22386 8 4.5 8H13.5C13.7761 8 14 7.77614 14 7.5C14 7.22386 13.7761 7 13.5 7H4.5ZM4.5 10C4.22386 10 4 10.2239 4 10.5C4 10.7761 4.22386 11 4.5 11H13.5C13.7761 11 14 10.7761 14 10.5C14 10.2239 13.7761 10 13.5 10H4.5ZM2.25 7.5C2.25 7.91421 1.91421 8.25 1.5 8.25C1.08579 8.25 0.75 7.91421 0.75 7.5C0.75 7.08579 1.08579 6.75 1.5 6.75C1.91421 6.75 2.25 7.08579 2.25 7.5ZM1.5 11.25C1.91421 11.25 2.25 10.9142 2.25 10.5C2.25 10.0858 1.91421 9.75 1.5 9.75C1.08579 9.75 0.75 10.0858 0.75 10.5C0.75 10.9142 1.08579 11.25 1.5 11.25Z",
6460
6461
  fill: color,
6461
6462
  fillRule: "evenodd",
6462
6463
  clipRule: "evenodd"
@@ -6604,6 +6605,10 @@ var UploadIcon = /* @__PURE__ */ forwardRef(function(_ref, forwardedRef) {
6604
6605
  clipRule: "evenodd"
6605
6606
  }));
6606
6607
  });
6608
+ const emptyBooleanField = {
6609
+ ...emptyBaseField,
6610
+ type: "boolean"
6611
+ };
6607
6612
  const _BooleanField = class _BooleanField extends BaseField {
6608
6613
  constructor(options) {
6609
6614
  super({ ...options, type: "boolean" });
@@ -8028,9 +8033,9 @@ const Tabs = Object.assign({}, {
8028
8033
  Content: TabsContent
8029
8034
  });
8030
8035
  const NumberInput$1 = memo(function NumberInput2(props) {
8031
- const [{ inputId, labelId, severity, helpText, label, fieldProps, field }, rest] = useFormikInput(props);
8036
+ const [{ inputId, labelId, size, severity, helpText, label, fieldProps, field }, rest] = useFormikInput(props);
8032
8037
  const color = useSeverityColor(severity);
8033
- return /* @__PURE__ */ jsx(InputWithLabelAndHelpText, { helpText, severity, children: /* @__PURE__ */ jsx(InputWithLabel, { severity, inputId, labelId, label, children: /* @__PURE__ */ jsx(
8038
+ return /* @__PURE__ */ jsx(InputWithLabelAndHelpText, { helpText, severity, children: /* @__PURE__ */ jsx(InputWithLabel, { size, severity, inputId, labelId, label, children: /* @__PURE__ */ jsx(
8034
8039
  TextField$1.Input,
8035
8040
  {
8036
8041
  ...rest,
@@ -8044,144 +8049,6 @@ const NumberInput$1 = memo(function NumberInput2(props) {
8044
8049
  }
8045
8050
  ) }) });
8046
8051
  });
8047
- const _NumberField = class _NumberField extends BaseField {
8048
- constructor(options) {
8049
- const {
8050
- minimum = Number.MIN_SAFE_INTEGER,
8051
- maximum = Number.MAX_SAFE_INTEGER,
8052
- integers = false,
8053
- ...base
8054
- } = options;
8055
- super({ ...base, type: "number" });
8056
- __publicField(this, "minimum");
8057
- __publicField(this, "maximum");
8058
- __publicField(this, "integers");
8059
- this.minimum = minimum;
8060
- this.maximum = maximum;
8061
- this.integers = integers;
8062
- }
8063
- getValueFromChangeEvent(event) {
8064
- const number = Number.parseFloat(event.target.value);
8065
- if (Number.isNaN(number))
8066
- return "";
8067
- return number;
8068
- }
8069
- static getFieldCreationSchema() {
8070
- return [
8071
- new _NumberField({
8072
- label: "Minimum",
8073
- description: "Minimum value",
8074
- integers: true,
8075
- required: false,
8076
- identifier: "minimum",
8077
- formValidators: [this._validateMin]
8078
- }),
8079
- new _NumberField({
8080
- label: "Maximum",
8081
- description: "Maximum value",
8082
- integers: true,
8083
- required: false,
8084
- identifier: "maximum",
8085
- formValidators: [this._validateMax]
8086
- }),
8087
- new BooleanField({
8088
- label: "Integers",
8089
- description: "Whole numbers only",
8090
- required: false,
8091
- identifier: "integers"
8092
- })
8093
- ];
8094
- }
8095
- getFieldValidators() {
8096
- const validators = super.getFieldValidators();
8097
- const min = this.minimum;
8098
- const max = this.maximum;
8099
- if (typeof min === "number") {
8100
- validators.push((value) => {
8101
- if (typeof value === "number" && value < min) {
8102
- return `Must be at least ${this.minimum}.`;
8103
- }
8104
- });
8105
- }
8106
- if (typeof max === "number") {
8107
- validators.push((value) => {
8108
- if (typeof value === "number" && value > max) {
8109
- return `Must be at most ${this.maximum}.`;
8110
- }
8111
- });
8112
- }
8113
- if (this.integers) {
8114
- validators.push((value) => {
8115
- if (typeof value === "number" && !Number.isInteger(value)) {
8116
- return "Must be a whole number.";
8117
- }
8118
- });
8119
- }
8120
- return validators;
8121
- }
8122
- serialize() {
8123
- return {
8124
- ...super._serialize(),
8125
- minimum: this.minimum,
8126
- maximum: this.maximum,
8127
- integers: this.integers
8128
- };
8129
- }
8130
- static deserialize(data) {
8131
- if (data.type !== "number")
8132
- throw new Error("Type mismatch.");
8133
- return new _NumberField(data);
8134
- }
8135
- getInput(props) {
8136
- return /* @__PURE__ */ jsx(NumberInput$1, { field: this, ...props });
8137
- }
8138
- };
8139
- __publicField(_NumberField, "fieldTypeName", "Number");
8140
- __publicField(_NumberField, "fieldTypeDescription", "Allows specifying a number within a given range.");
8141
- __publicField(_NumberField, "Icon", FontFamilyIcon);
8142
- __publicField(_NumberField, "_validateMin", (value, allValues) => {
8143
- if (typeof allValues.maximum === "number" && typeof value === "number" && allValues.maximum < value) {
8144
- return "Minimum cannot be greater than minimum.";
8145
- }
8146
- return null;
8147
- });
8148
- __publicField(_NumberField, "_validateMax", (value, allValues) => {
8149
- if (typeof allValues.minimum === "number" && typeof value === "number" && allValues.minimum > value) {
8150
- return "Maximum cannot be less than minimum.";
8151
- }
8152
- return null;
8153
- });
8154
- let NumberField = _NumberField;
8155
- const DateInput = memo(function DateInput2(props) {
8156
- const [{ inputId, labelId, severity, helpText, label, fieldProps }, rest] = useFormikInput(props);
8157
- const color = useSeverityColor(severity);
8158
- const value = fieldProps.value ? fieldProps.value.split("T")[0] : "";
8159
- 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 }) }) });
8160
- });
8161
- const _DateField = class _DateField extends BaseField {
8162
- constructor(options) {
8163
- super({ ...options, type: "date" });
8164
- __publicField(this, "onlyValidateAfterTouched", false);
8165
- }
8166
- serialize() {
8167
- return super._serialize();
8168
- }
8169
- getValueFromChangeEvent(event) {
8170
- return new Date(event.target.value).toISOString();
8171
- }
8172
- static deserialize(data) {
8173
- if (data.type !== "date")
8174
- throw new Error("Type mismatch.");
8175
- return new _DateField(data);
8176
- }
8177
- getInput(props) {
8178
- return /* @__PURE__ */ jsx(DateInput, { field: this, ...props });
8179
- }
8180
- };
8181
- __publicField(_DateField, "fieldTypeName", "Date");
8182
- __publicField(_DateField, "fieldTypeDescription", "Allows specifying a date.");
8183
- __publicField(_DateField, "Icon", CalendarIcon);
8184
- let DateField = _DateField;
8185
8052
  class StringOrTextField extends BaseField {
8186
8053
  constructor(options) {
8187
8054
  const { minLength, maxLength = 5e3, ...base } = options;
@@ -8191,31 +8058,40 @@ class StringOrTextField extends BaseField {
8191
8058
  this.minLength = minLength ? Math.max(minLength, 0) : void 0;
8192
8059
  this.maxLength = maxLength ? Math.max(maxLength, 0) : 5e3;
8193
8060
  }
8194
- static getFieldCreationSchema() {
8061
+ static getFieldCreationSchema(parentPath = "") {
8062
+ const path = parentPath && `${parentPath}.`;
8195
8063
  return [
8196
- // min, max
8197
- new NumberField({
8198
- label: "Minimum length",
8199
- description: "Minimum number of characters",
8200
- required: false,
8201
- identifier: "minimum_length",
8202
- minimum: 0,
8203
- maximum: 100,
8204
- formValidators: [this._validateMin],
8205
- integers: true
8206
- }),
8207
- new NumberField({
8208
- label: "Maximum length",
8209
- description: "Maximum number of characters",
8210
- required: false,
8211
- identifier: "maximum_length",
8212
- minimum: 1,
8213
- maximum: 5e3,
8214
- // TODO: depends on short vs long text
8215
- formValidators: [this._validateMax],
8216
- // TODO: default: 500 (see: "Short text fields can hold up to 500 characters on a single line.")
8217
- integers: true
8218
- })
8064
+ {
8065
+ field: (
8066
+ // min, max
8067
+ new NumberField({
8068
+ label: "Minimum length",
8069
+ description: "Minimum number of characters",
8070
+ required: false,
8071
+ identifier: `${path}minimum_length`,
8072
+ minimum: 0,
8073
+ maximum: 100,
8074
+ formValidators: [this._validateMin(parentPath)],
8075
+ integers: true
8076
+ })
8077
+ ),
8078
+ showDirectly: false
8079
+ },
8080
+ {
8081
+ field: new NumberField({
8082
+ label: "Maximum length",
8083
+ description: "Maximum number of characters",
8084
+ required: false,
8085
+ identifier: `${path}maximum_length`,
8086
+ minimum: 1,
8087
+ maximum: 5e3,
8088
+ // TODO: depends on short vs long text
8089
+ formValidators: [this._validateMax(parentPath)],
8090
+ // TODO: default: 500 (see: "Short text fields can hold up to 500 characters on a single line.")
8091
+ integers: true
8092
+ }),
8093
+ showDirectly: false
8094
+ }
8219
8095
  ];
8220
8096
  }
8221
8097
  getFieldValidators() {
@@ -8250,23 +8126,24 @@ class StringOrTextField extends BaseField {
8250
8126
  }
8251
8127
  }
8252
8128
  /**
8253
- * This function validates that the value given for "minimum length" (when creating a new field) is less than or
8129
+ * This function returns a function that validates that the value given for "minimum length" (when creating a new field) is less than or
8254
8130
  * equal to the value given for "maximum length".
8255
8131
  */
8256
- __publicField(StringOrTextField, "_validateMin", (value, allValues) => {
8257
- if (typeof allValues.maximum_length === "number" && typeof value === "number" && allValues.maximum_length < value) {
8132
+ __publicField(StringOrTextField, "_validateMin", (path) => (value, allValues) => {
8133
+ const field = valueIsFormikUserFormRevision(allValues) ? get(allValues, path) : allValues;
8134
+ if (typeof field.maximum_length === "number" && typeof value === "number" && field.maximum_length < value) {
8258
8135
  return "Minimum cannot be greater than maximum.";
8259
8136
  }
8260
8137
  return null;
8261
8138
  });
8262
8139
  /**
8263
- * This function validates that the value given for "maximum length" (when creating a new field) is greater than or
8140
+ * This function returns a function that validates that the value given for "maximum length" (when creating a new field) is greater than or
8264
8141
  * equal to the value given for "minimum length".
8265
8142
  */
8266
- __publicField(StringOrTextField, "_validateMax", (value, allValues) => {
8143
+ __publicField(StringOrTextField, "_validateMax", (path) => (value, allValues) => {
8267
8144
  if (typeof value !== "number")
8268
8145
  return null;
8269
- const { minimum_length: minimumLength } = allValues;
8146
+ const { minimum_length: minimumLength } = valueIsFormikUserFormRevision(allValues) ? get(allValues, path) : allValues;
8270
8147
  if (typeof minimumLength !== "number") {
8271
8148
  return null;
8272
8149
  }
@@ -8277,20 +8154,20 @@ __publicField(StringOrTextField, "_validateMax", (value, allValues) => {
8277
8154
  });
8278
8155
  const clickableLinkContainer = "_clickableLinkContainer_1ace7_1";
8279
8156
  const TextFieldInputCopy = "_TextFieldInputCopy_1ace7_5";
8280
- const styles$3 = {
8157
+ const styles$4 = {
8281
8158
  clickableLinkContainer,
8282
8159
  TextFieldInputCopy
8283
8160
  };
8284
8161
  const StringInput = memo(function StringInput2(props) {
8285
- const [{ inputId, labelId, severity, helpText, label, fieldProps, field }, rest] = useFormikInput(props);
8162
+ const [{ inputId, labelId, size, severity, helpText, label, fieldProps, field }, rest] = useFormikInput(props);
8286
8163
  const color = useSeverityColor(severity);
8287
- return /* @__PURE__ */ jsx(InputWithLabelAndHelpText, { helpText, severity, children: /* @__PURE__ */ jsx(InputWithLabel, { severity, inputId, labelId, label, children: !rest.disabled ? /* @__PURE__ */ jsx(TextField$1.Input, { ...rest, ...fieldProps, type: field.inputType, id: inputId, color }) : /* @__PURE__ */ jsxs(TextField$1.Root, { className: styles$3.clickableLinkContainer, children: [
8164
+ return /* @__PURE__ */ jsx(InputWithLabelAndHelpText, { helpText, severity, children: /* @__PURE__ */ jsx(InputWithLabel, { size, severity, inputId, labelId, label, children: !rest.disabled ? /* @__PURE__ */ jsx(TextField$1.Input, { ...rest, ...fieldProps, type: field.inputType, id: inputId, color }) : /* @__PURE__ */ jsxs(TextField$1.Root, { className: styles$4.clickableLinkContainer, children: [
8288
8165
  /* @__PURE__ */ jsx(
8289
8166
  "div",
8290
8167
  {
8291
8168
  className: classNames$1(
8292
8169
  "rt-TextFieldInput rt-r-size-2 rt-variant-surface",
8293
- styles$3.TextFieldInputCopy
8170
+ styles$4.TextFieldInputCopy
8294
8171
  ),
8295
8172
  children: /* @__PURE__ */ jsx(
8296
8173
  Linkify,
@@ -8307,6 +8184,12 @@ const StringInput = memo(function StringInput2(props) {
8307
8184
  /* @__PURE__ */ jsx("div", { className: "rt-TextFieldChrome" })
8308
8185
  ] }) }) });
8309
8186
  });
8187
+ const emptyStringField = {
8188
+ ...emptyBaseField,
8189
+ type: "string",
8190
+ maximum_length: 500,
8191
+ input_type: "text"
8192
+ };
8310
8193
  const _StringField = class _StringField extends StringOrTextField {
8311
8194
  constructor(options) {
8312
8195
  const { inputType = "text", ...rest } = options;
@@ -8334,9 +8217,14 @@ __publicField(_StringField, "fieldTypeDescription", "Short text fields can hold
8334
8217
  __publicField(_StringField, "Icon", InputIcon);
8335
8218
  let StringField = _StringField;
8336
8219
  const TextInput = memo(function TextInput2(props) {
8337
- const [{ inputId, labelId, severity, helpText, label, fieldProps }, rest] = useFormikInput(props);
8338
- 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 }) }) });
8220
+ const [{ inputId, labelId, size, severity, helpText, label, fieldProps }, rest] = useFormikInput(props);
8221
+ return /* @__PURE__ */ jsx(InputWithLabelAndHelpText, { helpText, severity, children: /* @__PURE__ */ jsx(InputWithLabel, { size, severity, inputId, labelId, label, children: /* @__PURE__ */ jsx(TextArea, { ...rest, ...fieldProps, resize: "vertical", id: inputId, severity }) }) });
8339
8222
  });
8223
+ const emptyTextField = {
8224
+ ...emptyBaseField,
8225
+ type: "text",
8226
+ maximum_length: 5e3
8227
+ };
8340
8228
  const _TextField = class _TextField extends StringOrTextField {
8341
8229
  constructor(options) {
8342
8230
  const maxLength = options.maxLength ? Math.min(5e3, options.maxLength) : 5e3;
@@ -8360,8 +8248,42 @@ __publicField(_TextField, "fieldTypeName", "Paragraph");
8360
8248
  __publicField(_TextField, "fieldTypeDescription", "Paragraph fields can hold up to 5000 characters and can have multiple lines.");
8361
8249
  __publicField(_TextField, "Icon", RowsIcon);
8362
8250
  let TextField = _TextField;
8251
+ const DateInput = memo(function DateInput2(props) {
8252
+ const [{ inputId, labelId, size, severity, helpText, label, fieldProps }, rest] = useFormikInput(props);
8253
+ const color = useSeverityColor(severity);
8254
+ const value = fieldProps.value ? fieldProps.value.split("T")[0] : "";
8255
+ return /* @__PURE__ */ jsx(InputWithLabelAndHelpText, { helpText, severity, children: /* @__PURE__ */ jsx(InputWithLabel, { size, severity, inputId, labelId, label, children: /* @__PURE__ */ jsx(TextField$1.Input, { ...rest, ...fieldProps, type: "date", id: inputId, color, value }) }) });
8256
+ });
8257
+ const emptyDateField = {
8258
+ ...emptyBaseField,
8259
+ type: "date"
8260
+ };
8261
+ const _DateField = class _DateField extends BaseField {
8262
+ constructor(options) {
8263
+ super({ ...options, type: "date" });
8264
+ __publicField(this, "onlyValidateAfterTouched", false);
8265
+ }
8266
+ serialize() {
8267
+ return super._serialize();
8268
+ }
8269
+ getValueFromChangeEvent(event) {
8270
+ return new Date(event.target.value).toISOString();
8271
+ }
8272
+ static deserialize(data) {
8273
+ if (data.type !== "date")
8274
+ throw new Error("Type mismatch.");
8275
+ return new _DateField(data);
8276
+ }
8277
+ getInput(props) {
8278
+ return /* @__PURE__ */ jsx(DateInput, { field: this, ...props });
8279
+ }
8280
+ };
8281
+ __publicField(_DateField, "fieldTypeName", "Date");
8282
+ __publicField(_DateField, "fieldTypeDescription", "Allows specifying a date.");
8283
+ __publicField(_DateField, "Icon", CalendarIcon);
8284
+ let DateField = _DateField;
8363
8285
  const SelectInput = memo(function SelectInput2(props) {
8364
- const [{ inputId, labelId, severity, helpText, label, fieldProps, field }, rest] = useFormikInput(props);
8286
+ const [{ inputId, labelId, size, severity, helpText, label, fieldProps, field }, rest] = useFormikInput(props);
8365
8287
  const { onChange, onBlur } = fieldProps;
8366
8288
  const options = useMemo(
8367
8289
  () => field.options.map((option) => ({ value: option.value, itemContent: option.label })),
@@ -8374,7 +8296,7 @@ const SelectInput = memo(function SelectInput2(props) {
8374
8296
  },
8375
8297
  [onChange, onBlur]
8376
8298
  );
8377
- return /* @__PURE__ */ jsx(InputWithLabelAndHelpText, { helpText, severity, children: /* @__PURE__ */ jsx(InputWithLabel, { severity, inputId, labelId, label, children: /* @__PURE__ */ jsx(
8299
+ return /* @__PURE__ */ jsx(InputWithLabelAndHelpText, { helpText, severity, children: /* @__PURE__ */ jsx(InputWithLabel, { size, severity, inputId, labelId, label, children: /* @__PURE__ */ jsx(
8378
8300
  Select,
8379
8301
  {
8380
8302
  items: options,
@@ -8425,11 +8347,6 @@ function reorder(list, source, destination) {
8425
8347
  result.splice(destination, 0, removed);
8426
8348
  return result;
8427
8349
  }
8428
- function replace(list, index2, value) {
8429
- const result = Array.from(list);
8430
- result[index2] = value;
8431
- return result;
8432
- }
8433
8350
  function insert(list, index2, value) {
8434
8351
  const result = Array.from(list ?? []);
8435
8352
  result.splice(index2, 0, value);
@@ -8464,9 +8381,7 @@ const makeConditionalSourceFields = (sections, index2) => {
8464
8381
  return sections.filter((_, i) => i < index2).flatMap((field) => field.fields);
8465
8382
  };
8466
8383
  const getTakenFieldLabels = (fields) => {
8467
- return fields.flatMap(
8468
- (field) => field.type === "section" ? [...field.fields.map((f) => f.label), field.label] : field.label
8469
- ).filter((id) => id !== null);
8384
+ return fields.flatMap((field) => [...field.fields.map((f) => f.label), field.label]).filter((id) => id !== null);
8470
8385
  };
8471
8386
  const incrementFieldLabel = (label, takenLabels) => {
8472
8387
  let count = 1;
@@ -8476,12 +8391,94 @@ const incrementFieldLabel = (label, takenLabels) => {
8476
8391
  }
8477
8392
  return newLabel;
8478
8393
  };
8479
- const MultiStringInput = memo(function MultiStringInput2(props) {
8480
- const [{ inputId, labelId, severity, helpText, label, fieldProps }, rest] = useFormikInput(props);
8481
- const color = useSeverityColor(severity);
8482
- const value = useMemo(
8483
- () => Array.isArray(fieldProps.value) ? fieldProps.value : [],
8484
- [fieldProps.value]
8394
+ const createNewField = (parentPath, index2, initialValues2, values, setFieldValue) => {
8395
+ const { label } = initialValues2;
8396
+ const newField = {
8397
+ ...initialValues2,
8398
+ identifier: makeIdentifier(null, label)
8399
+ };
8400
+ const parent = get(values, parentPath);
8401
+ if (parent === void 0) {
8402
+ throw new Error("Parent path must point to an existing field.");
8403
+ }
8404
+ if (!Array.isArray(parent)) {
8405
+ throw new Error("Parent path must point to an array.");
8406
+ }
8407
+ const updatedFields = insert(parent, index2, newField);
8408
+ void setFieldValue(parentPath, updatedFields).then();
8409
+ };
8410
+ const createNewEmptySection = (index2, values, setFieldValue) => {
8411
+ const initialValues2 = {
8412
+ ...emptySection(),
8413
+ label: ""
8414
+ };
8415
+ createNewField("fields", index2, initialValues2, values, setFieldValue);
8416
+ };
8417
+ const useFieldReordering = () => {
8418
+ const { showError } = useToast();
8419
+ const reorderSection = useCallback(
8420
+ (dropState, sectionId, sectionIndex, destinationIndex, values, setFieldValue) => {
8421
+ const state = dropState[sectionId];
8422
+ if (!state)
8423
+ throw new Error("Could not find section context.");
8424
+ let dest = typeof state.conditionIndex !== "undefined" ? (
8425
+ // cannot move a section with a condition before the condition's field
8426
+ Math.max(state.conditionIndex + 1, destinationIndex)
8427
+ ) : destinationIndex;
8428
+ for (const section of Object.values(dropState)) {
8429
+ if (section.conditionIndex === sectionIndex) {
8430
+ dest = Math.min(dest, section.index - 1);
8431
+ }
8432
+ }
8433
+ if (dest !== destinationIndex) {
8434
+ showError({
8435
+ title: "Could not reorder sections",
8436
+ description: "Sections with conditions must be below the fields they reference."
8437
+ });
8438
+ return;
8439
+ }
8440
+ void setFieldValue("fields", reorder(values.fields, sectionIndex, dest));
8441
+ },
8442
+ [showError]
8443
+ );
8444
+ const reorderField = useCallback(
8445
+ (srcSection, srcSectionIndex, srcFieldIndex, destSection, destSectionIndex, destFieldIndex, setFieldValue) => {
8446
+ var _a2;
8447
+ if (!(srcSection == null ? void 0 : srcSection.fields) || !destSection)
8448
+ throw new Error("Could not find section with fields.");
8449
+ if (srcSection.identifier === destSection.identifier) {
8450
+ void setFieldValue(
8451
+ `fields.${srcSectionIndex}.fields`,
8452
+ reorder(srcSection.fields, srcFieldIndex, destFieldIndex)
8453
+ ).then();
8454
+ } else {
8455
+ const removed = srcSection.fields[srcFieldIndex];
8456
+ if (!removed)
8457
+ throw new Error("Could not find field to reorder.");
8458
+ if (((_a2 = destSection.condition) == null ? void 0 : _a2.identifier) === removed.identifier) {
8459
+ showError({
8460
+ title: "Could not reorder field",
8461
+ description: "Field must be above the section whose condition references it."
8462
+ });
8463
+ return;
8464
+ }
8465
+ void setFieldValue(`fields.${srcSectionIndex}.fields`, remove(srcSection.fields, srcFieldIndex)).then();
8466
+ void setFieldValue(
8467
+ `fields.${destSectionIndex}.fields`,
8468
+ insert(destSection.fields, destFieldIndex, removed)
8469
+ ).then();
8470
+ }
8471
+ },
8472
+ [showError]
8473
+ );
8474
+ return { reorderSection, reorderField };
8475
+ };
8476
+ const MultiStringInput = memo(function MultiStringInput2(props) {
8477
+ const [{ inputId, labelId, size, severity, helpText, label, fieldProps }, rest] = useFormikInput(props);
8478
+ const color = useSeverityColor(severity);
8479
+ const value = useMemo(
8480
+ () => Array.isArray(fieldProps.value) ? fieldProps.value : [],
8481
+ [fieldProps.value]
8485
8482
  );
8486
8483
  const { onChange, onBlur } = fieldProps;
8487
8484
  const droppableId = `${inputId}-droppable`;
@@ -8547,7 +8544,7 @@ const MultiStringInput = memo(function MultiStringInput2(props) {
8547
8544
  [setValueAndTouched, value]
8548
8545
  );
8549
8546
  return /* @__PURE__ */ jsx(DragDropContext, { onDragEnd: handleDragEnd, children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: "2", children: [
8550
- /* @__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: [
8547
+ /* @__PURE__ */ jsx(InputWithLabelAndHelpText, { helpText: updatedHelpText, severity, children: /* @__PURE__ */ jsx(InputWithLabel, { size, severity, inputId, labelId, label, children: (!disabled || value.length === 0) && /* @__PURE__ */ jsxs(Flex, { gap: "2", children: [
8551
8548
  /* @__PURE__ */ jsx(Box, { grow: "1", children: /* @__PURE__ */ jsx(
8552
8549
  TextField$1.Input,
8553
8550
  {
@@ -8618,6 +8615,12 @@ const MultiStringInput = memo(function MultiStringInput2(props) {
8618
8615
  ] }) })
8619
8616
  ] }) });
8620
8617
  });
8618
+ const emptyMultiStringField = {
8619
+ ...emptyBaseField,
8620
+ type: "multi-string",
8621
+ minimum_length: 0,
8622
+ maximum_length: null
8623
+ };
8621
8624
  const _MultiStringField = class _MultiStringField extends BaseField {
8622
8625
  constructor(options) {
8623
8626
  const { minimum_length, maximum_length, ...rest } = options;
@@ -8692,19 +8695,28 @@ class BaseSelectField extends BaseField {
8692
8695
  options: this.options
8693
8696
  };
8694
8697
  }
8695
- static getFieldCreationSchema() {
8698
+ static getFieldCreationSchema(parentPath = "") {
8699
+ const path = parentPath && `${parentPath}.`;
8696
8700
  return [
8697
- new MultiStringField({
8698
- label: "Options",
8699
- description: "List possible options for the user to select from.",
8700
- required: true,
8701
- identifier: "options",
8702
- minimum_length: 2,
8703
- maximum_length: 20
8704
- })
8701
+ {
8702
+ field: new MultiStringField({
8703
+ label: "Options",
8704
+ description: "List possible options for the user to select from.",
8705
+ required: true,
8706
+ identifier: `${path}options`,
8707
+ minimum_length: 2,
8708
+ maximum_length: 20
8709
+ }),
8710
+ showDirectly: true
8711
+ }
8705
8712
  ];
8706
8713
  }
8707
8714
  }
8715
+ const emptySelectField = {
8716
+ ...emptyBaseField,
8717
+ type: "select",
8718
+ options: []
8719
+ };
8708
8720
  const _SelectField = class _SelectField extends BaseSelectField {
8709
8721
  constructor(options) {
8710
8722
  super({ ...options, type: "select" });
@@ -8738,7 +8750,7 @@ const parseValueToArray = (value) => {
8738
8750
  return [value];
8739
8751
  };
8740
8752
  const MultiSelectInput = memo(function MultiSelectInput2(props) {
8741
- const [{ inputId, labelId, severity, helpText, label, fieldProps, field }, rest] = useFormikInput(props);
8753
+ const [{ inputId, labelId, size, severity, helpText, label, fieldProps, field }, rest] = useFormikInput(props);
8742
8754
  const { onChange, onBlur } = fieldProps;
8743
8755
  const value = useMemo(() => parseValueToArray(fieldProps.value), [fieldProps.value]);
8744
8756
  const handleChange = useCallback(
@@ -8748,7 +8760,7 @@ const MultiSelectInput = memo(function MultiSelectInput2(props) {
8748
8760
  },
8749
8761
  [onChange, onBlur]
8750
8762
  );
8751
- return /* @__PURE__ */ jsx(InputWithLabelAndHelpText, { helpText, severity, children: /* @__PURE__ */ jsx(InputWithLabel, { severity, inputId, labelId, label, children: /* @__PURE__ */ jsx(
8763
+ return /* @__PURE__ */ jsx(InputWithLabelAndHelpText, { helpText, severity, children: /* @__PURE__ */ jsx(InputWithLabel, { size, severity, inputId, labelId, label, children: /* @__PURE__ */ jsx(
8752
8764
  MultiSelect,
8753
8765
  {
8754
8766
  value,
@@ -8762,6 +8774,11 @@ const MultiSelectInput = memo(function MultiSelectInput2(props) {
8762
8774
  }
8763
8775
  ) }) });
8764
8776
  });
8777
+ const emptyMultiSelectField = {
8778
+ ...emptyBaseField,
8779
+ type: "multi-select",
8780
+ options: []
8781
+ };
8765
8782
  const _MultiSelectField = class _MultiSelectField extends BaseSelectField {
8766
8783
  constructor(options) {
8767
8784
  super({ ...options, type: "multi-select" });
@@ -8790,16 +8807,10 @@ __publicField(_MultiSelectField, "fieldTypeName", "Multi-select");
8790
8807
  __publicField(_MultiSelectField, "fieldTypeDescription", "Allows the user to select a multiple options from a list of options.");
8791
8808
  __publicField(_MultiSelectField, "Icon", CheckboxIcon);
8792
8809
  let MultiSelectField = _MultiSelectField;
8793
- const FieldInputCloner = memo(function FieldInputCloner2({ field, ...props }) {
8794
- const [{ value: identifier }] = useField(field.options.clonedFieldIdentifier);
8795
- const deserializedField = useMemo(() => {
8796
- const options = field.options.getFieldToClone(identifier);
8797
- if (!options)
8798
- return null;
8799
- return deserialize(options);
8800
- }, [field.options, identifier]);
8801
- return useFieldInput(deserializedField, props);
8802
- });
8810
+ const emptyCustomField = {
8811
+ ...emptyBaseField,
8812
+ type: "custom"
8813
+ };
8803
8814
  class CustomField extends BaseField {
8804
8815
  constructor(options, Component) {
8805
8816
  super({ ...options, type: "custom" });
@@ -8819,13 +8830,8 @@ class CustomField extends BaseField {
8819
8830
  }
8820
8831
  __publicField(CustomField, "fieldTypeName", "Custom");
8821
8832
  __publicField(CustomField, "fieldTypeDescription", "Allows re-rendering of field already in the form");
8822
- class FieldInputClonerField extends CustomField {
8823
- constructor(options) {
8824
- super(options, FieldInputCloner);
8825
- }
8826
- }
8827
8833
  const previewImage = "_previewImage_1ig84_1";
8828
- const styles$2 = {
8834
+ const styles$3 = {
8829
8835
  previewImage
8830
8836
  };
8831
8837
  const convertBytesToLargestUnit = (bytes) => {
@@ -8846,7 +8852,7 @@ const convertBytesToLargestUnit = (bytes) => {
8846
8852
  };
8847
8853
  const NumberInput = memo(function NumberInput22(props) {
8848
8854
  var _a2;
8849
- const [{ inputId, labelId, severity, helpText, label, fieldProps, field }, rest] = useFormikInput(props);
8855
+ const [{ inputId, labelId, size, severity, helpText, label, fieldProps, field }, rest] = useFormikInput(props);
8850
8856
  const { onChange } = fieldProps;
8851
8857
  const color = useSeverityColor(severity);
8852
8858
  const input = useRef(null);
@@ -8876,7 +8882,7 @@ const NumberInput = memo(function NumberInput22(props) {
8876
8882
  const singleButtonText = value ? "Select new file" : "Select a file";
8877
8883
  const buttonText = field.maxFiles > 1 ? multipleButtonText : singleButtonText;
8878
8884
  return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: "2", children: [
8879
- /* @__PURE__ */ jsx(InputWithLabelAndHelpText, { helpText: updatedHelpText, severity, children: /* @__PURE__ */ jsxs(InputWithLabel, { severity, inputId, labelId, label, children: [
8885
+ /* @__PURE__ */ jsx(InputWithLabelAndHelpText, { helpText: updatedHelpText, severity, children: /* @__PURE__ */ jsxs(InputWithLabel, { size, severity, inputId, labelId, label, children: [
8880
8886
  /* @__PURE__ */ jsx(Flex, { direction: "row", gap: "2", children: /* @__PURE__ */ jsx(Box, { width: "max-content", asChild: true, children: /* @__PURE__ */ jsxs(Button, { ...rest, onClick: handleClick, children: [
8881
8887
  /* @__PURE__ */ jsx(UploadIcon, {}),
8882
8888
  " ",
@@ -8957,9 +8963,16 @@ const DisplayFile = memo(function DisplayFile2({ file, field, onRemove, disabled
8957
8963
  error && /* @__PURE__ */ jsx(Text, { size: "1", severity: "danger", children: error })
8958
8964
  ] })
8959
8965
  ] }),
8960
- url && /* @__PURE__ */ jsx("img", { className: styles$2.previewImage, src: url, alt: name })
8966
+ url && /* @__PURE__ */ jsx("img", { className: styles$3.previewImage, src: url, alt: name })
8961
8967
  ] }) });
8962
8968
  });
8969
+ const emptyUploadField = {
8970
+ ...emptyBaseField,
8971
+ type: "upload",
8972
+ extensions: [],
8973
+ maximum_size: void 0,
8974
+ maximum_files: 1
8975
+ };
8963
8976
  const largestSupportedSize = 50;
8964
8977
  const _UploadField = class _UploadField extends BaseField {
8965
8978
  constructor(options) {
@@ -8979,55 +8992,65 @@ const _UploadField = class _UploadField extends BaseField {
8979
8992
  isBlank(value) {
8980
8993
  return super.isBlank(value) || value.length === 0;
8981
8994
  }
8982
- static getFieldCreationSchema() {
8995
+ static getFieldCreationSchema(parentPath = "") {
8996
+ const path = parentPath && `${parentPath}.`;
8983
8997
  return [
8984
- new NumberField({
8985
- label: "How many files can be uploaded?",
8986
- description: "By default, only one file can be uploaded.",
8987
- required: false,
8988
- minimum: 1,
8989
- maximum: 10,
8990
- identifier: "maximum_files",
8991
- integers: true
8992
- }),
8993
- new NumberField({
8994
- // TODO: Default value
8995
- label: "What is the maximum size of each file?",
8996
- description: `Maximum file size in megabytes (between 1MB–${largestSupportedSize}MB).`,
8997
- required: false,
8998
- identifier: "maximum_size",
8999
- minimum: 1,
9000
- maximum: largestSupportedSize,
9001
- integers: true
9002
- }),
9003
- new MultiSelectField({
9004
- label: "Accepted file types",
9005
- description: "Types of allowed files to upload. If left blank, all files will be accepted.",
9006
- required: false,
9007
- identifier: "extensions",
9008
- options: [
9009
- {
9010
- value: "image/*",
9011
- label: "Images"
9012
- },
9013
- {
9014
- value: "audio/*",
9015
- label: "Audio files"
9016
- },
9017
- {
9018
- value: "video/*",
9019
- label: "Videos"
9020
- },
9021
- {
9022
- value: "text/*",
9023
- label: "Text files"
9024
- },
9025
- {
9026
- value: "application/*",
9027
- label: "Application files (includes PDFs and Word documents)"
9028
- }
9029
- ]
9030
- })
8998
+ {
8999
+ field: new NumberField({
9000
+ label: "How many files can be uploaded?",
9001
+ description: "By default, only one file can be uploaded.",
9002
+ required: false,
9003
+ minimum: 1,
9004
+ maximum: 10,
9005
+ identifier: `${path}maximum_files`,
9006
+ integers: true
9007
+ }),
9008
+ showDirectly: false
9009
+ },
9010
+ {
9011
+ field: new NumberField({
9012
+ // TODO: Default value
9013
+ label: "What is the maximum size of each file?",
9014
+ description: `Maximum file size in megabytes (between 1MB–${largestSupportedSize}MB).`,
9015
+ required: false,
9016
+ identifier: `${path}maximum_size`,
9017
+ minimum: 1,
9018
+ maximum: largestSupportedSize,
9019
+ integers: true
9020
+ }),
9021
+ showDirectly: false
9022
+ },
9023
+ {
9024
+ field: new MultiSelectField({
9025
+ label: "Accepted file types",
9026
+ description: "Types of allowed files to upload. If left blank, all files will be accepted.",
9027
+ required: false,
9028
+ identifier: `${path}extensions`,
9029
+ options: [
9030
+ {
9031
+ value: "image/*",
9032
+ label: "Images"
9033
+ },
9034
+ {
9035
+ value: "audio/*",
9036
+ label: "Audio files"
9037
+ },
9038
+ {
9039
+ value: "video/*",
9040
+ label: "Videos"
9041
+ },
9042
+ {
9043
+ value: "text/*",
9044
+ label: "Text files"
9045
+ },
9046
+ {
9047
+ value: "application/*",
9048
+ label: "Application files (includes PDFs and Word documents)"
9049
+ }
9050
+ ]
9051
+ }),
9052
+ showDirectly: false
9053
+ }
9031
9054
  ];
9032
9055
  }
9033
9056
  getFieldValidators() {
@@ -9081,44 +9104,33 @@ const FieldTypeToClsMapping = {
9081
9104
  "multi-string": MultiStringField,
9082
9105
  "multi-select": MultiSelectField
9083
9106
  };
9084
- const deserializeField = (serializedField) => {
9085
- const fieldType = serializedField.type;
9086
- const fieldCls = FieldTypeToClsMapping[fieldType];
9087
- return fieldCls.deserialize(serializedField);
9088
- };
9089
- const deserialize = (serialized) => {
9090
- if (serialized.type === "section") {
9091
- return FieldSection.deserialize(serialized);
9092
- }
9093
- return deserializeField(serialized);
9107
+ const FieldTypeToEmptyFieldMapping = {
9108
+ date: emptyDateField,
9109
+ number: emptyNumberField,
9110
+ boolean: emptyBooleanField,
9111
+ select: emptySelectField,
9112
+ string: emptyStringField,
9113
+ text: emptyTextField,
9114
+ custom: emptyCustomField,
9115
+ upload: emptyUploadField,
9116
+ // TODO: Underscore
9117
+ "multi-string": emptyMultiStringField,
9118
+ "multi-select": emptyMultiSelectField
9094
9119
  };
9095
- function formRevisionToSchema(formRevision, meta = {}) {
9096
- const { readonly = false } = meta;
9097
- return {
9098
- title: formRevision.title,
9099
- description: formRevision.description,
9100
- fields: formRevision.fields.map((serializedField) => deserialize(serializedField)),
9101
- meta: { readonly }
9102
- };
9103
- }
9104
- function valueIsFile(v) {
9105
- return Array.isArray(v) && v.some((v2) => v2 instanceof File || v2 instanceof Promise);
9106
- }
9107
- function isConditionMet(condition, value) {
9108
- if (!condition)
9109
- return true;
9110
- if (valueIsFile(value) || valueIsFile(condition.value))
9111
- throw new Error("Conditions do not support file uploads");
9112
- const valueAsPrimitive = Array.isArray(value) ? value.map((v) => typeof v === "string" ? v : v.value) : value;
9113
- const valueToCompare = Array.isArray(condition.value) ? condition.value.map((v) => typeof v === "string" ? v : v.value) : condition.value;
9114
- if (Array.isArray(valueToCompare) && Array.isArray(valueAsPrimitive)) {
9115
- for (const v of valueToCompare) {
9116
- if (!valueAsPrimitive.includes(v))
9117
- return false;
9118
- }
9119
- return true;
9120
+ const FieldInputCloner = memo(function FieldInputCloner2({ field, ...props }) {
9121
+ const [{ value: identifier }] = useField(field.options.clonedFieldIdentifier);
9122
+ const deserializedField = useMemo(() => {
9123
+ const options = field.options.getFieldToClone(identifier);
9124
+ if (!options)
9125
+ return null;
9126
+ return deserialize(options);
9127
+ }, [field.options, identifier]);
9128
+ return useFieldInput(deserializedField, props);
9129
+ });
9130
+ class FieldInputClonerField extends CustomField {
9131
+ constructor(options) {
9132
+ super(options, FieldInputCloner);
9120
9133
  }
9121
- return valueToCompare === value;
9122
9134
  }
9123
9135
  const useFieldInput = (field, props) => {
9124
9136
  return useMemo(() => {
@@ -9161,7 +9173,7 @@ const FieldSectionLayout = memo(function FieldSectionLayout2(props) {
9161
9173
  return /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: "3", children: [
9162
9174
  /* @__PURE__ */ jsxs(Flex, { direction: "column", children: [
9163
9175
  /* @__PURE__ */ jsx(Heading, { as: "h3", size: "3", children: label }),
9164
- /* @__PURE__ */ jsx(Text, { className: styles$4.description, children: description2 })
9176
+ /* @__PURE__ */ jsx(Text, { className: styles$5.description, children: description2 })
9165
9177
  ] }),
9166
9178
  inputs
9167
9179
  ] }) });
@@ -9180,85 +9192,89 @@ const _FieldSection = class _FieldSection extends BaseFormElement {
9180
9192
  this.condition = null;
9181
9193
  }
9182
9194
  }
9183
- static getFieldCreationSchema(options) {
9195
+ static getFieldCreationSchema(options, parentPath) {
9184
9196
  if (options.length === 0)
9185
9197
  return [];
9198
+ const path = parentPath ? `${parentPath}.` : "";
9186
9199
  return [
9187
- new BooleanField({
9188
- label: "Conditional",
9189
- description: "Conditionally show or hide this section.",
9190
- identifier: "conditional",
9191
- required: false
9192
- }),
9193
- // Declare a section that will hold options for the condition (if any).
9194
- new _FieldSection({
9195
- label: "Conditional settings",
9196
- identifier: "conditional-settings",
9197
- // This section will only be rendered if the above "Conditional" field is checked.
9198
- condition: {
9199
- identifier: "conditional",
9200
- value: true
9201
- },
9202
- // These are the options of the condition.
9203
- fields: [
9204
- // Declare a select field that will be used to select the field against which we will check the
9205
- // condition. This must be selected before the next field is rendered.
9206
- new SelectField({
9207
- label: "Field",
9208
- description: "The field to use for the condition.",
9209
- // The options (for the field against which we will check the condition) are all the labels of
9210
- // the fields in the previous section(s) (or fields declared before with no section) that
9211
- // support conditions. We pass in both the label and the identifier of each supported field. The
9212
- // identifier becomes the value of the option.
9213
- options: options.map((option) => {
9214
- if (!option.label)
9215
- return null;
9216
- if (option.type === "upload")
9217
- return null;
9218
- return {
9219
- label: option.label,
9220
- value: option.identifier
9221
- };
9222
- }).filter((option) => !!option),
9223
- identifier: "condition.identifier",
9224
- required: true
9225
- }),
9226
- // Declare a custom field that will be used to input a value for the condition. The value of the
9227
- // conditional field selected in the previous step must be equal to the value the user inputs into
9228
- // this field for the section to be rendered.
9229
- new FieldInputClonerField({
9230
- label: "Value",
9231
- identifier: "condition.value",
9232
- required: true,
9233
- clonedFieldIdentifier: "condition.identifier",
9234
- getFieldToClone(identifier) {
9235
- if (!identifier) {
9236
- return null;
9237
- }
9238
- const option = options.find((option2) => option2.identifier === identifier);
9239
- if (!option) {
9240
- console.error("Could not find field with identifier", identifier);
9241
- return null;
9200
+ {
9201
+ field: new BooleanField({
9202
+ label: "Conditionally render section",
9203
+ identifier: `${path}conditional`,
9204
+ required: false
9205
+ }),
9206
+ showDirectly: true
9207
+ },
9208
+ {
9209
+ // Declare a section that will hold options for the condition (if any).
9210
+ field: new _FieldSection({
9211
+ label: "Conditional settings",
9212
+ identifier: `${path}conditional-settings`,
9213
+ // This section will only be rendered if the above "Conditional" field is checked.
9214
+ condition: {
9215
+ identifier: `${path}conditional`,
9216
+ value: true
9217
+ },
9218
+ // These are the options of the condition.
9219
+ fields: [
9220
+ // Declare a select field that will be used to select the field against which we will check the
9221
+ // condition. This must be selected before the next field is rendered.
9222
+ new SelectField({
9223
+ label: "Field",
9224
+ description: "The field to use for the condition.",
9225
+ // The options (for the field against which we will check the condition) are all the labels of
9226
+ // the fields in the previous section(s) (or fields declared before with no section) that
9227
+ // support conditions. We pass in both the label and the identifier of each supported field. The
9228
+ // identifier becomes the value of the option.
9229
+ options: options.map((option) => {
9230
+ if (!option.label)
9231
+ return null;
9232
+ if (option.type === "upload")
9233
+ return null;
9234
+ return {
9235
+ label: option.label,
9236
+ value: option.identifier
9237
+ };
9238
+ }).filter((option) => !!option),
9239
+ identifier: `${path}condition.identifier`,
9240
+ required: true
9241
+ }),
9242
+ // Declare a custom field that will be used to input a value for the condition. The value of the
9243
+ // conditional field selected in the previous step must be equal to the value the user inputs into
9244
+ // this field for the section to be rendered.
9245
+ new FieldInputClonerField({
9246
+ label: "Value",
9247
+ identifier: `${path}condition.value`,
9248
+ required: true,
9249
+ clonedFieldIdentifier: `${path}condition.identifier`,
9250
+ getFieldToClone(identifier) {
9251
+ if (!identifier) {
9252
+ return null;
9253
+ }
9254
+ const option = options.find((option2) => option2.identifier === identifier);
9255
+ if (!option) {
9256
+ console.error("Could not find field with identifier", identifier);
9257
+ return null;
9258
+ }
9259
+ return {
9260
+ ...option,
9261
+ // Override some options to make it make sense in the context and to make it work with the framework.
9262
+ label: "Value",
9263
+ identifier: `${path}condition.value`,
9264
+ description: "The value to compare against.",
9265
+ required: option.type !== "boolean"
9266
+ };
9242
9267
  }
9243
- return {
9244
- ...option,
9245
- // Override some options to make it make sense in the context and to make it work with the framework.
9246
- label: "Value",
9247
- identifier: "condition.value",
9248
- description: "The value to compare against.",
9249
- required: option.type !== "boolean"
9250
- };
9251
- }
9252
- })
9253
- ]
9254
- })
9268
+ })
9269
+ ]
9270
+ }),
9271
+ showDirectly: false
9272
+ }
9255
9273
  ];
9256
9274
  }
9257
9275
  static deserialize(data) {
9258
9276
  if (data.type !== "section")
9259
9277
  throw new Error("Invalid type");
9260
- if (!Array.isArray(data.fields))
9261
- throw new Error(`Invalid fields: ${data.fields} (not an array)`);
9262
9278
  const fields = data.fields.map(deserializeField);
9263
9279
  return new _FieldSection({ ...data, fields });
9264
9280
  }
@@ -9292,89 +9308,258 @@ const _FieldSection = class _FieldSection extends BaseFormElement {
9292
9308
  __publicField(_FieldSection, "fieldTypeName", "Section");
9293
9309
  __publicField(_FieldSection, "fieldTypeDescription", "Sections can be useful for grouping fields together. They can also be conditionally shown or hidden.");
9294
9310
  let FieldSection = _FieldSection;
9295
- const hasKeys = (errors) => {
9296
- return Object.keys(errors).length > 0;
9311
+ const deserializeField = (serializedField) => {
9312
+ const fieldType = serializedField.type;
9313
+ const fieldCls = FieldTypeToClsMapping[fieldType];
9314
+ return fieldCls.deserialize(serializedField);
9297
9315
  };
9298
- const validateForm = (schema, form) => {
9299
- const errors = {};
9300
- for (const field of schema.fields) {
9301
- if (field instanceof FieldSection) {
9302
- if (field.condition) {
9303
- const { identifier } = field.condition;
9304
- if (!isConditionMet(field.condition, get(form, identifier))) {
9305
- continue;
9306
- }
9307
- }
9308
- Object.assign(errors, field.getErrors(form));
9309
- } else {
9310
- if (!(field instanceof BaseField)) {
9311
- throw new Error("Invalid field type");
9312
- }
9313
- const id = field.getId();
9314
- const error = field.getError(get(form, id), form);
9315
- if (error)
9316
- set(errors, id, error);
9317
- }
9316
+ const deserialize = (serialized) => {
9317
+ if (serialized.type === "section") {
9318
+ return FieldSection.deserialize(serialized);
9318
9319
  }
9319
- if (hasKeys(errors))
9320
- return errors;
9320
+ return deserializeField(serialized);
9321
9321
  };
9322
- const uncontrolledValues = [null, void 0];
9323
- const initialFormValues = (fields, values) => {
9324
- return fields.reduce(
9325
- (acc, field) => {
9326
- if (field instanceof FieldSection) {
9327
- return { ...acc, ...initialFormValues(field.fields, values) };
9328
- }
9329
- if (uncontrolledValues.includes(get(acc, field.getId()))) {
9330
- set(acc, field.getId(), "");
9331
- }
9332
- return acc;
9333
- },
9334
- // TODO: Had to do this because of this error:
9335
- // > Uncaught TypeError: can't define property "description":
9336
- // > Object is not extensible"
9337
- // This means that we can't mutate `acc` because it's frozen for some
9338
- // unknown reason.
9339
- cloneDeep(values)
9340
- );
9322
+ function formRevisionToSchema(formRevision, meta = {}) {
9323
+ const { readonly = false } = meta;
9324
+ return {
9325
+ title: formRevision.title,
9326
+ description: formRevision.description,
9327
+ fields: formRevision.fields.map((serializedField) => deserialize(serializedField)),
9328
+ meta: { readonly }
9329
+ };
9330
+ }
9331
+ function valueIsFile(v) {
9332
+ return Array.isArray(v) && v.some((v2) => v2 instanceof File || v2 instanceof Promise);
9333
+ }
9334
+ const valueIsFormikUserFormRevision = (form) => {
9335
+ return "fields" in form;
9341
9336
  };
9342
- const defaultHandleSubmit = () => {
9343
- throw new Error("onSubmit must be provided if form is not readonly.");
9337
+ function isConditionMet(condition, value) {
9338
+ if (!condition)
9339
+ return true;
9340
+ if (valueIsFile(value) || valueIsFile(condition.value))
9341
+ throw new Error("Conditions do not support file uploads");
9342
+ const valueAsPrimitive = Array.isArray(value) ? value.map((v) => typeof v === "string" ? v : v.value) : value;
9343
+ const valueToCompare = Array.isArray(condition.value) ? condition.value.map((v) => typeof v === "string" ? v : v.value) : condition.value;
9344
+ if (Array.isArray(valueToCompare) && Array.isArray(valueAsPrimitive)) {
9345
+ for (const v of valueToCompare) {
9346
+ if (!valueAsPrimitive.includes(v))
9347
+ return false;
9348
+ }
9349
+ return true;
9350
+ }
9351
+ return valueToCompare === value;
9352
+ }
9353
+ const emptyNumberField = {
9354
+ ...emptyBaseField,
9355
+ type: "number",
9356
+ minimum: Number.MIN_SAFE_INTEGER,
9357
+ maximum: Number.MAX_SAFE_INTEGER,
9358
+ integers: false
9344
9359
  };
9345
- const FormRenderer = memo(
9346
- forwardRef((props, ref) => {
9360
+ const _NumberField = class _NumberField extends BaseField {
9361
+ constructor(options) {
9347
9362
  const {
9348
- schema,
9349
- values = {},
9350
- onSubmit = defaultHandleSubmit,
9351
- submitText = "Submit",
9352
- cancelText,
9353
- onCancel,
9354
- onDirty,
9355
- onDirtyChange,
9356
- // if the title isn't provided, hide it by default
9357
- hideTitle = !schema.title,
9358
- hideDescription,
9359
- className
9360
- } = props;
9361
- const { readonly } = schema.meta;
9362
- const formId2 = useMemo(() => crypto.randomUUID(), []);
9363
- const formik = useFormik({
9364
- initialValues: initialFormValues(schema.fields, values),
9365
- onSubmit,
9366
- validate: (form) => validateForm(schema, form),
9367
- // only validate the entire form on submit
9368
- validateOnBlur: false,
9369
- validateOnChange: false
9370
- });
9371
- const { dirty } = formik;
9372
- const Title = useMemo(
9373
- () => typeof schema.title === "string" ? /* @__PURE__ */ jsx(Heading, { children: schema.title }) : schema.title,
9363
+ minimum = Number.MIN_SAFE_INTEGER,
9364
+ maximum = Number.MAX_SAFE_INTEGER,
9365
+ integers = false,
9366
+ ...base
9367
+ } = options;
9368
+ super({ ...base, type: "number" });
9369
+ __publicField(this, "minimum");
9370
+ __publicField(this, "maximum");
9371
+ __publicField(this, "integers");
9372
+ this.minimum = minimum;
9373
+ this.maximum = maximum;
9374
+ this.integers = integers;
9375
+ }
9376
+ getValueFromChangeEvent(event) {
9377
+ const number = Number.parseFloat(event.target.value);
9378
+ if (Number.isNaN(number))
9379
+ return "";
9380
+ return number;
9381
+ }
9382
+ static getFieldCreationSchema(parentPath = "") {
9383
+ const path = parentPath && `${parentPath}.`;
9384
+ return [
9385
+ {
9386
+ field: new _NumberField({
9387
+ label: "Minimum",
9388
+ description: "Minimum value",
9389
+ integers: true,
9390
+ required: false,
9391
+ identifier: `${path}minimum`,
9392
+ formValidators: [this._validateMin(parentPath)]
9393
+ }),
9394
+ showDirectly: false
9395
+ },
9396
+ {
9397
+ field: new _NumberField({
9398
+ label: "Maximum",
9399
+ description: "Maximum value",
9400
+ integers: true,
9401
+ required: false,
9402
+ identifier: `${path}maximum`,
9403
+ formValidators: [this._validateMax(parentPath)]
9404
+ }),
9405
+ showDirectly: false
9406
+ },
9407
+ {
9408
+ field: new BooleanField({
9409
+ label: "Integers",
9410
+ description: "Whole numbers only",
9411
+ required: false,
9412
+ identifier: `${path}integers`
9413
+ }),
9414
+ showDirectly: false
9415
+ }
9416
+ ];
9417
+ }
9418
+ getFieldValidators() {
9419
+ const validators = super.getFieldValidators();
9420
+ const min = this.minimum;
9421
+ const max = this.maximum;
9422
+ if (typeof min === "number") {
9423
+ validators.push((value) => {
9424
+ if (typeof value === "number" && value < min) {
9425
+ return `Must be at least ${this.minimum}.`;
9426
+ }
9427
+ });
9428
+ }
9429
+ if (typeof max === "number") {
9430
+ validators.push((value) => {
9431
+ if (typeof value === "number" && value > max) {
9432
+ return `Must be at most ${this.maximum}.`;
9433
+ }
9434
+ });
9435
+ }
9436
+ if (this.integers) {
9437
+ validators.push((value) => {
9438
+ if (typeof value === "number" && !Number.isInteger(value)) {
9439
+ return "Must be a whole number.";
9440
+ }
9441
+ });
9442
+ }
9443
+ return validators;
9444
+ }
9445
+ serialize() {
9446
+ return {
9447
+ ...super._serialize(),
9448
+ minimum: this.minimum,
9449
+ maximum: this.maximum,
9450
+ integers: this.integers
9451
+ };
9452
+ }
9453
+ static deserialize(data) {
9454
+ if (data.type !== "number")
9455
+ throw new Error("Type mismatch.");
9456
+ return new _NumberField(data);
9457
+ }
9458
+ getInput(props) {
9459
+ return /* @__PURE__ */ jsx(NumberInput$1, { field: this, ...props });
9460
+ }
9461
+ };
9462
+ __publicField(_NumberField, "fieldTypeName", "Number");
9463
+ __publicField(_NumberField, "fieldTypeDescription", "Allows specifying a number within a given range.");
9464
+ __publicField(_NumberField, "Icon", FontFamilyIcon);
9465
+ __publicField(_NumberField, "_validateMin", (path) => (value, allValues) => {
9466
+ const field = valueIsFormikUserFormRevision(allValues) ? get(allValues, path) : allValues;
9467
+ if (typeof field.maximum === "number" && typeof value === "number" && field.maximum < value) {
9468
+ return "Minimum cannot be greater than minimum.";
9469
+ }
9470
+ return null;
9471
+ });
9472
+ __publicField(_NumberField, "_validateMax", (path) => (value, allValues) => {
9473
+ const field = valueIsFormikUserFormRevision(allValues) ? get(allValues, path) : allValues;
9474
+ if (typeof field.minimum === "number" && typeof value === "number" && field.minimum > value) {
9475
+ return "Maximum cannot be less than minimum.";
9476
+ }
9477
+ return null;
9478
+ });
9479
+ let NumberField = _NumberField;
9480
+ const hasKeys = (errors) => {
9481
+ return Object.keys(errors).length > 0;
9482
+ };
9483
+ const validateForm = (schema, form) => {
9484
+ const errors = {};
9485
+ for (const field of schema.fields) {
9486
+ if (field instanceof FieldSection) {
9487
+ if (field.condition) {
9488
+ const { identifier } = field.condition;
9489
+ if (!isConditionMet(field.condition, get(form, identifier))) {
9490
+ continue;
9491
+ }
9492
+ }
9493
+ Object.assign(errors, field.getErrors(form));
9494
+ } else {
9495
+ if (!(field instanceof BaseField)) {
9496
+ throw new Error("Invalid field type");
9497
+ }
9498
+ const id = field.getId();
9499
+ const error = field.getError(get(form, id), form);
9500
+ if (error)
9501
+ set(errors, id, error);
9502
+ }
9503
+ }
9504
+ if (hasKeys(errors))
9505
+ return errors;
9506
+ };
9507
+ const uncontrolledValues = [null, void 0];
9508
+ const initialFormValues = (fields, values) => {
9509
+ return fields.reduce(
9510
+ (acc, field) => {
9511
+ if (field instanceof FieldSection) {
9512
+ return { ...acc, ...initialFormValues(field.fields, values) };
9513
+ }
9514
+ if (uncontrolledValues.includes(get(acc, field.getId()))) {
9515
+ set(acc, field.getId(), "");
9516
+ }
9517
+ return acc;
9518
+ },
9519
+ // TODO: Had to do this because of this error:
9520
+ // > Uncaught TypeError: can't define property "description":
9521
+ // > Object is not extensible"
9522
+ // This means that we can't mutate `acc` because it's frozen for some
9523
+ // unknown reason.
9524
+ cloneDeep(values)
9525
+ );
9526
+ };
9527
+ const defaultHandleSubmit = () => {
9528
+ throw new Error("onSubmit must be provided if form is not readonly.");
9529
+ };
9530
+ const FormRenderer = memo(
9531
+ forwardRef((props, ref) => {
9532
+ const {
9533
+ schema,
9534
+ values = {},
9535
+ onSubmit = defaultHandleSubmit,
9536
+ submitText = "Submit",
9537
+ cancelText,
9538
+ onCancel,
9539
+ onDirty,
9540
+ onDirtyChange,
9541
+ // if the title isn't provided, hide it by default
9542
+ hideTitle = !schema.title,
9543
+ hideDescription,
9544
+ className
9545
+ } = props;
9546
+ const { readonly } = schema.meta;
9547
+ const formId2 = useMemo(() => crypto.randomUUID(), []);
9548
+ const formik = useFormik({
9549
+ initialValues: initialFormValues(schema.fields, values),
9550
+ onSubmit,
9551
+ validate: (form) => validateForm(schema, form),
9552
+ // only validate the entire form on submit
9553
+ validateOnBlur: false,
9554
+ validateOnChange: false
9555
+ });
9556
+ const { dirty } = formik;
9557
+ const Title = useMemo(
9558
+ () => typeof schema.title === "string" ? /* @__PURE__ */ jsx(Heading, { children: schema.title }) : schema.title,
9374
9559
  [schema.title]
9375
9560
  );
9376
9561
  const Description = useMemo(
9377
- () => typeof schema.description === "string" ? /* @__PURE__ */ jsx(Text, { className: styles$4.description, children: schema.description }) : schema.description,
9562
+ () => typeof schema.description === "string" ? /* @__PURE__ */ jsx(Text, { className: styles$5.description, children: schema.description }) : schema.description,
9378
9563
  [schema.description]
9379
9564
  );
9380
9565
  const inputs = useFieldInputs(schema.fields, { formId: formId2, disabled: readonly });
@@ -9390,10 +9575,23 @@ const FormRenderer = memo(
9390
9575
  !hideDescription && Description
9391
9576
  ] }) }),
9392
9577
  inputs,
9393
- !readonly && /* @__PURE__ */ jsxs(Flex, { justify: "end", gap: "2", children: [
9394
- cancelText && /* @__PURE__ */ jsx(Button, { type: "button", variant: "soft", onClick: onCancel, children: cancelText }),
9395
- /* @__PURE__ */ jsx(Button, { type: "submit", disabled: !formik.isValid, children: submitText })
9396
- ] })
9578
+ !readonly && /* @__PURE__ */ jsxs(
9579
+ Flex,
9580
+ {
9581
+ justify: "end",
9582
+ gap: "2",
9583
+ align: "center",
9584
+ style: {
9585
+ position: "sticky",
9586
+ bottom: "0px",
9587
+ paddingBottom: "10px"
9588
+ },
9589
+ children: [
9590
+ cancelText && /* @__PURE__ */ jsx(Button, { type: "button", variant: "soft", onClick: onCancel, children: cancelText }),
9591
+ /* @__PURE__ */ jsx(Button, { type: "submit", disabled: !formik.isValid, children: submitText })
9592
+ ]
9593
+ }
9594
+ )
9397
9595
  ] }) }) });
9398
9596
  })
9399
9597
  );
@@ -9438,7 +9636,7 @@ const FormSubmissionViewer = memo(
9438
9636
  );
9439
9637
  const favoriteIcon = "_favoriteIcon_1bixi_1";
9440
9638
  const regularIcon = "_regularIcon_1bixi_9";
9441
- const styles$1 = {
9639
+ const styles$2 = {
9442
9640
  favoriteIcon,
9443
9641
  regularIcon
9444
9642
  };
@@ -9550,7 +9748,7 @@ const FormBrowserEntry = (props) => {
9550
9748
  /* @__PURE__ */ jsx(
9551
9749
  IconButton,
9552
9750
  {
9553
- className: classNames$1(form.favorite ? styles$1.favoriteIcon : styles$1.regularIcon),
9751
+ className: classNames$1(form.favorite ? styles$2.favoriteIcon : styles$2.regularIcon),
9554
9752
  variant: "ghost",
9555
9753
  onClick: handleFavoriteClick,
9556
9754
  "aria-label": form.favorite ? "Favorite form" : "Standard form",
@@ -9576,7 +9774,7 @@ const FormBrowserEntry = (props) => {
9576
9774
  };
9577
9775
  const submissionsContainer = "_submissionsContainer_9iirt_1";
9578
9776
  const stopHorizontalOverflow = "_stopHorizontalOverflow_9iirt_6";
9579
- const styles = {
9777
+ const styles$1 = {
9580
9778
  submissionsContainer,
9581
9779
  stopHorizontalOverflow
9582
9780
  };
@@ -9607,7 +9805,7 @@ const FormSubmissionBrowserEntry = memo(function FormSubmissionBrowserEntry2(pro
9607
9805
  }
9608
9806
  }, [submission, onSubmissionClick]);
9609
9807
  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: [
9610
- /* @__PURE__ */ jsxs(Flex, { gap: "2", align: "center", className: styles.stopHorizontalOverflow, children: [
9808
+ /* @__PURE__ */ jsxs(Flex, { gap: "2", align: "center", className: styles$1.stopHorizontalOverflow, children: [
9611
9809
  /* @__PURE__ */ jsx(Avatar, { src: creatorProfileSrc, size: "1", fallback: creatorProfileFallback }),
9612
9810
  /* @__PURE__ */ jsx(Text, { size: "2", noWrap: true, children: labelType === "creator" ? (createdBy || currentUser).username : revision.title })
9613
9811
  ] }),
@@ -9651,7 +9849,7 @@ const FormSubmissionBrowser = memo(function FormSubmissionBrowser2(props) {
9651
9849
  return /* @__PURE__ */ jsx(
9652
9850
  ButtonList.Root,
9653
9851
  {
9654
- className: classNames$1(styles.submissionsContainer, className),
9852
+ className: classNames$1(styles$1.submissionsContainer, className),
9655
9853
  size: "small",
9656
9854
  variant,
9657
9855
  before: !compact && /* @__PURE__ */ jsxs(Text, { severity: "info", children: [
@@ -9675,23 +9873,22 @@ const FormSubmissionBrowser = memo(function FormSubmissionBrowser2(props) {
9675
9873
  );
9676
9874
  });
9677
9875
  const PatchField = memo((props) => {
9678
- console.log("PatchField props:", props);
9679
9876
  const { name, render } = props;
9680
9877
  const { submitForm } = useFormikContext();
9681
9878
  const [fieldProps, _meta, helpers] = useField(name);
9682
- const ret = useMemo(() => {
9879
+ return useMemo(() => {
9683
9880
  const setValue = (value) => {
9684
9881
  void helpers.setValue(value, false);
9685
9882
  };
9686
9883
  return render({
9687
9884
  value: fieldProps.value,
9885
+ meta: _meta,
9688
9886
  setValue,
9689
9887
  patchValue: () => {
9690
9888
  void submitForm();
9691
9889
  }
9692
9890
  });
9693
- }, [submitForm, helpers, fieldProps.value, render]);
9694
- return /* @__PURE__ */ jsx(Fragment$1, { children: ret });
9891
+ }, [render, fieldProps.value, _meta, submitForm, helpers]);
9695
9892
  });
9696
9893
  PatchField.displayName = "PatchField";
9697
9894
  const PatchFormProvider = memo(
@@ -9740,365 +9937,437 @@ const PatchFormProvider = memo(
9740
9937
  return /* @__PURE__ */ jsx(FormikProvider, { value: formik, children: /* @__PURE__ */ jsx("form", { ...rest, ref, onSubmit: formik.handleSubmit, children }) });
9741
9938
  })
9742
9939
  );
9743
- const CompleteFieldTypeToClsMapping = {
9744
- ...FieldTypeToClsMapping,
9745
- section: FieldSection
9746
- };
9747
- const FieldToChoose = memo(function FieldToChoose2(props) {
9748
- const { field, setFieldType } = props;
9749
- const typeName = field.fieldTypeName;
9750
- const description2 = field.fieldTypeDescription;
9751
- const Icon = field.Icon;
9752
- return /* @__PURE__ */ jsxs(Flex, { gap: "4", align: "center", children: [
9753
- /* @__PURE__ */ jsx(Button, { type: "button", variant: "surface", onClick: setFieldType, style: { width: "135px" }, children: /* @__PURE__ */ jsxs(Flex, { gap: "3", align: "center", grow: "1", children: [
9754
- /* @__PURE__ */ jsx(Icon, {}),
9755
- typeName
9756
- ] }) }),
9757
- /* @__PURE__ */ jsx(Text, { children: description2 })
9758
- ] });
9759
- });
9940
+ const formId = "form-builder";
9760
9941
  const fieldsToChoose = [
9761
9942
  ["string", "text"],
9762
9943
  ["select", "multi-select", "upload"],
9763
9944
  ["boolean", "date", "number", "multi-string"]
9764
9945
  ];
9765
- const indexOfLastFieldGroup = fieldsToChoose.length - 1;
9766
- const ChooseFieldToAdd = memo(function ChooseFieldToAdd2(props) {
9767
- const { setFieldType } = props;
9768
- return /* @__PURE__ */ jsx(Flex, { direction: "column", gap: "3", children: fieldsToChoose.map((fieldGroup, index2) => /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: "3", children: [
9769
- /* @__PURE__ */ jsx(Flex, { direction: "column", gap: "2", children: fieldGroup.map((identifier) => /* @__PURE__ */ jsx(
9770
- FieldToChoose,
9771
- {
9772
- field: FieldTypeToClsMapping[identifier],
9773
- setFieldType: () => {
9774
- setFieldType(identifier);
9775
- }
9776
- },
9777
- identifier
9778
- )) }),
9779
- index2 < indexOfLastFieldGroup && /* @__PURE__ */ jsx(Separator, { size: "4" })
9780
- ] }, index2)) });
9781
- });
9782
- const validateNewFieldName = (takenLabels) => {
9783
- return (value) => {
9784
- if (!value || typeof value !== "string")
9785
- return;
9786
- if (takenLabels.includes(value.trim())) {
9787
- return "This name is already taken.";
9788
- }
9789
- };
9790
- };
9791
- const commonFields = (takenLabels, type) => {
9792
- const base = [
9793
- new StringField({
9794
- label: "Label",
9795
- required: true,
9796
- maxLength: 200,
9797
- identifier: "label",
9798
- fieldValidators: [validateNewFieldName(takenLabels)]
9799
- }),
9800
- new TextField({
9801
- label: "Description",
9802
- required: false,
9803
- maxLength: 1e3,
9804
- identifier: "description"
9805
- })
9806
- ];
9807
- if (type === "section")
9808
- return base;
9809
- return [
9810
- ...base,
9811
- new BooleanField({ label: "Required", description: null, required: false, identifier: "required" })
9812
- ];
9946
+ const CompleteFieldTypeToClsMapping = {
9947
+ ...FieldTypeToClsMapping,
9948
+ section: FieldSection
9813
9949
  };
9814
- const FieldOptionsForm = memo(function FieldOptionsForm2(props) {
9815
- const { fieldType, handleCancel, handleCreateField, handleDirtyChange, defaultField, conditionalSourceFields } = props;
9816
- const fieldCls = CompleteFieldTypeToClsMapping[fieldType];
9817
- const formik = useFormikContext();
9818
- const schema = useMemo(() => {
9819
- const takenFieldLabels = getTakenFieldLabels(formik.values.fields).filter((id) => id !== (defaultField == null ? void 0 : defaultField.label));
9820
- let fields = commonFields(takenFieldLabels, fieldType);
9821
- if (fieldCls === FieldSection) {
9822
- if (conditionalSourceFields === void 0) {
9823
- throw new Error("Conditional source fields must be provided when changing sections.");
9824
- }
9825
- fields = fields.concat(fieldCls.getFieldCreationSchema(conditionalSourceFields));
9826
- } else {
9827
- if (!(fieldCls.prototype instanceof BaseField)) {
9828
- throw new Error(`Field must be an instance of BaseField. Got ${fieldCls.toString()}.`);
9829
- }
9830
- fields = [...fields, ...fieldCls.getFieldCreationSchema()];
9831
- }
9832
- return {
9833
- fields,
9834
- meta: { readonly: false },
9835
- // using the dialog title as the form title
9836
- title: null
9837
- };
9838
- }, [formik.values.fields, fieldType, fieldCls, defaultField == null ? void 0 : defaultField.label, conditionalSourceFields]);
9839
- return /* @__PURE__ */ jsx(
9840
- FormRenderer,
9841
- {
9842
- schema,
9843
- values: defaultField,
9844
- onSubmit: handleCreateField,
9845
- cancelText: defaultField ? void 0 : "Back",
9846
- onCancel: handleCancel,
9847
- onDirtyChange: handleDirtyChange
9848
- }
9849
- );
9850
- });
9851
- const FieldBuilder = memo(function FieldBuilder2(props) {
9852
- const { parentPath, index: index2, isOpen, setIsOpen, initial, editing, conditionalSourceFields } = props;
9853
- const [fieldType, setFieldType] = useState();
9854
- const [formIsDirty, setFormIsDirty] = useState(false);
9855
- const type = (initial == null ? void 0 : initial.type) ?? fieldType;
9856
- const typeName = type ? CompleteFieldTypeToClsMapping[type].fieldTypeName : void 0;
9857
- const { setFieldValue, values } = useFormikContext();
9858
- if (editing && !initial)
9859
- throw new Error("Initial field must be provided if editing is true.");
9860
- const openConfirmDiscardChangesDialog = useDiscardAlertDialog();
9861
- const showChooseField = !type && !editing && !initial;
9862
- const title2 = showChooseField ? "Choose a field type" : `${typeName} settings`;
9863
- 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.`;
9864
- const handleCancel = useCallback(() => {
9865
- setFieldType(void 0);
9866
- setFormIsDirty(false);
9867
- }, []);
9868
- const handleCloseDialog = useCallback(() => {
9869
- if (!formIsDirty) {
9870
- setFieldType(void 0);
9871
- setIsOpen(false);
9872
- } else {
9873
- openConfirmDiscardChangesDialog({
9874
- onDiscard: () => {
9875
- setFieldType(void 0);
9876
- setIsOpen(false);
9877
- }
9950
+ const useFieldTypeItems = (onSelect = () => null) => {
9951
+ return useMemo(() => {
9952
+ return fieldsToChoose.map((fieldGroup) => {
9953
+ return fieldGroup.map((identifier) => {
9954
+ const field = FieldTypeToClsMapping[identifier];
9955
+ const Icon = field.Icon;
9956
+ return {
9957
+ content: /* @__PURE__ */ jsxs(Flex, { align: "center", gap: "2", children: [
9958
+ /* @__PURE__ */ jsx(Icon, {}),
9959
+ /* @__PURE__ */ jsx(Text, { children: field.fieldTypeName })
9960
+ ] }, identifier),
9961
+ value: identifier,
9962
+ onSelect: () => {
9963
+ onSelect(identifier);
9964
+ }
9965
+ };
9878
9966
  });
9879
- }
9880
- }, [formIsDirty, openConfirmDiscardChangesDialog, setIsOpen]);
9881
- const handleCreateField = useCallback(
9882
- (form) => {
9883
- const { label } = form;
9884
- if (!type)
9885
- throw new Error("Field type must be selected before creating a field.");
9886
- if (!label || typeof label !== "string")
9887
- throw new Error("Label must be provided before creating a field.");
9888
- const field = deserialize({
9889
- type,
9890
- ...form,
9891
- identifier: makeIdentifier(form.identifier, label)
9892
- }).serialize();
9893
- const parent = get(values, parentPath);
9894
- if (parent === void 0) {
9895
- throw new Error("Parent path must point to an existing field.");
9896
- }
9897
- let newFields;
9898
- if (!Array.isArray(parent))
9899
- throw new Error("Parent path must point to an array.");
9900
- if (editing) {
9901
- newFields = replace(parent, index2, field);
9902
- } else {
9903
- newFields = insert(parent, index2, field);
9904
- }
9905
- void setFieldValue(parentPath, newFields).then();
9906
- setIsOpen(false);
9907
- },
9908
- [type, values, parentPath, editing, setFieldValue, setIsOpen, index2]
9909
- );
9910
- const handleDirtyChange = useCallback((dirty) => {
9911
- setFormIsDirty(dirty);
9912
- }, []);
9913
- const dialogContent = useCallback(() => {
9914
- if (showChooseField) {
9915
- return /* @__PURE__ */ jsx(ChooseFieldToAdd, { setFieldType });
9916
- }
9917
- return /* @__PURE__ */ jsx(
9918
- FieldOptionsForm,
9919
- {
9920
- conditionalSourceFields,
9921
- handleCancel,
9922
- handleCreateField,
9923
- fieldType: type,
9924
- defaultField: initial,
9925
- handleDirtyChange
9926
- }
9927
- );
9928
- }, [conditionalSourceFields, handleCancel, handleCreateField, handleDirtyChange, initial, showChooseField, type]);
9929
- return /* @__PURE__ */ jsx(
9930
- Dialog,
9931
- {
9932
- title: title2,
9933
- description: description2,
9934
- content: dialogContent,
9935
- open: isOpen,
9936
- onOpenChange: handleCloseDialog
9937
- }
9938
- );
9967
+ });
9968
+ }, [onSelect]);
9969
+ };
9970
+ const FieldTypeDropdown = memo((props) => {
9971
+ const { setFieldType, children } = props;
9972
+ const fieldTypeItems = useFieldTypeItems(setFieldType);
9973
+ return /* @__PURE__ */ jsx(DropdownItemMenu, { trigger: children, items: fieldTypeItems.flat() });
9939
9974
  });
9975
+ FieldTypeDropdown.displayName = "FieldTypeDropdown";
9940
9976
  const forMobile = (mobile, display) => ({
9941
9977
  initial: mobile ? display : "none",
9942
9978
  sm: mobile ? "none" : display
9943
9979
  });
9944
- const FieldActions = memo(function FieldActions2(props) {
9945
- const { remove: remove2, dragHandleProps, editProps, insertAfterProps, duplicateProps } = props;
9946
- const [isEditDialogOpen, setIsEditDialogOpen] = useState(false);
9947
- const [isDuplicateDialogOpen, setIsDuplicateDialogOpen] = useState(false);
9948
- const [isAddDialogOpen, setIsAddDialogOpen] = useState(false);
9949
- const actions = useMemo(
9950
- () => [
9951
- {
9952
- SelectedContent: FieldBuilder,
9953
- selectedContentProps: { ...editProps, isOpen: isEditDialogOpen, setIsOpen: setIsEditDialogOpen },
9954
- Icon: Pencil1Icon,
9955
- text: "Edit",
9956
- buttonProps: {
9957
- onClick: () => {
9958
- setIsEditDialogOpen(true);
9959
- }
9960
- }
9961
- },
9962
- {
9963
- Icon: TrashIcon,
9964
- buttonProps: {
9965
- onClick: remove2
9966
- },
9967
- text: "Delete"
9968
- },
9969
- {
9970
- SelectedContent: FieldBuilder,
9971
- selectedContentProps: {
9972
- ...duplicateProps,
9973
- isOpen: isDuplicateDialogOpen,
9974
- setIsOpen: setIsDuplicateDialogOpen
9980
+ const FieldActions = memo((props) => {
9981
+ const { index: index2, sectionIndex, type, remove: remove2, duplicate, addAfter, move } = props;
9982
+ if (type !== "section" && !addAfter) {
9983
+ throw new Error("addAfter is required for non-section fields");
9984
+ }
9985
+ const { values } = useFormikContext();
9986
+ const fieldTypeItems = useFieldTypeItems(addAfter);
9987
+ const [actions, mobileActions] = useMemo(() => {
9988
+ const getActions = (isMobile) => {
9989
+ const actions2 = [
9990
+ {
9991
+ Icon: TrashIcon,
9992
+ buttonProps: { onClick: remove2 },
9993
+ key: "delete",
9994
+ text: "Delete"
9975
9995
  },
9976
- Icon: CopyIcon,
9977
- text: "Duplicate",
9978
- buttonProps: {
9979
- onClick: () => {
9980
- setIsDuplicateDialogOpen(true);
9981
- }
9996
+ {
9997
+ Icon: CopyIcon,
9998
+ key: "duplicate",
9999
+ text: "Duplicate",
10000
+ buttonProps: { onClick: duplicate }
9982
10001
  }
9983
- },
9984
- {
9985
- SelectedContent: FieldBuilder,
9986
- selectedContentProps: {
9987
- ...insertAfterProps,
9988
- isOpen: isAddDialogOpen,
9989
- setIsOpen: setIsAddDialogOpen
9990
- },
9991
- Icon: PlusIcon,
9992
- text: "Add after",
9993
- buttonProps: {
9994
- onClick: () => {
9995
- setIsAddDialogOpen(true);
10002
+ ];
10003
+ if (type !== "section") {
10004
+ actions2.push({
10005
+ // We want to show a dropdown with field types if "Add after" is clicked
10006
+ isComponent: true,
10007
+ Component: isMobile ? /* @__PURE__ */ jsx(DropdownMenuItemGroup, { items: fieldTypeItems.flat() }) : /* @__PURE__ */ jsx(FieldTypeDropdown, { setFieldType: addAfter, children: /* @__PURE__ */ jsx(IconButton, { type: "button", variant: "ghost", "aria-label": "Add after", children: /* @__PURE__ */ jsx(PlusIcon, {}) }) }),
10008
+ Icon: PlusIcon,
10009
+ key: "add",
10010
+ text: "Add after"
10011
+ });
10012
+ }
10013
+ if (sectionIndex === void 0 && index2 !== 0 || sectionIndex !== void 0 && (sectionIndex !== 0 || index2 !== 0)) {
10014
+ actions2.push({
10015
+ Icon: ArrowUpIcon,
10016
+ key: "moveUp",
10017
+ text: "Move up",
10018
+ buttonProps: {
10019
+ onClick: () => {
10020
+ move("up");
10021
+ }
9996
10022
  }
9997
- }
9998
- },
9999
- {
10000
- // Wrapping icon in a div so that the asChild turns the button into a div
10001
- // so that the drag handle props are not applied to the icon
10002
- // Note: b/c the <button> does not handle the space-press event correctly
10003
- Icon: (props2) => /* @__PURE__ */ jsx("div", { ...props2, children: /* @__PURE__ */ jsx(DragHandleDots2Icon, {}) }),
10004
- text: "Reorder",
10005
- disableOnMobile: true,
10006
- buttonProps: { ...dragHandleProps, asChild: true }
10023
+ });
10007
10024
  }
10008
- ],
10009
- [
10010
- dragHandleProps,
10011
- duplicateProps,
10012
- editProps,
10013
- insertAfterProps,
10014
- isAddDialogOpen,
10015
- isDuplicateDialogOpen,
10016
- isEditDialogOpen,
10017
- remove2
10018
- ]
10019
- );
10020
- return /* @__PURE__ */ jsxs(Fragment$1, { children: [
10021
- /* @__PURE__ */ jsx(Flex, { gap: "4", display: forMobile(false, "flex"), children: actions.map((action) => {
10022
- return /* @__PURE__ */ jsxs(Fragment, { children: [
10023
- /* @__PURE__ */ jsx(
10024
- IconButton,
10025
- {
10026
- type: "button",
10027
- variant: "ghost",
10028
- "aria-label": action.text,
10029
- ...action.buttonProps,
10030
- children: /* @__PURE__ */ jsx(action.Icon, {})
10025
+ if (sectionIndex === void 0 && index2 !== values.fields.length - 1 || sectionIndex !== void 0 && (sectionIndex < values.fields.length - 1 || index2 !== values.fields[sectionIndex].fields.length - 1)) {
10026
+ actions2.push({
10027
+ Icon: ArrowDownIcon,
10028
+ key: "moveDown",
10029
+ text: "Move down",
10030
+ buttonProps: {
10031
+ onClick: () => {
10032
+ move("down");
10033
+ }
10031
10034
  }
10032
- ),
10033
- action.SelectedContent && /* @__PURE__ */ jsx(action.SelectedContent, { ...action.selectedContentProps })
10034
- ] }, action.text);
10035
+ });
10036
+ }
10037
+ return actions2;
10038
+ };
10039
+ return [getActions(false), getActions(true)];
10040
+ }, [addAfter, duplicate, fieldTypeItems, index2, move, remove2, sectionIndex, type, values.fields]);
10041
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [
10042
+ /* @__PURE__ */ jsx(Flex, { gap: "4", display: forMobile(false, "flex"), children: actions.map((Action) => {
10043
+ if (Action.isComponent) {
10044
+ return Action.Component;
10045
+ }
10046
+ return /* @__PURE__ */ jsx(
10047
+ IconButton,
10048
+ {
10049
+ type: "button",
10050
+ variant: "ghost",
10051
+ severity: Action.key.startsWith("move") ? "info" : "primary",
10052
+ "aria-label": Action.text,
10053
+ ...Action.buttonProps,
10054
+ children: /* @__PURE__ */ jsx(Action.Icon, {})
10055
+ },
10056
+ Action.key
10057
+ );
10035
10058
  }) }),
10036
- /* @__PURE__ */ jsx(Box, { display: forMobile(true, "block"), children: /* @__PURE__ */ jsx(
10037
- DropdownItemMenu,
10059
+ /* @__PURE__ */ jsx(Box, { display: forMobile(true, "block"), children: /* @__PURE__ */ jsxs(
10060
+ DropdownMenu,
10038
10061
  {
10039
10062
  trigger: /* @__PURE__ */ jsx(IconButton, { variant: "ghost", "aria-label": "Actions menu", children: /* @__PURE__ */ jsx(DotsVerticalIcon, {}) }),
10040
- items: actions.map((action) => {
10041
- if (action.disableOnMobile)
10042
- return null;
10043
- return {
10044
- ...action.buttonProps,
10045
- onSelect: (event) => {
10046
- var _a2, _b;
10047
- (_b = (_a2 = action.buttonProps) == null ? void 0 : _a2.onClick) == null ? void 0 : _b.call(
10048
- _a2,
10049
- // TODO: Clean up types
10050
- event
10051
- );
10052
- },
10053
- content: /* @__PURE__ */ jsxs(Fragment, { children: [
10054
- /* @__PURE__ */ jsxs(Flex, { gap: "2", align: "center", children: [
10055
- /* @__PURE__ */ jsx(action.Icon, {}),
10056
- action.text
10057
- ] }),
10058
- action.SelectedContent && /* @__PURE__ */ jsx(action.SelectedContent, { ...action.selectedContentProps })
10059
- ] }, action.text)
10060
- };
10061
- }).filter((x) => x !== null)
10063
+ children: [
10064
+ /* @__PURE__ */ jsx(
10065
+ DropdownMenuItemGroup,
10066
+ {
10067
+ items: mobileActions.filter((Action) => !Action.isComponent).map((Action) => {
10068
+ var _a2;
10069
+ return {
10070
+ content: /* @__PURE__ */ jsxs(Flex, { gap: "2", align: "center", children: [
10071
+ /* @__PURE__ */ jsx(Action.Icon, {}),
10072
+ Action.text
10073
+ ] }, Action.key),
10074
+ value: Action.key,
10075
+ onSelect: (_a2 = Action.buttonProps) == null ? void 0 : _a2.onClick
10076
+ };
10077
+ })
10078
+ }
10079
+ ),
10080
+ /* @__PURE__ */ jsx(
10081
+ DropdownMenuSubMenuGroup,
10082
+ {
10083
+ items: mobileActions.filter((Action) => Action.isComponent).map((Action) => ({
10084
+ content: /* @__PURE__ */ jsxs(Flex, { gap: "2", align: "center", children: [
10085
+ /* @__PURE__ */ jsx(Action.Icon, {}),
10086
+ Action.text
10087
+ ] }, Action.key),
10088
+ subContent: Action.Component,
10089
+ triggerIndicator: null
10090
+ }))
10091
+ }
10092
+ )
10093
+ ]
10062
10094
  }
10063
10095
  ) })
10064
10096
  ] });
10065
10097
  });
10066
- const formId = "form-builder";
10067
- const FieldWithActions = memo(function FieldWithActions2(props) {
10068
- const { field, index: index2, sectionIndex, takenLabels, remove: remove2 } = props;
10069
- const deserializedField = useMemo(() => deserialize(field), [field]);
10070
- const input = useFieldInput(deserializedField, { formId, disabled: true });
10071
- const duplicateField = useCallback(
10072
- (field2) => {
10073
- const fieldLabel = field2.label ?? "Untitled field";
10074
- return { ...field2, label: incrementFieldLabel(fieldLabel, takenLabels), identifier: "" };
10075
- },
10076
- [takenLabels]
10098
+ FieldActions.displayName = "FieldActions";
10099
+ const description = "_description_17zed_1";
10100
+ const styles = {
10101
+ description
10102
+ };
10103
+ const FieldSettingsPopover = memo((props) => {
10104
+ const { popoverInputs, hasError } = props;
10105
+ return /* @__PURE__ */ jsx(
10106
+ Popover,
10107
+ {
10108
+ size: "1",
10109
+ trigger: /* @__PURE__ */ jsxs(
10110
+ Button,
10111
+ {
10112
+ variant: "soft",
10113
+ size: "small",
10114
+ "aria-label": "settings",
10115
+ ...hasError && { severity: "danger" },
10116
+ hoverEffects: ["spin90Clockwise"],
10117
+ children: [
10118
+ /* @__PURE__ */ jsx(GearIcon, {}),
10119
+ /* @__PURE__ */ jsx(Text, { children: "Settings" })
10120
+ ]
10121
+ },
10122
+ "settings"
10123
+ ),
10124
+ children: () => /* @__PURE__ */ jsx(Flex, { direction: "column", style: { maxWidth: "240px" }, children: popoverInputs })
10125
+ }
10077
10126
  );
10127
+ });
10128
+ FieldSettingsPopover.displayName = "FieldSettingsPopover";
10129
+ const FieldBuilder = memo((props) => {
10130
+ var _a2, _b, _c, _d, _e, _f;
10131
+ const { parentPath, index: index2, initial, conditionalSourceFields } = props;
10132
+ const RADIX_SM_MIN_WIDTH = 576;
10133
+ const [isLargeScreen, setIsLargeScreen] = useState(
10134
+ window.matchMedia(`(min-width: ${RADIX_SM_MIN_WIDTH}px)`).matches
10135
+ );
10136
+ useEffect(() => {
10137
+ const mediaQuery = window.matchMedia(`(min-width: ${RADIX_SM_MIN_WIDTH}px)`);
10138
+ const handleMediaQueryChange = (event) => {
10139
+ setIsLargeScreen(event.matches);
10140
+ };
10141
+ mediaQuery.addEventListener("change", handleMediaQueryChange);
10142
+ return () => {
10143
+ mediaQuery.removeEventListener("change", handleMediaQueryChange);
10144
+ };
10145
+ }, []);
10146
+ const { values, setFieldValue, errors } = useFormikContext();
10147
+ const fieldTypeItems = useFieldTypeItems();
10148
+ const isSection = useCallback((field) => {
10149
+ return field.type === "section";
10150
+ }, []);
10151
+ useEffect(() => {
10152
+ if (isSection(initial) && !initial.conditional) {
10153
+ void setFieldValue(`${parentPath}.${index2}.condition`, null).then();
10154
+ }
10155
+ }, [index2, initial, isSection, parentPath, setFieldValue]);
10156
+ const conditionLabel = useMemo(
10157
+ () => {
10158
+ var _a3, _b2;
10159
+ return isSection(initial) ? (_b2 = findFieldByIdentifier(values.fields, (_a3 = initial.condition) == null ? void 0 : _a3.identifier)) == null ? void 0 : _b2.label : void 0;
10160
+ },
10161
+ [initial, isSection, values.fields]
10162
+ );
10163
+ const conditionComparison = isSection(initial) ? Array.isArray((_a2 = initial.condition) == null ? void 0 : _a2.value) ? "contains all of" : "equals" : void 0;
10164
+ let conditionValue = void 0;
10165
+ if (isSection(initial)) {
10166
+ if (valueIsFile((_b = initial.condition) == null ? void 0 : _b.value)) {
10167
+ throw new Error("File values are not supported for conditions.");
10168
+ }
10169
+ conditionValue = Array.isArray((_c = initial.condition) == null ? void 0 : _c.value) ? initial.condition.value.map((v) => typeof v === "string" ? v : v.label).join(", ") : (_e = (_d = initial.condition) == null ? void 0 : _d.value) == null ? void 0 : _e.toString();
10170
+ }
10171
+ const type = initial.type;
10172
+ const fieldCls = CompleteFieldTypeToClsMapping[type];
10173
+ const [directlyShownFields, popoverFields] = useMemo(() => {
10174
+ let directlyShownFields2 = [];
10175
+ let popoverFields2 = [];
10176
+ if (fieldCls === FieldSection) {
10177
+ if (conditionalSourceFields === void 0) {
10178
+ throw new Error("Conditional source fields must be provided when changing sections.");
10179
+ }
10180
+ const fieldObject = fieldCls.getFieldCreationSchema(conditionalSourceFields, `${parentPath}.${index2}`);
10181
+ directlyShownFields2 = directlyShownFields2.concat(
10182
+ fieldObject.filter((field) => field.showDirectly).map((field) => field.field)
10183
+ );
10184
+ popoverFields2 = popoverFields2.concat(
10185
+ fieldObject.filter((field) => !field.showDirectly).map((field) => field.field)
10186
+ );
10187
+ } else {
10188
+ if (!(fieldCls.prototype instanceof BaseField)) {
10189
+ throw new Error(`Field must be an instance of BaseField. Got ${fieldCls.toString()}.`);
10190
+ }
10191
+ const fieldObject = fieldCls.getFieldCreationSchema(
10192
+ `${parentPath}.${index2}`
10193
+ );
10194
+ if (isLargeScreen) {
10195
+ directlyShownFields2 = [
10196
+ ...directlyShownFields2,
10197
+ ...fieldObject.filter((field) => field.showDirectly).map((field) => field.field)
10198
+ ];
10199
+ popoverFields2 = [
10200
+ ...popoverFields2,
10201
+ ...fieldObject.filter((field) => !field.showDirectly).map((field) => field.field)
10202
+ ];
10203
+ } else {
10204
+ popoverFields2 = [...popoverFields2, ...fieldObject.map((field) => field.field)];
10205
+ }
10206
+ }
10207
+ return [directlyShownFields2, popoverFields2];
10208
+ }, [fieldCls, conditionalSourceFields, parentPath, index2, isLargeScreen]);
10209
+ const directlyShownInputs = useFieldInputs(directlyShownFields, {
10210
+ formId,
10211
+ disabled: false,
10212
+ ...fieldCls === FieldSection && { size: "2" }
10213
+ });
10214
+ const popoverInputs = useFieldInputs(popoverFields, {
10215
+ formId,
10216
+ disabled: false
10217
+ });
10218
+ let showPopoverInputs = popoverFields.length > 0;
10219
+ if (isSection(initial) && popoverFields.length > 0) {
10220
+ showPopoverInputs = initial.conditional;
10221
+ }
10222
+ const popoverHasErrors = popoverFields.some((field) => {
10223
+ const error = get(errors, fieldCls === FieldSection ? `${parentPath}.${index2}.condition` : field.getId());
10224
+ return error && (typeof error !== "object" || hasKeys(error));
10225
+ });
10226
+ return /* @__PURE__ */ jsxs(Flex, { align: "center", grow: "1", children: [
10227
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", children: [
10228
+ fieldCls === FieldSection && /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: "1", children: [
10229
+ directlyShownFields.length > 0 && directlyShownInputs,
10230
+ /* @__PURE__ */ jsxs(Flex, { align: "center", gap: "2", children: [
10231
+ showPopoverInputs && /* @__PURE__ */ jsx(FieldSettingsPopover, { popoverInputs, hasError: popoverHasErrors }),
10232
+ isSection(initial) && initial.conditional && /* @__PURE__ */ jsx(Text, { size: "1", severity: popoverHasErrors ? "danger" : "primary", children: /* @__PURE__ */ jsxs(Em, { children: [
10233
+ "Display only if ",
10234
+ /* @__PURE__ */ jsx(Strong, { children: conditionLabel }),
10235
+ " ",
10236
+ conditionComparison,
10237
+ " ",
10238
+ /* @__PURE__ */ jsx(Strong, { children: conditionValue })
10239
+ ] }) })
10240
+ ] })
10241
+ ] }),
10242
+ fieldCls !== FieldSection && /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: "2", children: [
10243
+ /* @__PURE__ */ jsx(
10244
+ PatchField,
10245
+ {
10246
+ name: `${parentPath}.${index2}.required`,
10247
+ render: ({ setValue, value }) => /* @__PURE__ */ jsx(
10248
+ Checkbox,
10249
+ {
10250
+ checked: value,
10251
+ onCheckedChange: setValue,
10252
+ label: /* @__PURE__ */ jsx(Text, { size: "2", children: "Required field" }),
10253
+ alwaysShow: true
10254
+ }
10255
+ )
10256
+ }
10257
+ ),
10258
+ /* @__PURE__ */ jsxs(Flex, { align: "center", gap: "3", children: [
10259
+ /* @__PURE__ */ jsx(Badge, { style: { width: "fit-content", height: "24px" }, children: (_f = fieldTypeItems.flat().find((item) => item.value === type)) == null ? void 0 : _f.content }),
10260
+ showPopoverInputs && /* @__PURE__ */ jsx(FieldSettingsPopover, { popoverInputs, hasError: popoverHasErrors })
10261
+ ] })
10262
+ ] }),
10263
+ /* @__PURE__ */ jsx(
10264
+ PatchField,
10265
+ {
10266
+ name: `${parentPath}.${index2}.label`,
10267
+ render: ({ setValue, value }) => /* @__PURE__ */ jsx(
10268
+ Input,
10269
+ {
10270
+ className: styles.Input,
10271
+ placeholder: `Enter a ${type === "section" ? "section" : "field"} label`,
10272
+ value,
10273
+ onChange: (event) => {
10274
+ setValue(event.target.value);
10275
+ },
10276
+ maxLength: 200,
10277
+ showInputLength: false,
10278
+ variant: "ghost",
10279
+ size: "large"
10280
+ }
10281
+ )
10282
+ }
10283
+ ),
10284
+ /* @__PURE__ */ jsx(
10285
+ PatchField,
10286
+ {
10287
+ name: `${parentPath}.${index2}.description`,
10288
+ render: ({ setValue, value }) => /* @__PURE__ */ jsx(
10289
+ TextArea,
10290
+ {
10291
+ style: { minHeight: "max-content" },
10292
+ placeholder: `Enter a ${type === "section" ? "section" : "field"} description`,
10293
+ value,
10294
+ onChange: (event) => {
10295
+ setValue(event.target.value);
10296
+ },
10297
+ resize: "vertical",
10298
+ maxLength: 1e3,
10299
+ showInputLength: false,
10300
+ variant: "ghost"
10301
+ }
10302
+ )
10303
+ }
10304
+ )
10305
+ ] }),
10306
+ fieldCls !== FieldSection && directlyShownFields.length > 0 && directlyShownInputs
10307
+ ] });
10308
+ });
10309
+ FieldBuilder.displayName = "FieldBuilder";
10310
+ const FieldWithActions = memo((props) => {
10311
+ const { field, index: index2, sectionIndex, takenLabels, remove: remove2 } = props;
10312
+ const { setFieldValue, values } = useFormikContext();
10313
+ const { reorderField } = useFieldReordering();
10314
+ const parentPath = `fields.${sectionIndex}.fields`;
10078
10315
  const editFieldProps = useMemo(
10079
10316
  () => ({
10080
10317
  index: index2,
10081
- parentPath: `fields.${sectionIndex}.fields`,
10082
- initial: field,
10083
- editing: true
10318
+ parentPath,
10319
+ initial: field
10084
10320
  }),
10085
- [field, index2, sectionIndex]
10321
+ [field, index2, parentPath]
10086
10322
  );
10087
- const duplicateFieldProps = useMemo(
10088
- () => ({
10089
- parentPath: `fields.${sectionIndex}.fields`,
10090
- index: index2 + 1,
10091
- initial: duplicateField(field)
10092
- }),
10093
- [duplicateField, field, index2, sectionIndex]
10323
+ const duplicateField = useCallback(() => {
10324
+ const label = field.label ?? "Unlabelled field";
10325
+ const duplicatedField = {
10326
+ ...field,
10327
+ label: incrementFieldLabel(label, takenLabels)
10328
+ };
10329
+ createNewField(parentPath, index2 + 1, duplicatedField, values, setFieldValue);
10330
+ }, [field, takenLabels, parentPath, index2, values, setFieldValue]);
10331
+ const createFieldAfter = useCallback(
10332
+ (type) => {
10333
+ if (type === "section") {
10334
+ throw new Error("Section type unexpected");
10335
+ }
10336
+ createNewField(
10337
+ parentPath,
10338
+ index2 + 1,
10339
+ FieldTypeToEmptyFieldMapping[type],
10340
+ values,
10341
+ setFieldValue
10342
+ );
10343
+ },
10344
+ [parentPath, index2, values, setFieldValue]
10094
10345
  );
10095
- const insertAfterProps = useMemo(
10096
- () => ({
10097
- parentPath: `fields.${sectionIndex}.fields`,
10098
- index: index2 + 1,
10099
- initial: void 0
10100
- }),
10101
- [index2, sectionIndex]
10346
+ const moveField = useCallback(
10347
+ (direction) => {
10348
+ const srcSectionIndex = sectionIndex;
10349
+ const srcSection = values.fields[srcSectionIndex];
10350
+ let destSectionIndex = sectionIndex;
10351
+ let destFieldIndex = direction === "up" ? index2 - 1 : index2 + 1;
10352
+ if (direction === "up" && index2 === 0) {
10353
+ destSectionIndex = sectionIndex - 1;
10354
+ destFieldIndex = values.fields[destSectionIndex].fields.length;
10355
+ } else if (direction === "down" && index2 === srcSection.fields.length - 1) {
10356
+ destSectionIndex = sectionIndex + 1;
10357
+ destFieldIndex = 0;
10358
+ }
10359
+ const destSection = values.fields[destSectionIndex];
10360
+ reorderField(
10361
+ srcSection,
10362
+ srcSectionIndex,
10363
+ index2,
10364
+ destSection,
10365
+ destSectionIndex,
10366
+ destFieldIndex,
10367
+ setFieldValue
10368
+ );
10369
+ },
10370
+ [sectionIndex, values.fields, index2, reorderField, setFieldValue]
10102
10371
  );
10103
10372
  return /* @__PURE__ */ jsx(Draggable, { draggableId: field.identifier, index: index2, children: (draggableProvided) => /* @__PURE__ */ jsx(
10104
10373
  Card,
@@ -10106,30 +10375,38 @@ const FieldWithActions = memo(function FieldWithActions2(props) {
10106
10375
  ref: draggableProvided.innerRef,
10107
10376
  ...draggableProvided.draggableProps,
10108
10377
  ...draggableProvided.dragHandleProps,
10378
+ style: {
10379
+ ...draggableProvided.draggableProps.style,
10380
+ backgroundColor: "var(--accent-1)",
10381
+ borderColor: "var(--accent-3)"
10382
+ },
10109
10383
  mb: "4",
10110
10384
  children: /* @__PURE__ */ jsxs(Flex, { gap: "4", justify: "between", align: "center", children: [
10111
- input,
10385
+ /* @__PURE__ */ jsx(FieldBuilder, { ...editFieldProps }),
10112
10386
  /* @__PURE__ */ jsx(
10113
10387
  FieldActions,
10114
10388
  {
10389
+ index: index2,
10390
+ sectionIndex,
10391
+ type: field.type,
10115
10392
  remove: remove2,
10116
- editProps: editFieldProps,
10117
- duplicateProps: duplicateFieldProps,
10118
- insertAfterProps,
10119
- dragHandleProps: draggableProvided.dragHandleProps
10393
+ duplicate: duplicateField,
10394
+ addAfter: createFieldAfter,
10395
+ move: moveField
10120
10396
  }
10121
10397
  )
10122
10398
  ] })
10123
10399
  }
10124
10400
  ) });
10125
10401
  });
10126
- const FieldSectionWithActions = memo(function FieldSectionWithActions2(props) {
10127
- var _a2, _b, _c, _d, _e, _f, _g;
10402
+ FieldWithActions.displayName = "FieldWithActions";
10403
+ const FieldSectionWithActions = memo((props) => {
10404
+ var _a2;
10128
10405
  const { field, index: sectionIndex, dropState } = props;
10129
10406
  const isDropDisabled = (_a2 = dropState[field.identifier]) == null ? void 0 : _a2.disabled;
10130
- const [isAddFieldDialogOpen, setIsAddFieldDialogOpen] = useState(false);
10131
10407
  const { setFieldValue, values } = useFormikContext();
10132
10408
  const alertDialog = useAlertDialog();
10409
+ const { reorderSection } = useFieldReordering();
10133
10410
  const takenFieldLabels = getTakenFieldLabels(values.fields);
10134
10411
  const removeSectionConditions = useCallback(
10135
10412
  (sectionsToUpdate, allSections) => {
@@ -10216,152 +10493,113 @@ const FieldSectionWithActions = memo(function FieldSectionWithActions2(props) {
10216
10493
  alertDialog,
10217
10494
  removeSectionConditions
10218
10495
  ]);
10219
- const duplicateSection = useCallback(
10220
- (field2) => {
10221
- const fieldLabel = field2.label ?? "Untitled section";
10222
- const newSectionLabel = incrementFieldLabel(fieldLabel, takenFieldLabels);
10223
- const newFields = field2.fields.map((f) => {
10224
- const newLabel = incrementFieldLabel(f.label || "Untitled field", takenFieldLabels);
10225
- return {
10226
- ...f,
10227
- label: newLabel,
10228
- identifier: makeIdentifier(void 0, newLabel)
10229
- };
10230
- });
10231
- return { ...field2, label: newSectionLabel, fields: newFields, identifier: "" };
10496
+ const moveSection = useCallback(
10497
+ (direction) => {
10498
+ const destinationIndex = direction === "up" ? sectionIndex - 1 : sectionIndex + 1;
10499
+ reorderSection(dropState, field.identifier, sectionIndex, destinationIndex, values, setFieldValue);
10232
10500
  },
10233
- [takenFieldLabels]
10501
+ [sectionIndex, reorderSection, dropState, field.identifier, values, setFieldValue]
10234
10502
  );
10235
10503
  const editSectionProps = useMemo(
10236
10504
  () => ({
10237
10505
  index: sectionIndex,
10238
10506
  parentPath: "fields",
10239
10507
  initial: field,
10240
- editing: true,
10241
10508
  conditionalSourceFields: makeConditionalSourceFields(values.fields, sectionIndex)
10242
10509
  }),
10243
10510
  [field, sectionIndex, values.fields]
10244
10511
  );
10245
- const insertSectionProps = useMemo(
10246
- () => ({
10247
- index: sectionIndex + 1,
10248
- parentPath: "fields",
10249
- initial: emptySection(),
10250
- conditionalSourceFields: makeConditionalSourceFields(values.fields, sectionIndex + 1)
10251
- }),
10252
- [sectionIndex, values.fields]
10253
- );
10254
- const insertFieldAtEndOfSection = useMemo(
10255
- () => ({
10256
- parentPath: `fields.${sectionIndex}.fields`,
10257
- index: field.fields.length,
10258
- initial: void 0
10259
- }),
10260
- [field.fields.length, sectionIndex]
10261
- );
10262
- const duplicateSectionProps = useMemo(
10263
- () => ({
10264
- index: sectionIndex + 1,
10265
- parentPath: "fields",
10266
- initial: duplicateSection(field),
10267
- conditionalSourceFields: makeConditionalSourceFields(values.fields, sectionIndex + 1)
10268
- }),
10269
- [duplicateSection, field, sectionIndex, values.fields]
10270
- );
10271
- const conditionLabel = useMemo(
10272
- () => {
10273
- var _a3, _b2;
10274
- return (_b2 = findFieldByIdentifier(values.fields, (_a3 = field.condition) == null ? void 0 : _a3.identifier)) == null ? void 0 : _b2.label;
10512
+ const duplicateSection = useCallback(() => {
10513
+ const fieldLabel = field.label ?? "Untitled section";
10514
+ const newSectionLabel = incrementFieldLabel(fieldLabel, takenFieldLabels);
10515
+ const newFields = field.fields.map((f) => {
10516
+ const newLabel = incrementFieldLabel(f.label, takenFieldLabels);
10517
+ return {
10518
+ ...f,
10519
+ label: newLabel,
10520
+ identifier: makeIdentifier(null, newLabel)
10521
+ };
10522
+ });
10523
+ const duplicatedField = { ...field, label: newSectionLabel, fields: newFields };
10524
+ createNewField("fields", sectionIndex + 1, duplicatedField, values, setFieldValue);
10525
+ }, [field, takenFieldLabels, sectionIndex, values, setFieldValue]);
10526
+ const handleCreateField = useCallback(
10527
+ (type) => {
10528
+ createNewField(
10529
+ `fields.${sectionIndex}.fields`,
10530
+ field.fields.length,
10531
+ FieldTypeToEmptyFieldMapping[type],
10532
+ values,
10533
+ setFieldValue
10534
+ );
10275
10535
  },
10276
- [(_b = field.condition) == null ? void 0 : _b.identifier, values.fields]
10536
+ [sectionIndex, field.fields.length, values, setFieldValue]
10277
10537
  );
10278
- const conditionComparison = Array.isArray((_c = field.condition) == null ? void 0 : _c.value) ? "contains all of" : "equals";
10279
- if (valueIsFile((_d = field.condition) == null ? void 0 : _d.value))
10280
- throw new Error("File values are not supported for conditions.");
10281
- const conditionValue = Array.isArray((_e = field.condition) == null ? void 0 : _e.value) ? field.condition.value.map((v) => typeof v === "string" ? v : v.label).join(", ") : (_g = (_f = field.condition) == null ? void 0 : _f.value) == null ? void 0 : _g.toString();
10282
- return /* @__PURE__ */ jsx(Draggable, { draggableId: field.identifier, index: sectionIndex, children: (draggableProvided) => /* @__PURE__ */ jsx(
10283
- Card,
10284
- {
10285
- ref: draggableProvided.innerRef,
10286
- ...draggableProvided.draggableProps,
10287
- ...draggableProvided.dragHandleProps,
10288
- mb: "4",
10289
- children: /* @__PURE__ */ jsxs(Flex, { gap: "3", justify: "between", align: "center", children: [
10290
- /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: "2", grow: "1", children: [
10291
- /* @__PURE__ */ jsxs(Flex, { direction: "column", children: [
10292
- /* @__PURE__ */ jsx(Heading, { as: "h3", size: "3", children: field.label }),
10293
- /* @__PURE__ */ jsx(Text, { className: styles$4.description, children: field.description })
10294
- ] }),
10295
- field.condition && /* @__PURE__ */ jsx(Text, { size: "1", children: /* @__PURE__ */ jsxs(Em, { children: [
10296
- "Display only if ",
10297
- /* @__PURE__ */ jsx(Strong, { children: conditionLabel }),
10298
- " ",
10299
- conditionComparison,
10300
- " ",
10301
- /* @__PURE__ */ jsx(Strong, { children: conditionValue })
10302
- ] }) }),
10303
- /* @__PURE__ */ jsx(Droppable, { droppableId: field.identifier, type: "SECTION", isDropDisabled, children: (droppableProvided) => /* @__PURE__ */ jsxs(
10304
- Flex,
10305
- {
10306
- ref: droppableProvided.innerRef,
10307
- ...droppableProvided.droppableProps,
10308
- direction: "column",
10309
- gap: "0",
10310
- children: [
10311
- field.fields.map((child, i) => /* @__PURE__ */ jsx(
10312
- FieldWithActions,
10313
- {
10314
- field: child,
10315
- index: i,
10316
- sectionIndex,
10317
- remove: () => {
10318
- removeField(i);
10319
- },
10320
- takenLabels: takenFieldLabels
10321
- },
10322
- child.identifier
10323
- )),
10324
- droppableProvided.placeholder,
10325
- /* @__PURE__ */ jsxs(
10326
- Button,
10538
+ return /* @__PURE__ */ jsx(Draggable, { draggableId: field.identifier, index: sectionIndex, children: (draggableProvided) => {
10539
+ return /* @__PURE__ */ jsx(
10540
+ Card,
10541
+ {
10542
+ ref: draggableProvided.innerRef,
10543
+ ...draggableProvided.draggableProps,
10544
+ ...draggableProvided.dragHandleProps,
10545
+ mb: "4",
10546
+ children: /* @__PURE__ */ jsxs(Flex, { gap: "3", justify: "between", align: "center", children: [
10547
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: "2", grow: "1", children: [
10548
+ /* @__PURE__ */ jsx(FieldBuilder, { ...editSectionProps }),
10549
+ /* @__PURE__ */ jsx(
10550
+ Droppable,
10551
+ {
10552
+ droppableId: field.identifier,
10553
+ type: "SECTION",
10554
+ isDropDisabled,
10555
+ children: (droppableProvided) => /* @__PURE__ */ jsxs(
10556
+ Flex,
10327
10557
  {
10328
- type: "button",
10329
- variant: "outline",
10330
- onClick: () => {
10331
- setIsAddFieldDialogOpen(true);
10332
- },
10558
+ ref: droppableProvided.innerRef,
10559
+ ...droppableProvided.droppableProps,
10560
+ direction: "column",
10561
+ gap: "0",
10333
10562
  children: [
10334
- /* @__PURE__ */ jsx(PlusIcon, {}),
10335
- " Add a field"
10563
+ field.fields.map((child, i) => /* @__PURE__ */ jsx(
10564
+ FieldWithActions,
10565
+ {
10566
+ field: child,
10567
+ index: i,
10568
+ sectionIndex,
10569
+ remove: () => {
10570
+ removeField(i);
10571
+ },
10572
+ takenLabels: takenFieldLabels
10573
+ },
10574
+ child.identifier
10575
+ )),
10576
+ droppableProvided.placeholder,
10577
+ /* @__PURE__ */ jsx(FieldTypeDropdown, { setFieldType: handleCreateField, children: /* @__PURE__ */ jsxs(Button, { type: "button", variant: "soft", children: [
10578
+ /* @__PURE__ */ jsx(PlusIcon, {}),
10579
+ " Add field"
10580
+ ] }) })
10336
10581
  ]
10337
10582
  }
10338
- ),
10339
- /* @__PURE__ */ jsx(
10340
- FieldBuilder,
10341
- {
10342
- ...insertFieldAtEndOfSection,
10343
- isOpen: isAddFieldDialogOpen,
10344
- setIsOpen: setIsAddFieldDialogOpen
10345
- }
10346
10583
  )
10347
- ]
10584
+ }
10585
+ )
10586
+ ] }),
10587
+ /* @__PURE__ */ jsx(
10588
+ FieldActions,
10589
+ {
10590
+ index: sectionIndex,
10591
+ type: field.type,
10592
+ remove: removeSection,
10593
+ duplicate: duplicateSection,
10594
+ move: moveSection
10348
10595
  }
10349
- ) })
10350
- ] }),
10351
- /* @__PURE__ */ jsx(
10352
- FieldActions,
10353
- {
10354
- remove: removeSection,
10355
- insertAfterProps: insertSectionProps,
10356
- dragHandleProps: draggableProvided.dragHandleProps,
10357
- editProps: editSectionProps,
10358
- duplicateProps: duplicateSectionProps
10359
- }
10360
- )
10361
- ] })
10362
- }
10363
- ) });
10596
+ )
10597
+ ] })
10598
+ }
10599
+ );
10600
+ } });
10364
10601
  });
10602
+ FieldSectionWithActions.displayName = "FieldSectionWithActions";
10365
10603
  const reducer = (state, action) => {
10366
10604
  var _a2;
10367
10605
  const next = { ...state };
@@ -10423,11 +10661,40 @@ const findSection = (fields, sectionId) => {
10423
10661
  return [section, i];
10424
10662
  }
10425
10663
  };
10426
- const FieldsEditor = memo(function FieldsEditor2() {
10664
+ const BasicFieldSection = memo((props) => {
10665
+ const { field, provided } = props;
10666
+ return /* @__PURE__ */ jsx(
10667
+ Card,
10668
+ {
10669
+ ref: provided == null ? void 0 : provided.innerRef,
10670
+ ...provided == null ? void 0 : provided.draggableProps,
10671
+ ...provided == null ? void 0 : provided.dragHandleProps,
10672
+ style: { ...provided == null ? void 0 : provided.draggableProps.style, height: "80px" },
10673
+ children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: "2", children: [
10674
+ /* @__PURE__ */ jsxs(Flex, { direction: "row", gap: "2", children: [
10675
+ /* @__PURE__ */ jsx(Text, { size: "4", children: field.label }),
10676
+ /* @__PURE__ */ jsx(Badge, { style: { width: "fit-content" }, children: /* @__PURE__ */ jsxs(Text, { children: [
10677
+ field.fields.length,
10678
+ " Fields"
10679
+ ] }) })
10680
+ ] }),
10681
+ /* @__PURE__ */ jsx(Flex, { direction: "row", gap: "2", children: field.fields.map((child) => {
10682
+ const childInfo = FieldTypeToClsMapping[child.type];
10683
+ const Icon = childInfo.Icon;
10684
+ return /* @__PURE__ */ jsx(Flex, { gap: "3", children: /* @__PURE__ */ jsxs(Badge, { style: { width: "fit-content" }, children: [
10685
+ /* @__PURE__ */ jsx(Icon, {}),
10686
+ /* @__PURE__ */ jsx(Text, { children: childInfo.fieldTypeName })
10687
+ ] }) }, child.identifier);
10688
+ }) })
10689
+ ] })
10690
+ }
10691
+ );
10692
+ });
10693
+ BasicFieldSection.displayName = "BasicFieldSection";
10694
+ const FieldsEditor = memo(() => {
10427
10695
  const { values, setFieldValue } = useFormikContext();
10428
10696
  const [dropState, dispatch] = useReducer(reducer, values.fields, initializer);
10429
- const [isAddSectionDialogOpen, setIsAddSectionDialogOpen] = useState(false);
10430
- const { showInfo } = useToast();
10697
+ const { reorderSection, reorderField } = useFieldReordering();
10431
10698
  useEffect(() => {
10432
10699
  dispatch({ type: "update", state: initializer(values.fields) });
10433
10700
  }, [dispatch, values.fields]);
@@ -10443,58 +10710,30 @@ const FieldsEditor = memo(function FieldsEditor2() {
10443
10710
  if (!destination || reason === "CANCEL")
10444
10711
  return;
10445
10712
  if (type === "ROOT") {
10446
- const state = dropState[draggableId];
10447
- if (!state)
10448
- throw new Error("Could not find section context.");
10449
- let dest = typeof state.conditionIndex !== "undefined" ? (
10450
- // cannot move a section with a condition before the condition's field
10451
- Math.max(state.conditionIndex + 1, destination.index)
10452
- ) : destination.index;
10453
- for (const section of Object.values(dropState)) {
10454
- if (section.conditionIndex === source.index) {
10455
- dest = Math.min(dest, section.index - 1);
10456
- }
10457
- }
10458
- if (dest != destination.index) {
10459
- showInfo({
10460
- title: "Reordered sections",
10461
- description: "Sections with conditions must be below the fields they reference."
10462
- });
10463
- }
10464
- return setFieldValue("fields", reorder(values.fields, source.index, dest));
10713
+ reorderSection(dropState, draggableId, source.index, destination.index, values, setFieldValue);
10714
+ return;
10465
10715
  }
10466
10716
  if (type !== "SECTION")
10467
10717
  throw new Error("Unexpected droppable type.");
10468
10718
  const [sourceSection, srcIndex] = findSection(values.fields, source.droppableId) ?? [];
10469
10719
  const [destinationSection, destIndex] = findSection(values.fields, destination.droppableId) ?? [];
10470
- if (!(sourceSection == null ? void 0 : sourceSection.fields) || !destinationSection)
10471
- throw new Error("Could not find section with fields.");
10472
- if (sourceSection.identifier === destinationSection.identifier) {
10473
- void setFieldValue(
10474
- `fields.${srcIndex}.fields`,
10475
- reorder(sourceSection.fields, source.index, destination.index)
10476
- ).then();
10477
- } else {
10478
- const removed = sourceSection.fields[source.index];
10479
- if (!removed)
10480
- throw new Error("Could not find field to reorder.");
10481
- void setFieldValue(`fields.${srcIndex}.fields`, remove(sourceSection.fields, source.index)).then();
10482
- void setFieldValue(
10483
- `fields.${destIndex}.fields`,
10484
- insert(destinationSection.fields, destination.index, removed)
10485
- ).then();
10486
- }
10720
+ reorderField(
10721
+ sourceSection,
10722
+ srcIndex,
10723
+ source.index,
10724
+ destinationSection,
10725
+ destIndex,
10726
+ destination.index,
10727
+ setFieldValue
10728
+ );
10487
10729
  },
10488
- [values.fields, setFieldValue, dropState, showInfo]
10730
+ [values, reorderField, setFieldValue, reorderSection, dropState]
10489
10731
  );
10490
- const makeFieldSectionProps = useMemo(
10491
- () => ({
10492
- index: values.fields.length,
10493
- parentPath: "fields",
10494
- initial: emptySection(),
10495
- conditionalSourceFields: makeConditionalSourceFields(values.fields, values.fields.length)
10496
- }),
10497
- [values.fields]
10732
+ const handleCreateEmptySection = useCallback(
10733
+ (index2) => {
10734
+ createNewEmptySection(index2 + 1, values, setFieldValue);
10735
+ },
10736
+ [values, setFieldValue]
10498
10737
  );
10499
10738
  return /* @__PURE__ */ jsx(DragDropContext, { onDragStart: handleDragStart, onDragEnd: handleDragEnd, children: /* @__PURE__ */ jsx(Droppable, { droppableId: "droppable", type: "ROOT", children: (droppableProvided) => /* @__PURE__ */ jsxs(
10500
10739
  Flex,
@@ -10504,123 +10743,200 @@ const FieldsEditor = memo(function FieldsEditor2() {
10504
10743
  direction: "column",
10505
10744
  gap: "0",
10506
10745
  children: [
10507
- values.fields.map((field, index2) => /* @__PURE__ */ jsx(
10508
- FieldSectionWithActions,
10509
- {
10510
- field,
10511
- index: index2,
10512
- dropState
10513
- },
10514
- field.label
10515
- )),
10516
- droppableProvided.placeholder,
10517
- /* @__PURE__ */ jsxs(
10518
- Button,
10519
- {
10520
- type: "button",
10521
- variant: "outline",
10522
- onClick: () => {
10523
- setIsAddSectionDialogOpen(true);
10524
- },
10525
- children: [
10526
- /* @__PURE__ */ jsx(PlusIcon, {}),
10527
- " Add a section"
10528
- ]
10529
- }
10530
- ),
10531
- /* @__PURE__ */ jsx(
10532
- FieldBuilder,
10533
- {
10534
- ...makeFieldSectionProps,
10535
- isOpen: isAddSectionDialogOpen,
10536
- setIsOpen: setIsAddSectionDialogOpen
10537
- }
10538
- )
10746
+ values.fields.map((field, index2) => /* @__PURE__ */ jsxs(Fragment, { children: [
10747
+ /* @__PURE__ */ jsx(FieldSectionWithActions, { field, index: index2, dropState }),
10748
+ /* @__PURE__ */ jsxs(
10749
+ Button,
10750
+ {
10751
+ type: "button",
10752
+ variant: "surface",
10753
+ severity: "info",
10754
+ onClick: () => {
10755
+ handleCreateEmptySection(index2);
10756
+ },
10757
+ style: { marginBottom: "4px" },
10758
+ children: [
10759
+ /* @__PURE__ */ jsx(PlusIcon, {}),
10760
+ " Add section"
10761
+ ]
10762
+ }
10763
+ )
10764
+ ] }, field.identifier)),
10765
+ droppableProvided.placeholder
10539
10766
  ]
10540
10767
  }
10541
10768
  ) }) });
10542
10769
  });
10770
+ FieldsEditor.displayName = "FieldsEditor";
10543
10771
  const initialValues = {
10544
10772
  title: "",
10545
10773
  description: "",
10546
- fields: []
10547
- };
10548
- const title = new StringField({
10549
- label: "Title",
10550
- minLength: 0,
10551
- maxLength: 100,
10552
- required: true,
10553
- identifier: "title"
10554
- });
10555
- const titleProps = { formId, placeholder: "Give your form a title." };
10556
- const description = new TextField({
10557
- label: "Description",
10558
- minLength: 0,
10559
- maxLength: 1e3,
10560
- required: false,
10561
- identifier: "description"
10562
- });
10563
- const descriptionProps = { formId, placeholder: "Explain the purpose of this form." };
10774
+ fields: [{ ...emptySection(makeIdentifier(null, "")), label: "" }]
10775
+ };
10564
10776
  const previewSubmit = () => {
10565
10777
  alert("This is a form preview, your data will not be saved.");
10566
10778
  };
10567
10779
  const FormBuilder = memo(
10568
10780
  forwardRef((props, ref) => {
10569
10781
  const { onCancel, onSave, revision } = props;
10570
- const { heading = revision ? "Edit form" : "Create a new form" } = props;
10571
- const validate = useCallback((form) => {
10572
- const errors = {};
10573
- if (!form.title) {
10574
- errors.title = "Title is required.";
10575
- }
10576
- if (!form.fields || form.fields.length === 0) {
10577
- errors.fields = "At least one field is required.";
10578
- }
10579
- if (hasKeys(errors)) {
10580
- return errors;
10581
- }
10582
- }, []);
10782
+ const { showError } = useToast();
10783
+ const validate = useCallback(
10784
+ (form) => {
10785
+ const errors = {};
10786
+ if (!form.title) {
10787
+ errors.title = "Title is required.";
10788
+ }
10789
+ if (form.fields.length === 0) {
10790
+ errors.fields = "At least one field is required.";
10791
+ }
10792
+ let fieldsToValidate = [];
10793
+ for (const [sectionIndex, section] of form.fields.entries()) {
10794
+ const fieldCls = CompleteFieldTypeToClsMapping.section;
10795
+ const sectionSettings = fieldCls.getFieldCreationSchema(
10796
+ makeConditionalSourceFields(form.fields, sectionIndex),
10797
+ `fields.${sectionIndex}`
10798
+ ).map((field) => field.field);
10799
+ fieldsToValidate = [...fieldsToValidate, ...sectionSettings];
10800
+ for (const [fieldIndex, field] of section.fields.entries()) {
10801
+ const fieldCls2 = CompleteFieldTypeToClsMapping[field.type];
10802
+ const fieldSettings = fieldCls2.getFieldCreationSchema(`fields.${sectionIndex}.fields.${fieldIndex}`).map((field2) => field2.field);
10803
+ fieldsToValidate = [...fieldsToValidate, ...fieldSettings];
10804
+ }
10805
+ }
10806
+ const fieldErrors = validateForm(
10807
+ {
10808
+ title: "Validate form builder",
10809
+ fields: fieldsToValidate,
10810
+ meta: { readonly: true }
10811
+ },
10812
+ form
10813
+ );
10814
+ if (fieldErrors) {
10815
+ errors.fields = fieldErrors.fields;
10816
+ }
10817
+ if (hasKeys(errors)) {
10818
+ showError({
10819
+ title: "Some form settings are invalid",
10820
+ description: "Please check settings highlighted in red."
10821
+ });
10822
+ return errors;
10823
+ }
10824
+ },
10825
+ [showError]
10826
+ );
10583
10827
  const formik = useFormik({
10584
10828
  initialValues: wrapRootFieldsWithFieldSection(revision) ?? initialValues,
10585
10829
  validate,
10586
- onSubmit: onSave,
10587
- // only validate the entire for on submit
10830
+ // onSubmit: (form) => console.log("SUBMITTED", form),
10831
+ onSubmit: (form) => {
10832
+ onSave(form);
10833
+ },
10834
+ // only validate the entire form on submit
10588
10835
  validateOnChange: false,
10589
10836
  validateOnBlur: false
10590
10837
  });
10591
10838
  const previewSchema = useMemo(() => formRevisionToSchema(formik.values), [formik.values]);
10592
- const titleInput = useFieldInput(title, titleProps);
10593
- const descriptionInput = useFieldInput(description, descriptionProps);
10594
- const FormBuilderHeading = useMemo(
10595
- () => typeof heading === "object" ? heading : /* @__PURE__ */ jsx(Heading, { children: heading }),
10596
- [heading]
10597
- );
10598
10839
  return /* @__PURE__ */ jsx(Tabs.Root, { ref, defaultValue: "edit", children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: "2", children: [
10599
- /* @__PURE__ */ jsxs(Tabs.List, { children: [
10600
- /* @__PURE__ */ jsx(Tabs.Trigger, { value: "edit", children: "Edit" }),
10601
- /* @__PURE__ */ jsx(Tabs.Trigger, { value: "preview", children: "Preview" })
10602
- ] }),
10840
+ /* @__PURE__ */ jsxs(
10841
+ Tabs.List,
10842
+ {
10843
+ style: {
10844
+ display: "flex",
10845
+ position: "sticky",
10846
+ top: 0,
10847
+ zIndex: 2e3,
10848
+ backgroundColor: "var(--color-background)"
10849
+ },
10850
+ children: [
10851
+ /* @__PURE__ */ jsx(Tabs.Trigger, { style: { flex: 1 }, value: "edit", children: "Edit" }),
10852
+ /* @__PURE__ */ jsx(Tabs.Trigger, { style: { flex: 1 }, value: "preview", children: "Preview" })
10853
+ ]
10854
+ }
10855
+ ),
10603
10856
  /* @__PURE__ */ jsxs(Tabs.Content, { value: "edit", children: [
10604
- FormBuilderHeading,
10605
10857
  /* @__PURE__ */ jsxs(Text, { children: [
10606
- "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",
10858
+ "Create your form using various field types. Sections can be",
10607
10859
  " ",
10608
- /* @__PURE__ */ jsx("em", { children: "Preview" }),
10609
- " tab, but",
10860
+ /* @__PURE__ */ jsx("strong", { children: "conditionally rendered" }),
10861
+ " based on",
10610
10862
  " ",
10611
- /* @__PURE__ */ jsx("strong", { children: "field values entered on this page will not be saved." })
10863
+ /* @__PURE__ */ jsx("strong", { children: "answers to fields in preceding sections. " })
10612
10864
  ] }),
10613
10865
  /* @__PURE__ */ jsx(Flex, { asChild: true, direction: "column", gap: "2", mt: "3", children: /* @__PURE__ */ jsxs("form", { id: formId, onSubmit: formik.handleSubmit, children: [
10614
10866
  /* @__PURE__ */ jsxs(FormikProvider, { value: formik, children: [
10615
- titleInput,
10616
- descriptionInput,
10867
+ /* @__PURE__ */ jsx(
10868
+ PatchField,
10869
+ {
10870
+ name: "title",
10871
+ render: ({ setValue, value, meta }) => /* @__PURE__ */ jsx(InputWithHelpText, { severity: "danger", helpText: meta.error ?? null, children: /* @__PURE__ */ jsx(
10872
+ Input,
10873
+ {
10874
+ style: {
10875
+ // var(--accent-a11) for red and var(--accent-1) for gray
10876
+ border: meta.error ? "1px solid #ff9592" : "1px solid #5a6169",
10877
+ paddingLeft: "8px",
10878
+ fontWeight: "bold"
10879
+ },
10880
+ placeholder: "Form title",
10881
+ value,
10882
+ onChange: (event) => {
10883
+ setValue(event.target.value);
10884
+ },
10885
+ maxLength: 100,
10886
+ showInputLength: false,
10887
+ variant: "ghost",
10888
+ size: "large"
10889
+ }
10890
+ ) })
10891
+ }
10892
+ ),
10893
+ /* @__PURE__ */ jsx(
10894
+ PatchField,
10895
+ {
10896
+ name: "description",
10897
+ render: ({ setValue, value }) => /* @__PURE__ */ jsx(
10898
+ TextArea,
10899
+ {
10900
+ style: {
10901
+ minHeight: "max-content",
10902
+ border: "1px solid #5a6169",
10903
+ paddingLeft: "8px",
10904
+ paddingTop: "8px"
10905
+ },
10906
+ placeholder: "Explain the purpose of this form",
10907
+ value,
10908
+ onChange: (event) => {
10909
+ setValue(event.target.value);
10910
+ },
10911
+ resize: "vertical",
10912
+ maxLength: 1e3,
10913
+ showInputLength: false,
10914
+ variant: "ghost"
10915
+ }
10916
+ )
10917
+ }
10918
+ ),
10617
10919
  /* @__PURE__ */ jsx(FieldsEditor, {}),
10618
10920
  /* @__PURE__ */ jsx(Text, { severity: "danger", size: "1", children: typeof formik.errors.fields === "string" && formik.errors.fields })
10619
10921
  ] }),
10620
- /* @__PURE__ */ jsxs(Flex, { justify: "end", gap: "2", children: [
10621
- /* @__PURE__ */ jsx(Button, { type: "button", variant: "soft", onClick: onCancel, children: "Cancel" }),
10622
- /* @__PURE__ */ jsx(Button, { type: "submit", disabled: !formik.isValid, children: "Save" })
10623
- ] })
10922
+ /* @__PURE__ */ jsxs(
10923
+ Flex,
10924
+ {
10925
+ justify: "end",
10926
+ align: "center",
10927
+ gap: "2",
10928
+ style: {
10929
+ position: "sticky",
10930
+ bottom: 0,
10931
+ paddingBottom: "10px",
10932
+ paddingRight: "10px"
10933
+ },
10934
+ children: [
10935
+ /* @__PURE__ */ jsx(Button, { type: "button", variant: "solid", severity: "info", onClick: onCancel, children: "Cancel" }),
10936
+ /* @__PURE__ */ jsx(Button, { type: "submit", children: "Save form" })
10937
+ ]
10938
+ }
10939
+ )
10624
10940
  ] }) })
10625
10941
  ] }),
10626
10942
  /* @__PURE__ */ jsx(Tabs.Content, { value: "preview", children: /* @__PURE__ */ jsx(FormRenderer, { schema: previewSchema, onSubmit: previewSubmit }) })
@@ -10641,6 +10957,7 @@ const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePropert
10641
10957
  FormRenderer,
10642
10958
  FormSubmissionBrowser,
10643
10959
  FormSubmissionViewer,
10960
+ InputWithHelpText,
10644
10961
  InputWithLabel,
10645
10962
  InputWithLabelAndHelpText,
10646
10963
  MultiSelectField,
@@ -10659,12 +10976,22 @@ const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePropert
10659
10976
  TextInput,
10660
10977
  deserialize,
10661
10978
  deserializeField,
10979
+ emptyBaseField,
10980
+ emptyBooleanField,
10981
+ emptyDateField,
10982
+ emptyMultiSelectField,
10983
+ emptyMultiStringField,
10984
+ emptyNumberField,
10985
+ emptySelectField,
10986
+ emptyStringField,
10987
+ emptyTextField,
10662
10988
  formRevisionToSchema,
10663
10989
  isConditionMet,
10664
10990
  useFieldInput,
10665
10991
  useFieldInputs,
10666
10992
  useFormikInput,
10667
- valueIsFile
10993
+ valueIsFile,
10994
+ valueIsFormikUserFormRevision
10668
10995
  }, Symbol.toStringTag, { value: "Module" }));
10669
10996
  export {
10670
10997
  APIError,
@@ -10697,6 +11024,7 @@ export {
10697
11024
  FormSubmissionViewer,
10698
11025
  GREEN,
10699
11026
  HttpMethod,
11027
+ InputWithHelpText,
10700
11028
  InputWithLabel,
10701
11029
  InputWithLabelAndHelpText,
10702
11030
  IssueCommentService,
@@ -10740,7 +11068,6 @@ export {
10740
11068
  WorkspaceService,
10741
11069
  YELLOW,
10742
11070
  _setLatestRetryTime,
10743
- acceptProjectInvite,
10744
11071
  addAttachment,
10745
11072
  addAttachments,
10746
11073
  addCategory,
@@ -10811,6 +11138,15 @@ export {
10811
11138
  emailDomainsReducer,
10812
11139
  emailDomainsSlice,
10813
11140
  emailRegex,
11141
+ emptyBaseField,
11142
+ emptyBooleanField,
11143
+ emptyDateField,
11144
+ emptyMultiSelectField,
11145
+ emptyMultiStringField,
11146
+ emptyNumberField,
11147
+ emptySelectField,
11148
+ emptyStringField,
11149
+ emptyTextField,
10814
11150
  enqueue,
10815
11151
  enqueueRequest,
10816
11152
  errorColor,
@@ -10989,7 +11325,6 @@ export {
10989
11325
  selectProjectUsersAsMapping,
10990
11326
  selectProjectUsersIds,
10991
11327
  selectProjects,
10992
- selectProjectsWithAccess,
10993
11328
  selectRecentIssueIds,
10994
11329
  selectRecentIssuesAsSearchResults,
10995
11330
  selectRecentProjects,
@@ -11095,6 +11430,7 @@ export {
11095
11430
  userReducer,
11096
11431
  userSlice,
11097
11432
  valueIsFile,
11433
+ valueIsFormikUserFormRevision,
11098
11434
  warningColor,
11099
11435
  workspaceReducer,
11100
11436
  workspaceSlice,