@resistdesign/voltra 3.0.0-alpha.27 → 3.0.0-alpha.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/api/index.js +6 -383
- package/app/index.js +10 -1268
- package/app/utils/Route.d.ts +18 -0
- package/app/utils/UniversalRouteAdapter.d.ts +0 -11
- package/build/index.js +2 -12
- package/chunk-ATO2455Q.js +258 -0
- package/chunk-FQMZMCXU.js +71 -0
- package/chunk-G5CLUK4Y.js +621 -0
- package/chunk-GYWRAW3Y.js +78 -0
- package/chunk-HVY7POTD.js +22 -0
- package/chunk-I2KLQ2HA.js +19 -0
- package/chunk-IWRHGGGH.js +10 -0
- package/chunk-LGM75I6P.js +353 -0
- package/chunk-MUCSL3UR.js +1 -0
- package/chunk-WELZGQDJ.js +456 -0
- package/common/index.js +7 -384
- package/iac/index.js +2 -258
- package/iac/packs/index.js +2 -254
- package/iac-packs/index.d.ts +6 -0
- package/native/index.d.ts +4 -0
- package/native/index.js +62 -705
- package/native/testing/react-native.d.ts +28 -0
- package/native/utils/Route.d.ts +34 -1
- package/package.json +1 -14
- package/web/index.js +6 -539
- package/iac/types/CloudFormationResourceSpecification.d.ts +0 -2
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
type Listener = () => boolean;
|
|
3
|
+
export declare const Platform: {
|
|
4
|
+
OS: string;
|
|
5
|
+
};
|
|
6
|
+
export declare const BackHandler: {
|
|
7
|
+
addEventListener: (_eventName: "hardwareBackPress", listener: Listener) => {
|
|
8
|
+
remove: () => void;
|
|
9
|
+
};
|
|
10
|
+
removeEventListener: (_eventName: "hardwareBackPress", listener: Listener) => void;
|
|
11
|
+
__triggerHardwareBackPress: () => boolean;
|
|
12
|
+
};
|
|
13
|
+
export declare const View: ({ children, ...props }: Record<string, any>) => React.DOMElement<{
|
|
14
|
+
[x: string]: any;
|
|
15
|
+
}, Element>;
|
|
16
|
+
export declare const Text: ({ children, ...props }: Record<string, any>) => React.DOMElement<{
|
|
17
|
+
[x: string]: any;
|
|
18
|
+
}, Element>;
|
|
19
|
+
export declare const Pressable: ({ children, ...props }: Record<string, any>) => React.DOMElement<{
|
|
20
|
+
[x: string]: any;
|
|
21
|
+
}, Element>;
|
|
22
|
+
export declare const Switch: ({ children, ...props }: Record<string, any>) => React.DOMElement<{
|
|
23
|
+
[x: string]: any;
|
|
24
|
+
}, Element>;
|
|
25
|
+
export declare const TextInput: ({ children, ...props }: Record<string, any>) => React.DOMElement<{
|
|
26
|
+
[x: string]: any;
|
|
27
|
+
}, Element>;
|
|
28
|
+
export {};
|
package/native/utils/Route.d.ts
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Native routing helpers that adapt common navigation state to RouteAdapter.
|
|
5
5
|
*/
|
|
6
|
-
import
|
|
6
|
+
import { type PropsWithChildren } from "react";
|
|
7
|
+
import type { RouteAdapter, RouteContextType, RouteProps, RouteProviderProps, RouteQuery, RouteQueryValue, RouteRuntimeIntegration } from "../../app/utils/Route";
|
|
8
|
+
import { useRouteContext } from "../../app/utils/Route";
|
|
7
9
|
/**
|
|
8
10
|
* Options to adapt a navigation state container into a RouteAdapter.
|
|
9
11
|
*/
|
|
@@ -19,6 +21,15 @@ export type NavigationStateAdapterOptions<TState> = {
|
|
|
19
21
|
/** Optional navigation handler used for replace-style transitions. */
|
|
20
22
|
replace?: (path: string) => void;
|
|
21
23
|
};
|
|
24
|
+
/**
|
|
25
|
+
* Contract for React Native BackHandler-like integrations.
|
|
26
|
+
*/
|
|
27
|
+
export type NativeBackHandlerLike = {
|
|
28
|
+
addEventListener: (eventName: "hardwareBackPress", listener: () => boolean) => {
|
|
29
|
+
remove?: () => void;
|
|
30
|
+
} | void;
|
|
31
|
+
removeEventListener?: (eventName: "hardwareBackPress", listener: () => boolean) => void;
|
|
32
|
+
};
|
|
22
33
|
/**
|
|
23
34
|
* Route node in a navigation chain.
|
|
24
35
|
*/
|
|
@@ -39,6 +50,26 @@ export type NavigationRouteConfig = Record<string, string>;
|
|
|
39
50
|
* @returns RouteAdapter bound to the navigation state.
|
|
40
51
|
*/
|
|
41
52
|
export declare const createNavigationStateRouteAdapter: <TState>(options: NavigationStateAdapterOptions<TState>) => RouteAdapter;
|
|
53
|
+
/**
|
|
54
|
+
* Create a hardware-back listener from a route adapter.
|
|
55
|
+
*/
|
|
56
|
+
export declare const createNativeHardwareBackHandler: (adapter: RouteAdapter) => () => boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Register hardware-back handling against a BackHandler-like runtime.
|
|
59
|
+
*/
|
|
60
|
+
export declare const registerNativeHardwareBackHandler: (adapter: RouteAdapter, backHandler: NativeBackHandlerLike) => () => void;
|
|
61
|
+
/**
|
|
62
|
+
* Build a core Route runtime integration using a native BackHandler.
|
|
63
|
+
*/
|
|
64
|
+
export declare const createNativeRouteBackIntegration: (backHandler: NativeBackHandlerLike) => RouteRuntimeIntegration;
|
|
65
|
+
/**
|
|
66
|
+
* Native Route wrapper for root/provider mode.
|
|
67
|
+
*
|
|
68
|
+
* Behavior:
|
|
69
|
+
* - On mobile native runtimes, injects back-handler integration into app Route.
|
|
70
|
+
* - On web runtimes, passes no integration so app Route uses browser behavior.
|
|
71
|
+
*/
|
|
72
|
+
export declare const Route: <ParamsType extends Record<string, any>>(props: PropsWithChildren<RouteProps<ParamsType>>) => import("react/jsx-runtime").JSX.Element;
|
|
42
73
|
/**
|
|
43
74
|
* Build a path from a navigation route chain and route config mapping.
|
|
44
75
|
*
|
|
@@ -48,3 +79,5 @@ export declare const createNavigationStateRouteAdapter: <TState>(options: Naviga
|
|
|
48
79
|
* @returns Serialized path string.
|
|
49
80
|
*/
|
|
50
81
|
export declare const buildPathFromRouteChain: (routeChain: NavigationRouteNode[], config: NavigationRouteConfig, query?: RouteQuery) => string;
|
|
82
|
+
export { useRouteContext };
|
|
83
|
+
export type { RouteAdapter, RouteContextType, RouteProps, RouteProviderProps, RouteQuery, RouteQueryValue, RouteRuntimeIntegration, };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@resistdesign/voltra",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.29",
|
|
4
4
|
"description": "With our powers combined!",
|
|
5
5
|
"homepage": "https://voltra.app",
|
|
6
6
|
"repository": "git@github.com:resistdesign/voltra.git",
|
|
@@ -72,19 +72,6 @@
|
|
|
72
72
|
]
|
|
73
73
|
}
|
|
74
74
|
},
|
|
75
|
-
"files": [
|
|
76
|
-
"*.d.ts",
|
|
77
|
-
"*.d.ts.map",
|
|
78
|
-
"api/**",
|
|
79
|
-
"app/**",
|
|
80
|
-
"web/**",
|
|
81
|
-
"native/**",
|
|
82
|
-
"build/**",
|
|
83
|
-
"common/**",
|
|
84
|
-
"iac/**",
|
|
85
|
-
"README.md",
|
|
86
|
-
"package.json"
|
|
87
|
-
],
|
|
88
75
|
"dependencies": {
|
|
89
76
|
"@aws-sdk/client-dynamodb": "^3.490.0",
|
|
90
77
|
"@aws-sdk/client-s3": "^3.490.0",
|
package/web/index.js
CHANGED
|
@@ -1,132 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createEasyLayout } from '../chunk-FQMZMCXU.js';
|
|
2
|
+
import { createFormRenderer, AutoFormView, AutoForm } from '../chunk-WELZGQDJ.js';
|
|
3
|
+
import '../chunk-IWRHGGGH.js';
|
|
4
|
+
import { __export, __reExport } from '../chunk-I2KLQ2HA.js';
|
|
5
|
+
import { createElement } from 'react';
|
|
2
6
|
import * as styledBase from 'styled-components';
|
|
3
7
|
import styledBase__default from 'styled-components';
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
var __defProp = Object.defineProperty;
|
|
7
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
8
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
9
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
10
|
-
var __export = (target, all) => {
|
|
11
|
-
for (var name in all)
|
|
12
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
13
|
-
};
|
|
14
|
-
var __copyProps = (to, from, except, desc) => {
|
|
15
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
16
|
-
for (let key of __getOwnPropNames(from))
|
|
17
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
18
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
19
|
-
}
|
|
20
|
-
return to;
|
|
21
|
-
};
|
|
22
|
-
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget);
|
|
23
|
-
|
|
24
|
-
// src/app/forms/core/resolveSuite.ts
|
|
25
|
-
var fieldKinds = [
|
|
26
|
-
"string",
|
|
27
|
-
"number",
|
|
28
|
-
"boolean",
|
|
29
|
-
"enum_select",
|
|
30
|
-
"array",
|
|
31
|
-
"relation_single",
|
|
32
|
-
"relation_array",
|
|
33
|
-
"custom_single",
|
|
34
|
-
"custom_array"
|
|
35
|
-
];
|
|
36
|
-
var getMissingKinds = (renderers) => {
|
|
37
|
-
return fieldKinds.filter((kind) => !renderers[kind]);
|
|
38
|
-
};
|
|
39
|
-
var resolveSuite = (overrides, fallback) => {
|
|
40
|
-
const mergedRenderers = {
|
|
41
|
-
...fallback.renderers ?? {},
|
|
42
|
-
...overrides?.renderers ?? {}
|
|
43
|
-
};
|
|
44
|
-
const missingKinds = getMissingKinds(mergedRenderers);
|
|
45
|
-
if (missingKinds.length) {
|
|
46
|
-
throw new Error(
|
|
47
|
-
`Missing renderers for field kinds: ${missingKinds.join(", ")}`
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
const mergedPrimitives = {
|
|
51
|
-
...fallback.primitives ?? {},
|
|
52
|
-
...overrides?.primitives ?? {}
|
|
53
|
-
};
|
|
54
|
-
return {
|
|
55
|
-
renderers: mergedRenderers,
|
|
56
|
-
primitives: Object.keys(mergedPrimitives).length ? mergedPrimitives : void 0
|
|
57
|
-
};
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
// src/app/forms/core/getFieldKind.ts
|
|
61
|
-
var hasSelectableValues = (field) => {
|
|
62
|
-
const possibleValues = field.possibleValues ?? [];
|
|
63
|
-
return possibleValues.some(
|
|
64
|
-
(value) => typeof value === "string" || typeof value === "number"
|
|
65
|
-
);
|
|
66
|
-
};
|
|
67
|
-
var getFieldKind = (field) => {
|
|
68
|
-
if (field.typeReference) {
|
|
69
|
-
return field.array ? "relation_array" : "relation_single";
|
|
70
|
-
}
|
|
71
|
-
if (field.tags?.customType) {
|
|
72
|
-
return field.array ? "custom_array" : "custom_single";
|
|
73
|
-
}
|
|
74
|
-
if (field.array) {
|
|
75
|
-
return "array";
|
|
76
|
-
}
|
|
77
|
-
if (hasSelectableValues(field)) {
|
|
78
|
-
return "enum_select";
|
|
79
|
-
}
|
|
80
|
-
if (field.type === "boolean") {
|
|
81
|
-
return "boolean";
|
|
82
|
-
}
|
|
83
|
-
if (field.type === "number") {
|
|
84
|
-
return "number";
|
|
85
|
-
}
|
|
86
|
-
return "string";
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
// src/app/forms/core/createAutoField.ts
|
|
90
|
-
var createAutoField = (suite) => {
|
|
91
|
-
const renderField = (props) => {
|
|
92
|
-
const { field, fieldKey, value, onChange, error, disabled } = props;
|
|
93
|
-
const { tags } = field;
|
|
94
|
-
const context = {
|
|
95
|
-
field,
|
|
96
|
-
fieldKey,
|
|
97
|
-
label: tags?.label ?? fieldKey,
|
|
98
|
-
required: !field.optional,
|
|
99
|
-
disabled: !!disabled,
|
|
100
|
-
error,
|
|
101
|
-
value,
|
|
102
|
-
onChange,
|
|
103
|
-
constraints: tags?.constraints,
|
|
104
|
-
format: tags?.format,
|
|
105
|
-
possibleValues: field.possibleValues,
|
|
106
|
-
allowCustomSelection: tags?.allowCustomSelection,
|
|
107
|
-
customType: tags?.customType,
|
|
108
|
-
onRelationAction: props.onRelationAction,
|
|
109
|
-
onCustomTypeAction: props.onCustomTypeAction,
|
|
110
|
-
renderField
|
|
111
|
-
};
|
|
112
|
-
const kind = getFieldKind(field);
|
|
113
|
-
return suite.renderers[kind](context);
|
|
114
|
-
};
|
|
115
|
-
return renderField;
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
// src/app/forms/core/createFormRenderer.ts
|
|
119
|
-
var createFormRenderer = (options) => {
|
|
120
|
-
const resolvedSuite = resolveSuite(
|
|
121
|
-
options.suite,
|
|
122
|
-
options.fallbackSuite
|
|
123
|
-
);
|
|
124
|
-
const AutoField2 = createAutoField(resolvedSuite);
|
|
125
|
-
return {
|
|
126
|
-
AutoField: AutoField2,
|
|
127
|
-
suite: resolvedSuite
|
|
128
|
-
};
|
|
129
|
-
};
|
|
8
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
130
9
|
|
|
131
10
|
// src/app/helpers/styled.ts
|
|
132
11
|
var styled_exports = {};
|
|
@@ -599,209 +478,6 @@ var RelationActions = styled_default("div")`
|
|
|
599
478
|
gap: 0.5em;
|
|
600
479
|
flex-wrap: wrap;
|
|
601
480
|
`;
|
|
602
|
-
var getDeniedOperation = (deniedOperations, operation) => {
|
|
603
|
-
if (!deniedOperations) {
|
|
604
|
-
return false;
|
|
605
|
-
}
|
|
606
|
-
const denied = deniedOperations[operation];
|
|
607
|
-
if (typeof denied === "boolean") {
|
|
608
|
-
return denied;
|
|
609
|
-
}
|
|
610
|
-
return deniedOperations[operation.toLowerCase()] ?? false;
|
|
611
|
-
};
|
|
612
|
-
var buildInitialValues = (initialValues, typeInfo) => {
|
|
613
|
-
const values = { ...initialValues };
|
|
614
|
-
for (const [key, field] of Object.entries(typeInfo.fields ?? {})) {
|
|
615
|
-
if (values[key] !== void 0) {
|
|
616
|
-
continue;
|
|
617
|
-
}
|
|
618
|
-
const defaultValue = field.tags?.constraints?.defaultValue;
|
|
619
|
-
if (defaultValue !== void 0) {
|
|
620
|
-
let parsedDefaultValue = defaultValue;
|
|
621
|
-
try {
|
|
622
|
-
parsedDefaultValue = JSON.parse(defaultValue);
|
|
623
|
-
} catch (error) {
|
|
624
|
-
}
|
|
625
|
-
values[key] = parsedDefaultValue;
|
|
626
|
-
continue;
|
|
627
|
-
}
|
|
628
|
-
if (field.array && !field.typeReference && !field.optional) {
|
|
629
|
-
values[key] = [];
|
|
630
|
-
continue;
|
|
631
|
-
}
|
|
632
|
-
if (field.type === "boolean" && !field.optional) {
|
|
633
|
-
values[key] = false;
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
return values;
|
|
637
|
-
};
|
|
638
|
-
var useFormEngine = (initialValues = {}, typeInfo, options) => {
|
|
639
|
-
const operation = options?.operation ?? "CREATE" /* CREATE */;
|
|
640
|
-
const [values, setValues] = useState(
|
|
641
|
-
buildInitialValues(initialValues, typeInfo)
|
|
642
|
-
);
|
|
643
|
-
const [errors, setErrors] = useState({});
|
|
644
|
-
const setFieldValue = useCallback((path, value) => {
|
|
645
|
-
setValues((prev) => {
|
|
646
|
-
return {
|
|
647
|
-
...prev,
|
|
648
|
-
[path]: value
|
|
649
|
-
};
|
|
650
|
-
});
|
|
651
|
-
}, []);
|
|
652
|
-
const validate = useCallback(() => {
|
|
653
|
-
const newErrors = {};
|
|
654
|
-
for (const [key, field] of Object.entries(typeInfo.fields ?? {})) {
|
|
655
|
-
if (field.tags?.hidden) {
|
|
656
|
-
continue;
|
|
657
|
-
}
|
|
658
|
-
const val = values[key];
|
|
659
|
-
if (field.readonly && (val === void 0 || val === null || val === "")) {
|
|
660
|
-
continue;
|
|
661
|
-
}
|
|
662
|
-
const isMissing = val === void 0 || val === null || val === "" || field.array && (!Array.isArray(val) || val.length === 0);
|
|
663
|
-
if (!field.optional && isMissing) {
|
|
664
|
-
newErrors[key] = "This field is required";
|
|
665
|
-
continue;
|
|
666
|
-
}
|
|
667
|
-
if (isMissing) {
|
|
668
|
-
continue;
|
|
669
|
-
}
|
|
670
|
-
const constraints = field.tags?.constraints;
|
|
671
|
-
if (constraints?.pattern && typeof val === "string") {
|
|
672
|
-
const pattern = new RegExp(constraints.pattern);
|
|
673
|
-
if (!pattern.test(val)) {
|
|
674
|
-
newErrors[key] = "Value does not match required pattern";
|
|
675
|
-
continue;
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
if (field.type === "number" && typeof val === "number") {
|
|
679
|
-
if (constraints?.min !== void 0 && val < constraints.min) {
|
|
680
|
-
newErrors[key] = `Value must be at least ${constraints.min}`;
|
|
681
|
-
continue;
|
|
682
|
-
}
|
|
683
|
-
if (constraints?.max !== void 0 && val > constraints.max) {
|
|
684
|
-
newErrors[key] = `Value must be at most ${constraints.max}`;
|
|
685
|
-
continue;
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
setErrors(newErrors);
|
|
690
|
-
return Object.keys(newErrors).length === 0;
|
|
691
|
-
}, [typeInfo, values]);
|
|
692
|
-
const fields = useMemo(() => {
|
|
693
|
-
return Object.entries(typeInfo.fields ?? {}).map(([key, field]) => {
|
|
694
|
-
const { tags } = field;
|
|
695
|
-
const isPrimary = tags?.primaryField || typeInfo.primaryField === key;
|
|
696
|
-
return {
|
|
697
|
-
key,
|
|
698
|
-
field,
|
|
699
|
-
label: tags?.label ?? key,
|
|
700
|
-
required: !field.optional,
|
|
701
|
-
disabled: field.readonly || getDeniedOperation(typeInfo.tags?.deniedOperations, operation) || getDeniedOperation(tags?.deniedOperations, operation) || operation === "UPDATE" /* UPDATE */ && isPrimary,
|
|
702
|
-
hidden: !!tags?.hidden,
|
|
703
|
-
primary: isPrimary,
|
|
704
|
-
format: tags?.format,
|
|
705
|
-
constraints: tags?.constraints,
|
|
706
|
-
value: values[key],
|
|
707
|
-
onChange: (value) => setFieldValue(key, value),
|
|
708
|
-
error: errors[key]
|
|
709
|
-
};
|
|
710
|
-
});
|
|
711
|
-
}, [typeInfo, values, errors, setFieldValue, operation]);
|
|
712
|
-
return {
|
|
713
|
-
typeInfo,
|
|
714
|
-
typeTags: typeInfo.tags,
|
|
715
|
-
operation,
|
|
716
|
-
values,
|
|
717
|
-
errors,
|
|
718
|
-
fields,
|
|
719
|
-
setFieldValue,
|
|
720
|
-
validate,
|
|
721
|
-
setErrors
|
|
722
|
-
};
|
|
723
|
-
};
|
|
724
|
-
var fallbackFormRoot = ({
|
|
725
|
-
children,
|
|
726
|
-
onSubmit
|
|
727
|
-
}) => {
|
|
728
|
-
const handleSubmit = (event) => {
|
|
729
|
-
event.preventDefault();
|
|
730
|
-
onSubmit?.();
|
|
731
|
-
};
|
|
732
|
-
return /* @__PURE__ */ jsx("form", { onSubmit: handleSubmit, children });
|
|
733
|
-
};
|
|
734
|
-
var fallbackButton = ({
|
|
735
|
-
children,
|
|
736
|
-
disabled,
|
|
737
|
-
type,
|
|
738
|
-
onClick
|
|
739
|
-
}) => {
|
|
740
|
-
return /* @__PURE__ */ jsx("button", { type: type ?? "button", disabled, onClick, children });
|
|
741
|
-
};
|
|
742
|
-
var AutoFormView = ({
|
|
743
|
-
controller,
|
|
744
|
-
onSubmit,
|
|
745
|
-
renderer,
|
|
746
|
-
submitDisabled,
|
|
747
|
-
onRelationAction,
|
|
748
|
-
onCustomTypeAction
|
|
749
|
-
}) => {
|
|
750
|
-
const FormRoot2 = renderer.suite.primitives?.FormRoot ?? fallbackFormRoot;
|
|
751
|
-
const Button = renderer.suite.primitives?.Button ?? fallbackButton;
|
|
752
|
-
const AutoField2 = renderer.AutoField;
|
|
753
|
-
const submit = () => {
|
|
754
|
-
if (controller.validate()) {
|
|
755
|
-
onSubmit(controller.values);
|
|
756
|
-
}
|
|
757
|
-
};
|
|
758
|
-
return /* @__PURE__ */ jsx(FormRoot2, { onSubmit: submit, children: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
759
|
-
controller.fields.filter((fieldController) => !fieldController.hidden).map((fieldController) => /* @__PURE__ */ jsx(
|
|
760
|
-
AutoField2,
|
|
761
|
-
{
|
|
762
|
-
field: fieldController.field,
|
|
763
|
-
fieldKey: fieldController.key,
|
|
764
|
-
value: fieldController.value,
|
|
765
|
-
onChange: fieldController.onChange,
|
|
766
|
-
error: fieldController.error,
|
|
767
|
-
onRelationAction,
|
|
768
|
-
disabled: fieldController.disabled,
|
|
769
|
-
onCustomTypeAction
|
|
770
|
-
},
|
|
771
|
-
fieldController.key
|
|
772
|
-
)),
|
|
773
|
-
/* @__PURE__ */ jsx(Button, { type: "submit", onClick: submit, disabled: submitDisabled, children: /* @__PURE__ */ jsx(Fragment, { children: "Submit" }) })
|
|
774
|
-
] }) });
|
|
775
|
-
};
|
|
776
|
-
var AutoForm = ({
|
|
777
|
-
typeInfo,
|
|
778
|
-
onSubmit,
|
|
779
|
-
renderer,
|
|
780
|
-
initialValues,
|
|
781
|
-
onValuesChange,
|
|
782
|
-
onRelationAction,
|
|
783
|
-
onCustomTypeAction,
|
|
784
|
-
operation,
|
|
785
|
-
submitDisabled
|
|
786
|
-
}) => {
|
|
787
|
-
const controller = useFormEngine(initialValues, typeInfo, { operation });
|
|
788
|
-
useEffect(() => {
|
|
789
|
-
if (onValuesChange) {
|
|
790
|
-
onValuesChange(controller.values);
|
|
791
|
-
}
|
|
792
|
-
}, [controller.values, onValuesChange]);
|
|
793
|
-
return /* @__PURE__ */ jsx(
|
|
794
|
-
AutoFormView,
|
|
795
|
-
{
|
|
796
|
-
controller,
|
|
797
|
-
onSubmit,
|
|
798
|
-
renderer,
|
|
799
|
-
onRelationAction,
|
|
800
|
-
onCustomTypeAction,
|
|
801
|
-
submitDisabled
|
|
802
|
-
}
|
|
803
|
-
);
|
|
804
|
-
};
|
|
805
481
|
|
|
806
482
|
// src/web/forms/createWebFormRenderer.ts
|
|
807
483
|
var createWebFormRenderer = (options) => {
|
|
@@ -829,215 +505,6 @@ var AutoFormView2 = (props) => {
|
|
|
829
505
|
var AutoForm2 = (props) => {
|
|
830
506
|
return /* @__PURE__ */ jsx(AutoForm, { ...props, renderer: defaultWebRenderer });
|
|
831
507
|
};
|
|
832
|
-
|
|
833
|
-
// src/app/utils/easy-layout/parseTemplate.ts
|
|
834
|
-
var parseTrackSpec = (token) => {
|
|
835
|
-
const trimmed = token.trim();
|
|
836
|
-
const numericMatch = trimmed.match(/^([0-9]*\.?[0-9]+)(fr|px|%)$/);
|
|
837
|
-
if (!numericMatch) {
|
|
838
|
-
throw new Error(
|
|
839
|
-
`Invalid track token "${trimmed}". Supported units are fr, px, and %.`
|
|
840
|
-
);
|
|
841
|
-
}
|
|
842
|
-
const value = Number(numericMatch[1]);
|
|
843
|
-
const suffix = numericMatch[2];
|
|
844
|
-
if (!Number.isFinite(value) || value < 0) {
|
|
845
|
-
throw new Error(`Track value must be a non-negative number. Received "${trimmed}".`);
|
|
846
|
-
}
|
|
847
|
-
if (suffix === "fr") {
|
|
848
|
-
return { kind: "fr", value };
|
|
849
|
-
}
|
|
850
|
-
if (suffix === "px") {
|
|
851
|
-
return { kind: "px", value };
|
|
852
|
-
}
|
|
853
|
-
return { kind: "pct", value };
|
|
854
|
-
};
|
|
855
|
-
var normalizeAreas = (areaPart) => {
|
|
856
|
-
return areaPart.trim().split(/\s+/g).map((token) => token.trim()).filter(Boolean);
|
|
857
|
-
};
|
|
858
|
-
var parseTemplate = (template = "") => {
|
|
859
|
-
const lines = template.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
860
|
-
const areaGrid = [];
|
|
861
|
-
const rowTracks = [];
|
|
862
|
-
let colTracks = null;
|
|
863
|
-
for (const line of lines) {
|
|
864
|
-
if (line.startsWith("\\")) {
|
|
865
|
-
if (colTracks) {
|
|
866
|
-
throw new Error("Template can include only one column-track line.");
|
|
867
|
-
}
|
|
868
|
-
const colTokens = line.replace(/\\/g, " ").trim().split(/\s+/g).filter(Boolean);
|
|
869
|
-
colTracks = colTokens.map(parseTrackSpec);
|
|
870
|
-
continue;
|
|
871
|
-
}
|
|
872
|
-
const parts = line.split(",").map((part) => part.trim());
|
|
873
|
-
const areaPart = parts[0] || "";
|
|
874
|
-
if (!areaPart) {
|
|
875
|
-
continue;
|
|
876
|
-
}
|
|
877
|
-
if (parts.length > 2) {
|
|
878
|
-
throw new Error(
|
|
879
|
-
`Invalid row definition "${line}". Expected "<areas>, <row-track>".`
|
|
880
|
-
);
|
|
881
|
-
}
|
|
882
|
-
const areas = normalizeAreas(areaPart);
|
|
883
|
-
if (!areas.length) {
|
|
884
|
-
continue;
|
|
885
|
-
}
|
|
886
|
-
areaGrid.push(areas);
|
|
887
|
-
const rowTrack = parts[1];
|
|
888
|
-
if (rowTrack) {
|
|
889
|
-
rowTracks.push(parseTrackSpec(rowTrack));
|
|
890
|
-
}
|
|
891
|
-
}
|
|
892
|
-
if (!areaGrid.length) {
|
|
893
|
-
throw new Error("Template must include at least one area row.");
|
|
894
|
-
}
|
|
895
|
-
const expectedWidth = areaGrid[0].length;
|
|
896
|
-
for (let rowIndex = 0; rowIndex < areaGrid.length; rowIndex++) {
|
|
897
|
-
const width = areaGrid[rowIndex].length;
|
|
898
|
-
if (width !== expectedWidth) {
|
|
899
|
-
throw new Error(
|
|
900
|
-
`All area rows must have the same width. Expected ${expectedWidth}, received ${width} at row ${rowIndex + 1}.`
|
|
901
|
-
);
|
|
902
|
-
}
|
|
903
|
-
}
|
|
904
|
-
if (colTracks && colTracks.length !== expectedWidth) {
|
|
905
|
-
throw new Error(
|
|
906
|
-
`Column track count must match area width. Expected ${expectedWidth}, received ${colTracks.length}.`
|
|
907
|
-
);
|
|
908
|
-
}
|
|
909
|
-
const areaNames = Array.from(
|
|
910
|
-
new Set(
|
|
911
|
-
areaGrid.flat().map((name) => name.trim()).filter((name) => !!name && name !== ".")
|
|
912
|
-
)
|
|
913
|
-
);
|
|
914
|
-
return {
|
|
915
|
-
areaGrid,
|
|
916
|
-
rowTracks,
|
|
917
|
-
colTracks: colTracks || [],
|
|
918
|
-
areaNames
|
|
919
|
-
};
|
|
920
|
-
};
|
|
921
|
-
|
|
922
|
-
// src/app/utils/easy-layout/computeAreaBounds.ts
|
|
923
|
-
var computeAreaBounds = (parsed) => {
|
|
924
|
-
const result = {};
|
|
925
|
-
for (let rowIndex = 0; rowIndex < parsed.areaGrid.length; rowIndex++) {
|
|
926
|
-
const row = parsed.areaGrid[rowIndex];
|
|
927
|
-
for (let colIndex = 0; colIndex < row.length; colIndex++) {
|
|
928
|
-
const name = row[colIndex];
|
|
929
|
-
if (!name || name === ".") {
|
|
930
|
-
continue;
|
|
931
|
-
}
|
|
932
|
-
const row1 = rowIndex + 1;
|
|
933
|
-
const col1 = colIndex + 1;
|
|
934
|
-
const existing = result[name];
|
|
935
|
-
if (!existing) {
|
|
936
|
-
result[name] = {
|
|
937
|
-
name,
|
|
938
|
-
rowStart: row1,
|
|
939
|
-
rowEnd: row1,
|
|
940
|
-
colStart: col1,
|
|
941
|
-
colEnd: col1
|
|
942
|
-
};
|
|
943
|
-
continue;
|
|
944
|
-
}
|
|
945
|
-
existing.rowStart = Math.min(existing.rowStart, row1);
|
|
946
|
-
existing.rowEnd = Math.max(existing.rowEnd, row1);
|
|
947
|
-
existing.colStart = Math.min(existing.colStart, col1);
|
|
948
|
-
existing.colEnd = Math.max(existing.colEnd, col1);
|
|
949
|
-
}
|
|
950
|
-
}
|
|
951
|
-
return result;
|
|
952
|
-
};
|
|
953
|
-
|
|
954
|
-
// src/app/utils/easy-layout/validateAreas.ts
|
|
955
|
-
var validateAreas = (parsed) => {
|
|
956
|
-
const bounds = computeAreaBounds(parsed);
|
|
957
|
-
for (const areaName of parsed.areaNames) {
|
|
958
|
-
const bound = bounds[areaName];
|
|
959
|
-
if (!bound) {
|
|
960
|
-
continue;
|
|
961
|
-
}
|
|
962
|
-
for (let row = bound.rowStart; row <= bound.rowEnd; row++) {
|
|
963
|
-
for (let col = bound.colStart; col <= bound.colEnd; col++) {
|
|
964
|
-
const token = parsed.areaGrid[row - 1]?.[col - 1];
|
|
965
|
-
if (token !== areaName) {
|
|
966
|
-
throw new Error(
|
|
967
|
-
`Area "${areaName}" must be a rectangle. Missing "${areaName}" at row ${row}, col ${col}.`
|
|
968
|
-
);
|
|
969
|
-
}
|
|
970
|
-
}
|
|
971
|
-
}
|
|
972
|
-
}
|
|
973
|
-
};
|
|
974
|
-
|
|
975
|
-
// src/app/utils/EasyLayout.tsx
|
|
976
|
-
var getPascalCaseAreaName = (area) => {
|
|
977
|
-
return area.split("-").map((a) => a[0].toUpperCase() + a.slice(1)).join("");
|
|
978
|
-
};
|
|
979
|
-
var convertLayoutToCSS = (layout = "", spacing = {}) => {
|
|
980
|
-
const parsed = parseTemplate(layout);
|
|
981
|
-
validateAreas(parsed);
|
|
982
|
-
const renderTrack = (track) => {
|
|
983
|
-
if (track.kind === "px") {
|
|
984
|
-
return `${track.value}px`;
|
|
985
|
-
}
|
|
986
|
-
if (track.kind === "pct") {
|
|
987
|
-
return `${track.value}%`;
|
|
988
|
-
}
|
|
989
|
-
return `${track.value}fr`;
|
|
990
|
-
};
|
|
991
|
-
const areaRows = parsed.areaGrid.map((row) => row.join(" "));
|
|
992
|
-
const rows = parsed.rowTracks.map(renderTrack);
|
|
993
|
-
let css = "";
|
|
994
|
-
if (parsed.colTracks.length) {
|
|
995
|
-
css += `
|
|
996
|
-
grid-template-columns: ${parsed.colTracks.map(renderTrack).join(" ")};`;
|
|
997
|
-
}
|
|
998
|
-
css += `
|
|
999
|
-
grid-template-areas:
|
|
1000
|
-
${areaRows.map((a) => ` "${a}"`).join("\n")};`;
|
|
1001
|
-
if (rows.length) {
|
|
1002
|
-
css += `
|
|
1003
|
-
grid-template-rows: ${rows.join(" ")};`;
|
|
1004
|
-
}
|
|
1005
|
-
if (typeof spacing.gap !== "undefined") {
|
|
1006
|
-
css += `
|
|
1007
|
-
gap: ${typeof spacing.gap === "number" ? `${spacing.gap}px` : spacing.gap};`;
|
|
1008
|
-
}
|
|
1009
|
-
if (typeof spacing.padding !== "undefined") {
|
|
1010
|
-
css += `
|
|
1011
|
-
padding: ${typeof spacing.padding === "number" ? `${spacing.padding}px` : spacing.padding};`;
|
|
1012
|
-
}
|
|
1013
|
-
return {
|
|
1014
|
-
areasList: parsed.areaNames,
|
|
1015
|
-
css
|
|
1016
|
-
};
|
|
1017
|
-
};
|
|
1018
|
-
var createEasyLayout = (config, extendFrom, areasExtendFrom, spacing = {}) => {
|
|
1019
|
-
return (layoutTemplate, ...expressions) => {
|
|
1020
|
-
const mergedTemplate = layoutTemplate.reduce((acc, l, ind) => {
|
|
1021
|
-
const expr = expressions[ind - 1];
|
|
1022
|
-
const exprStr = typeof expr === "undefined" ? "" : expr;
|
|
1023
|
-
return `${acc}${l}${exprStr}`;
|
|
1024
|
-
}, "");
|
|
1025
|
-
const { areasList, css } = convertLayoutToCSS(mergedTemplate, spacing);
|
|
1026
|
-
const layout = config.createLayout({ base: extendFrom, css });
|
|
1027
|
-
const areas = areasList.reduce((acc, area) => {
|
|
1028
|
-
const pascalCaseAreaName = getPascalCaseAreaName(area);
|
|
1029
|
-
const component = config.createArea({ base: areasExtendFrom, area });
|
|
1030
|
-
return {
|
|
1031
|
-
...acc,
|
|
1032
|
-
[pascalCaseAreaName]: component
|
|
1033
|
-
};
|
|
1034
|
-
}, {});
|
|
1035
|
-
return {
|
|
1036
|
-
layout,
|
|
1037
|
-
areas
|
|
1038
|
-
};
|
|
1039
|
-
};
|
|
1040
|
-
};
|
|
1041
508
|
var EasyLayoutBase = styled_default("div")`
|
|
1042
509
|
display: grid;
|
|
1043
510
|
${({ $layoutCss }) => $layoutCss}
|