@fogpipe/forma-react 0.17.1 → 0.18.0

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 (36) hide show
  1. package/README.md +82 -0
  2. package/dist/FormRenderer-D_ZVK44t.d.ts +558 -0
  3. package/dist/chunk-5K4QITFH.js +1276 -0
  4. package/dist/chunk-5K4QITFH.js.map +1 -0
  5. package/dist/defaults/index.d.ts +56 -0
  6. package/dist/defaults/index.js +895 -0
  7. package/dist/defaults/index.js.map +1 -0
  8. package/dist/defaults/styles/forma-defaults.css +696 -0
  9. package/dist/index.d.ts +7 -559
  10. package/dist/index.js +28 -1292
  11. package/dist/index.js.map +1 -1
  12. package/package.json +17 -3
  13. package/src/__tests__/defaults/components.test.tsx +818 -0
  14. package/src/__tests__/defaults/integration.test.tsx +494 -0
  15. package/src/__tests__/defaults/layout.test.tsx +298 -0
  16. package/src/defaults/DefaultFormRenderer.tsx +43 -0
  17. package/src/defaults/componentMap.ts +45 -0
  18. package/src/defaults/components/ArrayField.tsx +183 -0
  19. package/src/defaults/components/BooleanInput.tsx +32 -0
  20. package/src/defaults/components/ComputedDisplay.tsx +26 -0
  21. package/src/defaults/components/DateInput.tsx +59 -0
  22. package/src/defaults/components/DisplayField.tsx +15 -0
  23. package/src/defaults/components/FallbackField.tsx +35 -0
  24. package/src/defaults/components/MatrixField.tsx +98 -0
  25. package/src/defaults/components/MultiSelectInput.tsx +51 -0
  26. package/src/defaults/components/NumberInput.tsx +73 -0
  27. package/src/defaults/components/ObjectField.tsx +22 -0
  28. package/src/defaults/components/SelectInput.tsx +44 -0
  29. package/src/defaults/components/TextInput.tsx +48 -0
  30. package/src/defaults/components/TextareaInput.tsx +46 -0
  31. package/src/defaults/index.ts +33 -0
  32. package/src/defaults/layout/FieldWrapper.tsx +83 -0
  33. package/src/defaults/layout/FormLayout.tsx +34 -0
  34. package/src/defaults/layout/PageWrapper.tsx +18 -0
  35. package/src/defaults/layout/WizardLayout.tsx +130 -0
  36. package/src/defaults/styles/forma-defaults.css +696 -0
@@ -0,0 +1,895 @@
1
+ import {
2
+ FormRenderer,
3
+ useFormaContext
4
+ } from "../chunk-5K4QITFH.js";
5
+
6
+ // src/defaults/DefaultFormRenderer.tsx
7
+ import { forwardRef } from "react";
8
+
9
+ // src/defaults/components/TextInput.tsx
10
+ import { jsx, jsxs } from "react/jsx-runtime";
11
+ function TextInput({ field }) {
12
+ const inputType = field.fieldType === "phone" ? "tel" : field.fieldType;
13
+ const hasErrors = field.visibleErrors.length > 0;
14
+ const describedBy = [
15
+ field.description ? `${field.name}-description` : null,
16
+ hasErrors ? `${field.name}-errors` : null
17
+ ].filter(Boolean).join(" ");
18
+ const input = /* @__PURE__ */ jsx(
19
+ "input",
20
+ {
21
+ id: field.name,
22
+ name: field.name,
23
+ type: inputType,
24
+ className: "forma-input",
25
+ value: field.value,
26
+ onChange: (e) => field.onChange(e.target.value),
27
+ onBlur: field.onBlur,
28
+ disabled: field.disabled,
29
+ readOnly: field.readonly,
30
+ placeholder: field.placeholder,
31
+ "aria-invalid": hasErrors || void 0,
32
+ "aria-required": field.required || void 0,
33
+ "aria-describedby": describedBy || void 0
34
+ }
35
+ );
36
+ if (field.prefix || field.suffix) {
37
+ return /* @__PURE__ */ jsxs("div", { className: "forma-input-adorner", children: [
38
+ field.prefix && /* @__PURE__ */ jsx("span", { className: "forma-input-adorner__prefix", children: field.prefix }),
39
+ input,
40
+ field.suffix && /* @__PURE__ */ jsx("span", { className: "forma-input-adorner__suffix", children: field.suffix })
41
+ ] });
42
+ }
43
+ return input;
44
+ }
45
+
46
+ // src/defaults/components/TextareaInput.tsx
47
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
48
+ function TextareaInput({ field }) {
49
+ const hasErrors = field.visibleErrors.length > 0;
50
+ const describedBy = [
51
+ field.description ? `${field.name}-description` : null,
52
+ hasErrors ? `${field.name}-errors` : null
53
+ ].filter(Boolean).join(" ");
54
+ const textarea = /* @__PURE__ */ jsx2(
55
+ "textarea",
56
+ {
57
+ id: field.name,
58
+ name: field.name,
59
+ className: "forma-textarea",
60
+ value: field.value,
61
+ onChange: (e) => field.onChange(e.target.value),
62
+ onBlur: field.onBlur,
63
+ disabled: field.disabled,
64
+ readOnly: field.readonly,
65
+ placeholder: field.placeholder,
66
+ rows: 3,
67
+ "aria-invalid": hasErrors || void 0,
68
+ "aria-required": field.required || void 0,
69
+ "aria-describedby": describedBy || void 0
70
+ }
71
+ );
72
+ if (field.prefix || field.suffix) {
73
+ return /* @__PURE__ */ jsxs2("div", { className: "forma-input-adorner", children: [
74
+ field.prefix && /* @__PURE__ */ jsx2("span", { className: "forma-input-adorner__prefix", children: field.prefix }),
75
+ textarea,
76
+ field.suffix && /* @__PURE__ */ jsx2("span", { className: "forma-input-adorner__suffix", children: field.suffix })
77
+ ] });
78
+ }
79
+ return textarea;
80
+ }
81
+
82
+ // src/defaults/components/NumberInput.tsx
83
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
84
+ function NumberInputBase({
85
+ field,
86
+ parseValue
87
+ }) {
88
+ const hasErrors = field.visibleErrors.length > 0;
89
+ const describedBy = [
90
+ field.description ? `${field.name}-description` : null,
91
+ hasErrors ? `${field.name}-errors` : null
92
+ ].filter(Boolean).join(" ");
93
+ const input = /* @__PURE__ */ jsx3(
94
+ "input",
95
+ {
96
+ id: field.name,
97
+ name: field.name,
98
+ type: "number",
99
+ className: `forma-input forma-input--${field.fieldType}`,
100
+ value: field.value != null ? String(field.value) : "",
101
+ onChange: (e) => {
102
+ const val = e.target.value;
103
+ if (val === "") {
104
+ field.onChange(null);
105
+ } else {
106
+ const num = parseValue(val);
107
+ field.onChange(isNaN(num) ? null : num);
108
+ }
109
+ },
110
+ onBlur: field.onBlur,
111
+ disabled: field.disabled,
112
+ readOnly: field.readonly,
113
+ placeholder: field.placeholder,
114
+ min: field.min,
115
+ max: field.max,
116
+ step: field.step ?? (field.fieldType === "integer" ? 1 : "any"),
117
+ "aria-invalid": hasErrors || void 0,
118
+ "aria-required": field.required || void 0,
119
+ "aria-describedby": describedBy || void 0
120
+ }
121
+ );
122
+ if (field.prefix || field.suffix) {
123
+ return /* @__PURE__ */ jsxs3("div", { className: "forma-input-adorner", children: [
124
+ field.prefix && /* @__PURE__ */ jsx3("span", { className: "forma-input-adorner__prefix", children: field.prefix }),
125
+ input,
126
+ field.suffix && /* @__PURE__ */ jsx3("span", { className: "forma-input-adorner__suffix", children: field.suffix })
127
+ ] });
128
+ }
129
+ return input;
130
+ }
131
+ function NumberInput({ field }) {
132
+ return /* @__PURE__ */ jsx3(NumberInputBase, { field, parseValue: parseFloat });
133
+ }
134
+ function IntegerInput({ field }) {
135
+ return /* @__PURE__ */ jsx3(NumberInputBase, { field, parseValue: (v) => parseInt(v, 10) });
136
+ }
137
+
138
+ // src/defaults/components/BooleanInput.tsx
139
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
140
+ function BooleanInput({ field }) {
141
+ const hasErrors = field.visibleErrors.length > 0;
142
+ const describedBy = [
143
+ field.description ? `${field.name}-description` : null,
144
+ hasErrors ? `${field.name}-errors` : null
145
+ ].filter(Boolean).join(" ");
146
+ return /* @__PURE__ */ jsxs4("div", { className: "forma-checkbox", children: [
147
+ /* @__PURE__ */ jsx4(
148
+ "input",
149
+ {
150
+ id: field.name,
151
+ name: field.name,
152
+ type: "checkbox",
153
+ className: "forma-checkbox__input",
154
+ checked: field.value ?? false,
155
+ onChange: (e) => field.onChange(e.target.checked),
156
+ onBlur: field.onBlur,
157
+ disabled: field.disabled,
158
+ "aria-invalid": hasErrors || void 0,
159
+ "aria-describedby": describedBy || void 0
160
+ }
161
+ ),
162
+ /* @__PURE__ */ jsx4("label", { htmlFor: field.name, className: "forma-checkbox__label", children: field.label })
163
+ ] });
164
+ }
165
+
166
+ // src/defaults/components/DateInput.tsx
167
+ import { jsx as jsx5 } from "react/jsx-runtime";
168
+ function DateInput({ field }) {
169
+ const hasErrors = field.visibleErrors.length > 0;
170
+ const describedBy = [
171
+ field.description ? `${field.name}-description` : null,
172
+ hasErrors ? `${field.name}-errors` : null
173
+ ].filter(Boolean).join(" ");
174
+ return /* @__PURE__ */ jsx5(
175
+ "input",
176
+ {
177
+ id: field.name,
178
+ name: field.name,
179
+ type: "date",
180
+ className: "forma-input forma-input--date",
181
+ value: field.value ?? "",
182
+ onChange: (e) => field.onChange(e.target.value || null),
183
+ onBlur: field.onBlur,
184
+ disabled: field.disabled,
185
+ readOnly: field.readonly,
186
+ "aria-invalid": hasErrors || void 0,
187
+ "aria-required": field.required || void 0,
188
+ "aria-describedby": describedBy || void 0
189
+ }
190
+ );
191
+ }
192
+ function DateTimeInput({ field }) {
193
+ const hasErrors = field.visibleErrors.length > 0;
194
+ const describedBy = [
195
+ field.description ? `${field.name}-description` : null,
196
+ hasErrors ? `${field.name}-errors` : null
197
+ ].filter(Boolean).join(" ");
198
+ const inputValue = field.value ?? "";
199
+ return /* @__PURE__ */ jsx5(
200
+ "input",
201
+ {
202
+ id: field.name,
203
+ name: field.name,
204
+ type: "datetime-local",
205
+ className: "forma-input forma-input--datetime",
206
+ value: inputValue,
207
+ onChange: (e) => field.onChange(e.target.value || null),
208
+ onBlur: field.onBlur,
209
+ disabled: field.disabled,
210
+ readOnly: field.readonly,
211
+ "aria-invalid": hasErrors || void 0,
212
+ "aria-required": field.required || void 0,
213
+ "aria-describedby": describedBy || void 0
214
+ }
215
+ );
216
+ }
217
+
218
+ // src/defaults/components/SelectInput.tsx
219
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
220
+ function SelectInput({ field }) {
221
+ const hasErrors = field.visibleErrors.length > 0;
222
+ const describedBy = [
223
+ field.description ? `${field.name}-description` : null,
224
+ hasErrors ? `${field.name}-errors` : null
225
+ ].filter(Boolean).join(" ");
226
+ return /* @__PURE__ */ jsxs5(
227
+ "select",
228
+ {
229
+ id: field.name,
230
+ name: field.name,
231
+ className: "forma-select",
232
+ value: field.value !== null ? String(field.value) : "",
233
+ onChange: (e) => {
234
+ const value = e.target.value;
235
+ if (!value) {
236
+ field.onChange(null);
237
+ } else {
238
+ field.onChange(value);
239
+ }
240
+ },
241
+ onBlur: field.onBlur,
242
+ disabled: field.disabled,
243
+ "aria-invalid": hasErrors || void 0,
244
+ "aria-required": field.required || void 0,
245
+ "aria-describedby": describedBy || void 0,
246
+ children: [
247
+ (!field.required || field.value === null) && /* @__PURE__ */ jsx6("option", { value: "", children: field.placeholder ?? "Select..." }),
248
+ field.options.map((opt) => /* @__PURE__ */ jsx6("option", { value: String(opt.value), children: opt.label }, String(opt.value)))
249
+ ]
250
+ }
251
+ );
252
+ }
253
+
254
+ // src/defaults/components/MultiSelectInput.tsx
255
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
256
+ function MultiSelectInput({ field }) {
257
+ const hasErrors = field.visibleErrors.length > 0;
258
+ const describedBy = [
259
+ field.description ? `${field.name}-description` : null,
260
+ hasErrors ? `${field.name}-errors` : null
261
+ ].filter(Boolean).join(" ");
262
+ const selected = field.value ?? [];
263
+ const handleToggle = (optionValue) => {
264
+ if (selected.includes(optionValue)) {
265
+ field.onChange(selected.filter((v) => v !== optionValue));
266
+ } else {
267
+ field.onChange([...selected, optionValue]);
268
+ }
269
+ field.onBlur();
270
+ };
271
+ return /* @__PURE__ */ jsxs6(
272
+ "fieldset",
273
+ {
274
+ className: "forma-multiselect",
275
+ "aria-describedby": describedBy || void 0,
276
+ "aria-invalid": hasErrors || void 0,
277
+ children: [
278
+ /* @__PURE__ */ jsx7("legend", { className: "forma-sr-only", children: field.label }),
279
+ field.options.map((opt) => {
280
+ const optId = `${field.name}-${opt.value}`;
281
+ return /* @__PURE__ */ jsxs6("div", { className: "forma-multiselect__option", children: [
282
+ /* @__PURE__ */ jsx7(
283
+ "input",
284
+ {
285
+ id: optId,
286
+ type: "checkbox",
287
+ className: "forma-checkbox__input",
288
+ checked: selected.includes(String(opt.value)),
289
+ onChange: () => handleToggle(String(opt.value)),
290
+ disabled: field.disabled
291
+ }
292
+ ),
293
+ /* @__PURE__ */ jsx7("label", { htmlFor: optId, className: "forma-checkbox__label", children: opt.label })
294
+ ] }, String(opt.value));
295
+ })
296
+ ]
297
+ }
298
+ );
299
+ }
300
+
301
+ // src/defaults/components/ArrayField.tsx
302
+ import { useRef } from "react";
303
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
304
+ function ArrayField({ field }) {
305
+ const hasErrors = field.visibleErrors.length > 0;
306
+ const itemKeysRef = useRef([]);
307
+ const nextKeyRef = useRef(0);
308
+ const currentLength = field.helpers.items.length;
309
+ const keysLength = itemKeysRef.current.length;
310
+ if (currentLength > keysLength) {
311
+ for (let i = keysLength; i < currentLength; i++) {
312
+ itemKeysRef.current.push(`item-${nextKeyRef.current++}`);
313
+ }
314
+ } else if (currentLength < keysLength) {
315
+ itemKeysRef.current.length = currentLength;
316
+ }
317
+ const fieldOrder = field.itemFieldOrder ?? Object.keys(field.itemFields);
318
+ return /* @__PURE__ */ jsxs7(
319
+ "div",
320
+ {
321
+ className: "forma-array",
322
+ "aria-invalid": hasErrors || void 0,
323
+ children: [
324
+ field.helpers.items.length === 0 && /* @__PURE__ */ jsx8("p", { className: "forma-array__empty", children: "No items" }),
325
+ field.helpers.items.map((_, index) => /* @__PURE__ */ jsxs7(
326
+ "div",
327
+ {
328
+ className: "forma-array__item",
329
+ children: [
330
+ /* @__PURE__ */ jsx8("div", { className: "forma-array__item-fields", children: fieldOrder.map((fieldName) => {
331
+ const itemProps = field.helpers.getItemFieldProps(
332
+ index,
333
+ fieldName
334
+ );
335
+ return /* @__PURE__ */ jsxs7("div", { className: "forma-field", children: [
336
+ /* @__PURE__ */ jsx8(
337
+ "label",
338
+ {
339
+ htmlFor: itemProps.name,
340
+ className: "forma-label",
341
+ children: itemProps.label
342
+ }
343
+ ),
344
+ renderItemField(itemProps),
345
+ itemProps.errors.length > 0 && itemProps.touched && /* @__PURE__ */ jsx8("div", { className: "forma-field__errors", role: "alert", children: itemProps.errors.map((err, i) => /* @__PURE__ */ jsx8("span", { className: "forma-field__error", children: err.message }, i)) })
346
+ ] }, fieldName);
347
+ }) }),
348
+ /* @__PURE__ */ jsx8(
349
+ "button",
350
+ {
351
+ type: "button",
352
+ className: "forma-button forma-button--danger forma-array__remove",
353
+ onClick: () => field.helpers.remove(index),
354
+ disabled: !field.helpers.canRemove || field.disabled,
355
+ children: "Remove"
356
+ }
357
+ )
358
+ ]
359
+ },
360
+ itemKeysRef.current[index]
361
+ )),
362
+ /* @__PURE__ */ jsx8(
363
+ "button",
364
+ {
365
+ type: "button",
366
+ className: "forma-button forma-button--secondary forma-array__add",
367
+ onClick: () => field.helpers.push(),
368
+ disabled: !field.helpers.canAdd || field.disabled,
369
+ children: "+ Add Item"
370
+ }
371
+ )
372
+ ]
373
+ }
374
+ );
375
+ }
376
+ function renderItemField(itemProps) {
377
+ const type = itemProps.type;
378
+ if (type === "select" && itemProps.options) {
379
+ return /* @__PURE__ */ jsxs7(
380
+ "select",
381
+ {
382
+ id: itemProps.name,
383
+ className: "forma-select",
384
+ value: String(itemProps.value ?? ""),
385
+ onChange: (e) => {
386
+ var _a;
387
+ const value = e.target.value;
388
+ if (!value) {
389
+ itemProps.onChange(null);
390
+ } else {
391
+ const option = (_a = itemProps.options) == null ? void 0 : _a.find(
392
+ (opt) => String(opt.value) === value
393
+ );
394
+ itemProps.onChange(option ? option.value : value);
395
+ }
396
+ },
397
+ onBlur: itemProps.onBlur,
398
+ disabled: !itemProps.enabled,
399
+ children: [
400
+ /* @__PURE__ */ jsx8("option", { value: "", children: "Select..." }),
401
+ itemProps.options.map((opt) => /* @__PURE__ */ jsx8("option", { value: String(opt.value), children: opt.label }, String(opt.value)))
402
+ ]
403
+ }
404
+ );
405
+ }
406
+ if (type === "number" || type === "integer") {
407
+ return /* @__PURE__ */ jsx8(
408
+ "input",
409
+ {
410
+ id: itemProps.name,
411
+ type: "number",
412
+ className: "forma-input",
413
+ value: itemProps.value != null ? String(itemProps.value) : "",
414
+ onChange: (e) => {
415
+ const val = e.target.value;
416
+ if (val === "") {
417
+ itemProps.onChange(null);
418
+ } else {
419
+ const num = type === "integer" ? parseInt(val, 10) : parseFloat(val);
420
+ itemProps.onChange(isNaN(num) ? null : num);
421
+ }
422
+ },
423
+ onBlur: itemProps.onBlur,
424
+ disabled: !itemProps.enabled,
425
+ step: type === "integer" ? 1 : "any"
426
+ }
427
+ );
428
+ }
429
+ if (type === "boolean") {
430
+ return /* @__PURE__ */ jsxs7("div", { className: "forma-checkbox", children: [
431
+ /* @__PURE__ */ jsx8(
432
+ "input",
433
+ {
434
+ id: itemProps.name,
435
+ type: "checkbox",
436
+ className: "forma-checkbox__input",
437
+ checked: Boolean(itemProps.value),
438
+ onChange: (e) => itemProps.onChange(e.target.checked),
439
+ onBlur: itemProps.onBlur,
440
+ disabled: !itemProps.enabled
441
+ }
442
+ ),
443
+ /* @__PURE__ */ jsx8("label", { htmlFor: itemProps.name, className: "forma-checkbox__label", children: itemProps.label })
444
+ ] });
445
+ }
446
+ return /* @__PURE__ */ jsx8(
447
+ "input",
448
+ {
449
+ id: itemProps.name,
450
+ type: "text",
451
+ className: "forma-input",
452
+ value: String(itemProps.value ?? ""),
453
+ onChange: (e) => itemProps.onChange(e.target.value),
454
+ onBlur: itemProps.onBlur,
455
+ disabled: !itemProps.enabled,
456
+ placeholder: itemProps.placeholder
457
+ }
458
+ );
459
+ }
460
+
461
+ // src/defaults/components/ObjectField.tsx
462
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
463
+ function ObjectField({ field }) {
464
+ const hasErrors = field.visibleErrors.length > 0;
465
+ return /* @__PURE__ */ jsxs8(
466
+ "fieldset",
467
+ {
468
+ className: "forma-object",
469
+ "aria-invalid": hasErrors || void 0,
470
+ children: [
471
+ /* @__PURE__ */ jsx9("legend", { className: "forma-object__legend", children: field.label }),
472
+ field.description && /* @__PURE__ */ jsx9("p", { className: "forma-object__description", children: field.description }),
473
+ /* @__PURE__ */ jsx9("div", { className: "forma-object__fields" })
474
+ ]
475
+ }
476
+ );
477
+ }
478
+
479
+ // src/defaults/components/ComputedDisplay.tsx
480
+ import { jsx as jsx10 } from "react/jsx-runtime";
481
+ function ComputedDisplay({ field }) {
482
+ let displayValue;
483
+ if (field.value === null || field.value === void 0) {
484
+ displayValue = "\u2014";
485
+ } else if (typeof field.value === "object") {
486
+ try {
487
+ displayValue = JSON.stringify(field.value);
488
+ } catch {
489
+ displayValue = String(field.value);
490
+ }
491
+ } else {
492
+ displayValue = String(field.value);
493
+ }
494
+ return /* @__PURE__ */ jsx10(
495
+ "output",
496
+ {
497
+ id: field.name,
498
+ className: "forma-computed",
499
+ children: displayValue
500
+ }
501
+ );
502
+ }
503
+
504
+ // src/defaults/components/DisplayField.tsx
505
+ import { jsx as jsx11 } from "react/jsx-runtime";
506
+ function DisplayField({ field }) {
507
+ const content = field.sourceValue !== void 0 ? String(field.sourceValue) : field.content;
508
+ return /* @__PURE__ */ jsx11("div", { className: "forma-display", children: content && /* @__PURE__ */ jsx11("p", { className: "forma-display__content", children: content }) });
509
+ }
510
+
511
+ // src/defaults/components/MatrixField.tsx
512
+ import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
513
+ function MatrixField({ field }) {
514
+ const hasErrors = field.visibleErrors.length > 0;
515
+ const visibleRows = field.rows.filter((r) => r.visible);
516
+ const currentValue = field.value ?? {};
517
+ const handleChange = (rowId, colValue) => {
518
+ if (field.disabled) return;
519
+ const next = { ...currentValue };
520
+ if (field.multiSelect) {
521
+ const current = currentValue[rowId] ?? [];
522
+ const colStr = String(colValue);
523
+ const exists = current.includes(colStr);
524
+ next[rowId] = exists ? current.filter((v) => v !== colStr) : [...current, colStr];
525
+ } else {
526
+ next[rowId] = colValue;
527
+ }
528
+ field.onChange(next);
529
+ field.onBlur();
530
+ };
531
+ const isChecked = (rowId, colValue) => {
532
+ const rowValue = currentValue[rowId];
533
+ if (rowValue === void 0 || rowValue === null) return false;
534
+ if (Array.isArray(rowValue)) {
535
+ return rowValue.includes(colValue);
536
+ }
537
+ return rowValue === colValue;
538
+ };
539
+ return /* @__PURE__ */ jsx12("div", { className: "forma-matrix", "aria-invalid": hasErrors || void 0, children: /* @__PURE__ */ jsxs9("table", { className: "forma-matrix__table", role: "grid", children: [
540
+ /* @__PURE__ */ jsx12("thead", { children: /* @__PURE__ */ jsxs9("tr", { children: [
541
+ /* @__PURE__ */ jsx12("th", { scope: "col", className: "forma-matrix__corner" }),
542
+ field.columns.map((col) => /* @__PURE__ */ jsx12(
543
+ "th",
544
+ {
545
+ scope: "col",
546
+ className: "forma-matrix__col-header",
547
+ children: col.label
548
+ },
549
+ String(col.value)
550
+ ))
551
+ ] }) }),
552
+ /* @__PURE__ */ jsx12("tbody", { children: visibleRows.map((row) => /* @__PURE__ */ jsxs9("tr", { className: "forma-matrix__row", children: [
553
+ /* @__PURE__ */ jsx12("th", { scope: "row", className: "forma-matrix__row-header", children: row.label }),
554
+ field.columns.map((col) => {
555
+ const cellId = `${field.name}-${row.id}-${col.value}`;
556
+ return /* @__PURE__ */ jsxs9("td", { className: "forma-matrix__cell", children: [
557
+ /* @__PURE__ */ jsx12(
558
+ "input",
559
+ {
560
+ id: cellId,
561
+ type: field.multiSelect ? "checkbox" : "radio",
562
+ name: field.multiSelect ? cellId : `${field.name}-${row.id}`,
563
+ className: field.multiSelect ? "forma-checkbox__input" : "forma-radio__input",
564
+ checked: isChecked(row.id, col.value),
565
+ onChange: () => handleChange(row.id, col.value),
566
+ disabled: field.disabled
567
+ }
568
+ ),
569
+ /* @__PURE__ */ jsxs9("label", { htmlFor: cellId, className: "forma-sr-only", children: [
570
+ row.label,
571
+ ": ",
572
+ col.label
573
+ ] })
574
+ ] }, String(col.value));
575
+ })
576
+ ] }, row.id)) })
577
+ ] }) });
578
+ }
579
+
580
+ // src/defaults/components/FallbackField.tsx
581
+ import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
582
+ function FallbackField({ field }) {
583
+ const hasErrors = field.visibleErrors.length > 0;
584
+ const isDev = typeof process !== "undefined" && process.env.NODE_ENV !== "production";
585
+ return /* @__PURE__ */ jsxs10("div", { className: "forma-fallback", children: [
586
+ isDev && /* @__PURE__ */ jsxs10("p", { className: "forma-fallback__warning", children: [
587
+ 'Unknown field type: "',
588
+ field.field.type,
589
+ '"'
590
+ ] }),
591
+ /* @__PURE__ */ jsx13(
592
+ "input",
593
+ {
594
+ id: field.name,
595
+ name: field.name,
596
+ type: "text",
597
+ className: "forma-input",
598
+ value: String(field.value ?? ""),
599
+ onChange: (e) => {
600
+ if ("onChange" in field && typeof field.onChange === "function") {
601
+ field.onChange(e.target.value);
602
+ }
603
+ },
604
+ onBlur: field.onBlur,
605
+ disabled: field.disabled,
606
+ "aria-invalid": hasErrors || void 0
607
+ }
608
+ )
609
+ ] });
610
+ }
611
+
612
+ // src/defaults/layout/FieldWrapper.tsx
613
+ import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
614
+ function FieldWrapper({
615
+ fieldPath,
616
+ field,
617
+ children,
618
+ errors,
619
+ touched,
620
+ showRequiredIndicator,
621
+ visible
622
+ }) {
623
+ const { isSubmitted } = useFormaContext();
624
+ if (!visible) return null;
625
+ const shouldShowMessages = touched || isSubmitted;
626
+ const visibleErrors = shouldShowMessages ? errors.filter((e) => e.severity === "error") : [];
627
+ const visibleWarnings = shouldShowMessages ? errors.filter((e) => e.severity === "warning") : [];
628
+ const hasErrors = visibleErrors.length > 0;
629
+ const hasWarnings = visibleWarnings.length > 0;
630
+ const classNames = [
631
+ "forma-field",
632
+ hasErrors && "forma-field--error",
633
+ !hasErrors && hasWarnings && "forma-field--warning",
634
+ showRequiredIndicator && "forma-field--required"
635
+ ].filter(Boolean).join(" ");
636
+ return /* @__PURE__ */ jsxs11("div", { className: classNames, "data-field-path": fieldPath, children: [
637
+ field.label && field.type !== "boolean" && /* @__PURE__ */ jsxs11("label", { htmlFor: fieldPath, className: "forma-label", children: [
638
+ field.label,
639
+ showRequiredIndicator && /* @__PURE__ */ jsxs11("span", { className: "forma-label__required", "aria-hidden": "true", children: [
640
+ " ",
641
+ "*"
642
+ ] })
643
+ ] }),
644
+ field.description && /* @__PURE__ */ jsx14(
645
+ "div",
646
+ {
647
+ id: `${fieldPath}-description`,
648
+ className: "forma-field__description",
649
+ children: field.description
650
+ }
651
+ ),
652
+ children,
653
+ hasErrors && /* @__PURE__ */ jsx14(
654
+ "div",
655
+ {
656
+ id: `${fieldPath}-errors`,
657
+ className: "forma-field__errors",
658
+ role: "alert",
659
+ children: visibleErrors.map((error, i) => /* @__PURE__ */ jsx14("span", { className: "forma-field__error", children: error.message }, i))
660
+ }
661
+ ),
662
+ hasWarnings && /* @__PURE__ */ jsx14("div", { className: "forma-field__warnings", children: visibleWarnings.map((warning, i) => /* @__PURE__ */ jsx14("span", { className: "forma-field__warning", children: warning.message }, i)) })
663
+ ] });
664
+ }
665
+
666
+ // src/defaults/layout/FormLayout.tsx
667
+ import { jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
668
+ function FormLayout({
669
+ children,
670
+ onSubmit,
671
+ isSubmitting
672
+ }) {
673
+ return /* @__PURE__ */ jsxs12(
674
+ "form",
675
+ {
676
+ className: "forma-form",
677
+ onSubmit: (e) => {
678
+ e.preventDefault();
679
+ onSubmit();
680
+ },
681
+ noValidate: true,
682
+ children: [
683
+ children,
684
+ /* @__PURE__ */ jsx15("div", { className: "forma-form__actions", children: /* @__PURE__ */ jsx15(
685
+ "button",
686
+ {
687
+ type: "submit",
688
+ className: `forma-button forma-button--primary forma-submit${isSubmitting ? " forma-submit--loading" : ""}`,
689
+ disabled: isSubmitting,
690
+ "aria-busy": isSubmitting || void 0,
691
+ children: isSubmitting ? "Submitting..." : "Submit"
692
+ }
693
+ ) })
694
+ ]
695
+ }
696
+ );
697
+ }
698
+
699
+ // src/defaults/layout/WizardLayout.tsx
700
+ import { useCallback } from "react";
701
+ import { jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
702
+ function WizardLayout({
703
+ children,
704
+ onSubmit,
705
+ isSubmitting
706
+ }) {
707
+ const { wizard } = useFormaContext();
708
+ const handleNext = useCallback(() => {
709
+ if (!wizard) return;
710
+ wizard.touchCurrentPageFields();
711
+ if (wizard.validateCurrentPage()) {
712
+ wizard.nextPage();
713
+ }
714
+ }, [wizard]);
715
+ const handleSubmit = useCallback(
716
+ (e) => {
717
+ e.preventDefault();
718
+ if (!wizard) {
719
+ onSubmit();
720
+ return;
721
+ }
722
+ const nativeEvent = e.nativeEvent;
723
+ const submitter = nativeEvent.submitter;
724
+ if (wizard.isLastPage && (submitter == null ? void 0 : submitter.dataset.action) === "submit") {
725
+ onSubmit();
726
+ } else if (!wizard.isLastPage) {
727
+ handleNext();
728
+ }
729
+ },
730
+ [wizard, onSubmit, handleNext]
731
+ );
732
+ if (!wizard) {
733
+ return /* @__PURE__ */ jsxs13(
734
+ "form",
735
+ {
736
+ className: "forma-form",
737
+ onSubmit: handleSubmit,
738
+ noValidate: true,
739
+ children: [
740
+ children,
741
+ /* @__PURE__ */ jsx16("div", { className: "forma-form__actions", children: /* @__PURE__ */ jsx16(
742
+ "button",
743
+ {
744
+ type: "submit",
745
+ className: "forma-button forma-button--primary forma-submit",
746
+ disabled: isSubmitting,
747
+ "aria-busy": isSubmitting || void 0,
748
+ children: isSubmitting ? "Submitting..." : "Submit"
749
+ }
750
+ ) })
751
+ ]
752
+ }
753
+ );
754
+ }
755
+ return /* @__PURE__ */ jsxs13("form", { className: "forma-wizard", onSubmit: handleSubmit, noValidate: true, children: [
756
+ /* @__PURE__ */ jsx16("div", { className: "forma-wizard__steps", role: "navigation", "aria-label": "Form progress", children: wizard.pages.map((page, index) => {
757
+ const isCompleted = index < wizard.currentPageIndex;
758
+ const isCurrent = index === wizard.currentPageIndex;
759
+ const stepClass = [
760
+ "forma-step",
761
+ isCompleted && "forma-step--completed",
762
+ isCurrent && "forma-step--current",
763
+ !isCompleted && !isCurrent && "forma-step--upcoming"
764
+ ].filter(Boolean).join(" ");
765
+ return /* @__PURE__ */ jsxs13("div", { className: stepClass, "aria-current": isCurrent ? "step" : void 0, children: [
766
+ /* @__PURE__ */ jsx16("span", { className: "forma-step__indicator", children: isCompleted ? "\u2713" : index + 1 }),
767
+ /* @__PURE__ */ jsx16("span", { className: "forma-step__label", children: page.title })
768
+ ] }, page.id);
769
+ }) }),
770
+ children,
771
+ /* @__PURE__ */ jsxs13("div", { className: "forma-wizard__nav", children: [
772
+ wizard.hasPreviousPage ? /* @__PURE__ */ jsx16(
773
+ "button",
774
+ {
775
+ type: "button",
776
+ className: "forma-button forma-button--secondary",
777
+ onClick: () => wizard.previousPage(),
778
+ children: "Previous"
779
+ }
780
+ ) : /* @__PURE__ */ jsx16("div", {}),
781
+ wizard.isLastPage ? /* @__PURE__ */ jsx16(
782
+ "button",
783
+ {
784
+ type: "submit",
785
+ "data-action": "submit",
786
+ className: `forma-button forma-button--primary forma-submit${isSubmitting ? " forma-submit--loading" : ""}`,
787
+ disabled: isSubmitting,
788
+ "aria-busy": isSubmitting || void 0,
789
+ children: isSubmitting ? "Submitting..." : "Submit"
790
+ }
791
+ ) : /* @__PURE__ */ jsx16(
792
+ "button",
793
+ {
794
+ type: "button",
795
+ className: "forma-button forma-button--primary",
796
+ onClick: handleNext,
797
+ children: "Next"
798
+ }
799
+ )
800
+ ] })
801
+ ] });
802
+ }
803
+
804
+ // src/defaults/layout/PageWrapper.tsx
805
+ import { jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
806
+ function PageWrapper({
807
+ title,
808
+ description,
809
+ children
810
+ }) {
811
+ return /* @__PURE__ */ jsxs14("div", { className: "forma-page", children: [
812
+ title && /* @__PURE__ */ jsx17("h2", { className: "forma-page__title", children: title }),
813
+ description && /* @__PURE__ */ jsx17("p", { className: "forma-page__description", children: description }),
814
+ children
815
+ ] });
816
+ }
817
+
818
+ // src/defaults/componentMap.ts
819
+ var defaultComponentMap = {
820
+ text: TextInput,
821
+ email: TextInput,
822
+ phone: TextInput,
823
+ url: TextInput,
824
+ password: TextInput,
825
+ textarea: TextareaInput,
826
+ number: NumberInput,
827
+ integer: IntegerInput,
828
+ boolean: BooleanInput,
829
+ date: DateInput,
830
+ datetime: DateTimeInput,
831
+ select: SelectInput,
832
+ multiselect: MultiSelectInput,
833
+ array: ArrayField,
834
+ object: ObjectField,
835
+ computed: ComputedDisplay,
836
+ display: DisplayField,
837
+ matrix: MatrixField,
838
+ fallback: FallbackField
839
+ };
840
+ var defaultFieldWrapper = FieldWrapper;
841
+ var defaultLayout = FormLayout;
842
+ var defaultWizardLayout = WizardLayout;
843
+ var defaultPageWrapper = PageWrapper;
844
+
845
+ // src/defaults/DefaultFormRenderer.tsx
846
+ import { jsx as jsx18 } from "react/jsx-runtime";
847
+ var DefaultFormRenderer = forwardRef(function DefaultFormRenderer2(props, ref) {
848
+ const {
849
+ components,
850
+ wizardLayout,
851
+ layout,
852
+ fieldWrapper,
853
+ pageWrapper,
854
+ ...rest
855
+ } = props;
856
+ return /* @__PURE__ */ jsx18(
857
+ FormRenderer,
858
+ {
859
+ ref,
860
+ components: components ?? defaultComponentMap,
861
+ fieldWrapper: fieldWrapper ?? defaultFieldWrapper,
862
+ layout: layout ?? (wizardLayout ? defaultWizardLayout : defaultLayout),
863
+ pageWrapper: pageWrapper ?? defaultPageWrapper,
864
+ ...rest
865
+ }
866
+ );
867
+ });
868
+ export {
869
+ ArrayField,
870
+ BooleanInput,
871
+ ComputedDisplay,
872
+ DateInput,
873
+ DateTimeInput,
874
+ DefaultFormRenderer,
875
+ DisplayField,
876
+ FallbackField,
877
+ FieldWrapper,
878
+ FormLayout,
879
+ IntegerInput,
880
+ MatrixField,
881
+ MultiSelectInput,
882
+ NumberInput,
883
+ ObjectField,
884
+ PageWrapper,
885
+ SelectInput,
886
+ TextInput,
887
+ TextareaInput,
888
+ WizardLayout,
889
+ defaultComponentMap,
890
+ defaultFieldWrapper,
891
+ defaultLayout,
892
+ defaultPageWrapper,
893
+ defaultWizardLayout
894
+ };
895
+ //# sourceMappingURL=index.js.map