ballerina-core 1.0.33 → 1.0.34
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/main.ts +5 -0
- package/package.json +1 -1
- package/src/forms/domains/launcher/domains/create/coroutines/runner.ts +1 -1
- package/src/forms/domains/launcher/domains/create/state.ts +1 -1
- package/src/forms/domains/launcher/domains/edit/coroutines/runner.ts +1 -1
- package/src/forms/domains/launcher/domains/edit/state.ts +1 -1
- package/src/forms/domains/parser/coroutines/runner.ts +8 -6
- package/src/forms/domains/parser/domains/built-ins/state.ts +82 -59
- package/src/forms/domains/parser/domains/injectables/state.ts +42 -0
- package/src/forms/domains/parser/domains/types/state.ts +4 -3
- package/src/forms/domains/parser/domains/validator/state.ts +43 -16
- package/src/forms/domains/parser/state.tsx +55 -27
- package/src/forms/domains/parser/template.tsx +2 -1
- package/src/forms/domains/primitives/domains/base-64-file/state.ts +12 -0
- package/src/forms/domains/primitives/domains/base-64-file/template.tsx +24 -0
- package/src/forms/domains/primitives/domains/list/template.tsx +16 -16
- package/src/forms/domains/primitives/domains/map/template.tsx +11 -10
- package/src/forms/domains/primitives/domains/secret/state.ts +12 -0
- package/src/forms/domains/primitives/domains/secret/template.tsx +24 -0
- package/src/forms/domains/singleton/state.ts +1 -1
- package/src/forms/domains/singleton/template.tsx +14 -4
package/main.ts
CHANGED
|
@@ -91,6 +91,10 @@ export * from "./src/forms/domains/primitives/domains/searchable-infinite-stream
|
|
|
91
91
|
export * from "./src/forms/domains/primitives/domains/searchable-infinite-stream-multiselect/template"
|
|
92
92
|
export * from "./src/forms/domains/primitives/domains/list/state"
|
|
93
93
|
export * from "./src/forms/domains/primitives/domains/list/template"
|
|
94
|
+
export * from "./src/forms/domains/primitives/domains/base-64-file/state"
|
|
95
|
+
export * from "./src/forms/domains/primitives/domains/base-64-file/template"
|
|
96
|
+
export * from "./src/forms/domains/primitives/domains/secret/state"
|
|
97
|
+
export * from "./src/forms/domains/primitives/domains/secret/template"
|
|
94
98
|
export * from "./src/forms/domains/primitives/domains/map/state"
|
|
95
99
|
export * from "./src/forms/domains/primitives/domains/map/template"
|
|
96
100
|
export * from "./src/forms/domains/singleton/domains/mapping/state"
|
|
@@ -111,6 +115,7 @@ export * from "./src/forms/domains/launcher/domains/merger/state"
|
|
|
111
115
|
export * from "./src/forms/domains/launcher/coroutines/runner"
|
|
112
116
|
export * from "./src/forms/domains/launcher/state"
|
|
113
117
|
export * from "./src/forms/domains/launcher/template"
|
|
118
|
+
export * from "./src/forms/domains/parser/domains/injectables/state"
|
|
114
119
|
|
|
115
120
|
// import { simpleUpdater, simpleUpdaterWithChildren } from "./src/fun/domains/updater/domains/simpleUpdater/state"
|
|
116
121
|
// import { Updater } from "./src/fun/domains/updater/state"
|
package/package.json
CHANGED
|
@@ -19,7 +19,7 @@ export const createFormRunner = <E, FS>() => {
|
|
|
19
19
|
Co.Seq([
|
|
20
20
|
Co.GetState().then(current =>
|
|
21
21
|
Debounce<Synchronized<Unit, Synchronized<E, ApiErrors>>, CreateFormContext<E, FS>>(
|
|
22
|
-
Synchronize<E, ApiErrors>(e => current.api.create(e), _ => "transient failure", 5, 50)
|
|
22
|
+
Synchronize<E, ApiErrors>(e => current.api.create([e, current.formState]), _ => "transient failure", 5, 50)
|
|
23
23
|
.embed(
|
|
24
24
|
_ => AsyncState.Operations.hasValue(_.sync) ? _.sync.value : undefined,
|
|
25
25
|
_ => Synchronized.Updaters.sync<Unit, Synchronized<E, ApiErrors>>(AsyncState.Operations.map(_))
|
|
@@ -5,7 +5,7 @@ export type CreateFormContext<E,FS> = {
|
|
|
5
5
|
entityId:string,
|
|
6
6
|
api:{
|
|
7
7
|
default:() => Promise<E>,
|
|
8
|
-
create:BasicFun<E, Promise<ApiErrors>>
|
|
8
|
+
create:BasicFun<[E, FS], Promise<ApiErrors>>
|
|
9
9
|
},
|
|
10
10
|
actualForm:Template<Value<E> & FS, FS, { onChange:SimpleCallback<BasicUpdater<E>> }>
|
|
11
11
|
}
|
|
@@ -40,7 +40,7 @@ export const editFormRunner = <E, FS>() => {
|
|
|
40
40
|
Synchronize<Unit, ApiErrors, EditFormWritableState<E, FS>>(
|
|
41
41
|
(_) =>
|
|
42
42
|
current.entity.sync.kind == "loaded"
|
|
43
|
-
? current.api.update(current.entityId, current.entity.sync.value)
|
|
43
|
+
? current.api.update(current.entityId, current.entity.sync.value, current.formState)
|
|
44
44
|
: Promise.resolve([]),
|
|
45
45
|
(_) => "transient failure",
|
|
46
46
|
5,
|
|
@@ -7,7 +7,7 @@ export type EditFormContext<E,FS> = {
|
|
|
7
7
|
entityId:string,
|
|
8
8
|
api:{
|
|
9
9
|
get:(id: Guid) => Promise<E>,
|
|
10
|
-
update:(id: Guid, entity:E) => Promise<ApiErrors>
|
|
10
|
+
update:(id: Guid, entity:E, formstate: FS) => Promise<ApiErrors>
|
|
11
11
|
},
|
|
12
12
|
actualForm:Template<Value<E> & FS, FS, { onChange:SimpleCallback<BasicUpdater<E>>}>
|
|
13
13
|
}
|
|
@@ -1,21 +1,23 @@
|
|
|
1
|
-
import { AsyncState, builtInsFromFieldViews, FormsConfig, Sum, Synchronize, Unit } from "../../../../../main"
|
|
1
|
+
import { AsyncState, builtInsFromFieldViews, FormsConfig, injectablesFromFieldViews, Sum, Synchronize, Unit } from "../../../../../main"
|
|
2
2
|
import { CoTypedFactory } from "../../../../coroutines/builder"
|
|
3
3
|
import { FormParsingResult, FormsParserContext, FormsParserState, parseForms, replaceKeywords } from "../state"
|
|
4
4
|
|
|
5
|
-
export const LoadValidateAndParseFormsConfig = () => {
|
|
6
|
-
const Co = CoTypedFactory<FormsParserContext
|
|
5
|
+
export const LoadValidateAndParseFormsConfig = <T extends {[key in keyof T] : {type: any, state: any}}>() => {
|
|
6
|
+
const Co = CoTypedFactory<FormsParserContext<T>, FormsParserState>()
|
|
7
7
|
|
|
8
8
|
return Co.Template<Unit>(
|
|
9
9
|
Co.GetState().then(current =>
|
|
10
10
|
Synchronize<Unit, FormParsingResult>(async() => {
|
|
11
11
|
const rawFormsConfig = await current.getFormsConfig();
|
|
12
12
|
const formsConfig = replaceKeywords(rawFormsConfig, "from api")
|
|
13
|
-
const builtIns = builtInsFromFieldViews(current.fieldViews
|
|
14
|
-
const
|
|
13
|
+
const builtIns = builtInsFromFieldViews(current.fieldViews)
|
|
14
|
+
const injectedPrimitives = current.injectedPrimitives ? injectablesFromFieldViews(current.fieldViews, current.injectedPrimitives) : undefined
|
|
15
|
+
const validationResult = FormsConfig.Default.validateAndParseAPIResponse(builtIns, current.fieldTypeConverters, injectedPrimitives)(formsConfig)
|
|
15
16
|
if (validationResult.kind == "r")
|
|
16
17
|
return Sum.Default.right(validationResult.value)
|
|
17
18
|
return parseForms(
|
|
18
19
|
builtIns,
|
|
20
|
+
injectedPrimitives,
|
|
19
21
|
current.fieldTypeConverters,
|
|
20
22
|
current.containerFormView,
|
|
21
23
|
current.nestedContainerFormView,
|
|
@@ -23,7 +25,7 @@ export const LoadValidateAndParseFormsConfig = () => {
|
|
|
23
25
|
current.infiniteStreamSources,
|
|
24
26
|
current.enumOptionsSources,
|
|
25
27
|
current.entityApis,
|
|
26
|
-
current.leafPredicates)(validationResult.value)
|
|
28
|
+
current.leafPredicates,)(validationResult.value)
|
|
27
29
|
}, _ => "transient failure", 5, 50)
|
|
28
30
|
.embed(
|
|
29
31
|
_ => _.formsConfig,
|
|
@@ -2,7 +2,7 @@ import { Map, List, Set, OrderedMap } from "immutable"
|
|
|
2
2
|
import { CollectionReference } from "../../../collection/domains/reference/state";
|
|
3
3
|
import { CollectionSelection } from "../../../collection/domains/selection/state";
|
|
4
4
|
import { BasicFun } from "../../../../../fun/state";
|
|
5
|
-
import { replaceKeyword, replaceKeywords, revertKeyword, Type, TypeDefinition, TypeName } from "../../../../../../main";
|
|
5
|
+
import { InjectedPrimitives, replaceKeyword, replaceKeywords, revertKeyword, Type, TypeDefinition, TypeName } from "../../../../../../main";
|
|
6
6
|
|
|
7
7
|
export const PrimitiveTypes =
|
|
8
8
|
["string",
|
|
@@ -10,7 +10,10 @@ export const PrimitiveTypes =
|
|
|
10
10
|
"boolean",
|
|
11
11
|
"maybeBoolean",
|
|
12
12
|
"Date",
|
|
13
|
-
"CollectionReference"
|
|
13
|
+
"CollectionReference",
|
|
14
|
+
"base64File",
|
|
15
|
+
"secret",
|
|
16
|
+
] as const
|
|
14
17
|
export type PrimitiveType = (typeof PrimitiveTypes)[number]
|
|
15
18
|
|
|
16
19
|
export const GenericTypes = [
|
|
@@ -20,22 +23,25 @@ export const GenericTypes = [
|
|
|
20
23
|
"Map"] as const
|
|
21
24
|
export type GenericType = (typeof GenericTypes)[number]
|
|
22
25
|
|
|
23
|
-
export type ApiConverter<T> =
|
|
24
|
-
export type ApiConverters = {
|
|
26
|
+
export type ApiConverter<T> = { fromAPIRawValue: BasicFun<any, T>, toAPIRawValue: BasicFun<[T, boolean], any> }
|
|
27
|
+
export type ApiConverters<U> = {[key in keyof U]: ApiConverter<U[key]> } & BuiltInApiConverters
|
|
28
|
+
export type BuiltInApiConverters = {
|
|
25
29
|
"string": ApiConverter<string>
|
|
26
30
|
"number": ApiConverter<number>
|
|
27
31
|
"boolean": ApiConverter<boolean>
|
|
28
32
|
"maybeBoolean": ApiConverter<boolean | undefined>
|
|
33
|
+
"base64File": ApiConverter<string>
|
|
34
|
+
"secret": ApiConverter<string>,
|
|
29
35
|
"Date": ApiConverter<Date>
|
|
30
36
|
"CollectionReference": ApiConverter<CollectionReference>
|
|
31
37
|
"SingleSelection": ApiConverter<CollectionSelection<any>>
|
|
32
38
|
"MultiSelection": ApiConverter<OrderedMap<string, any>>
|
|
33
|
-
"List": ApiConverter<List<any
|
|
39
|
+
"List": ApiConverter<List<any>>,
|
|
34
40
|
"Map": ApiConverter<List<[any, any]>>
|
|
35
41
|
}
|
|
36
42
|
|
|
37
|
-
export type PrimitiveBuiltIn = { renderers: Set<keyof BuiltIns["renderers"]>,
|
|
38
|
-
export type GenericBuiltIn = { defaultValue: any
|
|
43
|
+
export type PrimitiveBuiltIn = { renderers: Set<keyof BuiltIns["renderers"]>, defaultValue: any }
|
|
44
|
+
export type GenericBuiltIn = { defaultValue: any }
|
|
39
45
|
export type BuiltIns = {
|
|
40
46
|
primitives: Map<string, PrimitiveBuiltIn>;
|
|
41
47
|
generics: Map<string, GenericBuiltIn>;
|
|
@@ -44,6 +50,8 @@ export type BuiltIns = {
|
|
|
44
50
|
maybeBoolean: Set<string>;
|
|
45
51
|
number: Set<string>;
|
|
46
52
|
string: Set<string>;
|
|
53
|
+
base64File: Set<string>;
|
|
54
|
+
secret: Set<string>;
|
|
47
55
|
date: Set<string>;
|
|
48
56
|
enumSingleSelection: Set<string>;
|
|
49
57
|
enumMultiSelection: Set<string>;
|
|
@@ -54,22 +62,24 @@ export type BuiltIns = {
|
|
|
54
62
|
};
|
|
55
63
|
};
|
|
56
64
|
|
|
57
|
-
export const builtInsFromFieldViews = (fieldViews: any
|
|
65
|
+
export const builtInsFromFieldViews = (fieldViews: any): BuiltIns => {
|
|
58
66
|
let builtins: BuiltIns = {
|
|
59
67
|
"primitives": Map<string, PrimitiveBuiltIn>([
|
|
60
|
-
["string", { renderers: Set(["string"]),
|
|
61
|
-
["number", { renderers: Set(["number"]),
|
|
62
|
-
["boolean", { renderers: Set(["boolean"]),
|
|
63
|
-
["maybeBoolean", { renderers: Set(["maybeBoolean"]),
|
|
64
|
-
["date", { renderers: Set(["date"]),
|
|
65
|
-
["Date", { renderers: Set(["date"]),
|
|
66
|
-
["CollectionReference", { renderers: Set(["enumSingleSelection", "enumMultiSelection", "streamSingleSelection", "streamMultiSelection"]),
|
|
68
|
+
["string", { renderers: Set(["string"]), defaultValue: "" }] as [string, PrimitiveBuiltIn],
|
|
69
|
+
["number", { renderers: Set(["number"]), defaultValue: 0 }] as [string, PrimitiveBuiltIn],
|
|
70
|
+
["boolean", { renderers: Set(["boolean"]), defaultValue: false }],
|
|
71
|
+
["maybeBoolean", { renderers: Set(["maybeBoolean"]), defaultValue: undefined }] as [string, PrimitiveBuiltIn],
|
|
72
|
+
["date", { renderers: Set(["date"]), defaultValue: new Date(Date.now()) }] as [string, PrimitiveBuiltIn],
|
|
73
|
+
["Date", { renderers: Set(["date"]), defaultValue: new Date(Date.now()) }] as [string, PrimitiveBuiltIn],
|
|
74
|
+
["CollectionReference", { renderers: Set(["enumSingleSelection", "enumMultiSelection", "streamSingleSelection", "streamMultiSelection"]), defaultValue: CollectionReference.Default("", "") }] as [string, PrimitiveBuiltIn],
|
|
75
|
+
["base64File", { renderers: Set(["base64File"]), defaultValue: "" }] as [string, PrimitiveBuiltIn],
|
|
76
|
+
["secret", { renderers: Set(["secret"]), defaultValue: "" }] as [string, PrimitiveBuiltIn],
|
|
67
77
|
]),
|
|
68
78
|
"generics": Map([
|
|
69
|
-
["SingleSelection", {
|
|
70
|
-
["Multiselection", {
|
|
71
|
-
["List", {
|
|
72
|
-
["Map", {
|
|
79
|
+
["SingleSelection", { defaultValue: CollectionSelection().Default.right("no selection") }] as [string, GenericBuiltIn],
|
|
80
|
+
["Multiselection", { defaultValue: Map() }] as [string, GenericBuiltIn],
|
|
81
|
+
["List", { defaultValue: List() }] as [string, GenericBuiltIn],
|
|
82
|
+
["Map", { defaultValue: List() }] as [string, GenericBuiltIn]
|
|
73
83
|
]),
|
|
74
84
|
"renderers": {
|
|
75
85
|
"boolean": Set(),
|
|
@@ -82,6 +92,8 @@ export const builtInsFromFieldViews = (fieldViews: any, fieldTypeConverters: Api
|
|
|
82
92
|
"number": Set(),
|
|
83
93
|
"string": Set(),
|
|
84
94
|
"list": Set(),
|
|
95
|
+
"base64File": Set(),
|
|
96
|
+
"secret": Set(),
|
|
85
97
|
"map": Set(),
|
|
86
98
|
}
|
|
87
99
|
}
|
|
@@ -96,12 +108,17 @@ export const builtInsFromFieldViews = (fieldViews: any, fieldTypeConverters: Api
|
|
|
96
108
|
return builtins
|
|
97
109
|
}
|
|
98
110
|
|
|
99
|
-
|
|
100
|
-
export const defaultValue = (types: Map<TypeName, TypeDefinition>, builtIns: BuiltIns) => (t: TypeName | Type): any => {
|
|
111
|
+
export const defaultValue = <T>(types: Map<TypeName, TypeDefinition>, builtIns: BuiltIns, injectedPrimitives?: InjectedPrimitives<T>) => (t: TypeName | Type): any => {
|
|
101
112
|
if (typeof t == "string") {
|
|
102
113
|
let primitive = builtIns.primitives.get(t)
|
|
114
|
+
let injectedPrimitive = injectedPrimitives?.injectedPrimitives.get(t as keyof T)
|
|
115
|
+
if(primitive && injectedPrimitive) {
|
|
116
|
+
throw `both primitive and injected primitive are defined for ${t}`
|
|
117
|
+
}
|
|
103
118
|
if (primitive != undefined) {
|
|
104
119
|
return primitive.defaultValue
|
|
120
|
+
} else if (injectedPrimitive != undefined) {
|
|
121
|
+
return injectedPrimitive.defaultValue
|
|
105
122
|
} else {
|
|
106
123
|
let generic = builtIns.generics.get(t)
|
|
107
124
|
if (generic != undefined) {
|
|
@@ -143,11 +160,11 @@ const parseTypeIShouldBePartOfFormValidation = (t:any) : TypeName | Type => {
|
|
|
143
160
|
return null!
|
|
144
161
|
}
|
|
145
162
|
|
|
146
|
-
export const fromAPIRawValue = (t: Type, types: Map<TypeName, TypeDefinition>, builtIns: BuiltIns, converters:
|
|
163
|
+
export const fromAPIRawValue = <T>(t: Type, types: Map<TypeName, TypeDefinition>, builtIns: BuiltIns, converters: BuiltInApiConverters, isKeywordsReplaced: boolean = false, injectedPrimitives?: InjectedPrimitives<T>) => (raw: any): any => {
|
|
147
164
|
// alert(JSON.stringify(t))
|
|
148
165
|
if (raw == undefined) {
|
|
149
166
|
console.warn(`instantiating default value for type ${JSON.stringify(t)}: the value was undefined so something is missing from the API response`)
|
|
150
|
-
return defaultValue(types, builtIns)(t.kind == "primitive" ? t.value : t.kind == "lookup" ? t.name : t.value)
|
|
167
|
+
return defaultValue(types, builtIns, injectedPrimitives)(t.kind == "primitive" ? t.value : t.kind == "lookup" ? t.name : t.value)
|
|
151
168
|
}
|
|
152
169
|
|
|
153
170
|
const obj = !isKeywordsReplaced ? replaceKeywords(raw, "from api") : raw
|
|
@@ -158,43 +175,46 @@ export const fromAPIRawValue = (t: Type, types: Map<TypeName, TypeDefinition>, b
|
|
|
158
175
|
if (t.value == "SingleSelection" && t.args.length == 1) {
|
|
159
176
|
let result = converters[t.value].fromAPIRawValue(obj)
|
|
160
177
|
result = CollectionSelection().Updaters.left(
|
|
161
|
-
fromAPIRawValue({ kind: "lookup", name: t.args[0] }, types, builtIns, converters, true))(result)
|
|
178
|
+
fromAPIRawValue({ kind: "lookup", name: t.args[0] }, types, builtIns, converters, true, injectedPrimitives))(result)
|
|
162
179
|
return result
|
|
163
180
|
}
|
|
164
181
|
if ((t.value == "Multiselection" || t.value == "MultiSelection") && t.args.length == 1) {
|
|
165
182
|
let result = converters["MultiSelection"].fromAPIRawValue(obj)
|
|
166
|
-
result = result.map(fromAPIRawValue({ kind: "lookup", name: t.args[0] }, types, builtIns, converters, true))
|
|
183
|
+
result = result.map(fromAPIRawValue({ kind: "lookup", name: t.args[0] }, types, builtIns, converters, true, injectedPrimitives))
|
|
167
184
|
return result
|
|
168
185
|
}
|
|
169
186
|
if (t.value == "List" && t.args.length == 1) {
|
|
170
187
|
let result = converters[t.value].fromAPIRawValue(obj)
|
|
188
|
+
const isPrimitive = PrimitiveTypes.some(_ => _ == t.args[0]) || injectedPrimitives?.injectedPrimitives.has(t.args[0] as keyof T)
|
|
171
189
|
result = result.map(fromAPIRawValue(
|
|
172
|
-
|
|
190
|
+
isPrimitive ?
|
|
173
191
|
{ kind: "primitive", value: t.args[0] as PrimitiveType }
|
|
174
192
|
: { kind: "lookup", name: t.args[0] }
|
|
175
|
-
, types, builtIns, converters, true))
|
|
193
|
+
, types, builtIns, converters, true, injectedPrimitives))
|
|
176
194
|
return result
|
|
177
195
|
}
|
|
178
196
|
if (t.value == "Map" && t.args.length == 2) {
|
|
179
197
|
let result = converters[t.value].fromAPIRawValue(obj)
|
|
180
198
|
let t_args = t.args.map(parseTypeIShouldBePartOfFormValidation)
|
|
199
|
+
const isKeyPrimitive = PrimitiveTypes.some(_ => _ == t.args[0]) || injectedPrimitives?.injectedPrimitives.has(t.args[0] as keyof T)
|
|
200
|
+
const isValuePrimitive = PrimitiveTypes.some(_ => _ == t.args[1]) || injectedPrimitives?.injectedPrimitives.has(t.args[1] as keyof T)
|
|
181
201
|
result = result.map(keyValue => ([
|
|
182
202
|
fromAPIRawValue(
|
|
183
203
|
typeof t_args[0] == "string" ?
|
|
184
|
-
|
|
204
|
+
isKeyPrimitive ?
|
|
185
205
|
{ kind: "primitive", value: t_args[0] as PrimitiveType }
|
|
186
206
|
: { kind: "lookup", name: t_args[0] }
|
|
187
207
|
:
|
|
188
208
|
t_args[0],
|
|
189
|
-
types, builtIns, converters, true)(keyValue[0]),
|
|
209
|
+
types, builtIns, converters, true, injectedPrimitives)(keyValue[0]),
|
|
190
210
|
fromAPIRawValue(
|
|
191
211
|
typeof t_args[1] == "string" ?
|
|
192
|
-
|
|
212
|
+
isValuePrimitive ?
|
|
193
213
|
{ kind: "primitive", value: t_args[1] as PrimitiveType }
|
|
194
214
|
: { kind: "lookup", name: t_args[1] }
|
|
195
215
|
:
|
|
196
216
|
t_args[1],
|
|
197
|
-
types, builtIns, converters, true)(keyValue[1]),
|
|
217
|
+
types, builtIns, converters, true, injectedPrimitives)(keyValue[1]),
|
|
198
218
|
])
|
|
199
219
|
)
|
|
200
220
|
return result
|
|
@@ -205,7 +225,7 @@ export const fromAPIRawValue = (t: Type, types: Map<TypeName, TypeDefinition>, b
|
|
|
205
225
|
tDef.fields.forEach((fieldType, fieldName) => {
|
|
206
226
|
const replacedFieldName = replaceKeyword(fieldName)
|
|
207
227
|
const fieldValue = obj[replacedFieldName]
|
|
208
|
-
result[replacedFieldName] = fromAPIRawValue(fieldType, types, builtIns, converters, true)(fieldValue)
|
|
228
|
+
result[replacedFieldName] = fromAPIRawValue(fieldType, types, builtIns, converters, true, injectedPrimitives)(fieldValue)
|
|
209
229
|
})
|
|
210
230
|
return result
|
|
211
231
|
}
|
|
@@ -214,64 +234,67 @@ export const fromAPIRawValue = (t: Type, types: Map<TypeName, TypeDefinition>, b
|
|
|
214
234
|
}
|
|
215
235
|
|
|
216
236
|
|
|
217
|
-
export const toAPIRawValue = (t: Type, types: Map<TypeName, TypeDefinition>, builtIns: BuiltIns, converters:
|
|
218
|
-
|
|
237
|
+
export const toAPIRawValue = <T>(t: Type, types: Map<TypeName, TypeDefinition>, builtIns: BuiltIns, converters: BuiltInApiConverters, isKeywordsReverted: boolean = false, injectedPrimitives?: InjectedPrimitives<T>) => (raw: any, formState: any) : any => {
|
|
219
238
|
const obj = !isKeywordsReverted ? replaceKeywords(raw, "to api") : raw
|
|
220
239
|
|
|
221
240
|
if (t.kind == "primitive") {
|
|
222
|
-
return converters[t.value].toAPIRawValue(obj as never)
|
|
241
|
+
return converters[t.value].toAPIRawValue([obj, formState.modifiedByUser] as never)
|
|
223
242
|
} else if (t.kind == "application") { // application here means "generic type application"
|
|
224
243
|
if (t.value == "SingleSelection" && t.args.length == 1) {
|
|
225
|
-
let result = converters[t.value].toAPIRawValue(obj)
|
|
226
|
-
if (result != undefined && typeof result == "object")
|
|
227
|
-
result = toAPIRawValue({ kind:
|
|
244
|
+
let result = converters[t.value].toAPIRawValue([obj, formState.modifiedByUser])
|
|
245
|
+
if (result != undefined && typeof result == "object"){
|
|
246
|
+
result = toAPIRawValue({ kind:"lookup", name:t.args[0] }, types, builtIns, converters, true, injectedPrimitives)(result, formState)
|
|
247
|
+
}
|
|
228
248
|
return result
|
|
229
249
|
}
|
|
230
250
|
if ((t.value == "Multiselection" || t.value == "MultiSelection") && t.args.length == 1) {
|
|
231
251
|
// alert(`MultiSelect ${JSON.stringify(t)} ${JSON.stringify(obj)}`)
|
|
232
|
-
let result = converters["MultiSelection"].toAPIRawValue(obj)
|
|
252
|
+
let result = converters["MultiSelection"].toAPIRawValue([obj, formState.modifiedByUser])
|
|
233
253
|
// alert(`MultiSelect result1 = ${JSON.stringify(result)}`)
|
|
234
254
|
// alert(`${JSON.stringify(t.args[0])}`)
|
|
235
|
-
result = result.map((_:
|
|
236
|
-
typeof _ == "object" ? toAPIRawValue({ kind:
|
|
255
|
+
result = result.map((_:any) =>
|
|
256
|
+
typeof _ == "object" ? toAPIRawValue({ kind:"lookup", name: t.args[0] }, types, builtIns, converters, true, injectedPrimitives)(_, formState) : _)
|
|
237
257
|
// alert(`MultiSelect result2 = ${JSON.stringify(result)}`)
|
|
238
258
|
return result
|
|
239
259
|
}
|
|
240
260
|
if (t.value == "List" && t.args.length == 1) {
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
261
|
+
const converterResult = converters[t.value].toAPIRawValue([obj, formState.modifiedByUser])
|
|
262
|
+
const isPrimitive = PrimitiveTypes.some(_ => _ == t.args[0]) || injectedPrimitives?.injectedPrimitives.has(t.args[0] as keyof T)
|
|
263
|
+
return converterResult.map((item: any, index: number) =>
|
|
264
|
+
toAPIRawValue(
|
|
265
|
+
isPrimitive ?
|
|
266
|
+
{ kind:"primitive", value:t.args[0] as PrimitiveType }
|
|
267
|
+
: { kind:"lookup", name:t.args[0] },
|
|
268
|
+
types, builtIns, converters, true, injectedPrimitives)(item,
|
|
269
|
+
formState.elementFormStates.get(index)
|
|
270
|
+
))
|
|
249
271
|
}
|
|
250
272
|
if (t.value == "Map" && t.args.length == 2) {
|
|
251
|
-
|
|
252
|
-
|
|
273
|
+
const converterResult = converters[t.value].toAPIRawValue([obj, formState.modifiedByUser])
|
|
274
|
+
const isKeyPrimitive = PrimitiveTypes.some(_ => _ == t.args[0]) || injectedPrimitives?.injectedPrimitives.has(t.args[0] as keyof T)
|
|
275
|
+
const isValuePrimitive = PrimitiveTypes.some(_ => _ == t.args[1]) || injectedPrimitives?.injectedPrimitives.has(t.args[1] as keyof T)
|
|
253
276
|
let t_args = t.args.map(parseTypeIShouldBePartOfFormValidation)
|
|
254
|
-
|
|
277
|
+
return converterResult.map((keyValue: any, index: number) => ([
|
|
255
278
|
toAPIRawValue(
|
|
256
279
|
typeof t_args[0] == "string" ?
|
|
257
|
-
|
|
280
|
+
isKeyPrimitive ?
|
|
258
281
|
{ kind: "primitive", value: t_args[0] as PrimitiveType }
|
|
259
282
|
: { kind: "lookup", name: t_args[0] }
|
|
260
283
|
:
|
|
261
284
|
t_args[0],
|
|
262
|
-
types, builtIns, converters, true)(keyValue[0])
|
|
285
|
+
types, builtIns, converters, true, injectedPrimitives)(keyValue[0], formState.elementFormStates.get(index).KeyFormState
|
|
286
|
+
),
|
|
263
287
|
toAPIRawValue(
|
|
264
288
|
typeof t_args[1] == "string" ?
|
|
265
|
-
|
|
289
|
+
isValuePrimitive ?
|
|
266
290
|
{ kind: "primitive", value: t_args[1] as PrimitiveType }
|
|
267
291
|
: { kind: "lookup", name: t_args[1] }
|
|
268
292
|
:
|
|
269
293
|
t_args[1],
|
|
270
|
-
types, builtIns, converters, true)(keyValue[1])
|
|
294
|
+
types, builtIns, converters, true, injectedPrimitives)(keyValue[1], formState.elementFormStates.get(index).ValueFormState
|
|
295
|
+
),
|
|
271
296
|
])
|
|
272
297
|
)
|
|
273
|
-
|
|
274
|
-
return result
|
|
275
298
|
}
|
|
276
299
|
|
|
277
300
|
} else { // t.kind == lookup: we are dealing with a record/object
|
|
@@ -280,7 +303,7 @@ export const toAPIRawValue = (t: Type, types: Map<TypeName, TypeDefinition>, bui
|
|
|
280
303
|
tDef.fields.forEach((fieldType, fieldName) => {
|
|
281
304
|
const revertedFieldName = revertKeyword(fieldName)
|
|
282
305
|
const fieldValue = obj[revertedFieldName]
|
|
283
|
-
result[revertedFieldName] = toAPIRawValue(fieldType, types, builtIns, converters, true)(fieldValue)
|
|
306
|
+
result[revertedFieldName] = toAPIRawValue(fieldType, types, builtIns, converters, true, injectedPrimitives)(fieldValue, formState[fieldName])
|
|
284
307
|
})
|
|
285
308
|
return result
|
|
286
309
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Map, Set } from 'immutable';
|
|
2
|
+
import { Unit } from '../../../../../../main';
|
|
3
|
+
|
|
4
|
+
export type InjectablePrimitive<T, U = Unit> = {
|
|
5
|
+
defaultValue: T;
|
|
6
|
+
fieldView: any;
|
|
7
|
+
defaultState?: U;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type InjectedPrimitive<T> = {
|
|
11
|
+
renderers: Set<keyof T>;
|
|
12
|
+
defaultValue: any;
|
|
13
|
+
fieldView: any;
|
|
14
|
+
defaultState: any;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type Injectables<T extends {[key in keyof T] : {type: any, state: any}}> = Map<keyof T, InjectablePrimitive<T[keyof T]["type"], T[keyof T]["state"]>>;
|
|
18
|
+
|
|
19
|
+
export type InjectedPrimitives<T> =
|
|
20
|
+
{
|
|
21
|
+
injectedPrimitives: Map<keyof T, InjectedPrimitive<T>>;
|
|
22
|
+
renderers: {
|
|
23
|
+
[key in keyof T]: Set<string>;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const injectablesFromFieldViews = <T extends {[key in keyof T] : {type: any, state: any}}>(fieldViews: any, injectables: Injectables<T>): InjectedPrimitives<T> => {
|
|
28
|
+
let result = {injectedPrimitives: Map<string, InjectedPrimitive<T>>(), renderers: {}} as InjectedPrimitives<T>;
|
|
29
|
+
result.injectedPrimitives = injectables.map(((injectable, key) =>
|
|
30
|
+
({ renderers: Set([key]), defaultValue: injectable.defaultValue, fieldView: injectable.fieldView, defaultState: injectable.defaultState }) as InjectedPrimitive<T>
|
|
31
|
+
))
|
|
32
|
+
result.renderers = { ...injectables.map((_, key) => ({[key]: Set<string>()})).valueSeq().toArray().reduce((acc, x) => ({...acc, ...x}), {}) } as InjectedPrimitives<T>['renderers']
|
|
33
|
+
Object.keys(result.renderers).forEach((_categoryName) => {
|
|
34
|
+
const categoryName = _categoryName
|
|
35
|
+
if (categoryName in fieldViews) {
|
|
36
|
+
Object.keys(fieldViews[categoryName]).forEach(viewName => {
|
|
37
|
+
result.renderers[categoryName as keyof T] = result.renderers[categoryName as keyof T].add(viewName)
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
return result
|
|
42
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Map, OrderedMap } from "immutable";
|
|
2
2
|
import { BuiltIns } from "../built-ins/state";
|
|
3
|
+
import { InjectedPrimitives } from "../injectables/state";
|
|
3
4
|
|
|
4
5
|
export type FieldName = string;
|
|
5
6
|
export type TypeName = string;
|
|
@@ -8,7 +9,7 @@ export type TypeDefinition = {
|
|
|
8
9
|
name: TypeName;
|
|
9
10
|
fields: OrderedMap<FieldName, Type>;
|
|
10
11
|
};
|
|
11
|
-
export type PrimitiveTypeName = "string" | "number" | "maybeBoolean" | "boolean" | "Date" | "CollectionReference"
|
|
12
|
+
export type PrimitiveTypeName = "string" | "number" | "maybeBoolean" | "boolean" | "Date" | "CollectionReference" | "base64File" | "secret";
|
|
12
13
|
export type Type = {
|
|
13
14
|
kind: "lookup"; name: TypeName;
|
|
14
15
|
} | {
|
|
@@ -31,10 +32,10 @@ export const Type = {
|
|
|
31
32
|
fst.args.length == snd.args.length &&
|
|
32
33
|
fst.args.every((v, i) => v == snd.args[i]) :
|
|
33
34
|
false,
|
|
34
|
-
FromName: (types: Map<string, TypeDefinition>, builtIns: BuiltIns) => (typeName: string): Type | undefined => {
|
|
35
|
+
FromName: <T>(types: Map<string, TypeDefinition>, builtIns: BuiltIns, injectedPrimitives?: InjectedPrimitives<T>) => (typeName: string): Type | undefined => {
|
|
35
36
|
const recordTypeName = types.get(typeName)?.name
|
|
36
37
|
if (recordTypeName) return Type.Default.lookup(recordTypeName)
|
|
37
|
-
const primitiveTypeName = builtIns.primitives.get(typeName) && typeName
|
|
38
|
+
const primitiveTypeName = (builtIns.primitives.get(typeName) && typeName) ?? (injectedPrimitives?.injectedPrimitives.get(typeName as keyof T) && typeName)
|
|
38
39
|
if (primitiveTypeName) return Type.Default.primitive(primitiveTypeName as any)
|
|
39
40
|
return undefined
|
|
40
41
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { Set, Map, OrderedMap } from "immutable";
|
|
2
|
-
import { BoolExpr, BuiltIns, FieldName, FormsConfigMerger, MappingPaths, Sum, Type, TypeDefinition, TypeName } from "../../../../../../main";
|
|
2
|
+
import { ApiConverters, BoolExpr, BuiltIns, FieldName, FormsConfigMerger, InjectedPrimitives, MappingPaths, revertKeyword, Sum, Type, TypeDefinition, TypeName } from "../../../../../../main";
|
|
3
3
|
|
|
4
4
|
export type FieldConfig = {
|
|
5
5
|
renderer: string;
|
|
6
6
|
label: string
|
|
7
7
|
api: { stream?: string, enumOptions?: string },
|
|
8
8
|
elementRenderer?: string,
|
|
9
|
+
elementLabel?: string,
|
|
9
10
|
mapRenderer?: { keyRenderer: FieldConfig, valueRenderer: FieldConfig },
|
|
10
11
|
visible: BoolExpr<any>;
|
|
11
12
|
disabled: BoolExpr<any>;
|
|
@@ -16,6 +17,7 @@ export type FormDef = {
|
|
|
16
17
|
typeDef: TypeDefinition;
|
|
17
18
|
fields: Map<FieldName, FieldConfig>;
|
|
18
19
|
tabs: FormLayout;
|
|
20
|
+
header?: string;
|
|
19
21
|
};
|
|
20
22
|
export type FormLayout = OrderedMap<string, TabLayout>
|
|
21
23
|
export type GroupLayout = Array<FieldName>;
|
|
@@ -66,7 +68,7 @@ export type FormValidationError = string;
|
|
|
66
68
|
export type FormValidationResult = Sum<FormsConfig, Array<FormValidationError>>
|
|
67
69
|
export const FormsConfig = {
|
|
68
70
|
Default: {
|
|
69
|
-
validateAndParseAPIResponse: (builtIns: BuiltIns) => (fc: any): FormValidationResult => {
|
|
71
|
+
validateAndParseAPIResponse: <T>(builtIns: BuiltIns, apiConverters: ApiConverters<T>, injectedPrimitives?: InjectedPrimitives<T>) => (fc: any): FormValidationResult => {
|
|
70
72
|
let errors: Array<FormValidationError> = [];
|
|
71
73
|
const formsConfig = Array.isArray(fc) ? FormsConfigMerger.Default.merge(fc) : fc;
|
|
72
74
|
let types: Map<TypeName, TypeDefinition> = Map();
|
|
@@ -74,6 +76,12 @@ export const FormsConfig = {
|
|
|
74
76
|
errors.push("the formsConfig does not contain a 'types' field");
|
|
75
77
|
return Sum.Default.right(errors);
|
|
76
78
|
}
|
|
79
|
+
if(injectedPrimitives){
|
|
80
|
+
injectedPrimitives?.injectedPrimitives.keySeq().toArray().some((injectedPrimitiveName) => {
|
|
81
|
+
if(!Object.keys(apiConverters).includes(injectedPrimitiveName as string)){
|
|
82
|
+
errors.push(`the formsConfig does not contain an Api Converter for injected primitive: ${injectedPrimitiveName as string}`);
|
|
83
|
+
}})
|
|
84
|
+
}
|
|
77
85
|
Object.keys(formsConfig["types"]).forEach((typeName: any) => {
|
|
78
86
|
let typeDef: TypeDefinition = { name: typeName, extends: [], fields: OrderedMap() };
|
|
79
87
|
types = types.set(typeName, typeDef);
|
|
@@ -91,9 +99,12 @@ export const FormsConfig = {
|
|
|
91
99
|
Object.keys(configTypeDef["fields"]).forEach((fieldName: any) => {
|
|
92
100
|
let configFieldType = configTypeDef["fields"][fieldName];
|
|
93
101
|
if (typeof configFieldType == "string") {
|
|
94
|
-
if (
|
|
102
|
+
if (injectedPrimitives?.injectedPrimitives.has(configFieldType as keyof T) &&
|
|
103
|
+
(builtIns.primitives.has(configFieldType) || builtIns.generics.has(configFieldType))) {
|
|
104
|
+
errors.push(`field ${fieldName} in type ${typeName}: injectedPrimitive cannot have same name as builtIn primitive`);
|
|
105
|
+
}
|
|
106
|
+
if (builtIns.primitives.has(configFieldType) || injectedPrimitives?.injectedPrimitives.has(configFieldType as keyof T))
|
|
95
107
|
typeDef.fields = typeDef.fields.set(fieldName, { kind: "primitive", value: configFieldType as any });
|
|
96
|
-
|
|
97
108
|
else
|
|
98
109
|
typeDef.fields = typeDef.fields.set(fieldName, { kind: "lookup", name: configFieldType as any });
|
|
99
110
|
} else if (typeof configFieldType == "object") {
|
|
@@ -117,11 +128,11 @@ export const FormsConfig = {
|
|
|
117
128
|
});
|
|
118
129
|
types.forEach((typeDef, typeName) => {
|
|
119
130
|
typeDef.extends.forEach(extendedTypeName => {
|
|
120
|
-
if (!builtIns.primitives.has(extendedTypeName) && !types.has(extendedTypeName))
|
|
131
|
+
if ((!builtIns.primitives.has(extendedTypeName) && !injectedPrimitives?.injectedPrimitives.has(extendedTypeName as keyof T)) && !types.has(extendedTypeName))
|
|
121
132
|
errors.push(`type ${typeName} extends non-existent type ${extendedTypeName}`);
|
|
122
133
|
});
|
|
123
134
|
typeDef.fields.forEach((fieldDef, fieldName) => {
|
|
124
|
-
if (fieldDef.kind == "primitive" && !builtIns.primitives.has(fieldDef.value))
|
|
135
|
+
if (fieldDef.kind == "primitive" && (!builtIns.primitives.has(fieldDef.value) && !injectedPrimitives?.injectedPrimitives.has(fieldDef.value as keyof T) ))
|
|
125
136
|
errors.push(`field ${fieldName} of type ${typeName} is non-existent primitive type ${fieldDef.value}`);
|
|
126
137
|
if (fieldDef.kind == "lookup" && !types.has(fieldDef.name))
|
|
127
138
|
errors.push(`field ${fieldName} of type ${typeName} is non-existent type ${fieldDef.name}`);
|
|
@@ -300,7 +311,11 @@ export const FormsConfig = {
|
|
|
300
311
|
|
|
301
312
|
const rendererMatchesType = (formName:string, fieldName:string) => (fieldTypeDef:Type, fieldConfig:any) => {
|
|
302
313
|
if (fieldTypeDef?.kind == "primitive") {
|
|
303
|
-
if
|
|
314
|
+
if(injectedPrimitives?.injectedPrimitives.has(fieldTypeDef.value as keyof T)){
|
|
315
|
+
if (!injectedPrimitives.renderers[fieldTypeDef.value as keyof T].has(fieldConfig["renderer"]))
|
|
316
|
+
errors.push(`field ${fieldName} of form ${formName} references non-existing injected primitive 'renderer' ${fieldConfig["renderer"]}`);
|
|
317
|
+
}
|
|
318
|
+
else if (fieldTypeDef.value == "maybeBoolean") {
|
|
304
319
|
// alert(JSON.stringify(fieldConfig["renderer"]))
|
|
305
320
|
// alert(JSON.stringify(builtIns.renderers.MaybeBooleanViews))
|
|
306
321
|
if (!builtIns.renderers.maybeBoolean.has(fieldConfig["renderer"]))
|
|
@@ -322,6 +337,10 @@ export const FormsConfig = {
|
|
|
322
337
|
} else {
|
|
323
338
|
errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`);
|
|
324
339
|
}
|
|
340
|
+
if(injectedPrimitives?.injectedPrimitives.has(fieldTypeDef.value as keyof T)){
|
|
341
|
+
if (!injectedPrimitives.renderers[fieldTypeDef.value as keyof T].has(fieldConfig["renderer"]))
|
|
342
|
+
errors.push(`field ${fieldName} of form ${formName} references non-existing injected primitive 'renderer' ${fieldConfig["renderer"]}`);
|
|
343
|
+
}
|
|
325
344
|
} else if (fieldTypeDef?.kind == "application") {
|
|
326
345
|
if (fieldTypeDef?.value == "SingleSelection") {
|
|
327
346
|
if (!builtIns.renderers.enumSingleSelection.has(fieldConfig["renderer"]) &&
|
|
@@ -332,8 +351,9 @@ export const FormsConfig = {
|
|
|
332
351
|
!builtIns.renderers.streamMultiSelection.has(fieldConfig["renderer"]))
|
|
333
352
|
errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`);
|
|
334
353
|
} else if (fieldTypeDef?.value == "List") {
|
|
335
|
-
if (!builtIns.renderers.list.has(fieldConfig["renderer"]))
|
|
336
|
-
errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`)
|
|
354
|
+
if (!builtIns.renderers.list.has(fieldConfig["renderer"]) ){
|
|
355
|
+
errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`)
|
|
356
|
+
}
|
|
337
357
|
} else if (fieldTypeDef?.value == "Map") {
|
|
338
358
|
if (!builtIns.renderers.map.has(fieldConfig["renderer"]))
|
|
339
359
|
errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`);
|
|
@@ -347,8 +367,8 @@ export const FormsConfig = {
|
|
|
347
367
|
: "args" in typeDef == false ? undefined
|
|
348
368
|
: Array.isArray(typeDef.args) == false ? undefined
|
|
349
369
|
: Type.Default.application(typeDef.fun, typeDef.args)
|
|
350
|
-
const keyType:Type | undefined = typeof fieldTypeDef.args[0] == "string" ? Type.Operations.FromName(types, builtIns)(fieldTypeDef.args[0]) : typeDefToType(fieldTypeDef.args[0] as any)
|
|
351
|
-
const valueType:Type | undefined = typeof fieldTypeDef.args[1] == "string" ? Type.Operations.FromName(types, builtIns)(fieldTypeDef.args[1]) : typeDefToType(fieldTypeDef.args[1] as any)
|
|
370
|
+
const keyType:Type | undefined = typeof fieldTypeDef.args[0] == "string" ? Type.Operations.FromName(types, builtIns, injectedPrimitives)(fieldTypeDef.args[0]) : typeDefToType(fieldTypeDef.args[0] as any)
|
|
371
|
+
const valueType:Type | undefined = typeof fieldTypeDef.args[1] == "string" ? Type.Operations.FromName(types, builtIns, injectedPrimitives)(fieldTypeDef.args[1]) : typeDefToType(fieldTypeDef.args[1] as any)
|
|
352
372
|
if (!keyType) {
|
|
353
373
|
errors.push(`field ${fieldName} of form ${formName} references non-existing key type ${JSON.stringify(fieldTypeDef.args[0])}`);
|
|
354
374
|
} else if (!valueType) {
|
|
@@ -405,6 +425,9 @@ export const FormsConfig = {
|
|
|
405
425
|
let formDef: FormDef = forms.get(formName)!
|
|
406
426
|
const formTypeDef = types.get(formDef.type)
|
|
407
427
|
const configFormDef = formsConfig["forms"][formName];
|
|
428
|
+
if (formsConfig["forms"][formName].header){
|
|
429
|
+
formDef.header = formsConfig["forms"][formName].header
|
|
430
|
+
}
|
|
408
431
|
Object.keys(configFormDef["fields"]).forEach(fieldName => {
|
|
409
432
|
const fieldConfig = configFormDef["fields"][fieldName]
|
|
410
433
|
const fieldTypeDef = formTypeDef?.fields.get(fieldName);
|
|
@@ -415,9 +438,12 @@ export const FormsConfig = {
|
|
|
415
438
|
let elementType = fieldTypeDef.args[0]
|
|
416
439
|
const rendererHasType = (elementRenderer: string, elementType: string): Array<string> => {
|
|
417
440
|
const primitiveRendererNames = builtIns.primitives.get(elementType)
|
|
418
|
-
|
|
441
|
+
const injectedPrimitiveRendererNames = injectedPrimitives?.injectedPrimitives.get(elementType as keyof T)
|
|
442
|
+
if (primitiveRendererNames != undefined || injectedPrimitiveRendererNames != undefined) {
|
|
419
443
|
const primitiveRenderers =
|
|
420
|
-
Set(primitiveRendererNames.renderers.flatMap(_ => builtIns.renderers[_]).toArray())
|
|
444
|
+
Set(primitiveRendererNames ? primitiveRendererNames.renderers.flatMap(_ => builtIns.renderers[_]).toArray() : []).concat(
|
|
445
|
+
injectedPrimitives ? Set(injectedPrimitiveRendererNames?.renderers.flatMap(_ => injectedPrimitives.renderers[_])).toArray() : []
|
|
446
|
+
)
|
|
421
447
|
if (!primitiveRenderers.has(elementRenderer)) {
|
|
422
448
|
return [`${elementType} cannot be rendered by primitive renderer ${elementRenderer}`]
|
|
423
449
|
}
|
|
@@ -439,18 +465,19 @@ export const FormsConfig = {
|
|
|
439
465
|
}
|
|
440
466
|
if (fieldTypeDef?.kind == "lookup") {
|
|
441
467
|
if (!forms.has(fieldConfig.renderer))
|
|
442
|
-
errors.push(`field ${fieldName} of form ${formName} references non-existing form ${fieldConfig["renderer"]}`);
|
|
468
|
+
errors.push(`field ${revertKeyword(fieldName)} of form ${formName} references non-existing form ${fieldConfig["renderer"]}`);
|
|
443
469
|
else {
|
|
444
470
|
const otherForm = forms.get(fieldConfig.renderer)!
|
|
445
471
|
if (otherForm.type != fieldTypeDef.name)
|
|
446
|
-
errors.push(`field ${fieldName} of form ${formName} references form ${fieldConfig["renderer"]}, which has type ${otherForm.type} whereas ${fieldTypeDef.name} was expected`);
|
|
472
|
+
errors.push(`field ${revertKeyword(fieldName)} of form ${formName} references form ${fieldConfig["renderer"]}, which has type ${otherForm.type} whereas ${fieldTypeDef.name} was expected`);
|
|
447
473
|
}
|
|
448
474
|
}
|
|
449
475
|
formDef.fields = formDef.fields.set(
|
|
450
476
|
fieldName, {
|
|
451
477
|
renderer: fieldConfig.renderer,
|
|
452
|
-
label: fieldConfig.label ?? fieldName,
|
|
478
|
+
label: fieldConfig.label ?? revertKeyword(fieldName),
|
|
453
479
|
elementRenderer: fieldConfig.elementRenderer,
|
|
480
|
+
elementLabel: fieldConfig.elementLabel,
|
|
454
481
|
mapRenderer:
|
|
455
482
|
fieldConfig.keyRenderer && fieldConfig.valueRenderer ?
|
|
456
483
|
{ keyRenderer:fieldConfig.keyRenderer, valueRenderer:fieldConfig.valueRenderer }
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { List, Map, OrderedMap, OrderedSet, Set } from "immutable";
|
|
2
|
-
import { BoolExpr, Unit, PromiseRepo, Guid, LeafPredicatesEvaluators, Predicate, FormsConfig, BuiltIns, FormDef, Sum, BasicFun, Template, unit, EditFormState, EditFormTemplate, ApiErrors, CreateFormTemplate, EntityFormTemplate, SharedFormState, CreateFormState, Entity, EditFormContext, CreateFormContext, MappedEntityFormTemplate, Mapping, FormValidationResult, Synchronized, simpleUpdater, PrimitiveType, GenericType, ApiConverter, TypeName, ListFieldState, ListForm, TypeDefinition,
|
|
2
|
+
import { BoolExpr, Unit, PromiseRepo, Guid, LeafPredicatesEvaluators, Predicate, FormsConfig, BuiltIns, FormDef, Sum, BasicFun, Template, unit, EditFormState, EditFormTemplate, ApiErrors, CreateFormTemplate, EntityFormTemplate, SharedFormState, CreateFormState, Entity, EditFormContext, CreateFormContext, MappedEntityFormTemplate, Mapping, FormValidationResult, Synchronized, simpleUpdater, PrimitiveType, GenericType, ApiConverter, TypeName, ListFieldState, ListForm, TypeDefinition, BuiltInApiConverters, defaultValue, fromAPIRawValue, toAPIRawValue, EditFormForeignMutationsExpected, MapFieldState, MapForm, Type, FieldConfig, Base64FileForm, SecretForm, InjectedPrimitives, Injectables, ApiConverters } from "../../../../main";
|
|
3
3
|
import { Value } from "../../../value/state";
|
|
4
4
|
import { CollectionReference } from "../collection/domains/reference/state";
|
|
5
5
|
import { CollectionSelection } from "../collection/domains/selection/state";
|
|
@@ -24,7 +24,7 @@ const parseOptions = (leafPredicates: any, options: any) => {
|
|
|
24
24
|
};
|
|
25
25
|
|
|
26
26
|
export const FieldView = //<Context, FieldViews extends DefaultFieldViews, EnumFieldConfigs extends {}, EnumSources extends {}>() => <ViewType extends keyof FieldViews, ViewName extends keyof FieldViews[ViewType]>
|
|
27
|
-
(fieldConfig:FieldConfig, fieldViews: any, viewType: any, viewName: any, fieldName: string, label: string, enumFieldConfigs: EnumOptionsSources, enumSources: any, leafPredicates: any): any =>
|
|
27
|
+
<T,>(fieldConfig:FieldConfig, fieldViews: any, viewType: any, viewName: any, fieldName: string, label: string, enumFieldConfigs: EnumOptionsSources, enumSources: any, leafPredicates: any, injectedPrimitives?: InjectedPrimitives<T>): any =>
|
|
28
28
|
{
|
|
29
29
|
if (viewType == "maybeBoolean")
|
|
30
30
|
return MaybeBooleanForm<any & FormLabel, Unit>()
|
|
@@ -69,13 +69,35 @@ export const FieldView = //<Context, FieldViews extends DefaultFieldViews, EnumF
|
|
|
69
69
|
return InfiniteMultiselectDropdownForm<CollectionReference, any & FormLabel, Unit>()
|
|
70
70
|
.withView(((fieldViews as any)[viewType] as any)[viewName]() as any)
|
|
71
71
|
.mapContext<any & FormLabel & SharedFormState & SearchableInfiniteStreamState<CollectionReference> & Value<OrderedMap<Guid, CollectionReference>>>(_ => ({ ..._, label: label })) as any
|
|
72
|
+
if (viewType == "base64File")
|
|
73
|
+
return Base64FileForm<any & FormLabel, Unit>()
|
|
74
|
+
.withView(((fieldViews as any)[viewType] as any)[viewName]() as any)
|
|
75
|
+
.mapContext<any & FormLabel & SharedFormState & Value<string>>(_ => ({ ..._, label: label })) as any
|
|
76
|
+
if (viewType == "secret")
|
|
77
|
+
return SecretForm<any & FormLabel, Unit>()
|
|
78
|
+
.withView(((fieldViews as any)[viewType] as any)[viewName]() as any)
|
|
79
|
+
.mapContext<any & FormLabel & SharedFormState & Value<string>>(_ => ({ ..._, label: label })) as any
|
|
80
|
+
// check injectedViews
|
|
81
|
+
if (injectedPrimitives?.injectedPrimitives.has(viewType)) {
|
|
82
|
+
const injectedPrimitive = injectedPrimitives.injectedPrimitives.get(viewType)
|
|
83
|
+
return injectedPrimitive?.fieldView(fieldViews, viewType, viewName, label) as any
|
|
84
|
+
}
|
|
72
85
|
return `error: the view for ${viewType as string}::${viewName as string} cannot be found`;
|
|
73
86
|
}
|
|
74
87
|
|
|
75
88
|
export const FieldFormState = //<Context, FieldViews extends DefaultFieldViews, InfiniteStreamSources extends {}, InfiniteStreamConfigs extends {}>() => <ViewType extends keyof FieldViews, ViewName extends keyof FieldViews[ViewType]>
|
|
76
|
-
(fieldConfig:FieldConfig, fieldViews: any, viewType: any, viewName: any, fieldName: string, InfiniteStreamSources: any, infiniteStreamConfigs: any): any => {
|
|
77
|
-
if (viewType == "maybeBoolean" || viewType == "boolean" || viewType == "number" || viewType == "string")
|
|
89
|
+
<T,>(fieldConfig:FieldConfig, fieldViews: any, viewType: any, viewName: any, fieldName: string, InfiniteStreamSources: any, infiniteStreamConfigs: any, injectedPrimitives?: InjectedPrimitives<T>): any => {
|
|
90
|
+
if (viewType == "maybeBoolean" || viewType == "boolean" || viewType == "number" || viewType == "string" || viewType == "base64File" || viewType == "secret")
|
|
78
91
|
return SharedFormState.Default();
|
|
92
|
+
if( injectedPrimitives?.injectedPrimitives.has(viewType)){
|
|
93
|
+
const injectedPrimitiveDefaultState = injectedPrimitives.injectedPrimitives.get(viewType)?.defaultState;
|
|
94
|
+
return injectedPrimitiveDefaultState != undefined ?
|
|
95
|
+
({
|
|
96
|
+
...injectedPrimitiveDefaultState,
|
|
97
|
+
...SharedFormState.Default()
|
|
98
|
+
}) : SharedFormState.Default();
|
|
99
|
+
return SharedFormState.Default()
|
|
100
|
+
}
|
|
79
101
|
if (viewType == "date")
|
|
80
102
|
return DateFormState.Default("");
|
|
81
103
|
if (viewType == "enumSingleSelection" || viewType == "enumMultiSelection")
|
|
@@ -101,7 +123,7 @@ export type ParsedForm = {
|
|
|
101
123
|
visibleFields: any,
|
|
102
124
|
disabledFields: any,
|
|
103
125
|
}
|
|
104
|
-
export const ParseForm = (
|
|
126
|
+
export const ParseForm = <T,>(
|
|
105
127
|
formName: string,
|
|
106
128
|
formDef: FormDef,
|
|
107
129
|
containerFormView: any,
|
|
@@ -120,7 +142,8 @@ export const ParseForm = (
|
|
|
120
142
|
visibleFieldsBoolExprs: any,
|
|
121
143
|
disabledFieldsBoolExprs: any,
|
|
122
144
|
defaultValue: BasicFun<TypeName | Type, any>,
|
|
123
|
-
type: TypeDefinition
|
|
145
|
+
type: TypeDefinition,
|
|
146
|
+
injectedPrimitives?: InjectedPrimitives<T>
|
|
124
147
|
): ParsedForm => {
|
|
125
148
|
const fieldNameToViewCategory = (fieldName: string) => {
|
|
126
149
|
const fieldViewCategories = Object.keys(fieldViews)
|
|
@@ -152,7 +175,7 @@ export const ParseForm = (
|
|
|
152
175
|
const fieldConfig = formDef.fields.get(fieldName)!
|
|
153
176
|
initialFormState[fieldName] =
|
|
154
177
|
otherForms.get(viewName)?.initialFormState ??
|
|
155
|
-
FieldFormState(fieldConfig, fieldViews, fieldNameToViewCategory(fieldName) as any, (fieldsViewsConfig as any)[fieldName], fieldName, InfiniteStreamSources, fieldsInfiniteStreamsConfig);
|
|
178
|
+
FieldFormState(fieldConfig, fieldViews, fieldNameToViewCategory(fieldName) as any, (fieldsViewsConfig as any)[fieldName], fieldName, InfiniteStreamSources, fieldsInfiniteStreamsConfig, injectedPrimitives);
|
|
156
179
|
if (typeof initialFormState[fieldName] == "string") {
|
|
157
180
|
throw `cannot resolve initial state ${viewName} of field ${fieldName}`
|
|
158
181
|
}
|
|
@@ -171,6 +194,7 @@ export const ParseForm = (
|
|
|
171
194
|
if (viewType == "list") {
|
|
172
195
|
const elementRendererName = formFieldElementRenderers[fieldName]
|
|
173
196
|
const field = type.fields.get(fieldName)!
|
|
197
|
+
const elementLabel = formDef.fields.get(fieldName)!.elementLabel
|
|
174
198
|
const initialElementValue = defaultValue(field.kind == "primitive" ? field.value : field.kind == "lookup" ? field.name : field.args[0])
|
|
175
199
|
const elementForm = otherForms.get(elementRendererName)
|
|
176
200
|
if (elementForm != undefined) { // the list argument is a nested form
|
|
@@ -178,18 +202,18 @@ export const ParseForm = (
|
|
|
178
202
|
formConfig[fieldName] = ListForm<any, any, any & FormLabel, Unit>(
|
|
179
203
|
{ Default: () => ({ ...initialFormState }) },
|
|
180
204
|
{ Default: () => ({ ...initialElementValue }) },
|
|
181
|
-
elementForm.form.withView(nestedContainerFormView),
|
|
205
|
+
elementForm.form.withView(nestedContainerFormView).mapContext<any>(_ => ({ ..._, label: elementLabel }))
|
|
182
206
|
).withView(((fieldViews as any)[viewType] as any)[viewName]() as any)
|
|
183
|
-
.mapContext<any>(_ => ({ ..._, label
|
|
207
|
+
.mapContext<any>(_ => ({ ..._, label }))
|
|
184
208
|
} else { // the list argument is a primitive
|
|
185
|
-
const elementForm = FieldView(fieldConfig, fieldViews, fieldNameToElementViewCategory(formFieldElementRenderers)(fieldName) as any, elementRendererName, fieldName, label, EnumOptionsSources, fieldsOptionsConfig, leafPredicates)
|
|
186
|
-
const initialFormState = FieldFormState(fieldConfig, fieldViews, fieldNameToElementViewCategory(formFieldElementRenderers)(fieldName) as any, elementRendererName, fieldName, InfiniteStreamSources, fieldsInfiniteStreamsConfig);
|
|
209
|
+
const elementForm = FieldView(fieldConfig, fieldViews, fieldNameToElementViewCategory(formFieldElementRenderers)(fieldName) as any, elementRendererName, fieldName, elementLabel ?? label, EnumOptionsSources, fieldsOptionsConfig, leafPredicates, injectedPrimitives)
|
|
210
|
+
const initialFormState = FieldFormState(fieldConfig, fieldViews, fieldNameToElementViewCategory(formFieldElementRenderers)(fieldName) as any, elementRendererName, fieldName, InfiniteStreamSources, fieldsInfiniteStreamsConfig, injectedPrimitives);
|
|
187
211
|
formConfig[fieldName] = ListForm<any, any, any & FormLabel, Unit>(
|
|
188
212
|
{ Default: () => initialFormState },
|
|
189
213
|
{ Default: () => initialElementValue },
|
|
190
214
|
elementForm,
|
|
191
215
|
).withView(((fieldViews as any)[viewType] as any)[viewName]() as any)
|
|
192
|
-
.mapContext<any>(_ => ({ ..._, label
|
|
216
|
+
.mapContext<any>(_ => ({ ..._, label }))
|
|
193
217
|
}
|
|
194
218
|
} else {
|
|
195
219
|
if (viewType == "map") {
|
|
@@ -213,15 +237,16 @@ export const ParseForm = (
|
|
|
213
237
|
const initialValueValue = defaultValue(valueType)
|
|
214
238
|
const getFormAndInitialState = (elementRenderers:any, rendererName:any, fieldConfig:FieldConfig) => {
|
|
215
239
|
const formDef = otherForms.get(rendererName)
|
|
240
|
+
const elementLabel = elementRenderers[fieldName].label ?? label
|
|
216
241
|
if (formDef != undefined) {
|
|
217
242
|
return [
|
|
218
|
-
formDef.form.withView(nestedContainerFormView),
|
|
243
|
+
formDef.form.withView(nestedContainerFormView).mapContext<any>(_ => ({ ..._, label: elementLabel })),
|
|
219
244
|
formDef.initialFormState
|
|
220
245
|
]
|
|
221
246
|
} else {
|
|
222
247
|
const categoryName = fieldNameToElementViewCategory(elementRenderers)(fieldName) as any
|
|
223
|
-
const form = FieldView(fieldConfig, fieldViews, categoryName, rendererName, fieldName,
|
|
224
|
-
const initialFormState = FieldFormState(fieldConfig, fieldViews, categoryName, rendererName, fieldName, InfiniteStreamSources, fieldsInfiniteStreamsConfig);
|
|
248
|
+
const form = FieldView(fieldConfig, fieldViews, categoryName, rendererName, fieldName, elementLabel, EnumOptionsSources, fieldsOptionsConfig, leafPredicates, injectedPrimitives)
|
|
249
|
+
const initialFormState = FieldFormState(fieldConfig, fieldViews, categoryName, rendererName, fieldName, InfiniteStreamSources, fieldsInfiniteStreamsConfig, injectedPrimitives);
|
|
225
250
|
return [
|
|
226
251
|
form,
|
|
227
252
|
initialFormState
|
|
@@ -244,7 +269,7 @@ export const ParseForm = (
|
|
|
244
269
|
).withView(((fieldViews as any)[viewType] as any)[viewName]() as any)
|
|
245
270
|
.mapContext<any>(_ => ({ ..._, label: label }))
|
|
246
271
|
} else {
|
|
247
|
-
formConfig[fieldName] = FieldView(fieldConfig, fieldViews, viewType, viewName, fieldName, label, EnumOptionsSources, fieldsOptionsConfig, leafPredicates);
|
|
272
|
+
formConfig[fieldName] = FieldView(fieldConfig, fieldViews, viewType, viewName, fieldName, label, EnumOptionsSources, fieldsOptionsConfig, leafPredicates, injectedPrimitives);
|
|
248
273
|
}
|
|
249
274
|
}
|
|
250
275
|
}
|
|
@@ -338,9 +363,10 @@ export type EnumName = string
|
|
|
338
363
|
|
|
339
364
|
export type EnumOptionsSources = BasicFun<EnumName, BasicFun<Unit, Promise<Array<[CollectionReference, BoolExpr<Unit>]>>>>
|
|
340
365
|
export const parseForms =
|
|
341
|
-
<LeafPredicates,>(
|
|
366
|
+
<LeafPredicates, T,>(
|
|
342
367
|
builtIns: BuiltIns,
|
|
343
|
-
|
|
368
|
+
injectedPrimitives: InjectedPrimitives<T> | undefined,
|
|
369
|
+
apiConverters: BuiltInApiConverters,
|
|
344
370
|
containerFormView: any,
|
|
345
371
|
nestedContainerFormView: any,
|
|
346
372
|
fieldViews: any,
|
|
@@ -422,8 +448,9 @@ export const parseForms =
|
|
|
422
448
|
leafPredicates,
|
|
423
449
|
formFieldVisibilities,
|
|
424
450
|
formFieldDisabled,
|
|
425
|
-
defaultValue(formsConfig.types, builtIns),
|
|
451
|
+
defaultValue(formsConfig.types, builtIns, injectedPrimitives),
|
|
426
452
|
formConfig.typeDef,
|
|
453
|
+
injectedPrimitives
|
|
427
454
|
)
|
|
428
455
|
const formBuilder = Form<any, any, any, any>().Default<any>()
|
|
429
456
|
const form = formBuilder.template({
|
|
@@ -447,11 +474,11 @@ export const parseForms =
|
|
|
447
474
|
get: (id: string) => entityApis.get(launcher.api)(id).then((raw: any) => {
|
|
448
475
|
// alert(JSON.stringify(raw))
|
|
449
476
|
// alert(JSON.stringify(parsedForm.formDef.type))
|
|
450
|
-
const parsed = fromAPIRawValue({ kind: "lookup", name: parsedForm.formDef.type }, formsConfig.types, builtIns, apiConverters)(raw)
|
|
477
|
+
const parsed = fromAPIRawValue({ kind: "lookup", name: parsedForm.formDef.type }, formsConfig.types, builtIns, apiConverters, false, injectedPrimitives)(raw)
|
|
451
478
|
return parsed
|
|
452
479
|
}),
|
|
453
|
-
update: (id: Guid, value: any) =>
|
|
454
|
-
entityApis.update(launcher.api)(id, toAPIRawValue({ kind: "lookup", name: parsedForm.formDef.type }, formsConfig.types, builtIns, apiConverters)(value))
|
|
480
|
+
update: (id: Guid, value: any, formState: any) =>
|
|
481
|
+
entityApis.update(launcher.api)(id, toAPIRawValue({ kind: "lookup", name: parsedForm.formDef.type }, formsConfig.types, builtIns, apiConverters, false, injectedPrimitives)(value, formState))
|
|
455
482
|
}
|
|
456
483
|
parsedLaunchers.edit = parsedLaunchers.edit.set(
|
|
457
484
|
launcherName,
|
|
@@ -474,16 +501,16 @@ export const parseForms =
|
|
|
474
501
|
const form = parsedForm.form
|
|
475
502
|
const initialState = parsedForm.initialFormState
|
|
476
503
|
const api = {
|
|
477
|
-
create: (value: any) => {
|
|
504
|
+
create: ([value, formState]: [any, any]) => {
|
|
478
505
|
// alert(`type = ${JSON.stringify(parsedForm.formDef.type)}`)
|
|
479
506
|
// alert(`value = ${JSON.stringify(value)}`)
|
|
480
|
-
const raw = toAPIRawValue({ kind: "lookup", name: parsedForm.formDef.type }, formsConfig.types, builtIns, apiConverters)(value)
|
|
507
|
+
const raw = toAPIRawValue({ kind: "lookup", name: parsedForm.formDef.type }, formsConfig.types, builtIns, apiConverters, false, injectedPrimitives)(value, formState)
|
|
481
508
|
// alert(`raw = ${JSON.stringify(raw.interests)}`)
|
|
482
509
|
return entityApis.create(launcher.api)(raw)
|
|
483
510
|
},
|
|
484
511
|
default: (_: Unit) => entityApis.default(launcher.api)(unit)
|
|
485
512
|
.then((raw: any) => {
|
|
486
|
-
const parsed = fromAPIRawValue({ kind: "lookup", name: parsedForm.formDef.type }, formsConfig.types, builtIns, apiConverters)(raw)
|
|
513
|
+
const parsed = fromAPIRawValue({ kind: "lookup", name: parsedForm.formDef.type }, formsConfig.types, builtIns, apiConverters, false, injectedPrimitives)(raw)
|
|
487
514
|
return parsed
|
|
488
515
|
})
|
|
489
516
|
}
|
|
@@ -591,16 +618,17 @@ export const replaceKeywords = (obj: any, kind: "from api" | "to api"): any => {
|
|
|
591
618
|
return obj;
|
|
592
619
|
};
|
|
593
620
|
|
|
594
|
-
export type FormsParserContext = {
|
|
621
|
+
export type FormsParserContext<T extends {[key in keyof T] : {type: any, state: any}}> = {
|
|
595
622
|
containerFormView: any,
|
|
596
623
|
nestedContainerFormView: any,
|
|
597
624
|
fieldViews: any,
|
|
598
|
-
fieldTypeConverters: ApiConverters
|
|
625
|
+
fieldTypeConverters: ApiConverters<T>,
|
|
599
626
|
infiniteStreamSources: InfiniteStreamSources,
|
|
600
627
|
enumOptionsSources: EnumOptionsSources,
|
|
601
628
|
entityApis: EntityApis,
|
|
602
629
|
leafPredicates: any,
|
|
603
630
|
getFormsConfig: BasicFun<void, Promise<any>>
|
|
631
|
+
injectedPrimitives?: Injectables<T>,
|
|
604
632
|
}
|
|
605
633
|
export type FormsParserState = {
|
|
606
634
|
formsConfig: Synchronized<Unit, FormParsingResult>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
+
import { Unit } from "../../../../main";
|
|
1
2
|
import { LoadValidateAndParseFormsConfig } from "./coroutines/runner";
|
|
2
3
|
|
|
3
|
-
export const FormsParserTemplate = LoadValidateAndParseFormsConfig()
|
|
4
|
+
export const FormsParserTemplate = <T extends {[key in keyof T] : {type: any, state: any}} = Unit>() => LoadValidateAndParseFormsConfig<T>()
|
|
4
5
|
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { SimpleCallback } from "../../../../../../main";
|
|
2
|
+
import { View } from "../../../../../template/state";
|
|
3
|
+
import { Value } from "../../../../../value/state";
|
|
4
|
+
import { FormLabel } from "../../../singleton/domains/form-label/state";
|
|
5
|
+
import { OnChange, SharedFormState } from "../../../singleton/state";
|
|
6
|
+
|
|
7
|
+
export type Base64FileView<Context extends FormLabel, ForeignMutationsExpected> =
|
|
8
|
+
View<
|
|
9
|
+
Context & Value<string> & SharedFormState & { disabled:boolean },
|
|
10
|
+
SharedFormState,
|
|
11
|
+
ForeignMutationsExpected & { onChange: OnChange<string>; setNewValue: SimpleCallback<string> }
|
|
12
|
+
>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { List } from "immutable";
|
|
2
|
+
import { BasicFun, replaceWith, ValidateRunner } from "../../../../../../main";
|
|
3
|
+
import { Template } from "../../../../../template/state";
|
|
4
|
+
import { Value } from "../../../../../value/state";
|
|
5
|
+
import { FormLabel } from "../../../singleton/domains/form-label/state";
|
|
6
|
+
import { FieldValidation, FieldValidationWithPath, OnChange, SharedFormState } from "../../../singleton/state";
|
|
7
|
+
import { Base64FileView } from "./state";
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
export const Base64FileForm = <Context extends FormLabel, ForeignMutationsExpected>(
|
|
11
|
+
validation?:BasicFun<string, Promise<FieldValidation>>) => {
|
|
12
|
+
return Template.Default<Context & Value<string> & { disabled:boolean }, SharedFormState, ForeignMutationsExpected & { onChange: OnChange<string>; }, Base64FileView<Context, ForeignMutationsExpected>>(props => <>
|
|
13
|
+
<props.view {...props}
|
|
14
|
+
foreignMutations={{
|
|
15
|
+
...props.foreignMutations,
|
|
16
|
+
setNewValue: (_) => props.foreignMutations.onChange(replaceWith(_), List())
|
|
17
|
+
}} />
|
|
18
|
+
</>
|
|
19
|
+
).any([
|
|
20
|
+
ValidateRunner<Context & { disabled:boolean }, SharedFormState, ForeignMutationsExpected, string>(
|
|
21
|
+
validation ? _ => validation(_).then(FieldValidationWithPath.Default.fromFieldValidation) : undefined
|
|
22
|
+
),
|
|
23
|
+
])
|
|
24
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { List } from "immutable";
|
|
2
|
-
import { SimpleCallback, BasicFun, Unit, ValidateRunner, Updater, BasicUpdater, MapRepo, ListRepo } from "../../../../../../main";
|
|
2
|
+
import { SimpleCallback, BasicFun, Unit, ValidateRunner, Updater, BasicUpdater, MapRepo, ListRepo, CoTypedFactory, Debounce, Synchronize} from "../../../../../../main";
|
|
3
3
|
import { Template } from "../../../../../template/state";
|
|
4
4
|
import { Value } from "../../../../../value/state";
|
|
5
5
|
import { FormLabel } from "../../../singleton/domains/form-label/state";
|
|
6
|
-
import { FieldValidation, FieldValidationWithPath, OnChange } from "../../../singleton/state";
|
|
6
|
+
import { FieldValidation, FieldValidationWithPath, FormValidatorSynchronized, OnChange, SharedFormState } from "../../../singleton/state";
|
|
7
7
|
import { ListFieldState, ListFieldView } from "./state";
|
|
8
8
|
|
|
9
9
|
export const ListForm = <Element, ElementFormState, Context extends FormLabel, ForeignMutationsExpected>(
|
|
@@ -19,20 +19,20 @@ export const ListForm = <Element, ElementFormState, Context extends FormLabel, F
|
|
|
19
19
|
) => {
|
|
20
20
|
const embeddedElementTemplate = (elementIndex:number) =>
|
|
21
21
|
elementTemplate
|
|
22
|
-
.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
22
|
+
.mapForeignMutationsFromProps<ForeignMutationsExpected & {
|
|
23
|
+
onChange: OnChange<List<Element>>;
|
|
24
|
+
add: SimpleCallback<Unit>;
|
|
25
|
+
remove: SimpleCallback<number>;}>((props): ForeignMutationsExpected & {onChange: OnChange<Element>} => ({
|
|
26
|
+
...props.foreignMutations,
|
|
27
|
+
onChange: (elementUpdater, path) => {
|
|
28
|
+
props.foreignMutations.onChange(Updater((elements:List<Element>) =>
|
|
29
|
+
elements.update(elementIndex, (_:Element | undefined) => _ == undefined ? _ : elementUpdater(_))), path)
|
|
30
|
+
props.setState(_ => ({..._,
|
|
31
|
+
modifiedByUser:true,
|
|
32
|
+
})) },
|
|
33
|
+
add: (newElement: Element) => { },
|
|
34
|
+
remove: (elementIndex: number) => { }
|
|
35
|
+
}))
|
|
36
36
|
.mapContext((_: Context & Value<List<Element>> & ListFieldState<Element, ElementFormState>) : (Context & Value<Element> & ElementFormState) | undefined => {
|
|
37
37
|
const element = _.value.get(elementIndex)
|
|
38
38
|
if (element == undefined) return undefined
|
|
@@ -27,17 +27,17 @@ export const MapForm = <K, V, KeyFormState, ValueFormState, Context extends Form
|
|
|
27
27
|
) => {
|
|
28
28
|
const embeddedKeyTemplate = (elementIndex: number) =>
|
|
29
29
|
keyTemplate
|
|
30
|
-
.
|
|
30
|
+
.mapForeignMutationsFromProps<ForeignMutationsExpected & {
|
|
31
31
|
onChange: OnChange<List<[K, V]>>;
|
|
32
32
|
add: SimpleCallback<Unit>;
|
|
33
33
|
remove: SimpleCallback<number>;
|
|
34
|
-
}): ForeignMutationsExpected & {
|
|
35
|
-
onChange: OnChange<K>;
|
|
36
|
-
} =>
|
|
34
|
+
}>((props): ForeignMutationsExpected & {
|
|
35
|
+
onChange: OnChange<K>; } =>
|
|
37
36
|
({
|
|
38
|
-
...
|
|
37
|
+
...props.foreignMutations,
|
|
39
38
|
onChange: (elementUpdater, path) => {
|
|
40
|
-
|
|
39
|
+
props.foreignMutations.onChange(Updater((elements: List<[K, V]>) => elements.update(elementIndex, (_: [K, V] | undefined) => _ == undefined ? _ : [elementUpdater(_[0]), _[1]])), path)
|
|
40
|
+
props.setState(_ => ({ ..._, modifiedByUser: true }))
|
|
41
41
|
},
|
|
42
42
|
add: (newElement: [K, V]) => { },
|
|
43
43
|
remove: (elementIndex: number) => { }
|
|
@@ -56,17 +56,18 @@ export const MapForm = <K, V, KeyFormState, ValueFormState, Context extends Form
|
|
|
56
56
|
))
|
|
57
57
|
const embeddedValueTemplate = (elementIndex: number) =>
|
|
58
58
|
valueTemplate
|
|
59
|
-
.
|
|
59
|
+
.mapForeignMutationsFromProps<ForeignMutationsExpected & {
|
|
60
60
|
onChange: OnChange<List<[K, V]>>;
|
|
61
61
|
add: SimpleCallback<Unit>;
|
|
62
62
|
remove: SimpleCallback<number>;
|
|
63
|
-
}): ForeignMutationsExpected & {
|
|
63
|
+
}>((props): ForeignMutationsExpected & {
|
|
64
64
|
onChange: OnChange<V>;
|
|
65
65
|
} =>
|
|
66
66
|
({
|
|
67
|
-
...
|
|
67
|
+
...props.foreignMutations,
|
|
68
68
|
onChange: (elementUpdater, path) => {
|
|
69
|
-
|
|
69
|
+
props.foreignMutations.onChange(Updater((elements: List<[K, V]>) => elements.update(elementIndex, (_: [K, V] | undefined) => _ == undefined ? _ : [_[0], elementUpdater(_[1])])), path)
|
|
70
|
+
props.setState(_ => ({ ..._, modifiedByUser: true }))
|
|
70
71
|
},
|
|
71
72
|
add: (newElement: [K, V]) => { },
|
|
72
73
|
remove: (elementIndex: number) => { }
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { SimpleCallback } from "../../../../../../main";
|
|
2
|
+
import { View } from "../../../../../template/state";
|
|
3
|
+
import { Value } from "../../../../../value/state";
|
|
4
|
+
import { FormLabel } from "../../../singleton/domains/form-label/state";
|
|
5
|
+
import { OnChange, SharedFormState } from "../../../singleton/state";
|
|
6
|
+
|
|
7
|
+
export type SecretView<Context extends FormLabel, ForeignMutationsExpected> =
|
|
8
|
+
View<
|
|
9
|
+
Context & Value<string> & SharedFormState & { disabled:boolean },
|
|
10
|
+
SharedFormState,
|
|
11
|
+
ForeignMutationsExpected & { onChange: OnChange<string>; setNewValue: SimpleCallback<string> }
|
|
12
|
+
>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { List } from "immutable";
|
|
2
|
+
import { BasicFun, replaceWith, ValidateRunner } from "../../../../../../main";
|
|
3
|
+
import { Template } from "../../../../../template/state";
|
|
4
|
+
import { Value } from "../../../../../value/state";
|
|
5
|
+
import { FormLabel } from "../../../singleton/domains/form-label/state";
|
|
6
|
+
import { FieldValidation, FieldValidationWithPath, OnChange, SharedFormState } from "../../../singleton/state";
|
|
7
|
+
import { SecretView } from "./state";
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
export const SecretForm = <Context extends FormLabel, ForeignMutationsExpected>(
|
|
11
|
+
validation?:BasicFun<string, Promise<FieldValidation>>) => {
|
|
12
|
+
return Template.Default<Context & Value<string> & { disabled:boolean }, SharedFormState, ForeignMutationsExpected & { onChange: OnChange<string>; }, SecretView<Context, ForeignMutationsExpected>>(props => <>
|
|
13
|
+
<props.view {...props}
|
|
14
|
+
foreignMutations={{
|
|
15
|
+
...props.foreignMutations,
|
|
16
|
+
setNewValue: (_) => props.foreignMutations.onChange(replaceWith(_), List())
|
|
17
|
+
}} />
|
|
18
|
+
</>
|
|
19
|
+
).any([
|
|
20
|
+
ValidateRunner<Context & { disabled:boolean }, SharedFormState, ForeignMutationsExpected, string>(
|
|
21
|
+
validation ? _ => validation(_).then(FieldValidationWithPath.Default.fromFieldValidation) : undefined
|
|
22
|
+
),
|
|
23
|
+
])
|
|
24
|
+
}
|
|
@@ -54,7 +54,7 @@ export const SharedFormState = {
|
|
|
54
54
|
export type EntityFormState<Entity, Fields extends (keyof Entity) & (keyof FieldStates), FieldStates, Context, ForeignMutationsExpected> =
|
|
55
55
|
{ [f in Fields]: FieldStates[f] & SharedFormState } & SharedFormState
|
|
56
56
|
export type EntityFormContext<Entity, Fields extends (keyof Entity) & (keyof FieldStates), FieldStates, Context, ForeignMutationsExpected> =
|
|
57
|
-
Context & EntityFormState<Entity, Fields, FieldStates, Context, ForeignMutationsExpected> & { visibleFields: OrderedMap<Fields, BasicPredicate<Context>>, disabledFields: OrderedMap<Fields, BasicPredicate<Context
|
|
57
|
+
Context & EntityFormState<Entity, Fields, FieldStates, Context, ForeignMutationsExpected> & { visibleFields: OrderedMap<Fields, BasicPredicate<Context>>, disabledFields: OrderedMap<Fields, BasicPredicate<Context>>, header?: string } & Value<Entity>
|
|
58
58
|
export type OnChange<Entity> = (updater: BasicUpdater<Entity>, path: List<string>) => void
|
|
59
59
|
export type EntityFormForeignMutationsExpected<Entity, Fields extends (keyof Entity) & (keyof FieldStates), FieldStates, Context, ForeignMutationsExpected> =
|
|
60
60
|
ForeignMutationsExpected & { onChange: OnChange<Entity> }
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { List, OrderedMap, OrderedSet } from "immutable"
|
|
2
|
-
import { BasicUpdater, id, BasicPredicate, SimpleCallback, Unit, Debounced, Synchronized, unit, replaceWith, CoTypedFactory, Debounce, Synchronize, BasicFun, EntityFormState, EntityFormContext, EntityFormForeignMutationsExpected, EntityFormTemplate, EntityFormView, FieldTemplates, FieldValidationWithPath, FormValidatorSynchronized, OnChange, SharedFormState } from "../../../../main"
|
|
2
|
+
import { BasicUpdater, id, BasicPredicate, SimpleCallback, Unit, Debounced, Synchronized, unit, replaceWith, CoTypedFactory, Debounce, Synchronize, BasicFun, EntityFormState, EntityFormContext, EntityFormForeignMutationsExpected, EntityFormTemplate, EntityFormView, FieldTemplates, FieldValidationWithPath, FormValidatorSynchronized, OnChange, SharedFormState, DirtyStatus } from "../../../../main"
|
|
3
3
|
import { Template, View } from "../../../template/state"
|
|
4
4
|
import { Value } from "../../../value/state"
|
|
5
5
|
|
|
@@ -34,6 +34,13 @@ export const Form = <Entity, FieldStates, Context, ForeignMutationsExpected>() =
|
|
|
34
34
|
modifiedByUser:true,
|
|
35
35
|
validation:Debounced.Updaters.Template.value<FormValidatorSynchronized>(Synchronized.Updaters.value(replaceWith(unit)))(_[field].validation),
|
|
36
36
|
}) }))
|
|
37
|
+
} else {
|
|
38
|
+
props.setState(_ => ({ ..._,
|
|
39
|
+
modifiedByUser: true,
|
|
40
|
+
[field]:({
|
|
41
|
+
..._[field],
|
|
42
|
+
modifiedByUser:true,
|
|
43
|
+
}) }))
|
|
37
44
|
}
|
|
38
45
|
setTimeout(() =>
|
|
39
46
|
props.foreignMutations.onChange((current: Entity): Entity => ({
|
|
@@ -81,22 +88,25 @@ export const Form = <Entity, FieldStates, Context, ForeignMutationsExpected>() =
|
|
|
81
88
|
}
|
|
82
89
|
})
|
|
83
90
|
|
|
91
|
+
// TODO: Validate runner and dirty status are also used to ensure to element is initialised, but this should be further debugged with a more correct solution
|
|
84
92
|
export const ValidateRunner = <Context, FormState extends SharedFormState, ForeignMutationsExpected, Entity,>(
|
|
85
93
|
validation?:BasicFun<Entity, Promise<FieldValidationWithPath>>,
|
|
86
94
|
) => {
|
|
87
95
|
const Co = CoTypedFactory<Context & Value<Entity> & FormState, FormState>()
|
|
88
96
|
return Co.Template<ForeignMutationsExpected & { onChange: OnChange<Entity>; }>(
|
|
89
|
-
Co.Repeat(
|
|
97
|
+
validation ? Co.Repeat(
|
|
90
98
|
Debounce<FormValidatorSynchronized, Value<Entity>>(
|
|
91
99
|
Synchronize<Unit, FieldValidationWithPath, Value<Entity>>(
|
|
92
100
|
_ => validation ? validation(_.value) : Promise.resolve([]),
|
|
93
101
|
() => "transient failure", 3, 50
|
|
94
102
|
), 50
|
|
95
103
|
).embed(_ => ({..._.validation, value:_.value}), (_) => curr => ({...curr, validation:_(curr.validation)}))
|
|
96
|
-
)
|
|
104
|
+
) :
|
|
105
|
+
Co.SetState((curr) => ({...curr, validation: Debounced.Updaters.Core.dirty(replaceWith<DirtyStatus>("not dirty"))}))
|
|
106
|
+
,
|
|
97
107
|
{
|
|
98
108
|
interval:15,
|
|
99
|
-
runFilter:props =>
|
|
109
|
+
runFilter: props => Debounced.Operations.shouldCoroutineRun(props.context.validation)
|
|
100
110
|
}
|
|
101
111
|
)
|
|
102
112
|
}
|