@firecms/core 3.0.0-beta.2-pre.2 → 3.0.0-beta.2-pre.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/README.md +2 -3
  2. package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +0 -3
  3. package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +1 -8
  4. package/dist/components/EntityCollectionTable/index.d.ts +2 -4
  5. package/dist/components/EntityCollectionTable/internal/popup_field/PopupFormField.d.ts +1 -1
  6. package/dist/components/{ReferenceSelectionInner.d.ts → ReferenceTable/ReferenceSelectionTable.d.ts} +2 -2
  7. package/dist/components/{EntityCollectionTable/SimpleTable.d.ts → SelectableTable/SelectableTable.d.ts} +3 -4
  8. package/dist/components/SelectableTable/SelectableTableContext.d.ts +4 -0
  9. package/dist/components/common/index.d.ts +5 -0
  10. package/dist/components/{EntityCollectionTable → common}/types.d.ts +7 -0
  11. package/dist/components/common/useTableSearchHelper.d.ts +12 -0
  12. package/dist/components/index.d.ts +5 -2
  13. package/dist/form/EntityForm.d.ts +2 -0
  14. package/dist/form/components/index.d.ts +0 -1
  15. package/dist/hooks/index.d.ts +1 -0
  16. package/dist/{internal → hooks}/useBuildNavigationController.d.ts +11 -4
  17. package/dist/hooks/useSnackbarController.d.ts +1 -0
  18. package/dist/index.es.js +4081 -4049
  19. package/dist/index.es.js.map +1 -1
  20. package/dist/index.umd.js +5 -5
  21. package/dist/index.umd.js.map +1 -1
  22. package/dist/types/fields.d.ts +0 -1
  23. package/dist/types/firecms.d.ts +6 -25
  24. package/dist/types/navigation.d.ts +2 -2
  25. package/dist/types/plugins.d.ts +0 -8
  26. package/package.json +4 -4
  27. package/src/components/ArrayContainer.tsx +11 -4
  28. package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +16 -22
  29. package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +1 -5
  30. package/src/components/EntityCollectionTable/PropertyTableCell.tsx +2 -2
  31. package/src/components/EntityCollectionTable/column_utils.tsx +1 -1
  32. package/src/components/EntityCollectionTable/index.tsx +1 -9
  33. package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +133 -136
  34. package/src/components/EntityCollectionView/EntityCollectionView.tsx +20 -41
  35. package/src/components/{ReferenceSelectionInner.tsx → ReferenceTable/ReferenceSelectionTable.tsx} +31 -15
  36. package/src/components/{EntityCollectionTable/SimpleTable.tsx → SelectableTable/SelectableTable.tsx} +8 -9
  37. package/src/components/SelectableTable/SelectableTableContext.tsx +6 -0
  38. package/src/components/{EntityCollectionTable → SelectableTable}/filters/StringNumberFilterField.tsx +0 -1
  39. package/src/components/VirtualTable/VirtualTableHeader.tsx +10 -4
  40. package/src/components/common/index.ts +5 -0
  41. package/src/components/{EntityCollectionTable → common}/types.tsx +5 -0
  42. package/src/components/common/useTableSearchHelper.ts +47 -0
  43. package/src/components/index.tsx +6 -2
  44. package/src/core/EntitySidePanel.tsx +7 -5
  45. package/src/core/FireCMS.tsx +1 -16
  46. package/src/form/EntityForm.tsx +237 -196
  47. package/src/form/PropertyFieldBinding.tsx +7 -7
  48. package/src/form/components/CustomIdField.tsx +35 -34
  49. package/src/form/components/ErrorFocus.tsx +3 -3
  50. package/src/form/components/index.tsx +0 -1
  51. package/src/form/field_bindings/BlockFieldBinding.tsx +13 -12
  52. package/src/hooks/index.tsx +1 -0
  53. package/src/{internal → hooks}/useBuildNavigationController.tsx +23 -17
  54. package/src/hooks/useFireCMSContext.tsx +51 -3
  55. package/src/hooks/useReferenceDialog.tsx +2 -2
  56. package/src/hooks/useSnackbarController.tsx +1 -2
  57. package/src/internal/useBuildSideEntityController.tsx +1 -1
  58. package/src/types/fields.tsx +1 -1
  59. package/src/types/firecms.tsx +7 -28
  60. package/src/types/navigation.ts +2 -2
  61. package/src/types/plugins.tsx +0 -9
  62. package/src/util/resolutions.ts +1 -1
  63. package/dist/form/components/SubmitListener.d.ts +0 -1
  64. package/dist/form/components/SwitchControl.d.ts +0 -8
  65. package/src/form/components/SubmitListener.tsx +0 -32
  66. package/src/form/components/SwitchControl.tsx +0 -39
  67. /package/dist/components/{EntityCollectionTable → SelectableTable}/filters/BooleanFilterField.d.ts +0 -0
  68. /package/dist/components/{EntityCollectionTable → SelectableTable}/filters/DateTimeFilterField.d.ts +0 -0
  69. /package/dist/components/{EntityCollectionTable → SelectableTable}/filters/ReferenceFilterField.d.ts +0 -0
  70. /package/dist/components/{EntityCollectionTable → SelectableTable}/filters/StringNumberFilterField.d.ts +0 -0
  71. /package/dist/components/{EntityCollectionView → common}/useColumnsIds.d.ts +0 -0
  72. /package/dist/components/{EntityCollectionTable → common}/useDataSourceEntityCollectionTableController.d.ts +0 -0
  73. /package/dist/components/{EntityCollectionTable → common}/useDebouncedData.d.ts +0 -0
  74. /package/src/components/{EntityCollectionTable → SelectableTable}/filters/BooleanFilterField.tsx +0 -0
  75. /package/src/components/{EntityCollectionTable → SelectableTable}/filters/DateTimeFilterField.tsx +0 -0
  76. /package/src/components/{EntityCollectionTable → SelectableTable}/filters/ReferenceFilterField.tsx +0 -0
  77. /package/src/components/{EntityCollectionView → common}/useColumnsIds.tsx +0 -0
  78. /package/src/components/{EntityCollectionTable → common}/useDataSourceEntityCollectionTableController.tsx +0 -0
  79. /package/src/components/{EntityCollectionTable → common}/useDebouncedData.ts +0 -0
@@ -12,7 +12,7 @@ import {
12
12
  PropertyFieldBindingProps,
13
13
  ResolvedEntityCollection
14
14
  } from "../types";
15
- import { Form, Formik, FormikHelpers, FormikProps } from "formik";
15
+ import { Formex, FormexController, getIn, setIn, useCreateFormex } from "@firecms/formex";
16
16
  import { PropertyFieldBinding } from "./PropertyFieldBinding";
17
17
  import { CustomFieldValidator, getYupEntitySchema } from "./validation";
18
18
  import equal from "react-fast-compare"
@@ -41,6 +41,7 @@ import {
41
41
  deleteEntityAction
42
42
  } from "../components/EntityCollectionTable/internal/default_entity_actions";
43
43
  import { useAnalyticsController } from "../hooks/useAnalyticsController";
44
+ import { ValidationError } from "yup";
44
45
 
45
46
  /**
46
47
  * @group Components
@@ -139,6 +140,23 @@ export const EntityForm = React.memo<EntityFormProps<any>>(EntityFormInternal,
139
140
  equal(a.entity?.values, b.entity?.values);
140
141
  }) as typeof EntityFormInternal;
141
142
 
143
+ function getDataSourceEntityValues<M extends object>(initialResolvedCollection: ResolvedEntityCollection,
144
+ status: "new" | "existing" | "copy",
145
+ entity: Entity<M> | undefined): Partial<EntityValues<M>> {
146
+ const properties = initialResolvedCollection.properties;
147
+ if ((status === "existing" || status === "copy") && entity) {
148
+ return entity.values ?? getDefaultValuesFor(properties);
149
+ } else if (status === "new") {
150
+ return getDefaultValuesFor(properties);
151
+ } else {
152
+ console.error({
153
+ status,
154
+ entity
155
+ });
156
+ throw new Error("Form has not been initialised with the correct parameters");
157
+ }
158
+ }
159
+
142
160
  function EntityFormInternal<M extends Record<string, any>>({
143
161
  status,
144
162
  path,
@@ -187,20 +205,7 @@ function EntityFormInternal<M extends Record<string, any>>({
187
205
 
188
206
  const closeAfterSaveRef = useRef(false);
189
207
 
190
- const baseDataSourceValues: Partial<EntityValues<M>> = useMemo(() => {
191
- const properties = initialResolvedCollection.properties;
192
- if ((status === "existing" || status === "copy") && entity) {
193
- return entity.values ?? getDefaultValuesFor(properties);
194
- } else if (status === "new") {
195
- return getDefaultValuesFor(properties);
196
- } else {
197
- console.error({
198
- status,
199
- entity
200
- });
201
- throw new Error("Form has not been initialised with the correct parameters");
202
- }
203
- }, [status, initialResolvedCollection, entity]);
208
+ const baseDataSourceValuesRef = useRef<Partial<EntityValues<M>>>(getDataSourceEntityValues(initialResolvedCollection, status, entity));
204
209
 
205
210
  const [entityId, setEntityId] = React.useState<string | undefined>(initialEntityId);
206
211
  const [entityIdError, setEntityIdError] = React.useState<boolean>(false);
@@ -208,11 +213,114 @@ function EntityFormInternal<M extends Record<string, any>>({
208
213
 
209
214
  const [customIdLoading, setCustomIdLoading] = React.useState<boolean>(false);
210
215
 
211
- const initialValuesRef = useRef<EntityValues<M>>(entity?.values ?? baseDataSourceValues as EntityValues<M>);
212
- const initialValues = initialValuesRef.current;
213
- const [internalValues, setInternalValues] = useState<EntityValues<M> | undefined>(initialValues);
216
+ // const initialValuesRef = useRef<EntityValues<M>>(entity?.values ?? baseDataSourceValues as EntityValues<M>);
217
+ const [internalValues, setInternalValues] = useState<EntityValues<M> | undefined>(entity?.values ?? baseDataSourceValuesRef.current as EntityValues<M>);
218
+
219
+ const save = (values: EntityValues<M>): Promise<void> => {
220
+ return onEntitySaveRequested({
221
+ collection: resolvedCollection,
222
+ path,
223
+ entityId,
224
+ values,
225
+ previousValues: entity?.values,
226
+ closeAfterSave: closeAfterSaveRef.current,
227
+ autoSave: autoSave ?? false
228
+ }).then(_ => {
229
+ const eventName: CMSAnalyticsEvent = status === "new"
230
+ ? "new_entity_saved"
231
+ : (status === "copy" ? "entity_copied" : (status === "existing" ? "entity_edited" : "unmapped_event"));
232
+ analyticsController.onAnalyticsEvent?.(eventName, { path });
233
+ }).catch(e => {
234
+ console.error(e);
235
+ setSavingError(e);
236
+ }).finally(() => {
237
+ closeAfterSaveRef.current = false;
238
+ });
239
+ };
240
+
241
+ const onSubmit = (values: EntityValues<M>, formexController: FormexController<EntityValues<M>>) => {
242
+
243
+ if (mustSetCustomId && !entityId) {
244
+ console.error("Missing custom Id");
245
+ setEntityIdError(true);
246
+ formexController.setSubmitting(false);
247
+ return;
248
+ }
249
+
250
+ setSavingError(undefined);
251
+ setEntityIdError(false);
252
+
253
+ if (status === "existing") {
254
+ if (!entity?.id) throw Error("Form misconfiguration when saving, no id for existing entity");
255
+ } else if (status === "new" || status === "copy") {
256
+ if (inputCollection.customId) {
257
+ if (inputCollection.customId !== "optional" && !entityId) {
258
+ throw Error("Form misconfiguration when saving, entityId should be set");
259
+ }
260
+ }
261
+ } else {
262
+ throw Error("New FormType added, check EntityForm");
263
+ }
264
+
265
+ return save(values)
266
+ ?.then(_ => {
267
+ formexController.resetForm({
268
+ values,
269
+ submitCount: 0,
270
+ touched: {}
271
+ });
272
+ })
273
+ .finally(() => {
274
+ formexController.setSubmitting(false);
275
+ });
276
+
277
+ };
278
+
279
+ const formex: FormexController<M> = useCreateFormex<M>({
280
+ initialValues: baseDataSourceValuesRef.current as M,
281
+ onSubmit,
282
+ validation: (values) => {
283
+ return validationSchema?.validate(values, { abortEarly: false })
284
+ .then(() => {
285
+ return {};
286
+ })
287
+ .catch((e) => {
288
+
289
+ const errors: Record<string, string> = {};
290
+ e.inner.forEach((error: any) => {
291
+ errors[error.path] = error.message;
292
+ });
293
+ return yupToFormErrors(e);
294
+ });
295
+ }
296
+ });
297
+
298
+ useEffect(() => {
299
+ baseDataSourceValuesRef.current = getDataSourceEntityValues(initialResolvedCollection, status, entity);
300
+ const initialValues = formex.initialValues;
301
+ if (!formex.isSubmitting && initialValues && status === "existing") {
302
+ setUnderlyingChanges(
303
+ Object.entries(resolvedCollection.properties)
304
+ .map(([key, property]) => {
305
+ if (isHidden(property)) {
306
+ return {};
307
+ }
308
+ const initialValue = initialValues[key];
309
+ const latestValue = baseDataSourceValuesRef.current[key];
310
+ if (!equal(initialValue, latestValue)) {
311
+ return { [key]: latestValue };
312
+ }
313
+ return {};
314
+ })
315
+ .reduce((a, b) => ({ ...a, ...b }), {}) as Partial<EntityValues<M>>
316
+ );
317
+ } else {
318
+ setUnderlyingChanges({});
319
+ }
320
+ }, [entity, initialResolvedCollection, status]);
214
321
 
215
322
  const doOnValuesChanges = (values?: EntityValues<M>) => {
323
+ const initialValues = formex.initialValues;
216
324
  setInternalValues(values);
217
325
  if (onValuesChanged)
218
326
  onValuesChanged(values);
@@ -231,7 +339,7 @@ function EntityFormInternal<M extends Record<string, any>>({
231
339
  path,
232
340
  entityId,
233
341
  values: internalValues,
234
- previousValues: initialValues,
342
+ previousValues: formex.initialValues,
235
343
  fields: customizationController.propertyConfigs
236
344
  });
237
345
 
@@ -261,88 +369,7 @@ function EntityFormInternal<M extends Record<string, any>>({
261
369
  doOnIdUpdate();
262
370
  }, [doOnIdUpdate]);
263
371
 
264
- const underlyingChanges: Partial<EntityValues<M>> = useMemo(() => {
265
- if (initialValues && status === "existing") {
266
- return Object.entries(resolvedCollection.properties)
267
- .map(([key, property]) => {
268
- if (isHidden(property)) {
269
- return {};
270
- }
271
- const initialValue = initialValues[key];
272
- const latestValue = baseDataSourceValues[key];
273
- if (!equal(initialValue, latestValue)) {
274
- return { [key]: latestValue };
275
- }
276
- return {};
277
- })
278
- .reduce((a, b) => ({ ...a, ...b }), {}) as Partial<EntityValues<M>>;
279
- } else {
280
- return {};
281
- }
282
- }, [baseDataSourceValues, resolvedCollection.properties, initialValues, status]);
283
-
284
- const save = (values: EntityValues<M>) => {
285
- return onEntitySaveRequested({
286
- collection: resolvedCollection,
287
- path,
288
- entityId,
289
- values,
290
- previousValues: entity?.values,
291
- closeAfterSave: closeAfterSaveRef.current,
292
- autoSave: autoSave ?? false
293
- }).then(_ => {
294
- const eventName: CMSAnalyticsEvent = status === "new"
295
- ? "new_entity_saved"
296
- : (status === "copy" ? "entity_copied" : (status === "existing" ? "entity_edited" : "unmapped_event"));
297
- analyticsController.onAnalyticsEvent?.(eventName, { path });
298
- initialValuesRef.current = values;
299
- })
300
- .catch(e => {
301
- console.error(e);
302
- setSavingError(e);
303
- })
304
- .finally(() => {
305
- closeAfterSaveRef.current = false;
306
- });
307
- };
308
-
309
- const saveFormValues = (values: EntityValues<M>, formikActions: FormikHelpers<EntityValues<M>>) => {
310
-
311
- if (mustSetCustomId && !entityId) {
312
- console.error("Missing custom Id");
313
- setEntityIdError(true);
314
- formikActions.setSubmitting(false);
315
- return;
316
- }
317
-
318
- setSavingError(undefined);
319
- setEntityIdError(false);
320
-
321
- if (status === "existing") {
322
- if (!entity?.id) throw Error("Form misconfiguration when saving, no id for existing entity");
323
- } else if (status === "new" || status === "copy") {
324
- if (inputCollection.customId) {
325
- if (inputCollection.customId !== "optional" && !entityId) {
326
- throw Error("Form misconfiguration when saving, entityId should be set");
327
- }
328
- }
329
- } else {
330
- throw Error("New FormType added, check EntityForm");
331
- }
332
-
333
- save(values)
334
- ?.then(_ => {
335
- formikActions.resetForm({
336
- values,
337
- submitCount: 0,
338
- touched: {}
339
- });
340
- })
341
- .finally(() => {
342
- formikActions.setSubmitting(false);
343
- });
344
-
345
- };
372
+ const [underlyingChanges, setUnderlyingChanges] = useState<Partial<EntityValues<M>>>({});
346
373
 
347
374
  const uniqueFieldValidator: CustomFieldValidator = useCallback(({
348
375
  name,
@@ -374,110 +401,102 @@ function EntityFormInternal<M extends Record<string, any>>({
374
401
  return actions;
375
402
  }, [authController, inputCollection, path]);
376
403
 
377
- return (
378
- <Formik
379
- initialValues={baseDataSourceValues as M}
380
- onSubmit={saveFormValues}
381
- validationSchema={validationSchema}
382
- validate={(values) => console.debug("Validating", values)}
383
- onReset={() => onDiscard && onDiscard()}
384
- >
385
- {(props) => {
386
-
387
- const pluginActions: React.ReactNode[] = [];
388
-
389
- const formContext: FormContext<M> = {
390
- setFieldValue: props.setFieldValue,
391
- values: props.values,
392
- collection: resolvedCollection,
393
- entityId,
394
- path,
395
- save
396
- };
404
+ const pluginActions: React.ReactNode[] = [];
397
405
 
398
- // eslint-disable-next-line react-hooks/rules-of-hooks
399
- useEffect(() => {
400
- if (onFormContextChange) {
401
- onFormContextChange(formContext);
402
- }
403
- }, [onFormContextChange, formContext]);
404
-
405
- if (plugins && inputCollection) {
406
- const actionProps: PluginFormActionProps = {
407
- entityId,
408
- path,
409
- status,
410
- collection: inputCollection,
411
- context,
412
- currentEntityId: entityId,
413
- formContext
414
- };
415
- pluginActions.push(...plugins.map((plugin, i) => (
416
- plugin.form?.Actions
417
- ? <plugin.form.Actions
418
- key={`actions_${plugin.name}`} {...actionProps}/>
419
- : null
420
- )).filter(Boolean));
421
- }
406
+ const formContext: FormContext<M> = {
407
+ setFieldValue: formex.setFieldValue,
408
+ values: formex.values,
409
+ collection: resolvedCollection,
410
+ entityId,
411
+ path,
412
+ save
413
+ };
422
414
 
423
- return <div className="h-full overflow-auto">
415
+ // eslint-disable-next-line react-hooks/rules-of-hooks
416
+ useEffect(() => {
417
+ if (onFormContextChange) {
418
+ onFormContextChange(formContext);
419
+ }
420
+ }, [onFormContextChange, formContext]);
424
421
 
425
- {pluginActions.length > 0 && <div
426
- className={cn("w-full flex justify-end items-center sticky top-0 right-0 left-0 z-10 bg-opacity-60 bg-slate-200 dark:bg-opacity-60 dark:bg-slate-800 backdrop-blur-md")}>
427
- {pluginActions}
428
- </div>}
422
+ if (plugins && inputCollection) {
423
+ const actionProps: PluginFormActionProps = {
424
+ entityId,
425
+ path,
426
+ status,
427
+ collection: inputCollection,
428
+ context,
429
+ currentEntityId: entityId,
430
+ formContext
431
+ };
432
+ pluginActions.push(...plugins.map((plugin, i) => (
433
+ plugin.form?.Actions
434
+ ? <plugin.form.Actions
435
+ key={`actions_${plugin.name}`} {...actionProps}/>
436
+ : null
437
+ )).filter(Boolean));
438
+ }
439
+
440
+ return <Formex value={formex}>
441
+ <div className="h-full overflow-auto">
442
+
443
+ {pluginActions.length > 0 && <div
444
+ className={cn("w-full flex justify-end items-center sticky top-0 right-0 left-0 z-10 bg-opacity-60 bg-slate-200 dark:bg-opacity-60 dark:bg-slate-800 backdrop-blur-md")}>
445
+ {pluginActions}
446
+ </div>}
447
+
448
+ <div className="pt-12 pb-16 pl-8 pr-8 md:pl-10 md:pr-10">
449
+ <div
450
+ className={`w-full py-2 flex flex-col items-start mt-${4 + (pluginActions ? 8 : 0)} lg:mt-${8 + (pluginActions ? 8 : 0)} mb-8`}>
451
+
452
+ <Typography
453
+ className={"mt-4 flex-grow " + inputCollection.hideIdFromForm ? "mb-2" : "mb-0"}
454
+ variant={"h4"}>{inputCollection.singularName ?? inputCollection.name}
455
+ </Typography>
456
+ <Alert color={"base"} className={"w-full"} size={"small"}>
457
+ <code className={"text-xs select-all"}>{path}/{entityId}</code>
458
+ </Alert>
459
+ </div>
429
460
 
430
- <div className="pl-4 pr-4 pt-12 pb-16 md:pl-8">
431
- <div
432
- className={`w-full py-2 flex flex-col items-start mt-${4 + (pluginActions ? 8 : 0)} lg:mt-${8 + (pluginActions ? 8 : 0)} mb-8`}>
433
-
434
- <Typography
435
- className={"mt-4 flex-grow " + inputCollection.hideIdFromForm ? "mb-2" : "mb-0"}
436
- variant={"h4"}>{inputCollection.singularName ?? inputCollection.name}
437
- </Typography>
438
- <Alert color={"base"} className={"w-full"} size={"small"}>
439
- <code className={"text-xs select-all"}>{path}/{entityId}</code>
440
- </Alert>
441
- </div>
461
+ {!hideId &&
462
+ <CustomIdField customId={inputCollection.customId}
463
+ entityId={entityId}
464
+ status={status}
465
+ onChange={setEntityId}
466
+ error={entityIdError}
467
+ loading={customIdLoading}
468
+ entity={entity}/>}
469
+
470
+ {entityId && <InnerForm
471
+ {...formex}
472
+ initialValues={formex.initialValues}
473
+ onModified={onModified}
474
+ onDiscard={onDiscard}
475
+ onValuesChanged={doOnValuesChanges}
476
+ underlyingChanges={underlyingChanges}
477
+ entity={entity}
478
+ resolvedCollection={resolvedCollection}
479
+ formContext={formContext}
480
+ status={status}
481
+ savingError={savingError}
482
+ closeAfterSaveRef={closeAfterSaveRef}
483
+ autoSave={autoSave}
484
+ entityActions={getActionsForEntity({ entity, customEntityActions: inputCollection.entityActions })}/>}
442
485
 
443
- {!hideId &&
444
- <CustomIdField customId={inputCollection.customId}
445
- entityId={entityId}
446
- status={status}
447
- onChange={setEntityId}
448
- error={entityIdError}
449
- loading={customIdLoading}
450
- entity={entity}/>}
451
-
452
- {entityId && <InnerForm
453
- {...props}
454
- initialValues={initialValues}
455
- onModified={onModified}
456
- onValuesChanged={doOnValuesChanges}
457
- underlyingChanges={underlyingChanges}
458
- entity={entity}
459
- resolvedCollection={resolvedCollection}
460
- formContext={formContext}
461
- status={status}
462
- savingError={savingError}
463
- closeAfterSaveRef={closeAfterSaveRef}
464
- autoSave={autoSave}
465
- entityActions={getActionsForEntity({ entity, customEntityActions: inputCollection.entityActions })}/>}
466
-
467
- </div>
468
- </div>
469
- }}
470
- </Formik>
471
- );
486
+ </div>
487
+ </div>
488
+ </Formex>
472
489
  }
473
490
 
474
- function InnerForm<M extends Record<string, any>>(props: FormikProps<M> & {
491
+ function InnerForm<M extends Record<string, any>>(props: FormexController<M> & {
492
+ initialValues: EntityValues<M>,
475
493
  onModified: ((modified: boolean) => void) | undefined,
476
494
  onValuesChanged?: (changedValues?: EntityValues<M>) => void,
477
495
  underlyingChanges: Partial<M>,
478
496
  entity: Entity<M> | undefined,
479
497
  resolvedCollection: ResolvedEntityCollection<M>,
480
498
  formContext: FormContext<M>,
499
+ onDiscard?: () => void,
481
500
  status: "new" | "existing" | "copy",
482
501
  savingError?: Error,
483
502
  closeAfterSaveRef: MutableRefObject<boolean>,
@@ -487,6 +506,7 @@ function InnerForm<M extends Record<string, any>>(props: FormikProps<M> & {
487
506
 
488
507
  const {
489
508
  values,
509
+ onDiscard,
490
510
  onModified,
491
511
  onValuesChanged,
492
512
  underlyingChanges,
@@ -498,6 +518,7 @@ function InnerForm<M extends Record<string, any>>(props: FormikProps<M> & {
498
518
  isSubmitting,
499
519
  status,
500
520
  handleSubmit,
521
+ resetForm,
501
522
  savingError,
502
523
  dirty,
503
524
  closeAfterSaveRef,
@@ -518,7 +539,7 @@ function InnerForm<M extends Record<string, any>>(props: FormikProps<M> & {
518
539
  }, [modified, values]);
519
540
 
520
541
  useEffect(() => {
521
- if (!autoSave && underlyingChanges && entity) {
542
+ if (!autoSave && !isSubmitting && underlyingChanges && entity) {
522
543
  // we update the form fields from the Firestore data
523
544
  // if they were not touched
524
545
  Object.entries(underlyingChanges).forEach(([key, value]) => {
@@ -529,7 +550,7 @@ function InnerForm<M extends Record<string, any>>(props: FormikProps<M> & {
529
550
  }
530
551
  });
531
552
  }
532
- }, [autoSave, underlyingChanges, entity, values, touched, setFieldValue]);
553
+ }, [isSubmitting, autoSave, underlyingChanges, entity, values, touched, setFieldValue]);
533
554
 
534
555
  const formFields = (
535
556
  <div className={"flex flex-col gap-8"}>
@@ -582,7 +603,12 @@ function InnerForm<M extends Record<string, any>>(props: FormikProps<M> & {
582
603
 
583
604
  return (
584
605
 
585
- <Form onSubmit={handleSubmit}
606
+ <form onSubmit={handleSubmit}
607
+ onReset={() => {
608
+ console.debug("Resetting form")
609
+ resetForm();
610
+ return onDiscard && onDiscard();
611
+ }}
586
612
  noValidate>
587
613
  <div className="mt-12"
588
614
  ref={formRef}>
@@ -662,6 +688,21 @@ function InnerForm<M extends Record<string, any>>(props: FormikProps<M> & {
662
688
  </Button>
663
689
 
664
690
  </DialogActions>}
665
- </Form>
691
+ </form>
666
692
  );
667
693
  }
694
+
695
+ export function yupToFormErrors(yupError: ValidationError): Record<string, any> {
696
+ let errors: Record<string, any> = {};
697
+ if (yupError.inner) {
698
+ if (yupError.inner.length === 0) {
699
+ return setIn(errors, yupError.path!, yupError.message);
700
+ }
701
+ for (const err of yupError.inner) {
702
+ if (!getIn(errors, err.path!)) {
703
+ errors = setIn(errors, err.path!, err.message);
704
+ }
705
+ }
706
+ }
707
+ return errors;
708
+ }
@@ -1,7 +1,7 @@
1
1
  import React, { ComponentType, ReactElement, useCallback, useRef } from "react";
2
2
  import equal from "react-fast-compare"
3
3
 
4
- import { FastField, Field, FieldProps as FormikFieldProps, getIn } from "formik";
4
+ import { Field, FieldProps as FormexFieldProps, getIn } from "@firecms/formex";
5
5
 
6
6
  import {
7
7
  CMSType,
@@ -85,14 +85,14 @@ function PropertyFieldBindingInternal<T extends CMSType = CMSType, CustomProps =
85
85
 
86
86
  const shouldAlwaysRerender = shouldPropertyReRender(property, customizationController.plugins);
87
87
  // we use the standard Field for user defined fields, since it rebuilds
88
- // when there are changes in other values, in contrast to FastField
89
- const FieldComponent = shouldAlwaysRerender ? Field : FastField;
88
+ // when there are changes in other values, in contrast to Field
89
+ const FieldComponent = Field;
90
90
 
91
91
  return (
92
92
  <FieldComponent
93
93
  name={propertyKey}
94
94
  >
95
- {(fieldProps: FormikFieldProps<T>) => {
95
+ {(fieldProps) => {
96
96
 
97
97
  let Component: ComponentType<FieldProps<T>> | undefined;
98
98
  const resolvedProperty: ResolvedProperty<T> | null = resolveProperty({
@@ -186,14 +186,14 @@ function FieldInternal<T extends CMSType, CustomProps, M extends Record<string,
186
186
  {
187
187
  Component: ComponentType<FieldProps<T, any, M>>,
188
188
  componentProps: ResolvedPropertyFieldBindingProps<T, M>,
189
- fieldProps: FormikFieldProps<T>
189
+ fieldProps: FormexFieldProps<T, any>
190
190
  }) {
191
191
 
192
192
  const { plugins } = useCustomizationController();
193
193
 
194
194
  const customFieldProps: any = property.customProps;
195
195
  const value = fieldProps.field.value;
196
- const initialValue = fieldProps.meta.initialValue;
196
+ // const initialValue = fieldProps.meta.initialValue;
197
197
  const error = getIn(fieldProps.form.errors, propertyKey);
198
198
  const touched = getIn(fieldProps.form.touched, propertyKey);
199
199
 
@@ -219,7 +219,7 @@ function FieldInternal<T extends CMSType, CustomProps, M extends Record<string,
219
219
  const cmsFieldProps: FieldProps<T, CustomProps, M> = {
220
220
  propertyKey,
221
221
  value: value as T,
222
- initialValue,
222
+ // initialValue,
223
223
  setValue,
224
224
  setFieldValue,
225
225
  error,