@overmap-ai/forms 1.0.10-conditional-arrows-2.5 → 1.0.10-conditional-arrows-2.7

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.
@@ -1,11 +1,12 @@
1
1
  /// <reference types="react" />
2
+ import { DraggableProvidedDragHandleProps } from "@hello-pangea/dnd";
2
3
  import { FieldBuilderArgs } from "./FieldBuilder";
3
4
  interface FieldActionsProps {
4
5
  remove: () => void;
6
+ dragHandleProps: DraggableProvidedDragHandleProps | null | undefined;
5
7
  editProps: FieldBuilderArgs;
6
8
  insertAfterProps: FieldBuilderArgs;
7
9
  duplicateProps: FieldBuilderArgs;
8
- type?: string;
9
10
  }
10
11
  export declare const FieldActions: import("react").NamedExoticComponent<FieldActionsProps>;
11
12
  export {};
@@ -1,5 +1,5 @@
1
1
  import { NestedFieldPath } from "./typings";
2
- import { FC, ReactNode } from "react";
2
+ import { FC, Dispatch, SetStateAction } from "react";
3
3
  import { FieldTypeIdentifier, ISerializedField } from "@overmap-ai/core";
4
4
  import { Form } from '../typings';
5
5
  interface FieldOptionsFormProps {
@@ -11,13 +11,14 @@ interface FieldOptionsFormProps {
11
11
  handleDirtyChange: (dirty: boolean) => void;
12
12
  handleCreateField: (form: Form) => void;
13
13
  }
14
- interface FieldBuilderProps extends Pick<FieldOptionsFormProps, "conditionalSourceFields"> {
14
+ export interface FieldBuilderProps extends Pick<FieldOptionsFormProps, "conditionalSourceFields"> {
15
15
  index: number;
16
16
  parentPath: NestedFieldPath;
17
+ isOpen: boolean;
18
+ setIsOpen: Dispatch<SetStateAction<boolean>>;
17
19
  initial?: ISerializedField;
18
20
  editing?: boolean;
19
- children: ReactNode;
20
21
  }
21
- export type FieldBuilderArgs = Omit<FieldBuilderProps, "children">;
22
+ export type FieldBuilderArgs = Omit<FieldBuilderProps, "children" | "isOpen" | "setIsOpen">;
22
23
  export declare const FieldBuilder: FC<FieldBuilderProps>;
23
24
  export {};
@@ -116,7 +116,7 @@ export declare const useFormikInput: <TField extends AnyField>(props: ComponentP
116
116
  readonly accessKey?: string | undefined;
117
117
  readonly autoFocus?: boolean | undefined;
118
118
  readonly className?: string | undefined;
119
- readonly contentEditable?: (boolean | "true" | "false") | "inherit" | undefined;
119
+ readonly contentEditable?: (boolean | "true" | "false") | "inherit" | "plaintext-only" | undefined;
120
120
  readonly contextMenu?: string | undefined;
121
121
  readonly draggable?: (boolean | "true" | "false") | undefined;
122
122
  readonly hidden?: boolean | undefined;
@@ -2,7 +2,7 @@ import { ISerializedField, SerializedBooleanField } from "@overmap-ai/core";
2
2
  import { BaseField, ChildFieldOptions } from "../BaseField";
3
3
  import { ChangeEvent, ReactNode } from "react";
4
4
  import { ComponentProps } from "../typings";
5
- import { CheckCircledIcon } from "@overmap-ai/blocks";
5
+ import { CheckCircledIcon } from "@radix-ui/react-icons";
6
6
  export declare class BooleanField extends BaseField<boolean, "boolean"> {
7
7
  static readonly fieldTypeName = "Checkbox";
8
8
  static readonly fieldTypeDescription = "Perfect for both optional and required yes/no questions.";
@@ -2,7 +2,7 @@ import { ISerializedField, SerializedDateField } from "@overmap-ai/core";
2
2
  import { BaseField, ChildFieldOptions } from "../BaseField";
3
3
  import { GetInputProps } from "../typings";
4
4
  import React from "react";
5
- import { CalendarIcon } from "@overmap-ai/blocks";
5
+ import { CalendarIcon } from "@radix-ui/react-icons";
6
6
  export declare class DateField extends BaseField<string, "date"> {
7
7
  static readonly fieldTypeName = "Date";
8
8
  static readonly fieldTypeDescription = "Allows specifying a date.";
@@ -2,7 +2,7 @@ import { BaseField, ChildFieldOptions } from "../BaseField";
2
2
  import { ISerializedField, SelectFieldOption, SerializedMultiStringField } from "@overmap-ai/core";
3
3
  import { GetInputProps, InputFieldLevelValidator } from "../typings.ts";
4
4
  import { ReactNode } from "react";
5
- import { ListBulletIcon } from "@overmap-ai/blocks";
5
+ import { ListBulletIcon } from "@radix-ui/react-icons";
6
6
  type MultiStringFieldOptions = ChildFieldOptions<SelectFieldOption[]> & {
7
7
  minimum_length?: number;
8
8
  maximum_length?: number;
@@ -3,7 +3,7 @@ import { BooleanField } from "../BooleanField";
3
3
  import { BaseField, ChildFieldOptions } from "../BaseField";
4
4
  import { GetInputProps, InputFieldLevelValidator, InputValidator } from "../typings";
5
5
  import { ChangeEvent, ReactNode } from "react";
6
- import { FontFamilyIcon } from "@overmap-ai/blocks";
6
+ import { FontFamilyIcon } from "@radix-ui/react-icons";
7
7
  export type NumberFieldValue = number | "";
8
8
  export interface NumberFieldOptions extends ChildFieldOptions<NumberFieldValue> {
9
9
  maximum?: number;
@@ -2,7 +2,7 @@ import { ISerializedField, SelectFieldOptionValue, SerializedMultiSelectField }
2
2
  import { ReactNode } from "react";
3
3
  import { GetInputProps } from "../typings";
4
4
  import { BaseSelectField, BaseSelectFieldOptions } from "./BaseSelectField";
5
- import { CheckboxIcon } from "@overmap-ai/blocks";
5
+ import { CheckboxIcon } from "@radix-ui/react-icons";
6
6
  /**
7
7
  * The options passed to the constructor of MultiSelectField.
8
8
  */
@@ -2,7 +2,7 @@ import { ISerializedField, SelectFieldOptionValue, SerializedSelectField } from
2
2
  import { ReactNode } from "react";
3
3
  import { GetInputProps } from "../typings";
4
4
  import { BaseSelectField, BaseSelectFieldOptions } from "./BaseSelectField";
5
- import { DropdownMenuIcon } from "@overmap-ai/blocks";
5
+ import { DropdownMenuIcon } from "@radix-ui/react-icons";
6
6
  /**
7
7
  * The options passed to the constructor of SelectField.
8
8
  */
@@ -2,7 +2,7 @@ import { ISerializedField, SerializedStringField, StringInputType } from "@overm
2
2
  import { StringOrTextField, StringOrTextFieldOptions } from "../StringOrTextField";
3
3
  import { GetInputProps } from "../../typings";
4
4
  import React from "react";
5
- import { InputIcon } from "@overmap-ai/blocks";
5
+ import { InputIcon } from "@radix-ui/react-icons";
6
6
  interface StringFieldOptions extends Omit<StringOrTextFieldOptions, "type"> {
7
7
  inputType?: StringInputType;
8
8
  }
@@ -2,7 +2,7 @@ import { ISerializedField, SerializedTextField } from "@overmap-ai/core";
2
2
  import { StringOrTextField, StringOrTextFieldOptions } from "../StringOrTextField";
3
3
  import { GetInputProps } from "../../typings";
4
4
  import React from "react";
5
- import { RowsIcon } from "@overmap-ai/blocks";
5
+ import { RowsIcon } from "@radix-ui/react-icons";
6
6
  export interface TextFieldOptions extends Omit<StringOrTextFieldOptions, "type"> {
7
7
  }
8
8
  export declare class TextField extends StringOrTextField<"text"> {
@@ -2,7 +2,7 @@ import { ISerializedField, SerializedUploadField } from "@overmap-ai/core";
2
2
  import { BaseField, ChildFieldOptions } from "../BaseField";
3
3
  import { GetInputProps, InputFieldLevelValidator } from "../typings";
4
4
  import { ChangeEvent, ReactNode } from "react";
5
- import { UploadIcon } from "@overmap-ai/blocks";
5
+ import { UploadIcon } from "@radix-ui/react-icons";
6
6
  import { NumberField } from "../NumberField";
7
7
  import { MultiSelectField } from "../SelectField";
8
8
  export interface UploadFieldOptions extends ChildFieldOptions<File[]> {
@@ -1 +1 @@
1
- export declare const convertBytesToLargestUnit: (bytes: number) => string;
1
+ export declare const convertKilobytesToLargestUnit: (kilobytes: number) => string;
package/dist/forms.js CHANGED
@@ -5,9 +5,11 @@ var __publicField = (obj, key, value) => {
5
5
  return value;
6
6
  };
7
7
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
8
- import { Flex, Text, useSeverityColor, Checkbox, CheckCircledIcon, TextField as TextField$1, FontFamilyIcon, CalendarIcon, InputIcon, TextArea, RowsIcon, Select, Box, IconButton, PlusIcon, Badge, Cross1Icon, ListBulletIcon, DropdownMenuIcon, MultiSelect, CheckboxIcon, Card, Heading, Button, UploadIcon, ButtonList, divButtonProps, StarFilledIcon, StarIcon, QuestionMarkCircledIcon, PersonIcon, Tooltip, Avatar, Separator, useDiscardAlertDialog, Dialog, Pencil1Icon, TrashIcon, CopyIcon, DropdownItemMenu, DotsVerticalIcon, useAlertDialog, Em, Strong, useToast, Tabs } from "@overmap-ai/blocks";
8
+ import { Flex, Text, useSeverityColor, Checkbox, TextArea, Select, IconButton, Badge, MultiSelect, Button, ButtonList, divButtonProps, Tooltip, Separator, useDiscardAlertDialog, Dialog, DropdownItemMenu, useAlertDialog, useToast } from "@overmap-ai/blocks";
9
9
  import { useField, useFormikContext, useFormik, FormikProvider } from "formik";
10
- import React, { useMemo, memo, useCallback, useState, useEffect, useRef, forwardRef, useReducer } from "react";
10
+ import React, { useMemo, memo, useCallback, useState, useEffect, useRef, forwardRef, Fragment as Fragment$1, useReducer } from "react";
11
+ import { CheckCircledIcon, FontFamilyIcon, CalendarIcon, InputIcon, RowsIcon, PlusIcon, Cross1Icon, ListBulletIcon, DropdownMenuIcon, CheckboxIcon, UploadIcon, StarFilledIcon, StarIcon, QuestionMarkCircledIcon, PersonIcon, Pencil1Icon, TrashIcon, CopyIcon, DragHandleDots2Icon, DotsVerticalIcon } from "@radix-ui/react-icons";
12
+ import { TextField as TextField$1, Box, Card, Heading, Avatar, Em, Strong, Tabs } from "@radix-ui/themes";
11
13
  import { DragDropContext, Droppable, Draggable } from "@hello-pangea/dnd";
12
14
  import { slugify, useAppSelector, selectFormRevision, useSDK, selectSubmissionAttachments, selectFilteredUserForms, selectUserFormMapping, selectOrganization, selectUser, selectNumberOfUserForms, selectCurrentUser, classNames, isToday, getLocalDateString, selectLatestFormRevision, useFileSrc, selectSubmissionsForForm } from "@overmap-ai/core";
13
15
  import get from "lodash.get";
@@ -1146,15 +1148,20 @@ const previewImage = "_previewImage_1ig84_1";
1146
1148
  const styles$2 = {
1147
1149
  previewImage
1148
1150
  };
1149
- const convertBytesToLargestUnit = (bytes) => {
1150
- const units = ["byte", "kilobyte", "megabyte"];
1151
- let sizeInUnit = bytes;
1151
+ const convertKilobytesToLargestUnit = (kilobytes) => {
1152
+ const units = ["kilobyte", "megabyte"];
1153
+ let sizeInUnit = kilobytes;
1152
1154
  let unitIndex = 0;
1153
1155
  while (sizeInUnit > 1024 && unitIndex < units.length - 1) {
1154
1156
  sizeInUnit /= 1024;
1155
1157
  unitIndex++;
1156
1158
  }
1157
- const formatter = new Intl.NumberFormat([], { maximumFractionDigits: 2, style: "unit", unit: units[unitIndex] });
1159
+ const formatter = new Intl.NumberFormat([], {
1160
+ // 0 for kilobytes, 1 for megabytes
1161
+ maximumFractionDigits: unitIndex,
1162
+ style: "unit",
1163
+ unit: units[unitIndex]
1164
+ });
1158
1165
  return formatter.format(sizeInUnit);
1159
1166
  };
1160
1167
  const NumberInput = memo(function NumberInput22(props) {
@@ -1168,7 +1175,7 @@ const NumberInput = memo(function NumberInput22(props) {
1168
1175
  if (helpText)
1169
1176
  return helpText;
1170
1177
  if (field.maxFileSize) {
1171
- const size = convertBytesToLargestUnit(field.maxFileSize);
1178
+ const size = convertKilobytesToLargestUnit(field.maxFileSize);
1172
1179
  return `Maximum file size: ${size}`;
1173
1180
  }
1174
1181
  return null;
@@ -1236,7 +1243,7 @@ const DisplayFile = memo(function DisplayFile2({ file, field, onRemove, disabled
1236
1243
  }
1237
1244
  if (resolvedFile) {
1238
1245
  name2 = resolvedFile.name;
1239
- size2 = convertBytesToLargestUnit(resolvedFile.size);
1246
+ size2 = convertKilobytesToLargestUnit(resolvedFile.size);
1240
1247
  } else {
1241
1248
  name2 = "Downloading...";
1242
1249
  size2 = "...";
@@ -1272,7 +1279,7 @@ const DisplayFile = memo(function DisplayFile2({ file, field, onRemove, disabled
1272
1279
  url && /* @__PURE__ */ jsx("img", { className: styles$2.previewImage, src: url, alt: name })
1273
1280
  ] }) });
1274
1281
  });
1275
- const largestSupportedSize = 50 * 1024 * 1024;
1282
+ const largestSupportedSize = 50 * 1024;
1276
1283
  const _UploadField = class _UploadField extends BaseField {
1277
1284
  constructor(options) {
1278
1285
  const { extensions, maximum_files, maximum_size, ...base } = options;
@@ -1299,11 +1306,13 @@ const _UploadField = class _UploadField extends BaseField {
1299
1306
  required: false,
1300
1307
  minimum: 1,
1301
1308
  maximum: 10,
1302
- identifier: "maximum_files"
1309
+ identifier: "maximum_files",
1310
+ integers: true
1303
1311
  }),
1304
1312
  new NumberField({
1313
+ // TODO: Default value
1305
1314
  label: "What is the maximum size of each file?",
1306
- description: "Maximum file size in bytes.",
1315
+ description: "Maximum file size in kilobytes.",
1307
1316
  required: false,
1308
1317
  identifier: "maximum_size",
1309
1318
  minimum: 1,
@@ -1346,7 +1355,7 @@ const _UploadField = class _UploadField extends BaseField {
1346
1355
  const maxFiles = this.maxFiles ?? 1;
1347
1356
  validators.push((value) => {
1348
1357
  if (value && value.some((file) => file.size > maxFileSize)) {
1349
- return `Files must be at most ${convertBytesToLargestUnit(maxFileSize)}.`;
1358
+ return `Files must be at most ${convertKilobytesToLargestUnit(maxFileSize)}.`;
1350
1359
  }
1351
1360
  });
1352
1361
  validators.push((value) => {
@@ -1986,7 +1995,7 @@ const FieldOptionsForm = memo(function FieldOptionsForm2(props) {
1986
1995
  );
1987
1996
  });
1988
1997
  const FieldBuilder = memo(function FieldBuilder2(props) {
1989
- const { parentPath, index, children, initial, editing, conditionalSourceFields } = props;
1998
+ const { parentPath, index, isOpen, setIsOpen, initial, editing, conditionalSourceFields } = props;
1990
1999
  const [fieldType, setFieldType] = useState();
1991
2000
  const [formIsDirty, setFormIsDirty] = useState(false);
1992
2001
  const type = (initial == null ? void 0 : initial.type) ?? fieldType;
@@ -2002,24 +2011,21 @@ const FieldBuilder = memo(function FieldBuilder2(props) {
2002
2011
  setFieldType(void 0);
2003
2012
  setFormIsDirty(false);
2004
2013
  }, []);
2005
- const handleCloseInterrupt = useCallback(
2006
- (confirmClose) => {
2007
- if (formIsDirty) {
2008
- openConfirmDiscardChangesDialog({
2009
- onDiscard: () => {
2010
- setFieldType(void 0);
2011
- confirmClose();
2012
- }
2013
- });
2014
- } else {
2015
- setFieldType(void 0);
2016
- confirmClose();
2017
- }
2018
- },
2019
- [formIsDirty, openConfirmDiscardChangesDialog]
2020
- );
2014
+ const handleCloseDialog = useCallback(() => {
2015
+ if (!formIsDirty) {
2016
+ setFieldType(void 0);
2017
+ setIsOpen(false);
2018
+ } else {
2019
+ openConfirmDiscardChangesDialog({
2020
+ onDiscard: () => {
2021
+ setFieldType(void 0);
2022
+ setIsOpen(false);
2023
+ }
2024
+ });
2025
+ }
2026
+ }, [formIsDirty, openConfirmDiscardChangesDialog, setIsOpen]);
2021
2027
  const handleCreateField = useCallback(
2022
- (form, closeDialog) => {
2028
+ (form) => {
2023
2029
  const { label } = form;
2024
2030
  if (!type)
2025
2031
  throw new Error("Field type must be selected before creating a field.");
@@ -2043,42 +2049,55 @@ const FieldBuilder = memo(function FieldBuilder2(props) {
2043
2049
  newFields = insert(parent, index, field);
2044
2050
  }
2045
2051
  setFieldValue(parentPath, newFields).then();
2046
- closeDialog({ force: true });
2052
+ setIsOpen(false);
2047
2053
  },
2048
- [editing, type, values, parentPath, setFieldValue, index]
2054
+ [type, values, parentPath, editing, setFieldValue, setIsOpen, index]
2049
2055
  );
2050
2056
  const handleDirtyChange = useCallback((dirty) => setFormIsDirty(dirty), []);
2051
- const dialogContent = useCallback(
2052
- (closeDialog) => {
2053
- if (showChooseField) {
2054
- return /* @__PURE__ */ jsx(ChooseFieldToAdd, { setFieldType });
2057
+ const dialogContent = useCallback(() => {
2058
+ if (showChooseField) {
2059
+ return /* @__PURE__ */ jsx(ChooseFieldToAdd, { setFieldType });
2060
+ }
2061
+ return /* @__PURE__ */ jsx(
2062
+ FieldOptionsForm,
2063
+ {
2064
+ conditionalSourceFields,
2065
+ handleCancel,
2066
+ handleCreateField,
2067
+ fieldType: type,
2068
+ defaultField: initial,
2069
+ handleDirtyChange
2055
2070
  }
2056
- return /* @__PURE__ */ jsx(
2057
- FieldOptionsForm,
2058
- {
2059
- conditionalSourceFields,
2060
- handleCancel,
2061
- handleCreateField: (form) => handleCreateField(form, closeDialog),
2062
- fieldType: type,
2063
- defaultField: initial,
2064
- handleDirtyChange
2065
- }
2066
- );
2067
- },
2068
- [conditionalSourceFields, handleCancel, handleCreateField, handleDirtyChange, initial, showChooseField, type]
2071
+ );
2072
+ }, [conditionalSourceFields, handleCancel, handleCreateField, handleDirtyChange, initial, showChooseField, type]);
2073
+ return /* @__PURE__ */ jsx(
2074
+ Dialog,
2075
+ {
2076
+ title: title2,
2077
+ description: description2,
2078
+ content: dialogContent,
2079
+ open: isOpen,
2080
+ onOpenChange: handleCloseDialog
2081
+ }
2069
2082
  );
2070
- return /* @__PURE__ */ jsx(Dialog, { onCloseInterrupt: handleCloseInterrupt, title: title2, description: description2, content: dialogContent, children });
2071
2083
  });
2072
- const DefaultWrapper = ({ children }) => /* @__PURE__ */ jsx(Fragment, { children });
2084
+ const forMobile = (mobile, display) => ({
2085
+ initial: mobile ? display : "none",
2086
+ sm: mobile ? "none" : display
2087
+ });
2073
2088
  const FieldActions = memo(function FieldActions2(props) {
2074
- const { remove: remove2, editProps, insertAfterProps, duplicateProps, type } = props;
2089
+ const { remove: remove2, dragHandleProps, editProps, insertAfterProps, duplicateProps } = props;
2090
+ const [isEditDialogOpen, setIsEditDialogOpen] = useState(false);
2091
+ const [isDuplicateDialogOpen, setIsDuplicateDialogOpen] = useState(false);
2092
+ const [isAddDialogOpen, setIsAddDialogOpen] = useState(false);
2075
2093
  const actions = useMemo(
2076
2094
  () => [
2077
2095
  {
2078
- Wrapper: FieldBuilder,
2079
- wrapperProps: editProps,
2096
+ SelectedContent: FieldBuilder,
2097
+ selectedContentProps: { ...editProps, isOpen: isEditDialogOpen, setIsOpen: setIsEditDialogOpen },
2080
2098
  Icon: Pencil1Icon,
2081
- text: "Edit"
2099
+ text: "Edit",
2100
+ buttonProps: { onClick: () => setIsEditDialogOpen(true) }
2082
2101
  },
2083
2102
  {
2084
2103
  Icon: TrashIcon,
@@ -2088,41 +2107,75 @@ const FieldActions = memo(function FieldActions2(props) {
2088
2107
  text: "Delete"
2089
2108
  },
2090
2109
  {
2091
- Wrapper: FieldBuilder,
2092
- wrapperProps: duplicateProps,
2110
+ SelectedContent: FieldBuilder,
2111
+ selectedContentProps: {
2112
+ ...duplicateProps,
2113
+ isOpen: isDuplicateDialogOpen,
2114
+ setIsOpen: setIsDuplicateDialogOpen
2115
+ },
2093
2116
  Icon: CopyIcon,
2094
- text: "Duplicate"
2117
+ text: "Duplicate",
2118
+ buttonProps: { onClick: () => setIsDuplicateDialogOpen(true) }
2095
2119
  },
2096
2120
  {
2097
- Wrapper: FieldBuilder,
2098
- wrapperProps: insertAfterProps,
2121
+ SelectedContent: FieldBuilder,
2122
+ selectedContentProps: { ...insertAfterProps, isOpen: isAddDialogOpen, setIsOpen: setIsAddDialogOpen },
2099
2123
  Icon: PlusIcon,
2100
- text: `Add ${type === "section" ? "section" : "field"}`
2124
+ text: "Add after",
2125
+ buttonProps: { onClick: () => setIsAddDialogOpen(true) }
2126
+ },
2127
+ {
2128
+ // Wrapping icon in a div so that the asChild turns the button into a div
2129
+ // so that the drag handle props are not applied to the icon
2130
+ // Note: b/c the <button> does not handle the space-press event correctly
2131
+ Icon: (props2) => /* @__PURE__ */ jsx("div", { ...props2, children: /* @__PURE__ */ jsx(DragHandleDots2Icon, {}) }),
2132
+ text: "Reorder",
2133
+ disableOnMobile: true,
2134
+ buttonProps: { ...dragHandleProps, asChild: true }
2101
2135
  }
2102
2136
  ],
2103
- [duplicateProps, editProps, insertAfterProps, remove2, type]
2137
+ [
2138
+ dragHandleProps,
2139
+ duplicateProps,
2140
+ editProps,
2141
+ insertAfterProps,
2142
+ isAddDialogOpen,
2143
+ isDuplicateDialogOpen,
2144
+ isEditDialogOpen,
2145
+ remove2
2146
+ ]
2104
2147
  );
2105
- const actionDropdownMenuItems = useMemo(() => {
2106
- return actions.map((Action) => {
2107
- var _a;
2108
- const Wrapper = Action.Wrapper ?? DefaultWrapper;
2109
- return {
2110
- ...Action.buttonProps,
2111
- onSelect: (_a = Action.buttonProps) == null ? void 0 : _a.onClick,
2112
- content: /* @__PURE__ */ jsx(Wrapper, { ...Action.wrapperProps, children: /* @__PURE__ */ jsxs(Flex, { gap: "2", align: "center", role: "button", children: [
2113
- /* @__PURE__ */ jsx(Action.Icon, {}),
2114
- Action.text
2115
- ] }) })
2116
- };
2117
- });
2118
- }, [actions]);
2119
- return /* @__PURE__ */ jsx(Box, { display: "block", children: /* @__PURE__ */ jsx(
2120
- DropdownItemMenu,
2121
- {
2122
- trigger: /* @__PURE__ */ jsx(IconButton, { variant: "ghost", "aria-label": "Actions menu", children: /* @__PURE__ */ jsx(DotsVerticalIcon, {}) }),
2123
- items: actionDropdownMenuItems
2124
- }
2125
- ) });
2148
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
2149
+ /* @__PURE__ */ jsx(Flex, { gap: "4", display: forMobile(false, "flex"), children: actions.map((Action) => {
2150
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [
2151
+ /* @__PURE__ */ jsx(IconButton, { type: "button", variant: "ghost", "aria-label": Action.text, ...Action.buttonProps, children: /* @__PURE__ */ jsx(Action.Icon, {}) }),
2152
+ Action.SelectedContent && /* @__PURE__ */ jsx(Action.SelectedContent, { ...Action.selectedContentProps })
2153
+ ] }, Action.text);
2154
+ }) }),
2155
+ /* @__PURE__ */ jsx(Box, { display: forMobile(true, "block"), children: /* @__PURE__ */ jsx(
2156
+ DropdownItemMenu,
2157
+ {
2158
+ trigger: /* @__PURE__ */ jsx(IconButton, { variant: "ghost", "aria-label": "Actions menu", children: /* @__PURE__ */ jsx(DotsVerticalIcon, {}) }),
2159
+ closeOnSelect: false,
2160
+ items: actions.map((Action) => {
2161
+ var _a;
2162
+ if (Action.disableOnMobile)
2163
+ return null;
2164
+ return {
2165
+ ...Action.buttonProps,
2166
+ onSelect: (_a = Action.buttonProps) == null ? void 0 : _a.onClick,
2167
+ content: /* @__PURE__ */ jsxs(Fragment$1, { children: [
2168
+ /* @__PURE__ */ jsxs(Flex, { gap: "2", align: "center", children: [
2169
+ /* @__PURE__ */ jsx(Action.Icon, {}),
2170
+ Action.text
2171
+ ] }),
2172
+ Action.SelectedContent && /* @__PURE__ */ jsx(Action.SelectedContent, { ...Action.selectedContentProps })
2173
+ ] }, Action.text)
2174
+ };
2175
+ }).filter((x) => x !== null)
2176
+ }
2177
+ ) })
2178
+ ] });
2126
2179
  });
2127
2180
  const formId = "form-builder";
2128
2181
  const FieldWithActions = memo(function FieldWithActions2(props) {
@@ -2132,10 +2185,8 @@ const FieldWithActions = memo(function FieldWithActions2(props) {
2132
2185
  const updateXarrow = useXarrow();
2133
2186
  const duplicateField = useCallback(
2134
2187
  (field2) => {
2135
- if (field2.label === null) {
2136
- throw new Error(`Expected a label for field ${field2.identifier}`);
2137
- }
2138
- return { ...field2, label: incrementFieldLabel(field2.label, takenLabels), identifier: "" };
2188
+ const fieldLabel = field2.label ?? "Untitled field";
2189
+ return { ...field2, label: incrementFieldLabel(fieldLabel, takenLabels), identifier: "" };
2139
2190
  },
2140
2191
  [takenLabels]
2141
2192
  );
@@ -2182,7 +2233,8 @@ const FieldWithActions = memo(function FieldWithActions2(props) {
2182
2233
  remove: remove2,
2183
2234
  editProps: editFieldProps,
2184
2235
  duplicateProps: duplicateFieldProps,
2185
- insertAfterProps
2236
+ insertAfterProps,
2237
+ dragHandleProps: draggableProvided.dragHandleProps
2186
2238
  }
2187
2239
  )
2188
2240
  ] })
@@ -2249,12 +2301,12 @@ const ConditionalArrow = (props) => {
2249
2301
  /* @__PURE__ */ jsx(
2250
2302
  Xarrow,
2251
2303
  {
2252
- start: `${identifier}-floating-point`,
2253
- end: `${condition.identifier}-card`,
2304
+ start: `${condition.identifier}-card`,
2305
+ end: `${identifier}-floating-point`,
2254
2306
  startAnchor: "left",
2255
2307
  endAnchor: "right",
2256
2308
  path: "grid",
2257
- gridBreak: "0",
2309
+ gridBreak: "100%",
2258
2310
  ...settings
2259
2311
  }
2260
2312
  )
@@ -2264,6 +2316,7 @@ const FieldSectionWithActions = memo(function FieldSectionWithActions2(props) {
2264
2316
  var _a, _b, _c, _d, _e, _f, _g, _h, _i;
2265
2317
  const { field, index: sectionIndex, dropState, conditionalFieldCounts } = props;
2266
2318
  const isDropDisabled = (_a = dropState[field.identifier]) == null ? void 0 : _a.disabled;
2319
+ const [isAddFieldDialogOpen, setIsAddFieldDialogOpen] = useState(false);
2267
2320
  const { setFieldValue, values } = useFormikContext();
2268
2321
  const alertDialog = useAlertDialog();
2269
2322
  const updateXarrow = useXarrow();
@@ -2353,12 +2406,10 @@ const FieldSectionWithActions = memo(function FieldSectionWithActions2(props) {
2353
2406
  ]);
2354
2407
  const duplicateSection = useCallback(
2355
2408
  (field2) => {
2356
- if (field2.label === null) {
2357
- throw new Error(`Expected a label for field ${field2.identifier}`);
2358
- }
2359
- const newSectionLabel = incrementFieldLabel(field2.label, takenFieldLabels);
2409
+ const fieldLabel = field2.label ?? "Untitled section";
2410
+ const newSectionLabel = incrementFieldLabel(fieldLabel, takenFieldLabels);
2360
2411
  const newFields = field2.fields.map((f) => {
2361
- const newLabel = incrementFieldLabel(f.label, takenFieldLabels);
2412
+ const newLabel = incrementFieldLabel(f.label ?? "Untitled field", takenFieldLabels);
2362
2413
  return {
2363
2414
  ...f,
2364
2415
  label: newLabel,
@@ -2475,10 +2526,26 @@ const FieldSectionWithActions = memo(function FieldSectionWithActions2(props) {
2475
2526
  child.identifier
2476
2527
  )),
2477
2528
  droppableProvided.placeholder,
2478
- /* @__PURE__ */ jsx(FieldBuilder, { ...insertFieldAtEndOfSection, children: /* @__PURE__ */ jsxs(Button, { type: "button", variant: "outline", children: [
2479
- /* @__PURE__ */ jsx(PlusIcon, {}),
2480
- " Add a field"
2481
- ] }) })
2529
+ /* @__PURE__ */ jsxs(
2530
+ Button,
2531
+ {
2532
+ type: "button",
2533
+ variant: "outline",
2534
+ onClick: () => setIsAddFieldDialogOpen(true),
2535
+ children: [
2536
+ /* @__PURE__ */ jsx(PlusIcon, {}),
2537
+ " Add a field"
2538
+ ]
2539
+ }
2540
+ ),
2541
+ /* @__PURE__ */ jsx(
2542
+ FieldBuilder,
2543
+ {
2544
+ ...insertFieldAtEndOfSection,
2545
+ isOpen: isAddFieldDialogOpen,
2546
+ setIsOpen: setIsAddFieldDialogOpen
2547
+ }
2548
+ )
2482
2549
  ]
2483
2550
  }
2484
2551
  )
@@ -2492,7 +2559,7 @@ const FieldSectionWithActions = memo(function FieldSectionWithActions2(props) {
2492
2559
  insertAfterProps: insertSectionProps,
2493
2560
  editProps: editSectionProps,
2494
2561
  duplicateProps: duplicateSectionProps,
2495
- type: "section"
2562
+ dragHandleProps: draggableProvided.dragHandleProps
2496
2563
  }
2497
2564
  )
2498
2565
  ] })
@@ -2575,6 +2642,7 @@ const findSection = (fields, sectionId) => {
2575
2642
  const FieldsEditor = memo(function FieldsEditor2() {
2576
2643
  const { values, setFieldValue } = useFormikContext();
2577
2644
  const [dropState, dispatch] = useReducer(reducer, values.fields, initializer);
2645
+ const [isAddSectionDialogOpen, setIsAddSectionDialogOpen] = useState(false);
2578
2646
  const { showInfo } = useToast();
2579
2647
  useEffect(() => {
2580
2648
  dispatch({ type: "update", state: initializer(values.fields) });
@@ -2671,10 +2739,18 @@ const FieldsEditor = memo(function FieldsEditor2() {
2671
2739
  )),
2672
2740
  droppableProvided.placeholder
2673
2741
  ] }),
2674
- /* @__PURE__ */ jsx(FieldBuilder, { ...makeFieldSectionProps, children: /* @__PURE__ */ jsxs(Button, { type: "button", variant: "outline", children: [
2742
+ /* @__PURE__ */ jsxs(Button, { type: "button", variant: "outline", onClick: () => setIsAddSectionDialogOpen(true), children: [
2675
2743
  /* @__PURE__ */ jsx(PlusIcon, {}),
2676
2744
  " Add a section"
2677
- ] }) })
2745
+ ] }),
2746
+ /* @__PURE__ */ jsx(
2747
+ FieldBuilder,
2748
+ {
2749
+ ...makeFieldSectionProps,
2750
+ isOpen: isAddSectionDialogOpen,
2751
+ setIsOpen: setIsAddSectionDialogOpen
2752
+ }
2753
+ )
2678
2754
  ]
2679
2755
  }
2680
2756
  ) }) });
@@ -2706,8 +2782,7 @@ const previewSubmit = () => {
2706
2782
  const FormBuilder = memo(
2707
2783
  forwardRef((props, ref) => {
2708
2784
  const { onCancel, onSave, revision } = props;
2709
- const defaultHeading = revision ? "Edit form" : "Create a new form";
2710
- const { heading = defaultHeading } = props;
2785
+ const { heading = revision ? "Edit form" : "Create a new form" } = props;
2711
2786
  const validate = useCallback((form) => {
2712
2787
  const errors = {};
2713
2788
  if (!form.title) {