@bluprynt/forms-viewer 1.0.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.
@@ -0,0 +1,194 @@
1
+ import { ArrayItemDef, ArrayItemDef as ArrayItemDef$1, ContentItem, DocumentError, DocumentValidationError, DocumentValidationError as DocumentValidationError$1, DocumentValidationErrorCode, FieldContentItem, FieldContentItem as FieldContentItem$1, FieldEntry, FieldEntry as FieldEntry$1, FieldType, FieldValidationError, FieldValidationError as FieldValidationError$1, FileValue, FileValue as FileValue$1, FormDefinition, FormDefinition as FormDefinition$1, FormDocument, FormDocument as FormDocument$1, FormEngine, FormSnapshot, FormValidationResult, FormValidationResult as FormValidationResult$1, FormValues, FormValuesEditor, SectionContentItem, SectionContentItem as SectionContentItem$1, SelectOption, SelectOption as SelectOption$1 } from "@bluprynt/forms-core";
2
+ import { ComponentType, FC, PropsWithChildren, ReactNode } from "react";
3
+
4
+ //#region src/constants.d.ts
5
+ declare const ROOT: unique symbol;
6
+ type ROOT = typeof ROOT;
7
+ //#endregion
8
+ //#region src/form-context.d.ts
9
+ type FormContextValue = {
10
+ definition: FormDefinition$1 | undefined;
11
+ data: FormDocument$1 | undefined;
12
+ engine: FormEngine | undefined;
13
+ visibilityMap: Map<number, boolean>;
14
+ validation: FormValidationResult$1 | undefined;
15
+ documentErrors: readonly DocumentValidationError$1[] | undefined;
16
+ fieldErrors: Map<number, FieldValidationError$1[]>;
17
+ section: typeof ROOT | number | undefined;
18
+ showInlineValidation: boolean;
19
+ };
20
+ declare const useFormContext: () => FormContextValue;
21
+ type FormProps = {
22
+ definition?: FormDefinition$1;
23
+ data?: FormDocument$1;
24
+ section?: typeof ROOT | number;
25
+ showInlineValidation?: boolean;
26
+ children: ReactNode;
27
+ };
28
+ declare const Form: FC<FormProps>;
29
+ //#endregion
30
+ //#region src/form-document-validation.d.ts
31
+ type FormDocumentValidationProps = {
32
+ container: ComponentType<PropsWithChildren>;
33
+ error: ComponentType<DocumentValidationError$1>;
34
+ };
35
+ declare const FormDocumentValidation: FC<FormDocumentValidationProps>;
36
+ //#endregion
37
+ //#region src/types/base.d.ts
38
+ type BaseViewFieldProps = {
39
+ field: FieldContentItem$1;
40
+ errors: FieldValidationError$1[];
41
+ };
42
+ type BaseEditFieldProps = BaseViewFieldProps & {
43
+ onChange: (value: unknown) => void;
44
+ };
45
+ type ErrorProps = {
46
+ errors: FieldValidationError$1[];
47
+ field: FieldContentItem$1;
48
+ };
49
+ //#endregion
50
+ //#region src/types/editor.d.ts
51
+ type StringEditProps = BaseEditFieldProps & {
52
+ value: string | undefined;
53
+ onChange: (value: string | undefined) => void;
54
+ };
55
+ type NumberEditProps = BaseEditFieldProps & {
56
+ value: number | undefined;
57
+ onChange: (value: number | undefined) => void;
58
+ };
59
+ type BooleanEditProps = BaseEditFieldProps & {
60
+ value: boolean | undefined;
61
+ onChange: (value: boolean | undefined) => void;
62
+ };
63
+ type DateEditProps = BaseEditFieldProps & {
64
+ value: string | undefined;
65
+ onChange: (value: string | undefined) => void;
66
+ };
67
+ type SelectEditProps = BaseEditFieldProps & {
68
+ value: string | number | undefined;
69
+ options: SelectOption$1[];
70
+ onChange: (value: string | number | undefined) => void;
71
+ };
72
+ type ArrayEditProps = BaseEditFieldProps & {
73
+ value: unknown[] | undefined;
74
+ itemDef: ArrayItemDef$1;
75
+ children: ReactNode[];
76
+ onAddItem: () => void;
77
+ onRemoveItem: (index: number) => void;
78
+ onMoveItem: (fromIndex: number, toIndex: number) => void;
79
+ };
80
+ type FileEditProps = BaseEditFieldProps & {
81
+ value: FileValue$1 | undefined;
82
+ onChange: (value: FileValue$1 | undefined) => void;
83
+ };
84
+ type SectionEditProps = {
85
+ section: SectionContentItem$1;
86
+ children: ReactNode;
87
+ };
88
+ type EditorComponentMap = {
89
+ string: ComponentType<StringEditProps>;
90
+ number: ComponentType<NumberEditProps>;
91
+ boolean: ComponentType<BooleanEditProps>;
92
+ date: ComponentType<DateEditProps>;
93
+ select: ComponentType<SelectEditProps>;
94
+ array: ComponentType<ArrayEditProps>;
95
+ file: ComponentType<FileEditProps>;
96
+ section: ComponentType<SectionEditProps>;
97
+ error?: ComponentType<ErrorProps>;
98
+ };
99
+ type EditorFieldProps = {
100
+ onChange: (value: unknown) => void;
101
+ onAddItem?: () => void;
102
+ onRemoveItem?: (index: number) => void;
103
+ onMoveItem?: (fromIndex: number, toIndex: number) => void;
104
+ };
105
+ type EditorArrayItemProps = {
106
+ onChange: (value: unknown) => void;
107
+ };
108
+ //#endregion
109
+ //#region src/types/viewer.d.ts
110
+ type StringViewProps = BaseViewFieldProps & {
111
+ value: string | undefined;
112
+ };
113
+ type NumberViewProps = BaseViewFieldProps & {
114
+ value: number | undefined;
115
+ };
116
+ type BooleanViewProps = BaseViewFieldProps & {
117
+ value: boolean | undefined;
118
+ };
119
+ type DateViewProps = BaseViewFieldProps & {
120
+ value: string | undefined;
121
+ };
122
+ type SelectViewProps = BaseViewFieldProps & {
123
+ value: string | number | undefined;
124
+ options: SelectOption$1[];
125
+ };
126
+ type ArrayViewProps = BaseViewFieldProps & {
127
+ value: unknown[] | undefined;
128
+ itemDef: ArrayItemDef$1;
129
+ children: ReactNode[];
130
+ };
131
+ type FileViewProps = BaseViewFieldProps & {
132
+ value: FileValue$1 | undefined;
133
+ };
134
+ type SectionViewProps = {
135
+ section: SectionContentItem$1;
136
+ children: ReactNode;
137
+ };
138
+ type ViewerComponentMap = {
139
+ string: ComponentType<StringViewProps>;
140
+ number: ComponentType<NumberViewProps>;
141
+ boolean: ComponentType<BooleanViewProps>;
142
+ date: ComponentType<DateViewProps>;
143
+ select: ComponentType<SelectViewProps>;
144
+ array: ComponentType<ArrayViewProps>;
145
+ file: ComponentType<FileViewProps>;
146
+ section: ComponentType<SectionViewProps>;
147
+ error?: ComponentType<ErrorProps>;
148
+ };
149
+ //#endregion
150
+ //#region src/form-editor.d.ts
151
+ type FormEditorProps = {
152
+ components: EditorComponentMap;
153
+ onChange: (data: FormDocument$1, validation: FormValidationResult$1) => void;
154
+ };
155
+ declare const FormEditor: FC<FormEditorProps>;
156
+ //#endregion
157
+ //#region src/form-fields-validation.d.ts
158
+ type FieldValidationFieldEntry = {
159
+ field: FieldEntry$1 | undefined;
160
+ errors: FieldValidationError$1[];
161
+ };
162
+ type FormFieldsValidationProps = {
163
+ container: ComponentType<PropsWithChildren>;
164
+ field: ComponentType<PropsWithChildren<FieldValidationFieldEntry>>;
165
+ error: ComponentType<FieldValidationError$1>;
166
+ };
167
+ declare const FormFieldsValidation: FC<FormFieldsValidationProps>;
168
+ //#endregion
169
+ //#region src/form-sections.d.ts
170
+ type FormSectionEntry = Omit<SectionContentItem$1, 'id' | 'type'> & {
171
+ id: typeof ROOT | number;
172
+ };
173
+ type FormSectionItemProps = {
174
+ section: FormSectionEntry;
175
+ active: boolean;
176
+ select: () => void;
177
+ };
178
+ type FormSectionsProps = {
179
+ container: ComponentType<PropsWithChildren>;
180
+ item: ComponentType<FormSectionItemProps>;
181
+ defaultSectionTitle?: string;
182
+ defaultSectionDescription?: string;
183
+ onSelect?: (id: typeof ROOT | number) => void;
184
+ };
185
+ declare const FormSections: FC<FormSectionsProps>;
186
+ //#endregion
187
+ //#region src/form-viewer.d.ts
188
+ type FormViewerProps = {
189
+ components: ViewerComponentMap;
190
+ };
191
+ declare const FormViewer: FC<FormViewerProps>;
192
+ //#endregion
193
+ export { type ArrayEditProps, type ArrayItemDef, type ArrayViewProps, type BaseEditFieldProps, type BaseViewFieldProps, type BooleanEditProps, type BooleanViewProps, type ContentItem, type DateEditProps, type DateViewProps, DocumentError, type DocumentValidationError, type DocumentValidationErrorCode, type EditorArrayItemProps, type EditorComponentMap, type EditorFieldProps, type ErrorProps, type FieldContentItem, type FieldEntry, type FieldType, type FieldValidationError, type FieldValidationFieldEntry, type FileEditProps, type FileValue, type FileViewProps, Form, type FormDefinition, type FormDocument, FormDocumentValidation, FormEditor, FormFieldsValidation, type FormSectionEntry, type FormSectionItemProps, FormSections, type FormSnapshot, type FormValidationResult, type FormValues, FormValuesEditor, FormViewer, type NumberEditProps, type NumberViewProps, ROOT, type SectionContentItem, type SectionEditProps, type SectionViewProps, type SelectEditProps, type SelectOption, type SelectViewProps, type StringEditProps, type StringViewProps, type ViewerComponentMap, useFormContext };
194
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/constants.ts","../src/form-context.tsx","../src/form-document-validation.tsx","../src/types/base.ts","../src/types/editor.ts","../src/types/viewer.ts","../src/form-editor.tsx","../src/form-fields-validation.tsx","../src/form-sections.tsx","../src/form-viewer.tsx"],"mappings":"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"}
package/dist/index.mjs ADDED
@@ -0,0 +1,470 @@
1
+ import { DocumentError, DocumentError as DocumentError$1, FormEngine, FormValuesEditor } from "@bluprynt/forms-core";
2
+ import { Fragment, createContext, useCallback, useContext, useMemo, useRef } from "react";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ //#region src/constants.ts
5
+ const ROOT = Symbol("ROOT");
6
+ //#endregion
7
+ //#region src/form-context.tsx
8
+ const FormContext = createContext(void 0);
9
+ const useFormContext = () => {
10
+ const context = useContext(FormContext);
11
+ if (!context) throw new Error("useFormContext must be used within a <Form> provider");
12
+ return context;
13
+ };
14
+ const Form = ({ definition, data, section, showInlineValidation = true, children }) => {
15
+ const { engine, documentErrors: definitionErrors } = useMemo(() => {
16
+ if (!definition) return {};
17
+ try {
18
+ return { engine: new FormEngine(definition) };
19
+ } catch (error) {
20
+ if (error instanceof DocumentError$1) return { documentErrors: error.errors };
21
+ throw error;
22
+ }
23
+ }, [definition]);
24
+ const { visibilityMap, validation, documentErrors, fieldErrors } = useMemo(() => {
25
+ const visibilityMap = engine && data ? engine.getVisibilityMap(data) : /* @__PURE__ */ new Map();
26
+ const validation = engine && data ? engine.validate(data) : {
27
+ valid: true,
28
+ fieldErrors: /* @__PURE__ */ new Map()
29
+ };
30
+ return {
31
+ visibilityMap,
32
+ validation,
33
+ documentErrors: [...definitionErrors ?? [], ...validation?.documentErrors ?? []],
34
+ fieldErrors: validation?.fieldErrors ?? /* @__PURE__ */ new Map()
35
+ };
36
+ }, [
37
+ engine,
38
+ data,
39
+ definitionErrors
40
+ ]);
41
+ const value = {
42
+ definition,
43
+ data,
44
+ engine,
45
+ visibilityMap,
46
+ validation,
47
+ documentErrors,
48
+ fieldErrors,
49
+ section,
50
+ showInlineValidation
51
+ };
52
+ return /* @__PURE__ */ jsx(FormContext.Provider, {
53
+ value,
54
+ children
55
+ });
56
+ };
57
+ //#endregion
58
+ //#region src/form-document-validation.tsx
59
+ const FormDocumentValidation = ({ container: ContainerComponent, error: ErrorComponent }) => {
60
+ const { documentErrors } = useFormContext();
61
+ if (!documentErrors?.length) return null;
62
+ return /* @__PURE__ */ jsx(ContainerComponent, { children: documentErrors.map((error, index) => /* @__PURE__ */ jsx(ErrorComponent, { ...error }, `error-${error.code}-${error.itemId ?? ""}-${index}`)) });
63
+ };
64
+ //#endregion
65
+ //#region src/form/utils.ts
66
+ /**
67
+ * Retrieves validation errors for a specific field from the pre-indexed error map.
68
+ *
69
+ * Uses O(1) map lookup by `fieldId`. When `itemIndex` is omitted, returns only
70
+ * **field-level** errors (those without an `itemIndex`). When `itemIndex` is
71
+ * provided, returns only **item-level** errors matching the given array item index.
72
+ *
73
+ * @param fieldErrors - Map from field id to its validation errors.
74
+ * @param fieldId - The numeric ID of the field to retrieve errors for.
75
+ * @param itemIndex - Optional zero-based index of the array item. When omitted, only
76
+ * field-level errors (where `itemIndex` is `null` / `undefined`) are returned.
77
+ * @returns Errors for the given field (and optionally item index). Empty array if none.
78
+ *
79
+ * @example
80
+ * ```ts
81
+ * // Field-level errors only (no itemIndex on the error)
82
+ * getFieldErrors(fieldErrors, 5)
83
+ *
84
+ * // Errors for the third item of array field 5
85
+ * getFieldErrors(fieldErrors, 5, 2)
86
+ * ```
87
+ */
88
+ const getFieldErrors = (fieldErrors, fieldId, itemIndex) => {
89
+ const errors = fieldErrors.get(fieldId) ?? [];
90
+ if (itemIndex === void 0) return errors.filter((e) => e.itemIndex == null);
91
+ return errors.filter((e) => e.itemIndex === itemIndex);
92
+ };
93
+ /**
94
+ * Creates a synthetic {@link FieldContentItem} that represents a single item inside an array field.
95
+ *
96
+ * Array fields store their per-item schema in `arrayField.item` ({@link ArrayItemDef}).
97
+ * Components that render individual array items need a standard `FieldContentItem` shape,
98
+ * so this helper projects the item definition into one, preserving the parent field's `id`
99
+ * while adopting the item's `type`, `label`, `description`, `validation`, and `options`.
100
+ *
101
+ * @param arrayField - The array field definition whose `.item` property describes the item schema.
102
+ * @param _index - The zero-based position of the item in the array (reserved for future use,
103
+ * e.g. per-item overrides).
104
+ * @returns A `FieldContentItem` that can be passed directly to a typed field component.
105
+ *
106
+ * @example
107
+ * ```ts
108
+ * const arrayField: FieldContentItem = {
109
+ * id: 10, type: 'array', label: 'Tags',
110
+ * item: { type: 'string', label: 'Tag' },
111
+ * }
112
+ *
113
+ * const itemDef = getArrayField(arrayField, 0)
114
+ * // → { id: 10, type: 'string', label: 'Tag', ... }
115
+ * ```
116
+ */
117
+ const getArrayField = (arrayField, _index) => {
118
+ const itemDef = arrayField.item;
119
+ return {
120
+ id: arrayField.id,
121
+ type: itemDef.type,
122
+ label: itemDef.label,
123
+ description: itemDef.description,
124
+ validation: itemDef.validation,
125
+ options: itemDef.options
126
+ };
127
+ };
128
+ /**
129
+ * Recursively searches content items for a section with the given id.
130
+ *
131
+ * Walks the content tree depth-first, checking sections and their nested
132
+ * content. Returns the first matching {@link SectionContentItem}, or
133
+ * `undefined` if no section with that id exists.
134
+ *
135
+ * @param items - The content items to search through.
136
+ * @param sectionId - The numeric id of the section to find.
137
+ * @returns The matching section, or `undefined` if not found.
138
+ *
139
+ * @example
140
+ * ```ts
141
+ * const section = findSection(definition.content, 42)
142
+ * if (section) {
143
+ * // render section.content
144
+ * }
145
+ * ```
146
+ */
147
+ const findSection = (items, sectionId) => {
148
+ for (const item of items) if (item.type === "section") {
149
+ if (item.id === sectionId) return item;
150
+ const found = findSection(item.content, sectionId);
151
+ if (found) return found;
152
+ }
153
+ };
154
+ //#endregion
155
+ //#region src/form/form-items.tsx
156
+ const FormItems = ({ items, visibilityMap, values, fieldErrors, components, showInlineValidation, renderFieldProps, renderArrayItemProps }) => {
157
+ const SectionComponent = components.section;
158
+ const ErrorComponent = showInlineValidation ? components.error : void 0;
159
+ const elements = [];
160
+ for (const item of items) {
161
+ if (!visibilityMap.get(item.id)) continue;
162
+ if (item.type === "section") elements.push(/* @__PURE__ */ jsx(SectionComponent, {
163
+ section: item,
164
+ children: /* @__PURE__ */ jsx(FormItems, {
165
+ items: item.content,
166
+ visibilityMap,
167
+ values,
168
+ fieldErrors,
169
+ components,
170
+ showInlineValidation,
171
+ renderFieldProps,
172
+ renderArrayItemProps
173
+ })
174
+ }, item.id));
175
+ else if (item.type === "array") {
176
+ const errors = getFieldErrors(fieldErrors, item.id);
177
+ const modeProps = renderFieldProps?.(item) ?? {};
178
+ const ArrayComponent = components.array;
179
+ const arrayValue = values[String(item.id)] ?? [];
180
+ const itemDef = item.item;
181
+ const ItemComponent = components[itemDef.type];
182
+ elements.push(/* @__PURE__ */ jsx(ArrayComponent, {
183
+ field: item,
184
+ value: values[String(item.id)],
185
+ itemDef: item.item,
186
+ errors,
187
+ ...modeProps,
188
+ children: ItemComponent ? arrayValue.map((itemValue, index) => {
189
+ const itemErrors = getFieldErrors(fieldErrors, item.id, index);
190
+ const synthetic = getArrayField(item, index);
191
+ const baseProps = {
192
+ field: synthetic,
193
+ value: itemValue,
194
+ errors: itemErrors,
195
+ ...renderArrayItemProps?.(item, index) ?? {}
196
+ };
197
+ if (itemDef.type === "select") baseProps.options = itemDef.options ?? [];
198
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(ItemComponent, { ...baseProps }), ErrorComponent && itemErrors.length > 0 && /* @__PURE__ */ jsx(ErrorComponent, {
199
+ errors: itemErrors,
200
+ field: synthetic
201
+ })] }, `${item.id}-${index}`);
202
+ }) : null
203
+ }, item.id));
204
+ if (ErrorComponent && errors.length > 0) elements.push(/* @__PURE__ */ jsx(ErrorComponent, {
205
+ errors,
206
+ field: item
207
+ }, `${item.id}-error`));
208
+ } else {
209
+ const errors = getFieldErrors(fieldErrors, item.id);
210
+ const modeProps = renderFieldProps?.(item) ?? {};
211
+ const fieldProps = {
212
+ field: item,
213
+ value: values[String(item.id)],
214
+ errors,
215
+ ...modeProps
216
+ };
217
+ if (item.type === "select") fieldProps.options = item.options ?? [];
218
+ const FieldComponent = components[item.type];
219
+ elements.push(/* @__PURE__ */ jsx(FieldComponent, { ...fieldProps }, item.id));
220
+ if (ErrorComponent && errors.length > 0) elements.push(/* @__PURE__ */ jsx(ErrorComponent, {
221
+ errors,
222
+ field: item
223
+ }, `${item.id}-error`));
224
+ }
225
+ }
226
+ if (elements.length === 0) return null;
227
+ return elements;
228
+ };
229
+ //#endregion
230
+ //#region src/form/form-content.tsx
231
+ const FormContent = (props) => {
232
+ const { items, visibilityMap, values, fieldErrors, section, showInlineValidation } = props;
233
+ const sharedProps = props.mode === "editor" ? {
234
+ visibilityMap,
235
+ values,
236
+ fieldErrors,
237
+ components: props.components,
238
+ showInlineValidation,
239
+ renderFieldProps: props.renderFieldProps,
240
+ renderArrayItemProps: props.renderArrayItemProps
241
+ } : {
242
+ visibilityMap,
243
+ values,
244
+ fieldErrors,
245
+ components: props.components,
246
+ showInlineValidation
247
+ };
248
+ if (section === void 0) return /* @__PURE__ */ jsx(FormItems, {
249
+ items,
250
+ ...sharedProps
251
+ });
252
+ if (section === ROOT) return /* @__PURE__ */ jsx(FormItems, {
253
+ items: items.filter((item) => item.type !== "section"),
254
+ ...sharedProps
255
+ });
256
+ const foundSection = findSection(items, section);
257
+ if (!foundSection || !visibilityMap.get(foundSection.id)) return null;
258
+ const Section = props.components.section;
259
+ return /* @__PURE__ */ jsx(Section, {
260
+ section: foundSection,
261
+ children: /* @__PURE__ */ jsx(FormItems, {
262
+ items: foundSection.content,
263
+ ...sharedProps
264
+ })
265
+ }, foundSection.id);
266
+ };
267
+ //#endregion
268
+ //#region src/form-editor.tsx
269
+ const FormEditor = ({ components, onChange }) => {
270
+ const { definition, data, visibilityMap, fieldErrors, section, engine, showInlineValidation, documentErrors } = useFormContext();
271
+ const { renderFieldProps, renderArrayItemProps } = useEditorHandlers(engine, data, onChange);
272
+ if (!definition || !data || documentErrors && documentErrors.length > 0) return null;
273
+ return /* @__PURE__ */ jsx(FormContent, {
274
+ mode: "editor",
275
+ items: definition.content,
276
+ visibilityMap,
277
+ values: data.values,
278
+ fieldErrors,
279
+ components,
280
+ section,
281
+ showInlineValidation,
282
+ renderFieldProps,
283
+ renderArrayItemProps
284
+ });
285
+ };
286
+ const useEditorHandlers = (engine, data, onChange) => {
287
+ const stateRef = useRef({
288
+ data,
289
+ onChange,
290
+ engine
291
+ });
292
+ stateRef.current = {
293
+ data,
294
+ onChange,
295
+ engine
296
+ };
297
+ const fieldOnChangeMap = useRef(/* @__PURE__ */ new Map());
298
+ const getFieldOnChange = useCallback((fieldId) => {
299
+ const key = String(fieldId);
300
+ let handler = fieldOnChangeMap.current.get(key);
301
+ if (!handler) {
302
+ handler = (newValue) => {
303
+ const { data, onChange, engine } = stateRef.current;
304
+ if (!data || !engine) return;
305
+ const document = {
306
+ ...data,
307
+ values: {
308
+ ...data.values,
309
+ [key]: newValue
310
+ }
311
+ };
312
+ onChange(document, engine.validate(document));
313
+ };
314
+ fieldOnChangeMap.current.set(key, handler);
315
+ }
316
+ return handler;
317
+ }, []);
318
+ const arrayHandlersMap = useRef(/* @__PURE__ */ new Map());
319
+ const getArrayHandlers = useCallback((fieldId) => {
320
+ const key = String(fieldId);
321
+ let handlers = arrayHandlersMap.current.get(key);
322
+ if (!handlers) {
323
+ const fire = (mutate) => {
324
+ const { data, onChange, engine } = stateRef.current;
325
+ if (!data || !engine) return;
326
+ const items = data.values[key] ?? [];
327
+ const document = {
328
+ ...data,
329
+ values: {
330
+ ...data.values,
331
+ [key]: mutate([...items])
332
+ }
333
+ };
334
+ onChange(document, engine.validate(document));
335
+ };
336
+ handlers = {
337
+ onAddItem: () => fire((values) => {
338
+ values.push(void 0);
339
+ return values;
340
+ }),
341
+ onRemoveItem: (index) => fire((values) => {
342
+ values.splice(index, 1);
343
+ return values;
344
+ }),
345
+ onMoveItem: (fromIndex, toIndex) => fire((values) => {
346
+ const [moved] = values.splice(fromIndex, 1);
347
+ values.splice(toIndex, 0, moved);
348
+ return values;
349
+ })
350
+ };
351
+ arrayHandlersMap.current.set(key, handlers);
352
+ }
353
+ return handlers;
354
+ }, []);
355
+ const arrayItemOnChangeMap = useRef(/* @__PURE__ */ new Map());
356
+ const getArrayItemOnChange = useCallback((fieldId, index) => {
357
+ const mapKey = `${fieldId}-${index}`;
358
+ let handler = arrayItemOnChangeMap.current.get(mapKey);
359
+ if (!handler) {
360
+ handler = (newValue) => {
361
+ const fieldKey = String(fieldId);
362
+ const { data, onChange, engine } = stateRef.current;
363
+ if (!data || !engine) return;
364
+ const items = [...data.values[fieldKey] ?? []];
365
+ items[index] = newValue;
366
+ const document = {
367
+ ...data,
368
+ values: {
369
+ ...data.values,
370
+ [fieldKey]: items
371
+ }
372
+ };
373
+ onChange(document, engine.validate(document));
374
+ };
375
+ arrayItemOnChangeMap.current.set(mapKey, handler);
376
+ }
377
+ return handler;
378
+ }, []);
379
+ return {
380
+ renderFieldProps: (field) => {
381
+ if (field.type === "array") return {
382
+ onChange: getFieldOnChange(field.id),
383
+ ...getArrayHandlers(field.id)
384
+ };
385
+ return { onChange: getFieldOnChange(field.id) };
386
+ },
387
+ renderArrayItemProps: (field, index) => ({ onChange: getArrayItemOnChange(field.id, index) })
388
+ };
389
+ };
390
+ //#endregion
391
+ //#region src/form-fields-validation.tsx
392
+ const FormFieldsValidation = ({ container: ContainerComponent, field: FieldComponent, error: ErrorComponent }) => {
393
+ const { engine, data, fieldErrors, visibilityMap } = useFormContext();
394
+ const groups = useMemo(() => {
395
+ if (!engine || !data || fieldErrors.size === 0) return [];
396
+ const result = [];
397
+ for (const [fieldId, errors] of fieldErrors) {
398
+ if (visibilityMap.get(fieldId) === false) continue;
399
+ result.push({
400
+ field: engine.getFieldDef(fieldId),
401
+ errors
402
+ });
403
+ }
404
+ return result;
405
+ }, [
406
+ engine,
407
+ data,
408
+ fieldErrors,
409
+ visibilityMap
410
+ ]);
411
+ if (groups.length === 0) return null;
412
+ return /* @__PURE__ */ jsx(ContainerComponent, { children: groups.map((group) => /* @__PURE__ */ jsx(FieldComponent, {
413
+ ...group,
414
+ children: group.errors.map((error) => /* @__PURE__ */ jsx(ErrorComponent, { ...error }, `${error.fieldId}-${error.rule}-${error.itemIndex ?? ""}`))
415
+ }, group.field?.id)) });
416
+ };
417
+ //#endregion
418
+ //#region src/form-sections.tsx
419
+ const FormSections = ({ container: Container, item: Item, defaultSectionTitle = "General", defaultSectionDescription, onSelect }) => {
420
+ const { definition, section, visibilityMap, data, documentErrors } = useFormContext();
421
+ const entries = useMemo(() => {
422
+ if (!definition) return [];
423
+ const result = [];
424
+ const rootFields = definition.content.filter((item) => item.type !== "section");
425
+ if (rootFields.some((item) => visibilityMap.get(item.id))) result.push({
426
+ id: ROOT,
427
+ title: defaultSectionTitle,
428
+ description: defaultSectionDescription,
429
+ content: rootFields,
430
+ condition: void 0
431
+ });
432
+ for (const item of definition.content) if (item.type === "section") {
433
+ if (visibilityMap.get(item.id) === false) continue;
434
+ result.push(item);
435
+ }
436
+ return result;
437
+ }, [
438
+ definition?.content,
439
+ visibilityMap,
440
+ data,
441
+ defaultSectionTitle,
442
+ defaultSectionDescription
443
+ ]);
444
+ if (!definition || !data || documentErrors && documentErrors.length > 0) return null;
445
+ return /* @__PURE__ */ jsx(Container, { children: entries.map((entry) => /* @__PURE__ */ jsx(Item, {
446
+ section: entry,
447
+ active: entry.id === section,
448
+ select: () => onSelect?.(entry.id)
449
+ }, entry.id === ROOT ? "root" : String(entry.id))) });
450
+ };
451
+ //#endregion
452
+ //#region src/form-viewer.tsx
453
+ const FormViewer = ({ components }) => {
454
+ const { definition, data, visibilityMap, fieldErrors, section, showInlineValidation, documentErrors } = useFormContext();
455
+ if (!definition || !data || documentErrors && documentErrors.length > 0) return null;
456
+ return /* @__PURE__ */ jsx(FormContent, {
457
+ mode: "viewer",
458
+ items: definition.content,
459
+ visibilityMap,
460
+ values: data.values,
461
+ fieldErrors,
462
+ components,
463
+ section,
464
+ showInlineValidation
465
+ });
466
+ };
467
+ //#endregion
468
+ export { DocumentError, Form, FormDocumentValidation, FormEditor, FormFieldsValidation, FormSections, FormValuesEditor, FormViewer, ROOT, useFormContext };
469
+
470
+ //# sourceMappingURL=index.mjs.map