@overmap-ai/forms 1.0.6 → 1.0.8

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.
@@ -8,6 +8,7 @@ interface FieldOptionsFormProps {
8
8
  /** fields that can be used as a condition */
9
9
  conditionalSourceFields?: ISerializedField[];
10
10
  handleCancel: () => void;
11
+ handleDirtyChange: (dirty: boolean) => void;
11
12
  handleCreateField: (form: Form) => void;
12
13
  }
13
14
  interface FieldBuilderProps extends Pick<FieldOptionsFormProps, "conditionalSourceFields"> {
package/dist/forms.js CHANGED
@@ -5,7 +5,7 @@ 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, Dialog, Pencil1Icon, TrashIcon, CopyIcon, DragHandleDots2Icon, DropdownMenu, DotsVerticalIcon, useAlertDialog, Em, Strong, useToast, Tabs } from "@overmap-ai/blocks";
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, DragHandleDots2Icon, DropdownMenu, DotsVerticalIcon, useAlertDialog, Em, Strong, useToast, Tabs } from "@overmap-ai/blocks";
9
9
  import { useField, useFormikContext, useFormik, FormikProvider } from "formik";
10
10
  import React, { useMemo, memo, useCallback, useState, useEffect, useRef, forwardRef, useReducer } from "react";
11
11
  import { DragDropContext, Droppable, Draggable } from "@hello-pangea/dnd";
@@ -1503,6 +1503,7 @@ const FormRenderer = memo(
1503
1503
  cancelText,
1504
1504
  onCancel,
1505
1505
  onDirty,
1506
+ onDirtyChange,
1506
1507
  // if the title isn't provided, hide it by default
1507
1508
  hideTitle = !schema.title,
1508
1509
  hideDescription,
@@ -1531,7 +1532,9 @@ const FormRenderer = memo(
1531
1532
  useEffect(() => {
1532
1533
  if (dirty && onDirty)
1533
1534
  onDirty();
1534
- }, [dirty, onDirty]);
1535
+ if (onDirtyChange)
1536
+ onDirtyChange(dirty);
1537
+ }, [dirty, onDirty, onDirtyChange]);
1535
1538
  return /* @__PURE__ */ jsx(FormikProvider, { value: formik, children: /* @__PURE__ */ jsx(Flex, { ref, direction: "column", gap: "2", className, asChild: true, children: /* @__PURE__ */ jsxs("form", { id: formId2, onSubmit: formik.handleSubmit, children: [
1536
1539
  !hideTitle && /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: "1", children: [
1537
1540
  Title,
@@ -1943,7 +1946,7 @@ const commonFields = (takenLabels, type) => {
1943
1946
  ];
1944
1947
  };
1945
1948
  const FieldOptionsForm = memo(function FieldOptionsForm2(props) {
1946
- const { fieldType, handleCancel, handleCreateField, defaultField, conditionalSourceFields } = props;
1949
+ const { fieldType, handleCancel, handleCreateField, handleDirtyChange, defaultField, conditionalSourceFields } = props;
1947
1950
  const fieldCls = CompleteFieldTypeToClsMapping[fieldType];
1948
1951
  const formik = useFormikContext();
1949
1952
  const schema = useMemo(() => {
@@ -1974,26 +1977,44 @@ const FieldOptionsForm = memo(function FieldOptionsForm2(props) {
1974
1977
  values: defaultField,
1975
1978
  onSubmit: handleCreateField,
1976
1979
  cancelText: defaultField ? void 0 : "Back",
1977
- onCancel: handleCancel
1980
+ onCancel: handleCancel,
1981
+ onDirtyChange: handleDirtyChange
1978
1982
  }
1979
1983
  );
1980
1984
  });
1981
1985
  const FieldBuilder = memo(function FieldBuilder2(props) {
1982
1986
  const { parentPath, index, children, initial, editing, conditionalSourceFields } = props;
1983
1987
  const [fieldType, setFieldType] = useState();
1988
+ const [formIsDirty, setFormIsDirty] = useState(false);
1984
1989
  const type = (initial == null ? void 0 : initial.type) ?? fieldType;
1985
1990
  const typeName = type ? CompleteFieldTypeToClsMapping[type].fieldTypeName : void 0;
1986
1991
  const { setFieldValue, values } = useFormikContext();
1987
1992
  if (editing && !initial)
1988
1993
  throw new Error("Initial field must be provided if editing is true.");
1994
+ const openConfirmDiscardChangesDialog = useDiscardAlertDialog();
1989
1995
  const showChooseField = !type && !editing && !initial;
1990
1996
  const title2 = showChooseField ? "Choose a field type" : `${typeName} settings`;
1991
1997
  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.`;
1992
- const handleCancel = useCallback(() => setFieldType(void 0), []);
1993
- const handleCloseInterrupt = useCallback((confirmClose) => {
1998
+ const handleCancel = useCallback(() => {
1994
1999
  setFieldType(void 0);
1995
- confirmClose();
2000
+ setFormIsDirty(false);
1996
2001
  }, []);
2002
+ const handleCloseInterrupt = useCallback(
2003
+ (confirmClose) => {
2004
+ if (formIsDirty) {
2005
+ openConfirmDiscardChangesDialog({
2006
+ onDiscard: () => {
2007
+ setFieldType(void 0);
2008
+ confirmClose();
2009
+ }
2010
+ });
2011
+ } else {
2012
+ setFieldType(void 0);
2013
+ confirmClose();
2014
+ }
2015
+ },
2016
+ [formIsDirty, openConfirmDiscardChangesDialog]
2017
+ );
1997
2018
  const handleCreateField = useCallback(
1998
2019
  (form, closeDialog) => {
1999
2020
  const { label } = form;
@@ -2019,10 +2040,11 @@ const FieldBuilder = memo(function FieldBuilder2(props) {
2019
2040
  newFields = insert(parent, index, field);
2020
2041
  }
2021
2042
  setFieldValue(parentPath, newFields).then();
2022
- closeDialog();
2043
+ closeDialog({ force: true });
2023
2044
  },
2024
2045
  [editing, type, values, parentPath, setFieldValue, index]
2025
2046
  );
2047
+ const handleDirtyChange = useCallback((dirty) => setFormIsDirty(dirty), []);
2026
2048
  const dialogContent = useCallback(
2027
2049
  (closeDialog) => {
2028
2050
  if (showChooseField) {
@@ -2035,11 +2057,12 @@ const FieldBuilder = memo(function FieldBuilder2(props) {
2035
2057
  handleCancel,
2036
2058
  handleCreateField: (form) => handleCreateField(form, closeDialog),
2037
2059
  fieldType: type,
2038
- defaultField: initial
2060
+ defaultField: initial,
2061
+ handleDirtyChange
2039
2062
  }
2040
2063
  );
2041
2064
  },
2042
- [conditionalSourceFields, handleCancel, handleCreateField, initial, showChooseField, type]
2065
+ [conditionalSourceFields, handleCancel, handleCreateField, handleDirtyChange, initial, showChooseField, type]
2043
2066
  );
2044
2067
  return /* @__PURE__ */ jsx(Dialog, { onCloseInterrupt: handleCloseInterrupt, title: title2, description: description2, content: dialogContent, children });
2045
2068
  });
@@ -2180,7 +2203,7 @@ const FieldWithActions = memo(function FieldWithActions2(props) {
2180
2203
  ) });
2181
2204
  });
2182
2205
  const FieldSectionWithActions = memo(function FieldSectionWithActions2(props) {
2183
- var _a, _b, _c, _d, _e, _f, _g;
2206
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
2184
2207
  const { field, index: sectionIndex, dropState } = props;
2185
2208
  const isDropDisabled = (_a = dropState[field.identifier]) == null ? void 0 : _a.disabled;
2186
2209
  const { setFieldValue, values } = useFormikContext();
@@ -2333,7 +2356,7 @@ const FieldSectionWithActions = memo(function FieldSectionWithActions2(props) {
2333
2356
  const conditionComparison = Array.isArray((_c = field.condition) == null ? void 0 : _c.value) ? "contains all of" : "equals";
2334
2357
  if (valueIsFile((_d = field.condition) == null ? void 0 : _d.value))
2335
2358
  throw new Error("File values are not supported for conditions.");
2336
- const conditionValue = Array.isArray((_e = field.condition) == null ? void 0 : _e.value) ? (_f = field.condition) == null ? void 0 : _f.value.map((v) => typeof v === "string" ? v : v.label).join(", ") : (_g = field.condition) == null ? void 0 : _g.value.toString();
2359
+ const conditionValue = Array.isArray((_e = field.condition) == null ? void 0 : _e.value) ? (_g = (_f = field.condition) == null ? void 0 : _f.value) == null ? void 0 : _g.map((v) => typeof v === "string" ? v : v.label).join(", ") : (_i = (_h = field.condition) == null ? void 0 : _h.value) == null ? void 0 : _i.toString();
2337
2360
  return /* @__PURE__ */ jsx(Draggable, { draggableId: field.identifier, index: sectionIndex, children: (draggableProvided) => /* @__PURE__ */ jsx(
2338
2361
  Card,
2339
2362
  {