@resistdesign/voltra 3.0.0-alpha.23 → 3.0.0-alpha.24

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,73 @@
1
+ /**
2
+ * @packageDocumentation
3
+ *
4
+ * Shared AutoForm UI components driven by renderer suites.
5
+ */
6
+ import { FC } from "react";
7
+ import type { ReactElement } from "react";
8
+ import type { TypeInfo, TypeOperation } from "../../common/TypeParsing/TypeInfo";
9
+ import type { AutoFieldProps, CustomTypeActionPayload, FormController, FormValues, RelationActionPayload } from "./types";
10
+ import type { ResolvedSuite } from "./core/types";
11
+ /**
12
+ * Renderer contract used by shared AutoForm components.
13
+ */
14
+ export interface AutoFormRenderer {
15
+ /** Suite-backed field component for each field controller. */
16
+ AutoField: FC<AutoFieldProps>;
17
+ /** Resolved suite that provides primitives for container controls. */
18
+ suite: ResolvedSuite<ReactElement>;
19
+ }
20
+ /**
21
+ * Props for the shared AutoFormView component.
22
+ */
23
+ export interface AutoFormViewProps {
24
+ /** Prepared controller that supplies field state. */
25
+ controller: FormController;
26
+ /** Submit handler invoked with validated form values. */
27
+ onSubmit: (values: FormValues) => void;
28
+ /** Renderer containing AutoField and suite primitives. */
29
+ renderer: AutoFormRenderer;
30
+ /** Disable the submit button when true. */
31
+ submitDisabled?: boolean;
32
+ /** Optional relation action handler for reference fields. */
33
+ onRelationAction?: (payload: RelationActionPayload) => void;
34
+ /** Optional custom type action handler. */
35
+ onCustomTypeAction?: (payload: CustomTypeActionPayload) => void;
36
+ }
37
+ /**
38
+ * Render a form UI from a prepared form controller.
39
+ *
40
+ * @param props - View props including controller and callbacks.
41
+ * @returns Rendered form view.
42
+ */
43
+ export declare const AutoFormView: FC<AutoFormViewProps>;
44
+ /**
45
+ * Props for the shared AutoForm component.
46
+ */
47
+ export interface AutoFormProps {
48
+ /** Type metadata used to build the form. */
49
+ typeInfo: TypeInfo;
50
+ /** Submit handler invoked with validated form values. */
51
+ onSubmit: (values: FormValues) => void;
52
+ /** Renderer containing AutoField and suite primitives. */
53
+ renderer: AutoFormRenderer;
54
+ /** Optional initial values applied before defaults. */
55
+ initialValues?: FormValues;
56
+ /** Optional change handler invoked when values update. */
57
+ onValuesChange?: (values: FormValues) => void;
58
+ /** Optional relation action handler for reference fields. */
59
+ onRelationAction?: (payload: RelationActionPayload) => void;
60
+ /** Optional custom type action handler. */
61
+ onCustomTypeAction?: (payload: CustomTypeActionPayload) => void;
62
+ /** Optional operation override for field state. */
63
+ operation?: TypeOperation;
64
+ /** Disable the submit button when true. */
65
+ submitDisabled?: boolean;
66
+ }
67
+ /**
68
+ * Build a controller from type metadata and render an auto form.
69
+ *
70
+ * @param props - Auto form props including type info and callbacks.
71
+ * @returns Rendered form bound to a new controller.
72
+ */
73
+ export declare const AutoForm: FC<AutoFormProps>;
@@ -110,6 +110,11 @@ export type PrimitiveComponent<Props, RenderOutput = unknown> = (props: Props) =
110
110
  * Primitive components that suites may override.
111
111
  */
112
112
  export type PrimitiveComponents<RenderOutput = unknown> = {
113
+ /** Root container for the form view. */
114
+ FormRoot: PrimitiveComponent<{
115
+ children: RenderOutput;
116
+ onSubmit?: () => void;
117
+ }, RenderOutput>;
113
118
  /** Wrapper for grouped field content. */
114
119
  FieldWrapper: PrimitiveComponent<{
115
120
  children: RenderOutput;
@@ -7,3 +7,4 @@ export * from "./types";
7
7
  export type { ComponentSuite, FieldKind, FieldRenderContext, FieldRenderer, FieldValue, PrimitiveComponent, PrimitiveComponents, ResolvedSuite, } from "./core";
8
8
  export { createAutoField, createFormRenderer, getFieldKind, mergeSuites, resolveSuite, withRendererOverride, } from "./core";
9
9
  export * from "./Engine";
10
+ export * from "./UI";
package/app/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createContext, useContext, useRef, useMemo, useCallback, useState, useEffect } from 'react';
2
- import { jsx } from 'react/jsx-runtime';
2
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
3
 
4
4
  // src/app/utils/ApplicationState.tsx
5
5
  var getApplicationStateIdentifier = (subStateIdMap) => subStateIdMap ? subStateIdMap : {};
@@ -1651,5 +1651,86 @@ var useFormEngine = (initialValues = {}, typeInfo, options) => {
1651
1651
  setErrors
1652
1652
  };
1653
1653
  };
1654
+ var fallbackFormRoot = ({
1655
+ children,
1656
+ onSubmit
1657
+ }) => {
1658
+ const handleSubmit = (event) => {
1659
+ event.preventDefault();
1660
+ onSubmit?.();
1661
+ };
1662
+ return /* @__PURE__ */ jsx("form", { onSubmit: handleSubmit, children });
1663
+ };
1664
+ var fallbackButton = ({
1665
+ children,
1666
+ disabled,
1667
+ type,
1668
+ onClick
1669
+ }) => {
1670
+ return /* @__PURE__ */ jsx("button", { type: type ?? "button", disabled, onClick, children });
1671
+ };
1672
+ var AutoFormView = ({
1673
+ controller,
1674
+ onSubmit,
1675
+ renderer,
1676
+ submitDisabled,
1677
+ onRelationAction,
1678
+ onCustomTypeAction
1679
+ }) => {
1680
+ const FormRoot = renderer.suite.primitives?.FormRoot ?? fallbackFormRoot;
1681
+ const Button = renderer.suite.primitives?.Button ?? fallbackButton;
1682
+ const AutoField = renderer.AutoField;
1683
+ const submit = () => {
1684
+ if (controller.validate()) {
1685
+ onSubmit(controller.values);
1686
+ }
1687
+ };
1688
+ return /* @__PURE__ */ jsx(FormRoot, { onSubmit: submit, children: /* @__PURE__ */ jsxs(Fragment, { children: [
1689
+ controller.fields.filter((fieldController) => !fieldController.hidden).map((fieldController) => /* @__PURE__ */ jsx(
1690
+ AutoField,
1691
+ {
1692
+ field: fieldController.field,
1693
+ fieldKey: fieldController.key,
1694
+ value: fieldController.value,
1695
+ onChange: fieldController.onChange,
1696
+ error: fieldController.error,
1697
+ onRelationAction,
1698
+ disabled: fieldController.disabled,
1699
+ onCustomTypeAction
1700
+ },
1701
+ fieldController.key
1702
+ )),
1703
+ /* @__PURE__ */ jsx(Button, { type: "submit", onClick: submit, disabled: submitDisabled, children: /* @__PURE__ */ jsx(Fragment, { children: "Submit" }) })
1704
+ ] }) });
1705
+ };
1706
+ var AutoForm = ({
1707
+ typeInfo,
1708
+ onSubmit,
1709
+ renderer,
1710
+ initialValues,
1711
+ onValuesChange,
1712
+ onRelationAction,
1713
+ onCustomTypeAction,
1714
+ operation,
1715
+ submitDisabled
1716
+ }) => {
1717
+ const controller = useFormEngine(initialValues, typeInfo, { operation });
1718
+ useEffect(() => {
1719
+ if (onValuesChange) {
1720
+ onValuesChange(controller.values);
1721
+ }
1722
+ }, [controller.values, onValuesChange]);
1723
+ return /* @__PURE__ */ jsx(
1724
+ AutoFormView,
1725
+ {
1726
+ controller,
1727
+ onSubmit,
1728
+ renderer,
1729
+ onRelationAction,
1730
+ onCustomTypeAction,
1731
+ submitDisabled
1732
+ }
1733
+ );
1734
+ };
1654
1735
 
1655
- export { ApplicationStateContext, ApplicationStateProvider, Route, RouteContext, RouteContextConsumer, RouteContextProvider, RouteProvider, TypeInfoORMClient, buildHistoryPath, buildQueryString, buildRoutePath, canUseBrowserHistory, computeAreaBounds, computeTrackPixels, createAutoField, createBrowserRouteAdapter, createEasyLayout, createFormRenderer, createManualRouteAdapter, createMemoryHistory, createNativeRouteAdapter, createRouteAdapterFromHistory, createUniversalAdapter, getApplicationStateIdentifier, getApplicationStateModified, getApplicationStateValue, getApplicationStateValueStructure, getChangedDependencyIndexes, getEasyLayoutTemplateDetails, getFieldKind, getFullUrl, getPascalCaseAreaName, handleRequest, mergeSuites, parseHistoryPath, parseTemplate, requestHandlerFactory, resolveSuite, sendServiceRequest, setApplicationStateModified, setApplicationStateValue, useApplicationStateLoader, useApplicationStateValue, useApplicationStateValueStructure, useController, useDebugDependencies, useFormEngine, useRouteContext, useTypeInfoORMAPI, validateAreas, withRendererOverride };
1736
+ export { ApplicationStateContext, ApplicationStateProvider, AutoForm, AutoFormView, Route, RouteContext, RouteContextConsumer, RouteContextProvider, RouteProvider, TypeInfoORMClient, buildHistoryPath, buildQueryString, buildRoutePath, canUseBrowserHistory, computeAreaBounds, computeTrackPixels, createAutoField, createBrowserRouteAdapter, createEasyLayout, createFormRenderer, createManualRouteAdapter, createMemoryHistory, createNativeRouteAdapter, createRouteAdapterFromHistory, createUniversalAdapter, getApplicationStateIdentifier, getApplicationStateModified, getApplicationStateValue, getApplicationStateValueStructure, getChangedDependencyIndexes, getEasyLayoutTemplateDetails, getFieldKind, getFullUrl, getPascalCaseAreaName, handleRequest, mergeSuites, parseHistoryPath, parseTemplate, requestHandlerFactory, resolveSuite, sendServiceRequest, setApplicationStateModified, setApplicationStateValue, useApplicationStateLoader, useApplicationStateValue, useApplicationStateValueStructure, useController, useDebugDependencies, useFormEngine, useRouteContext, useTypeInfoORMAPI, validateAreas, withRendererOverride };
@@ -0,0 +1,67 @@
1
+ /**
2
+ * @packageDocumentation
3
+ *
4
+ * Native AutoForm wrappers backed by the default native renderer.
5
+ */
6
+ import type { FC } from "react";
7
+ import type { AutoFieldProps, CustomTypeActionPayload, FormController, FormValues, RelationActionPayload } from "../../app/forms/types";
8
+ import type { TypeInfo, TypeOperation } from "../../common/TypeParsing/TypeInfo";
9
+ /**
10
+ * Render a form field based on TypeInfo metadata.
11
+ *
12
+ * @category Forms
13
+ *
14
+ * @param props - AutoField props describing the field and handlers.
15
+ * @returns Rendered field UI.
16
+ */
17
+ export declare const AutoField: FC<AutoFieldProps>;
18
+ /**
19
+ * Props for the AutoFormView component.
20
+ */
21
+ export interface AutoFormViewProps {
22
+ /** Prepared controller that supplies field state. */
23
+ controller: FormController;
24
+ /** Submit handler invoked with validated form values. */
25
+ onSubmit: (values: FormValues) => void;
26
+ /** Disable the submit button when true. */
27
+ submitDisabled?: boolean;
28
+ /** Optional relation action handler for reference fields. */
29
+ onRelationAction?: (payload: RelationActionPayload) => void;
30
+ /** Optional custom type action handler. */
31
+ onCustomTypeAction?: (payload: CustomTypeActionPayload) => void;
32
+ }
33
+ /**
34
+ * Render a native form UI from a prepared form controller.
35
+ *
36
+ * @param props - View props including controller and callbacks.
37
+ * @returns Rendered form view.
38
+ */
39
+ export declare const AutoFormView: FC<AutoFormViewProps>;
40
+ /**
41
+ * Props for the AutoForm component.
42
+ */
43
+ export interface AutoFormProps {
44
+ /** Type metadata used to build the form. */
45
+ typeInfo: TypeInfo;
46
+ /** Submit handler invoked with validated form values. */
47
+ onSubmit: (values: FormValues) => void;
48
+ /** Optional initial values applied before defaults. */
49
+ initialValues?: FormValues;
50
+ /** Optional change handler invoked when values update. */
51
+ onValuesChange?: (values: FormValues) => void;
52
+ /** Optional relation action handler for reference fields. */
53
+ onRelationAction?: (payload: RelationActionPayload) => void;
54
+ /** Optional custom type action handler. */
55
+ onCustomTypeAction?: (payload: CustomTypeActionPayload) => void;
56
+ /** Optional operation override for field state. */
57
+ operation?: TypeOperation;
58
+ /** Disable the submit button when true. */
59
+ submitDisabled?: boolean;
60
+ }
61
+ /**
62
+ * Build a controller from type metadata and render a native auto form.
63
+ *
64
+ * @param props - Auto form props including type info and callbacks.
65
+ * @returns Rendered native form.
66
+ */
67
+ export declare const AutoForm: FC<AutoFormProps>;
@@ -4,5 +4,6 @@
4
4
  * Native form rendering exports.
5
5
  */
6
6
  export * from "./suite";
7
+ export * from "./UI";
7
8
  export * from "./primitives";
8
9
  export * from "./createNativeFormRenderer";
package/native/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { createContext, createElement, useMemo } from 'react';
2
- import 'react/jsx-runtime';
1
+ import { createContext, createElement, useMemo, useEffect, useState, useCallback } from 'react';
2
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
3
 
4
4
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
5
5
  get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
@@ -106,9 +106,9 @@ var createFormRenderer = (options) => {
106
106
  options.suite,
107
107
  options.fallbackSuite
108
108
  );
109
- const AutoField = createAutoField(resolvedSuite);
109
+ const AutoField2 = createAutoField(resolvedSuite);
110
110
  return {
111
- AutoField,
111
+ AutoField: AutoField2,
112
112
  suite: resolvedSuite
113
113
  };
114
114
  };
@@ -460,6 +460,32 @@ var renderEnumSelect = (context) => {
460
460
  error ? createElement(ErrorMessage, null, error) : null
461
461
  );
462
462
  };
463
+ var FormRoot = ({
464
+ children,
465
+ onSubmit
466
+ }) => {
467
+ const { Platform, View } = getNative2();
468
+ if (Platform?.OS === "web") {
469
+ return createElement(
470
+ "form",
471
+ {
472
+ onSubmit: (event) => {
473
+ event?.preventDefault?.();
474
+ onSubmit?.();
475
+ }
476
+ },
477
+ children
478
+ );
479
+ }
480
+ return createElement(View, null, children);
481
+ };
482
+ var SuiteButton = ({
483
+ children,
484
+ disabled,
485
+ onClick
486
+ }) => {
487
+ return createElement(Button, { disabled, onPress: onClick }, children);
488
+ };
463
489
  var nativeSuite = {
464
490
  renderers: {
465
491
  string: renderString,
@@ -471,11 +497,224 @@ var nativeSuite = {
471
497
  relation_array: renderRelationArray,
472
498
  custom_single: renderCustomSingle,
473
499
  custom_array: renderCustomArray
500
+ },
501
+ primitives: {
502
+ FormRoot,
503
+ FieldWrapper,
504
+ ErrorMessage,
505
+ Label: ({ children }) => {
506
+ const { Text } = getNative2();
507
+ return createElement(Text, null, children);
508
+ },
509
+ Button: SuiteButton
474
510
  }
475
511
  };
476
512
  var resolvedNativeSuite = resolveSuite(void 0, nativeSuite);
477
513
  autoFieldRenderer = createAutoField(resolvedNativeSuite);
478
514
  var nativeAutoField = autoFieldRenderer;
515
+ var getDeniedOperation = (deniedOperations, operation) => {
516
+ if (!deniedOperations) {
517
+ return false;
518
+ }
519
+ const denied = deniedOperations[operation];
520
+ if (typeof denied === "boolean") {
521
+ return denied;
522
+ }
523
+ return deniedOperations[operation.toLowerCase()] ?? false;
524
+ };
525
+ var buildInitialValues = (initialValues, typeInfo) => {
526
+ const values = { ...initialValues };
527
+ for (const [key, field] of Object.entries(typeInfo.fields ?? {})) {
528
+ if (values[key] !== void 0) {
529
+ continue;
530
+ }
531
+ const defaultValue = field.tags?.constraints?.defaultValue;
532
+ if (defaultValue !== void 0) {
533
+ let parsedDefaultValue = defaultValue;
534
+ try {
535
+ parsedDefaultValue = JSON.parse(defaultValue);
536
+ } catch (error) {
537
+ }
538
+ values[key] = parsedDefaultValue;
539
+ continue;
540
+ }
541
+ if (field.array && !field.typeReference && !field.optional) {
542
+ values[key] = [];
543
+ continue;
544
+ }
545
+ if (field.type === "boolean" && !field.optional) {
546
+ values[key] = false;
547
+ }
548
+ }
549
+ return values;
550
+ };
551
+ var useFormEngine = (initialValues = {}, typeInfo, options) => {
552
+ const operation = options?.operation ?? "CREATE" /* CREATE */;
553
+ const [values, setValues] = useState(
554
+ buildInitialValues(initialValues, typeInfo)
555
+ );
556
+ const [errors, setErrors] = useState({});
557
+ const setFieldValue = useCallback((path, value) => {
558
+ setValues((prev) => {
559
+ return {
560
+ ...prev,
561
+ [path]: value
562
+ };
563
+ });
564
+ }, []);
565
+ const validate = useCallback(() => {
566
+ const newErrors = {};
567
+ for (const [key, field] of Object.entries(typeInfo.fields ?? {})) {
568
+ if (field.tags?.hidden) {
569
+ continue;
570
+ }
571
+ const val = values[key];
572
+ if (field.readonly && (val === void 0 || val === null || val === "")) {
573
+ continue;
574
+ }
575
+ const isMissing = val === void 0 || val === null || val === "" || field.array && (!Array.isArray(val) || val.length === 0);
576
+ if (!field.optional && isMissing) {
577
+ newErrors[key] = "This field is required";
578
+ continue;
579
+ }
580
+ if (isMissing) {
581
+ continue;
582
+ }
583
+ const constraints = field.tags?.constraints;
584
+ if (constraints?.pattern && typeof val === "string") {
585
+ const pattern = new RegExp(constraints.pattern);
586
+ if (!pattern.test(val)) {
587
+ newErrors[key] = "Value does not match required pattern";
588
+ continue;
589
+ }
590
+ }
591
+ if (field.type === "number" && typeof val === "number") {
592
+ if (constraints?.min !== void 0 && val < constraints.min) {
593
+ newErrors[key] = `Value must be at least ${constraints.min}`;
594
+ continue;
595
+ }
596
+ if (constraints?.max !== void 0 && val > constraints.max) {
597
+ newErrors[key] = `Value must be at most ${constraints.max}`;
598
+ continue;
599
+ }
600
+ }
601
+ }
602
+ setErrors(newErrors);
603
+ return Object.keys(newErrors).length === 0;
604
+ }, [typeInfo, values]);
605
+ const fields = useMemo(() => {
606
+ return Object.entries(typeInfo.fields ?? {}).map(([key, field]) => {
607
+ const { tags } = field;
608
+ const isPrimary = tags?.primaryField || typeInfo.primaryField === key;
609
+ return {
610
+ key,
611
+ field,
612
+ label: tags?.label ?? key,
613
+ required: !field.optional,
614
+ disabled: field.readonly || getDeniedOperation(typeInfo.tags?.deniedOperations, operation) || getDeniedOperation(tags?.deniedOperations, operation) || operation === "UPDATE" /* UPDATE */ && isPrimary,
615
+ hidden: !!tags?.hidden,
616
+ primary: isPrimary,
617
+ format: tags?.format,
618
+ constraints: tags?.constraints,
619
+ value: values[key],
620
+ onChange: (value) => setFieldValue(key, value),
621
+ error: errors[key]
622
+ };
623
+ });
624
+ }, [typeInfo, values, errors, setFieldValue, operation]);
625
+ return {
626
+ typeInfo,
627
+ typeTags: typeInfo.tags,
628
+ operation,
629
+ values,
630
+ errors,
631
+ fields,
632
+ setFieldValue,
633
+ validate,
634
+ setErrors
635
+ };
636
+ };
637
+ var fallbackFormRoot = ({
638
+ children,
639
+ onSubmit
640
+ }) => {
641
+ const handleSubmit = (event) => {
642
+ event.preventDefault();
643
+ onSubmit?.();
644
+ };
645
+ return /* @__PURE__ */ jsx("form", { onSubmit: handleSubmit, children });
646
+ };
647
+ var fallbackButton = ({
648
+ children,
649
+ disabled,
650
+ type,
651
+ onClick
652
+ }) => {
653
+ return /* @__PURE__ */ jsx("button", { type: type ?? "button", disabled, onClick, children });
654
+ };
655
+ var AutoFormView = ({
656
+ controller,
657
+ onSubmit,
658
+ renderer,
659
+ submitDisabled,
660
+ onRelationAction,
661
+ onCustomTypeAction
662
+ }) => {
663
+ const FormRoot2 = renderer.suite.primitives?.FormRoot ?? fallbackFormRoot;
664
+ const Button2 = renderer.suite.primitives?.Button ?? fallbackButton;
665
+ const AutoField2 = renderer.AutoField;
666
+ const submit = () => {
667
+ if (controller.validate()) {
668
+ onSubmit(controller.values);
669
+ }
670
+ };
671
+ return /* @__PURE__ */ jsx(FormRoot2, { onSubmit: submit, children: /* @__PURE__ */ jsxs(Fragment, { children: [
672
+ controller.fields.filter((fieldController) => !fieldController.hidden).map((fieldController) => /* @__PURE__ */ jsx(
673
+ AutoField2,
674
+ {
675
+ field: fieldController.field,
676
+ fieldKey: fieldController.key,
677
+ value: fieldController.value,
678
+ onChange: fieldController.onChange,
679
+ error: fieldController.error,
680
+ onRelationAction,
681
+ disabled: fieldController.disabled,
682
+ onCustomTypeAction
683
+ },
684
+ fieldController.key
685
+ )),
686
+ /* @__PURE__ */ jsx(Button2, { type: "submit", onClick: submit, disabled: submitDisabled, children: /* @__PURE__ */ jsx(Fragment, { children: "Submit" }) })
687
+ ] }) });
688
+ };
689
+ var AutoForm = ({
690
+ typeInfo,
691
+ onSubmit,
692
+ renderer,
693
+ initialValues,
694
+ onValuesChange,
695
+ onRelationAction,
696
+ onCustomTypeAction,
697
+ operation,
698
+ submitDisabled
699
+ }) => {
700
+ const controller = useFormEngine(initialValues, typeInfo, { operation });
701
+ useEffect(() => {
702
+ if (onValuesChange) {
703
+ onValuesChange(controller.values);
704
+ }
705
+ }, [controller.values, onValuesChange]);
706
+ return /* @__PURE__ */ jsx(
707
+ AutoFormView,
708
+ {
709
+ controller,
710
+ onSubmit,
711
+ renderer,
712
+ onRelationAction,
713
+ onCustomTypeAction,
714
+ submitDisabled
715
+ }
716
+ );
717
+ };
479
718
 
480
719
  // src/native/forms/createNativeFormRenderer.ts
481
720
  var createNativeFormRenderer = (options) => {
@@ -484,6 +723,25 @@ var createNativeFormRenderer = (options) => {
484
723
  suite: options?.suite
485
724
  });
486
725
  };
726
+ var defaultNativeRenderer = createNativeFormRenderer();
727
+ var AutoField = (props) => {
728
+ return nativeAutoField({
729
+ field: props.field,
730
+ fieldKey: props.fieldKey,
731
+ value: props.value,
732
+ onChange: props.onChange,
733
+ error: props.error,
734
+ disabled: props.disabled,
735
+ onRelationAction: props.onRelationAction,
736
+ onCustomTypeAction: props.onCustomTypeAction
737
+ });
738
+ };
739
+ var AutoFormView2 = (props) => {
740
+ return /* @__PURE__ */ jsx(AutoFormView, { ...props, renderer: defaultNativeRenderer });
741
+ };
742
+ var AutoForm2 = (props) => {
743
+ return /* @__PURE__ */ jsx(AutoForm, { ...props, renderer: defaultNativeRenderer });
744
+ };
487
745
 
488
746
  // src/app/utils/History.ts
489
747
  var ensurePrefix = (value, prefix) => value ? value.startsWith(prefix) ? value : `${prefix}${value}` : "";
@@ -1121,4 +1379,4 @@ var buildPathFromRouteChain = (routeChain, config, query) => {
1121
1379
  return buildRoutePath(segments, query);
1122
1380
  };
1123
1381
 
1124
- export { ArrayContainer, ArrayItemWrapper, Button, ErrorMessage, FieldWrapper, NativeEasyLayoutView, buildHistoryPath, buildPathFromRouteChain, createMemoryHistory, createNativeBackHandler, createNativeFormRenderer, createNativeHistory, createNavigationStateRouteAdapter, makeNativeEasyLayout, mapNativeURLToPath, nativeAutoField, nativeSuite, parseHistoryPath, useNativeEasyLayout };
1382
+ export { ArrayContainer, ArrayItemWrapper, AutoField, AutoForm2 as AutoForm, AutoFormView2 as AutoFormView, Button, ErrorMessage, FieldWrapper, NativeEasyLayoutView, buildHistoryPath, buildPathFromRouteChain, createMemoryHistory, createNativeBackHandler, createNativeFormRenderer, createNativeHistory, createNavigationStateRouteAdapter, makeNativeEasyLayout, mapNativeURLToPath, nativeAutoField, nativeSuite, parseHistoryPath, useNativeEasyLayout };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@resistdesign/voltra",
3
- "version": "3.0.0-alpha.23",
3
+ "version": "3.0.0-alpha.24",
4
4
  "description": "With our powers combined!",
5
5
  "homepage": "https://voltra.app",
6
6
  "repository": "git@github.com:resistdesign/voltra.git",
package/web/forms/UI.d.ts CHANGED
@@ -1,11 +1,12 @@
1
1
  /**
2
2
  * @packageDocumentation
3
3
  *
4
- * Tier 2 UI components: AutoForm, AutoField.
4
+ * Web AutoForm wrappers backed by the default web renderer.
5
5
  */
6
- import { FC } from "react";
6
+ import type { FC } from "react";
7
+ import type { AutoFieldProps, CustomTypeActionPayload, FormValues, RelationActionPayload } from "../../app/forms/types";
7
8
  import type { TypeInfo, TypeOperation } from "../../common/TypeParsing/TypeInfo";
8
- import type { AutoFieldProps, CustomTypeActionPayload, FormController, FormValues, RelationActionPayload } from "../../app/forms/types";
9
+ import type { FormController } from "../../app/forms/types";
9
10
  /**
10
11
  * Render a form field based on TypeInfo metadata.
11
12
  *
@@ -31,7 +32,7 @@ export interface AutoFormViewProps {
31
32
  onCustomTypeAction?: (payload: CustomTypeActionPayload) => void;
32
33
  }
33
34
  /**
34
- * Render a form UI from a prepared form controller.
35
+ * Render a web form UI from a prepared form controller.
35
36
  *
36
37
  * @param props - View props including controller and callbacks.
37
38
  * @returns Rendered form view.
@@ -59,9 +60,9 @@ export interface AutoFormProps {
59
60
  submitDisabled?: boolean;
60
61
  }
61
62
  /**
62
- * Build a controller from type metadata and render an auto form.
63
+ * Build a controller from type metadata and render a web auto form.
63
64
  *
64
65
  * @param props - Auto form props including type info and callbacks.
65
- * @returns Rendered form bound to a new controller.
66
+ * @returns Rendered web form.
66
67
  */
67
68
  export declare const AutoForm: FC<AutoFormProps>;
package/web/index.js CHANGED
@@ -1,7 +1,7 @@
1
+ import { createElement, useEffect, useState, useCallback, useMemo } from 'react';
1
2
  import * as styledBase from 'styled-components';
2
3
  import styledBase__default from 'styled-components';
3
4
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
4
- import { useEffect, useState, useCallback, useMemo } from 'react';
5
5
 
6
6
  var __defProp = Object.defineProperty;
7
7
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -524,6 +524,34 @@ var renderEnumSelect = (context) => {
524
524
  error && /* @__PURE__ */ jsx(ErrorMessage, { children: error })
525
525
  ] });
526
526
  };
527
+ var FormRoot = ({
528
+ children,
529
+ onSubmit
530
+ }) => {
531
+ const handleSubmit = (event) => {
532
+ event.preventDefault();
533
+ onSubmit?.();
534
+ };
535
+ return /* @__PURE__ */ jsx("form", { onSubmit: handleSubmit, children });
536
+ };
537
+ var SuiteButton = ({
538
+ children,
539
+ disabled,
540
+ type,
541
+ onClick,
542
+ "data-signifier": dataSignifier
543
+ }) => {
544
+ return createElement(
545
+ "button",
546
+ {
547
+ type: type ?? "button",
548
+ disabled,
549
+ onClick: type === "submit" ? void 0 : onClick,
550
+ "data-signifier": dataSignifier
551
+ },
552
+ children
553
+ );
554
+ };
527
555
  var webSuite = {
528
556
  renderers: {
529
557
  string: renderString,
@@ -535,6 +563,13 @@ var webSuite = {
535
563
  relation_array: renderRelationArray,
536
564
  custom_single: renderCustomSingle,
537
565
  custom_array: renderCustomArray
566
+ },
567
+ primitives: {
568
+ FormRoot,
569
+ FieldWrapper,
570
+ ErrorMessage,
571
+ Label: ({ children, htmlFor }) => createElement("label", { htmlFor }, children),
572
+ Button: SuiteButton
538
573
  }
539
574
  };
540
575
  var resolvedWebSuite = resolveSuite(void 0, webSuite);
@@ -685,34 +720,43 @@ var useFormEngine = (initialValues = {}, typeInfo, options) => {
685
720
  setErrors
686
721
  };
687
722
  };
688
- var AutoField = (props) => {
689
- return webAutoField({
690
- field: props.field,
691
- fieldKey: props.fieldKey,
692
- value: props.value,
693
- onChange: props.onChange,
694
- error: props.error,
695
- disabled: props.disabled,
696
- onRelationAction: props.onRelationAction,
697
- onCustomTypeAction: props.onCustomTypeAction
698
- });
723
+ var fallbackFormRoot = ({
724
+ children,
725
+ onSubmit
726
+ }) => {
727
+ const handleSubmit = (event) => {
728
+ event.preventDefault();
729
+ onSubmit?.();
730
+ };
731
+ return /* @__PURE__ */ jsx("form", { onSubmit: handleSubmit, children });
732
+ };
733
+ var fallbackButton = ({
734
+ children,
735
+ disabled,
736
+ type,
737
+ onClick
738
+ }) => {
739
+ return /* @__PURE__ */ jsx("button", { type: type ?? "button", disabled, onClick, children });
699
740
  };
700
741
  var AutoFormView = ({
701
742
  controller,
702
743
  onSubmit,
744
+ renderer,
703
745
  submitDisabled,
704
746
  onRelationAction,
705
747
  onCustomTypeAction
706
748
  }) => {
707
- const handleSubmit = (e) => {
708
- e.preventDefault();
749
+ const FormRoot2 = renderer.suite.primitives?.FormRoot ?? fallbackFormRoot;
750
+ const Button = renderer.suite.primitives?.Button ?? fallbackButton;
751
+ const AutoField2 = renderer.AutoField;
752
+ const submit = () => {
709
753
  if (controller.validate()) {
710
754
  onSubmit(controller.values);
711
755
  }
712
756
  };
713
- return /* @__PURE__ */ jsxs(FormContainer, { onSubmit: handleSubmit, children: [
757
+ return /* @__PURE__ */ jsx(FormRoot2, { onSubmit: submit, children: /* @__PURE__ */ jsxs(Fragment, { children: [
714
758
  controller.fields.filter((fieldController) => !fieldController.hidden).map((fieldController) => /* @__PURE__ */ jsx(
715
- AutoField,
759
+ AutoField2,
716
760
  {
717
761
  field: fieldController.field,
718
762
  fieldKey: fieldController.key,
@@ -725,12 +769,13 @@ var AutoFormView = ({
725
769
  },
726
770
  fieldController.key
727
771
  )),
728
- /* @__PURE__ */ jsx("button", { type: "submit", disabled: submitDisabled, children: "Submit" })
729
- ] });
772
+ /* @__PURE__ */ jsx(Button, { type: "submit", onClick: submit, disabled: submitDisabled, children: /* @__PURE__ */ jsx(Fragment, { children: "Submit" }) })
773
+ ] }) });
730
774
  };
731
775
  var AutoForm = ({
732
776
  typeInfo,
733
777
  onSubmit,
778
+ renderer,
734
779
  initialValues,
735
780
  onValuesChange,
736
781
  onRelationAction,
@@ -749,19 +794,13 @@ var AutoForm = ({
749
794
  {
750
795
  controller,
751
796
  onSubmit,
797
+ renderer,
752
798
  onRelationAction,
753
799
  onCustomTypeAction,
754
800
  submitDisabled
755
801
  }
756
802
  );
757
803
  };
758
- var FormContainer = styled_default("form")`
759
- display: flex;
760
- flex-direction: column;
761
- align-items: flex-start;
762
- justify-content: flex-start;
763
- gap: 1em;
764
- `;
765
804
 
766
805
  // src/web/forms/createWebFormRenderer.ts
767
806
  var createWebFormRenderer = (options) => {
@@ -770,6 +809,25 @@ var createWebFormRenderer = (options) => {
770
809
  suite: options?.suite
771
810
  });
772
811
  };
812
+ var defaultWebRenderer = createWebFormRenderer();
813
+ var AutoField = (props) => {
814
+ return webAutoField({
815
+ field: props.field,
816
+ fieldKey: props.fieldKey,
817
+ value: props.value,
818
+ onChange: props.onChange,
819
+ error: props.error,
820
+ disabled: props.disabled,
821
+ onRelationAction: props.onRelationAction,
822
+ onCustomTypeAction: props.onCustomTypeAction
823
+ });
824
+ };
825
+ var AutoFormView2 = (props) => {
826
+ return /* @__PURE__ */ jsx(AutoFormView, { ...props, renderer: defaultWebRenderer });
827
+ };
828
+ var AutoForm2 = (props) => {
829
+ return /* @__PURE__ */ jsx(AutoForm, { ...props, renderer: defaultWebRenderer });
830
+ };
773
831
 
774
832
  // src/app/utils/easy-layout/parseTemplate.ts
775
833
  var parseTrackSpec = (token) => {
@@ -1018,4 +1076,4 @@ var getEasyLayout = (extendFrom, areasExtendFrom, options = {}) => {
1018
1076
  return createEasyLayout(styledFactory, extendFrom, areasExtendFrom, options);
1019
1077
  };
1020
1078
 
1021
- export { ArrayContainer, ArrayItemWrapper, AutoField, AutoForm, AutoFormView, ErrorMessage, FieldWrapper, createWebFormRenderer, getEasyLayout, webAutoField, webSuite };
1079
+ export { ArrayContainer, ArrayItemWrapper, AutoField, AutoForm2 as AutoForm, AutoFormView2 as AutoFormView, ErrorMessage, FieldWrapper, createWebFormRenderer, getEasyLayout, webAutoField, webSuite };