@fogpipe/forma-react 0.16.0 → 0.17.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -199,13 +199,13 @@ The event system lets you observe form lifecycle events for side effects like an
199
199
 
200
200
  ### Available Events
201
201
 
202
- | Event | Description |
203
- | -------------- | ------------------------------------------------------- |
204
- | `fieldChanged` | Fires after a field value changes |
205
- | `preSubmit` | Fires before validation; `data` is mutable |
206
- | `postSubmit` | Fires after submission (success, error, or invalid) |
207
- | `pageChanged` | Fires when wizard page changes |
208
- | `formReset` | Fires after `resetForm()` completes |
202
+ | Event | Description |
203
+ | -------------- | --------------------------------------------------- |
204
+ | `fieldChanged` | Fires after a field value changes |
205
+ | `preSubmit` | Fires before validation; `data` is mutable |
206
+ | `postSubmit` | Fires after submission (success, error, or invalid) |
207
+ | `pageChanged` | Fires when wizard page changes |
208
+ | `formReset` | Fires after `resetForm()` completes |
209
209
 
210
210
  ### Declarative Registration
211
211
 
@@ -217,7 +217,10 @@ const forma = useForma({
217
217
  onSubmit: handleSubmit,
218
218
  on: {
219
219
  fieldChanged: (event) => {
220
- analytics.track("field_changed", { path: event.path, source: event.source });
220
+ analytics.track("field_changed", {
221
+ path: event.path,
222
+ source: event.source,
223
+ });
221
224
  },
222
225
  preSubmit: async (event) => {
223
226
  event.data.token = await getCSRFToken();
@@ -302,28 +305,28 @@ formRef.current?.setValues({ name: "John" });
302
305
 
303
306
  ### useForma Methods
304
307
 
305
- | Method | Description |
306
- | --------------------------------- | ------------------------------------ |
307
- | `setFieldValue(path, value)` | Set field value |
308
- | `setFieldTouched(path, touched?)` | Mark field as touched |
309
- | `setValues(values)` | Set multiple values |
310
- | `validateField(path)` | Validate single field |
311
- | `validateForm()` | Validate entire form |
312
- | `submitForm()` | Submit the form |
313
- | `resetForm()` | Reset to initial values |
308
+ | Method | Description |
309
+ | --------------------------------- | -------------------------------------------- |
310
+ | `setFieldValue(path, value)` | Set field value |
311
+ | `setFieldTouched(path, touched?)` | Mark field as touched |
312
+ | `setValues(values)` | Set multiple values |
313
+ | `validateField(path)` | Validate single field |
314
+ | `validateForm()` | Validate entire form |
315
+ | `submitForm()` | Submit the form |
316
+ | `resetForm()` | Reset to initial values |
314
317
  | `on(event, listener)` | Register event listener; returns unsubscribe |
315
318
 
316
319
  ### useForma Options
317
320
 
318
- | Option | Type | Default | Description |
319
- | ---------------------- | -------------------------------- | -------- | ------------------------- |
320
- | `spec` | `Forma` | required | The Forma specification |
321
- | `initialData` | `Record<string, unknown>` | `{}` | Initial form values |
322
- | `onSubmit` | `(data) => void` | - | Submit handler |
323
- | `onChange` | `(data, computed) => void` | - | Change handler |
324
- | `validateOn` | `"change" \| "blur" \| "submit"` | `"blur"` | When to validate |
325
- | `referenceData` | `Record<string, unknown>` | - | Additional reference data |
326
- | `validationDebounceMs` | `number` | `0` | Debounce validation (ms) |
321
+ | Option | Type | Default | Description |
322
+ | ---------------------- | -------------------------------- | -------- | --------------------------- |
323
+ | `spec` | `Forma` | required | The Forma specification |
324
+ | `initialData` | `Record<string, unknown>` | `{}` | Initial form values |
325
+ | `onSubmit` | `(data) => void` | - | Submit handler |
326
+ | `onChange` | `(data, computed) => void` | - | Change handler |
327
+ | `validateOn` | `"change" \| "blur" \| "submit"` | `"blur"` | When to validate |
328
+ | `referenceData` | `Record<string, unknown>` | - | Additional reference data |
329
+ | `validationDebounceMs` | `number` | `0` | Debounce validation (ms) |
327
330
  | `on` | `FormaEvents` | - | Declarative event listeners |
328
331
 
329
332
  ## Error Boundary
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { FieldError, SelectOption, FieldDefinition, Forma, OptionsVisibilityResult, ValidationResult } from '@fogpipe/forma-core';
1
+ import { FieldError, SelectOption, FieldDefinition, Forma, MatrixColumn, OptionsVisibilityResult, ValidationResult } from '@fogpipe/forma-core';
2
2
  export { ComputedField, FieldDefinition, FieldError, FieldType, Forma, PageDefinition, SelectOption, ValidationResult, ValidationRule } from '@fogpipe/forma-core';
3
3
  import * as React$1 from 'react';
4
4
  import React__default from 'react';
@@ -102,8 +102,13 @@ interface BaseFieldProps {
102
102
  required: boolean;
103
103
  /** Whether the field is disabled */
104
104
  disabled: boolean;
105
- /** Validation errors for this field */
105
+ /** Validation errors for this field (always populated — use visibleErrors for display) */
106
106
  errors: FieldError[];
107
+ /**
108
+ * Errors filtered by interaction state (touched or submitted).
109
+ * Use this for displaying errors in the UI to avoid showing errors on untouched fields.
110
+ */
111
+ visibleErrors: FieldError[];
107
112
  /** Handler for value changes */
108
113
  onChange: (value: unknown) => void;
109
114
  /** Handler for blur events */
@@ -331,10 +336,29 @@ interface DisplayFieldProps extends Omit<BaseFieldProps, "value" | "onChange"> {
331
336
  onChange?: never;
332
337
  value?: never;
333
338
  }
339
+ /**
340
+ * Props for matrix/grid fields
341
+ */
342
+ interface MatrixFieldProps extends Omit<BaseFieldProps, "value" | "onChange"> {
343
+ fieldType: "matrix";
344
+ /** Current matrix value: row ID → selected column value(s) */
345
+ value: Record<string, string | number | string[] | number[]> | null;
346
+ onChange: (value: Record<string, string | number | string[] | number[]>) => void;
347
+ /** Row definitions with visibility state */
348
+ rows: Array<{
349
+ id: string;
350
+ label: string;
351
+ visible: boolean;
352
+ }>;
353
+ /** Column definitions (shared options for all rows) */
354
+ columns: MatrixColumn[];
355
+ /** Whether multiple selections per row are allowed */
356
+ multiSelect: boolean;
357
+ }
334
358
  /**
335
359
  * Union of all field prop types
336
360
  */
337
- type FieldProps = TextFieldProps | NumberFieldProps | IntegerFieldProps | BooleanFieldProps | DateFieldProps | DateTimeFieldProps | SelectFieldProps | MultiSelectFieldProps | ArrayFieldProps | ObjectFieldProps | ComputedFieldProps | DisplayFieldProps;
361
+ type FieldProps = TextFieldProps | NumberFieldProps | IntegerFieldProps | BooleanFieldProps | DateFieldProps | DateTimeFieldProps | SelectFieldProps | MultiSelectFieldProps | ArrayFieldProps | ObjectFieldProps | ComputedFieldProps | DisplayFieldProps | MatrixFieldProps;
338
362
  /**
339
363
  * Map of field types to React components
340
364
  * Components receive wrapper props with { field, spec } structure
@@ -357,6 +381,7 @@ interface ComponentMap {
357
381
  object?: React.ComponentType<ObjectComponentProps>;
358
382
  computed?: React.ComponentType<ComputedComponentProps>;
359
383
  display?: React.ComponentType<DisplayComponentProps>;
384
+ matrix?: React.ComponentType<MatrixComponentProps>;
360
385
  fallback?: React.ComponentType<FieldComponentProps>;
361
386
  }
362
387
  /**
@@ -364,7 +389,7 @@ interface ComponentMap {
364
389
  */
365
390
  interface LayoutProps {
366
391
  children: React.ReactNode;
367
- onSubmit: () => void;
392
+ onSubmit: (e?: React.FormEvent) => void;
368
393
  isSubmitting: boolean;
369
394
  isValid: boolean;
370
395
  }
@@ -449,6 +474,10 @@ interface DisplayComponentProps {
449
474
  field: DisplayFieldProps;
450
475
  spec: Forma;
451
476
  }
477
+ interface MatrixComponentProps {
478
+ field: MatrixFieldProps;
479
+ spec: Forma;
480
+ }
452
481
  /**
453
482
  * Generic field component props (for fallback/dynamic components)
454
483
  */
@@ -489,8 +518,13 @@ interface GetFieldPropsResult {
489
518
  showRequiredIndicator: boolean;
490
519
  /** Whether field has been touched */
491
520
  touched: boolean;
492
- /** Validation errors for this field */
521
+ /** Validation errors for this field (always populated — use visibleErrors for display) */
493
522
  errors: FieldError[];
523
+ /**
524
+ * Errors filtered by interaction state (touched or submitted).
525
+ * Use this for displaying errors in the UI to avoid showing errors on untouched fields.
526
+ */
527
+ visibleErrors: FieldError[];
494
528
  /** Handler for value changes */
495
529
  onChange: (value: unknown) => void;
496
530
  /** Handler for blur events */
@@ -607,6 +641,12 @@ interface WizardHelpers {
607
641
  goToPage: (index: number) => void;
608
642
  nextPage: () => void;
609
643
  previousPage: () => void;
644
+ /**
645
+ * Safe "Next" handler for wizard navigation.
646
+ * Advances to the next page if one exists. Never triggers submission.
647
+ * Use this instead of conditionally calling nextPage/onSubmit in a single button.
648
+ */
649
+ handleNext: () => void;
610
650
  hasNextPage: boolean;
611
651
  hasPreviousPage: boolean;
612
652
  canProceed: boolean;
@@ -814,4 +854,4 @@ declare const FormaContext: React$1.Context<UseFormaReturn | null>;
814
854
  */
815
855
  declare function useFormaContext(): UseFormaReturn;
816
856
 
817
- export { type ArrayComponentProps, type ArrayFieldProps, type ArrayHelpers, type ArrayItemFieldProps, type ArrayItemFieldPropsResult, type BaseFieldProps, type BooleanComponentProps, type BooleanFieldProps, type ComponentMap, type ComputedComponentProps, type ComputedFieldProps, type DateComponentProps, type DateFieldProps, type DateTimeComponentProps, type DateTimeFieldProps, type DisplayComponentProps, type DisplayFieldProps, type FieldComponentProps, type FieldProps, FieldRenderer, type FieldRendererProps, type FieldWrapperProps, FormRenderer, type FormRendererHandle, type FormRendererProps, type UseFormaReturn as FormState, FormaContext, FormaErrorBoundary, type FormaErrorBoundaryProps, type FormaEventListener, type FormaEventMap, type FormaEvents, type GetArrayHelpersResult, type GetFieldPropsResult, type GetSelectFieldPropsResult, type IntegerComponentProps, type IntegerFieldProps, type LayoutProps, type LegacyFieldProps, type MultiSelectComponentProps, type MultiSelectFieldProps, type NumberComponentProps, type NumberFieldProps, type ObjectComponentProps, type ObjectFieldProps, type PageState, type PageWrapperProps, type SelectComponentProps, type SelectFieldProps, type SelectionFieldProps, type TextComponentProps, type TextFieldProps, type UseFormaOptions, type UseFormaReturn, type WizardHelpers, useForma, useFormaContext };
857
+ export { type ArrayComponentProps, type ArrayFieldProps, type ArrayHelpers, type ArrayItemFieldProps, type ArrayItemFieldPropsResult, type BaseFieldProps, type BooleanComponentProps, type BooleanFieldProps, type ComponentMap, type ComputedComponentProps, type ComputedFieldProps, type DateComponentProps, type DateFieldProps, type DateTimeComponentProps, type DateTimeFieldProps, type DisplayComponentProps, type DisplayFieldProps, type FieldComponentProps, type FieldProps, FieldRenderer, type FieldRendererProps, type FieldWrapperProps, FormRenderer, type FormRendererHandle, type FormRendererProps, type UseFormaReturn as FormState, FormaContext, FormaErrorBoundary, type FormaErrorBoundaryProps, type FormaEventListener, type FormaEventMap, type FormaEvents, type GetArrayHelpersResult, type GetFieldPropsResult, type GetSelectFieldPropsResult, type IntegerComponentProps, type IntegerFieldProps, type LayoutProps, type LegacyFieldProps, type MatrixComponentProps, type MatrixFieldProps, type MultiSelectComponentProps, type MultiSelectFieldProps, type NumberComponentProps, type NumberFieldProps, type ObjectComponentProps, type ObjectFieldProps, type PageState, type PageWrapperProps, type SelectComponentProps, type SelectFieldProps, type SelectionFieldProps, type TextComponentProps, type TextFieldProps, type UseFormaOptions, type UseFormaReturn, type WizardHelpers, useForma, useFormaContext };
package/dist/index.js CHANGED
@@ -443,6 +443,20 @@ function useForma(options) {
443
443
  const hasNextPage = clampedPageIndex < visiblePages.length - 1;
444
444
  const hasPreviousPage = clampedPageIndex > 0;
445
445
  const isLastPage = clampedPageIndex === visiblePages.length - 1;
446
+ const advanceToNextPage = () => {
447
+ if (hasNextPage) {
448
+ const toIndex = clampedPageIndex + 1;
449
+ dispatch({ type: "SET_PAGE", page: toIndex });
450
+ const newPage = visiblePages[toIndex];
451
+ if (newPage) {
452
+ fireEvent("pageChanged", {
453
+ fromIndex: clampedPageIndex,
454
+ toIndex,
455
+ page: newPage
456
+ });
457
+ }
458
+ }
459
+ };
446
460
  return {
447
461
  pages,
448
462
  currentPageIndex: clampedPageIndex,
@@ -461,20 +475,7 @@ function useForma(options) {
461
475
  }
462
476
  }
463
477
  },
464
- nextPage: () => {
465
- if (hasNextPage) {
466
- const toIndex = clampedPageIndex + 1;
467
- dispatch({ type: "SET_PAGE", page: toIndex });
468
- const newPage = visiblePages[toIndex];
469
- if (newPage) {
470
- fireEvent("pageChanged", {
471
- fromIndex: clampedPageIndex,
472
- toIndex,
473
- page: newPage
474
- });
475
- }
476
- }
477
- },
478
+ nextPage: advanceToNextPage,
478
479
  previousPage: () => {
479
480
  if (hasPreviousPage) {
480
481
  const toIndex = clampedPageIndex - 1;
@@ -489,6 +490,10 @@ function useForma(options) {
489
490
  }
490
491
  }
491
492
  },
493
+ // Same function as nextPage — exposed as a separate name so consumers can
494
+ // bind a single "Next" button without risk of accidentally triggering submission.
495
+ // nextPage is already a no-op on the last page.
496
+ handleNext: advanceToNextPage,
492
497
  hasNextPage,
493
498
  hasPreviousPage,
494
499
  canProceed: (() => {
@@ -517,7 +522,15 @@ function useForma(options) {
517
522
  return pageErrors.length === 0;
518
523
  }
519
524
  };
520
- }, [spec, state.data, state.currentPage, computed, validation, visibility, fireEvent]);
525
+ }, [
526
+ spec,
527
+ state.data,
528
+ state.currentPage,
529
+ computed,
530
+ validation,
531
+ visibility,
532
+ fireEvent
533
+ ]);
521
534
  useEffect(() => {
522
535
  const events = pendingEventsRef.current;
523
536
  if (events.length === 0) return;
@@ -621,9 +634,9 @@ function useForma(options) {
621
634
  }
622
635
  const fieldErrors = validation.errors.filter((e) => e.field === path);
623
636
  const isTouched = state.touched[path] ?? false;
624
- const showErrors = validateOn === "change" || validateOn === "blur" && isTouched || state.isSubmitted;
625
- const displayedErrors = showErrors ? fieldErrors : [];
626
- const hasErrors = displayedErrors.length > 0;
637
+ const shouldShowErrors = validateOn === "change" || validateOn === "blur" && isTouched || state.isSubmitted;
638
+ const visibleFieldErrors = shouldShowErrors ? fieldErrors : [];
639
+ const hasVisibleErrors = visibleFieldErrors.length > 0;
627
640
  const isRequired = required[path] ?? false;
628
641
  const schemaProperty = spec.schema.properties[path];
629
642
  const isBooleanField = (schemaProperty == null ? void 0 : schemaProperty.type) === "boolean" || (fieldDef == null ? void 0 : fieldDef.type) === "boolean";
@@ -643,12 +656,13 @@ function useForma(options) {
643
656
  required: isRequired,
644
657
  showRequiredIndicator,
645
658
  touched: isTouched,
646
- errors: displayedErrors,
659
+ errors: fieldErrors,
660
+ visibleErrors: visibleFieldErrors,
647
661
  onChange: handlers.onChange,
648
662
  onBlur: handlers.onBlur,
649
- // ARIA accessibility attributes
650
- "aria-invalid": hasErrors || void 0,
651
- "aria-describedby": hasErrors ? `${path}-error` : void 0,
663
+ // ARIA accessibility attributes (driven by visibleErrors, not all errors)
664
+ "aria-invalid": hasVisibleErrors || void 0,
665
+ "aria-describedby": hasVisibleErrors ? `${path}-error` : void 0,
652
666
  "aria-required": isRequired || void 0,
653
667
  // Adorner props (only for adornable field types)
654
668
  ...adornerProps,
@@ -719,7 +733,8 @@ function useForma(options) {
719
733
  showRequiredIndicator: false,
720
734
  // Item fields don't show required indicator
721
735
  touched: isTouched,
722
- errors: showErrors ? fieldErrors : [],
736
+ errors: fieldErrors,
737
+ visibleErrors: showErrors ? fieldErrors : [],
723
738
  onChange: handlers.onChange,
724
739
  onBlur: handlers.onBlur,
725
740
  options: visibleOptions
@@ -871,19 +886,10 @@ function useFormaContext() {
871
886
  // src/FormRenderer.tsx
872
887
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
873
888
  function DefaultLayout({ children, onSubmit, isSubmitting }) {
874
- return /* @__PURE__ */ jsxs(
875
- "form",
876
- {
877
- onSubmit: (e) => {
878
- e.preventDefault();
879
- onSubmit();
880
- },
881
- children: [
882
- children,
883
- /* @__PURE__ */ jsx("button", { type: "submit", disabled: isSubmitting, children: isSubmitting ? "Submitting..." : "Submit" })
884
- ]
885
- }
886
- );
889
+ return /* @__PURE__ */ jsxs("form", { onSubmit, children: [
890
+ children,
891
+ /* @__PURE__ */ jsx("button", { type: "submit", disabled: isSubmitting, children: isSubmitting ? "Submitting..." : "Submit" })
892
+ ] });
887
893
  }
888
894
  function DefaultFieldWrapper({
889
895
  fieldPath,
@@ -967,7 +973,7 @@ var FormRenderer = forwardRef(
967
973
  layout: Layout = DefaultLayout,
968
974
  fieldWrapper: FieldWrapper = DefaultFieldWrapper,
969
975
  pageWrapper: PageWrapper = DefaultPageWrapper,
970
- validateOn
976
+ validateOn = "blur"
971
977
  } = props;
972
978
  const forma = useForma({
973
979
  spec,
@@ -1012,6 +1018,7 @@ var FormRenderer = forwardRef(
1012
1018
  optionsVisibility: formaOptionsVisibility,
1013
1019
  touched: formaTouched,
1014
1020
  errors: formaErrors,
1021
+ isSubmitted: formaIsSubmitted,
1015
1022
  setFieldValue,
1016
1023
  setFieldTouched,
1017
1024
  getArrayHelpers
@@ -1045,6 +1052,8 @@ var FormRenderer = forwardRef(
1045
1052
  }
1046
1053
  const errors = formaErrors.filter((e) => e.field === fieldPath);
1047
1054
  const touched = formaTouched[fieldPath] ?? false;
1055
+ const showErrors = validateOn === "change" || validateOn === "blur" && touched || formaIsSubmitted;
1056
+ const visibleErrors = showErrors ? errors : [];
1048
1057
  const required = formaRequired[fieldPath] ?? false;
1049
1058
  const disabled = formaEnabled[fieldPath] === false;
1050
1059
  const schemaProperty = spec.schema.properties[fieldPath];
@@ -1060,6 +1069,7 @@ var FormRenderer = forwardRef(
1060
1069
  required,
1061
1070
  disabled,
1062
1071
  errors,
1072
+ visibleErrors,
1063
1073
  onChange: (value) => setFieldValue(fieldPath, value),
1064
1074
  onBlur: () => setFieldTouched(fieldPath),
1065
1075
  // Convenience properties
@@ -1143,6 +1153,22 @@ var FormRenderer = forwardRef(
1143
1153
  minItems,
1144
1154
  maxItems
1145
1155
  };
1156
+ } else if (fieldType === "matrix" && fieldDef.type === "matrix") {
1157
+ const matrixValue = baseProps.value ?? null;
1158
+ const rows = fieldDef.rows.map((row) => ({
1159
+ id: row.id,
1160
+ label: row.label,
1161
+ visible: formaVisibility[`${fieldPath}.${row.id}`] !== false
1162
+ }));
1163
+ fieldProps = {
1164
+ ...baseProps,
1165
+ fieldType: "matrix",
1166
+ value: matrixValue,
1167
+ onChange: baseProps.onChange,
1168
+ rows,
1169
+ columns: fieldDef.columns,
1170
+ multiSelect: fieldDef.multiSelect ?? false
1171
+ };
1146
1172
  } else if (fieldType === "display" && fieldDef.type === "display") {
1147
1173
  const sourceValue = fieldDef.source ? formaData[fieldDef.source] ?? formaComputed[fieldDef.source] : void 0;
1148
1174
  const {
@@ -1196,6 +1222,8 @@ var FormRenderer = forwardRef(
1196
1222
  formaOptionsVisibility,
1197
1223
  formaTouched,
1198
1224
  formaErrors,
1225
+ formaIsSubmitted,
1226
+ validateOn,
1199
1227
  setFieldValue,
1200
1228
  setFieldTouched,
1201
1229
  getArrayHelpers
@@ -1222,10 +1250,17 @@ var FormRenderer = forwardRef(
1222
1250
  }
1223
1251
  return /* @__PURE__ */ jsx(Fragment, { children: renderedFields });
1224
1252
  }, [spec.pages, forma.wizard, PageWrapper, renderedFields]);
1253
+ const handleSubmit = useCallback2(
1254
+ (e) => {
1255
+ e == null ? void 0 : e.preventDefault();
1256
+ forma.submitForm();
1257
+ },
1258
+ [forma.submitForm]
1259
+ );
1225
1260
  return /* @__PURE__ */ jsx(FormaContext.Provider, { value: forma, children: /* @__PURE__ */ jsx(
1226
1261
  Layout,
1227
1262
  {
1228
- onSubmit: forma.submitForm,
1263
+ onSubmit: handleSubmit,
1229
1264
  isSubmitting: forma.isSubmitting,
1230
1265
  isValid: forma.isValid,
1231
1266
  children: content
@@ -1291,6 +1326,7 @@ function FieldRenderer({
1291
1326
  }
1292
1327
  const errors = forma.errors.filter((e) => e.field === fieldPath);
1293
1328
  const touched = forma.touched[fieldPath] ?? false;
1329
+ const visibleErrors = touched || forma.isSubmitted ? errors : [];
1294
1330
  const required = forma.required[fieldPath] ?? false;
1295
1331
  const disabled = forma.enabled[fieldPath] === false;
1296
1332
  const schemaProperty = spec.schema.properties[fieldPath];
@@ -1303,6 +1339,7 @@ function FieldRenderer({
1303
1339
  required,
1304
1340
  disabled,
1305
1341
  errors,
1342
+ visibleErrors,
1306
1343
  onChange: (value) => forma.setFieldValue(fieldPath, value),
1307
1344
  onBlur: () => forma.setFieldTouched(fieldPath),
1308
1345
  // Convenience properties
@@ -1442,13 +1479,33 @@ function FieldRenderer({
1442
1479
  minItems,
1443
1480
  maxItems
1444
1481
  };
1482
+ } else if (fieldType === "matrix" && fieldDef.type === "matrix") {
1483
+ const matrixValue = baseProps.value ?? null;
1484
+ const rows = fieldDef.rows.map((row) => ({
1485
+ id: row.id,
1486
+ label: row.label,
1487
+ visible: forma.visibility[`${fieldPath}.${row.id}`] !== false
1488
+ }));
1489
+ fieldProps = {
1490
+ ...baseProps,
1491
+ fieldType: "matrix",
1492
+ value: matrixValue,
1493
+ onChange: baseProps.onChange,
1494
+ rows,
1495
+ columns: fieldDef.columns,
1496
+ multiSelect: fieldDef.multiSelect ?? false
1497
+ };
1445
1498
  } else if (fieldType === "display" && fieldDef.type === "display") {
1446
1499
  const sourceValue = fieldDef.source ? forma.data[fieldDef.source] ?? forma.computed[fieldDef.source] : void 0;
1447
1500
  const {
1448
1501
  onChange: _onChange,
1502
+ // omit from display props
1449
1503
  value: _value,
1504
+ // omit from display props
1450
1505
  ...displayBaseProps
1451
1506
  } = baseProps;
1507
+ void _onChange;
1508
+ void _value;
1452
1509
  fieldProps = {
1453
1510
  ...displayBaseProps,
1454
1511
  fieldType: "display",