ballerina-core 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/main.ts +149 -0
- package/package.json +20 -0
- package/src/apiResultStatus/state.ts +7 -0
- package/src/async/domains/mirroring/domains/collection/coroutines/synchronizers.ts +334 -0
- package/src/async/domains/mirroring/domains/collection/state.ts +137 -0
- package/src/async/domains/mirroring/domains/entity/domains/loaded-collection/state.ts +24 -0
- package/src/async/domains/mirroring/domains/entity/domains/loaded-collection-entity/state.ts +27 -0
- package/src/async/domains/mirroring/domains/entity/domains/loaded-entities/state.ts +12 -0
- package/src/async/domains/mirroring/domains/entity/domains/loaded-entity/state.ts +29 -0
- package/src/async/domains/mirroring/domains/entity/state.ts +22 -0
- package/src/async/domains/mirroring/domains/singleton/coroutines/synchronizers.ts +216 -0
- package/src/async/domains/mirroring/domains/singleton/state.ts +57 -0
- package/src/async/domains/mirroring/domains/synchronization-result/state.ts +2 -0
- package/src/async/domains/mirroring/domains/synchronized-entities/state.ts +23 -0
- package/src/async/domains/promise/state.ts +13 -0
- package/src/async/domains/synchronized/coroutines/synchronize.ts +62 -0
- package/src/async/domains/synchronized/state.ts +20 -0
- package/src/async/state.ts +144 -0
- package/src/baseEntity/domains/identifiable/state.ts +29 -0
- package/src/collections/domains/array/state.ts +16 -0
- package/src/collections/domains/immutable/domains/list/state.ts +20 -0
- package/src/collections/domains/immutable/domains/map/state.ts +33 -0
- package/src/collections/domains/immutable/domains/orderedMap/state.ts +117 -0
- package/src/collections/domains/immutable/domains/ordereredSet/state.ts +19 -0
- package/src/collections/domains/immutable/domains/set/state.ts +15 -0
- package/src/collections/domains/maybe/state.ts +20 -0
- package/src/collections/domains/product/state.ts +47 -0
- package/src/collections/domains/sum/state.ts +82 -0
- package/src/coroutines/builder.tsx +66 -0
- package/src/coroutines/state.ts +574 -0
- package/src/coroutines/template.tsx +119 -0
- package/src/debounced/coroutines/debounce.ts +118 -0
- package/src/debounced/state.ts +61 -0
- package/src/diagnostics/domains/message-box/state.ts +4 -0
- package/src/foreignMutations/state.ts +4 -0
- package/src/forms/domains/attachments/views/attachments-view.tsx +22 -0
- package/src/forms/domains/collection/domains/reference/state.ts +18 -0
- package/src/forms/domains/collection/domains/selection/state.ts +5 -0
- package/src/forms/domains/launcher/domains/create/coroutines/runner.ts +60 -0
- package/src/forms/domains/launcher/domains/create/state.ts +71 -0
- package/src/forms/domains/launcher/domains/create/template.tsx +66 -0
- package/src/forms/domains/launcher/domains/edit/coroutines/runner.ts +47 -0
- package/src/forms/domains/launcher/domains/edit/state.ts +55 -0
- package/src/forms/domains/launcher/domains/edit/template.tsx +39 -0
- package/src/forms/domains/parser/domains/validator/state.ts +423 -0
- package/src/forms/domains/parser/state.tsx +311 -0
- package/src/forms/domains/primitives/domains/boolean/state.ts +13 -0
- package/src/forms/domains/primitives/domains/boolean/template.tsx +24 -0
- package/src/forms/domains/primitives/domains/date/state.ts +23 -0
- package/src/forms/domains/primitives/domains/date/template.tsx +31 -0
- package/src/forms/domains/primitives/domains/enum/state.ts +27 -0
- package/src/forms/domains/primitives/domains/enum/template.tsx +52 -0
- package/src/forms/domains/primitives/domains/enum-multiselect/state.ts +18 -0
- package/src/forms/domains/primitives/domains/enum-multiselect/template.tsx +56 -0
- package/src/forms/domains/primitives/domains/number/state.ts +8 -0
- package/src/forms/domains/primitives/domains/number/template.tsx +26 -0
- package/src/forms/domains/primitives/domains/searchable-infinite-stream/state.ts +55 -0
- package/src/forms/domains/primitives/domains/searchable-infinite-stream/template.tsx +114 -0
- package/src/forms/domains/primitives/domains/searchable-infinite-stream-multiselect/state.ts +27 -0
- package/src/forms/domains/primitives/domains/searchable-infinite-stream-multiselect/template.tsx +119 -0
- package/src/forms/domains/primitives/domains/string/state.ts +12 -0
- package/src/forms/domains/primitives/domains/string/template.tsx +24 -0
- package/src/forms/domains/singleton/domains/descriptors/entity/state.ts +9 -0
- package/src/forms/domains/singleton/domains/descriptors/field/domains/base/state.ts +5 -0
- package/src/forms/domains/singleton/domains/descriptors/field/domains/boolean/state.ts +6 -0
- package/src/forms/domains/singleton/domains/descriptors/field/domains/custom/state.ts +21 -0
- package/src/forms/domains/singleton/domains/descriptors/field/domains/date/state.ts +6 -0
- package/src/forms/domains/singleton/domains/descriptors/field/domains/enum/state.ts +7 -0
- package/src/forms/domains/singleton/domains/descriptors/field/domains/infinite-enum/state.ts +8 -0
- package/src/forms/domains/singleton/domains/descriptors/field/domains/number/state.ts +7 -0
- package/src/forms/domains/singleton/domains/descriptors/field/domains/string/state.ts +7 -0
- package/src/forms/domains/singleton/domains/descriptors/field/state.ts +16 -0
- package/src/forms/domains/singleton/domains/descriptors/form/state.ts +13 -0
- package/src/forms/domains/singleton/domains/form-label/state.ts +2 -0
- package/src/forms/domains/singleton/state.ts +87 -0
- package/src/forms/domains/singleton/template.tsx +92 -0
- package/src/forms/domains/singleton/views/field-views.ts +23 -0
- package/src/forms/domains/singleton/views/simple-inputs/base.ts +5 -0
- package/src/forms/domains/singleton/views/simple-inputs/boolean.tsx +4 -0
- package/src/forms/domains/singleton/views/simple-inputs/date.tsx +4 -0
- package/src/forms/domains/singleton/views/simple-inputs/number.tsx +4 -0
- package/src/forms/domains/singleton/views/simple-inputs/string.tsx +4 -0
- package/src/fun/domains/curry/state.ts +6 -0
- package/src/fun/domains/id/state.ts +2 -0
- package/src/fun/domains/predicate/domains/bool-expr.ts +98 -0
- package/src/fun/domains/predicate/state.ts +29 -0
- package/src/fun/domains/simpleCallback/state.ts +3 -0
- package/src/fun/domains/uncurry/state.ts +5 -0
- package/src/fun/domains/unit/state.ts +3 -0
- package/src/fun/domains/updater/domains/caseUpdater/state.ts +44 -0
- package/src/fun/domains/updater/domains/mapUpdater/state.ts +36 -0
- package/src/fun/domains/updater/domains/maybeUpdater/state.ts +45 -0
- package/src/fun/domains/updater/domains/orderedMapUpdater/state.ts +26 -0
- package/src/fun/domains/updater/domains/orderedSetUpdater/state.ts +23 -0
- package/src/fun/domains/updater/domains/replaceWith/state.ts +3 -0
- package/src/fun/domains/updater/domains/simpleUpdater/domains/baseSimpleUpdater/state.ts +18 -0
- package/src/fun/domains/updater/domains/simpleUpdater/state.ts +72 -0
- package/src/fun/domains/updater/state.ts +86 -0
- package/src/fun/state.ts +33 -0
- package/src/infinite-data-stream/coroutines/builder.ts +8 -0
- package/src/infinite-data-stream/coroutines/infiniteLoader.ts +51 -0
- package/src/infinite-data-stream/coroutines/runner.ts +12 -0
- package/src/infinite-data-stream/state.ts +175 -0
- package/src/infinite-data-stream/template.tsx +16 -0
- package/src/math/domains/DOMRect/state.ts +12 -0
- package/src/math/domains/number/state.ts +9 -0
- package/src/math/domains/rect/state.ts +22 -0
- package/src/math/domains/rgba/state.ts +87 -0
- package/src/math/domains/size2/state.ts +14 -0
- package/src/math/domains/vector2/state.ts +16 -0
- package/src/queue/state.ts +42 -0
- package/src/state/domains/repository/state.ts +14 -0
- package/src/template/state.tsx +198 -0
- package/src/validation/state.ts +2 -0
- package/src/value/domains/mutable-value/state.ts +13 -0
- package/src/value/state.ts +12 -0
- package/src/visibility/state.ts +3 -0
- package/tsconfig.json +22 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { ApiResultStatus } from "../../apiResultStatus/state";
|
|
2
|
+
import { CoTypedFactory } from "../../coroutines/builder";
|
|
3
|
+
import { Coroutine } from "../../coroutines/state";
|
|
4
|
+
import { Unit } from "../../fun/domains/unit/state";
|
|
5
|
+
import { replaceWith } from "../../fun/domains/updater/domains/replaceWith/state";
|
|
6
|
+
import { Debounced, DebouncedStatus, DirtyStatus } from "../state";
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
export const Debounce = <value, context = Unit>(
|
|
10
|
+
k: Coroutine<value & context, value, ApiResultStatus>,
|
|
11
|
+
debounceDurationInMs: number,
|
|
12
|
+
waitBeforeRetryOnTransientFailure: number = debounceDurationInMs * 2
|
|
13
|
+
) => {
|
|
14
|
+
const Co = CoTypedFactory<context, Debounced<value>>();
|
|
15
|
+
const updaters = Debounced.Updaters;
|
|
16
|
+
return Co.Seq([
|
|
17
|
+
Co.SetState(
|
|
18
|
+
updaters.Core.status(replaceWith<DebouncedStatus>("waiting for dirty"))
|
|
19
|
+
),
|
|
20
|
+
Co.While(
|
|
21
|
+
([current]) =>
|
|
22
|
+
current.dirty != "dirty" ||
|
|
23
|
+
Date.now() - current.lastUpdated <= debounceDurationInMs,
|
|
24
|
+
Co.Wait(debounceDurationInMs / 5)
|
|
25
|
+
),
|
|
26
|
+
Co.SetState(
|
|
27
|
+
updaters.Core.dirty(replaceWith<DirtyStatus>("dirty but being processed"))
|
|
28
|
+
),
|
|
29
|
+
Co.SetState(
|
|
30
|
+
updaters.Core.status(
|
|
31
|
+
replaceWith<DebouncedStatus>("just detected dirty, starting processing")
|
|
32
|
+
)
|
|
33
|
+
),
|
|
34
|
+
Co.Any([
|
|
35
|
+
// shortcircuit the validation if it takes longer than the whole cycle in the presence of an underwater update of the field
|
|
36
|
+
Co.Seq([
|
|
37
|
+
Co.While(
|
|
38
|
+
([current]) =>
|
|
39
|
+
current.dirty != "dirty" ||
|
|
40
|
+
Date.now() - current.lastUpdated <= debounceDurationInMs,
|
|
41
|
+
Co.Wait(debounceDurationInMs / 5)
|
|
42
|
+
),
|
|
43
|
+
Co.SetState(
|
|
44
|
+
updaters.Core.status(
|
|
45
|
+
replaceWith<DebouncedStatus>("processing shortcircuited")
|
|
46
|
+
)
|
|
47
|
+
),
|
|
48
|
+
Co.Wait(debounceDurationInMs / 2),
|
|
49
|
+
]),
|
|
50
|
+
k
|
|
51
|
+
.embed((_: Debounced<value & context>) => _, updaters.Core.value)
|
|
52
|
+
.then((apiResult) => {
|
|
53
|
+
return Co.Seq([
|
|
54
|
+
Co.SetState(
|
|
55
|
+
updaters.Core.status(
|
|
56
|
+
replaceWith<DebouncedStatus>("processing finished")
|
|
57
|
+
)
|
|
58
|
+
),
|
|
59
|
+
// Co.Wait(250)
|
|
60
|
+
]).then(() => {
|
|
61
|
+
return Co.GetState().then((current) => {
|
|
62
|
+
// alert(apiResult)
|
|
63
|
+
if (apiResult == "success" || apiResult == "permanent failure") {
|
|
64
|
+
// maybe a new change has already reset dirty, in that case we need to start all over again
|
|
65
|
+
if (current.dirty == "dirty but being processed") {
|
|
66
|
+
return Co.Seq([
|
|
67
|
+
Co.SetState(
|
|
68
|
+
updaters.Core.status(
|
|
69
|
+
replaceWith<DebouncedStatus>(
|
|
70
|
+
"state was still dirty but being processed, resetting to not dirty"
|
|
71
|
+
)
|
|
72
|
+
)
|
|
73
|
+
),
|
|
74
|
+
// use UpdateState to make sure that we look up the state at the last possible moment to account for delays
|
|
75
|
+
Co.UpdateState((state) =>
|
|
76
|
+
state.dirty == "dirty but being processed"
|
|
77
|
+
? updaters.Core.dirty(
|
|
78
|
+
replaceWith<DirtyStatus>("not dirty")
|
|
79
|
+
)
|
|
80
|
+
: updaters.Core.dirty(replaceWith<DirtyStatus>("dirty"))
|
|
81
|
+
),
|
|
82
|
+
]);
|
|
83
|
+
} else {
|
|
84
|
+
return Co.Seq([
|
|
85
|
+
Co.SetState(
|
|
86
|
+
updaters.Core.status(
|
|
87
|
+
replaceWith<DebouncedStatus>(
|
|
88
|
+
"state was changed underwater back to dirty, leaving the dirty flag alone"
|
|
89
|
+
)
|
|
90
|
+
)
|
|
91
|
+
),
|
|
92
|
+
Co.SetState(
|
|
93
|
+
updaters.Core.dirty(replaceWith<DirtyStatus>("dirty"))
|
|
94
|
+
),
|
|
95
|
+
// Co.Wait(250)
|
|
96
|
+
]);
|
|
97
|
+
}
|
|
98
|
+
} else {
|
|
99
|
+
return Co.Seq([
|
|
100
|
+
Co.SetState(
|
|
101
|
+
updaters.Core.status(
|
|
102
|
+
replaceWith<DebouncedStatus>(
|
|
103
|
+
"inner call failed with transient failure"
|
|
104
|
+
)
|
|
105
|
+
)
|
|
106
|
+
),
|
|
107
|
+
Co.SetState(
|
|
108
|
+
updaters.Core.dirty(replaceWith<DirtyStatus>("dirty"))
|
|
109
|
+
),
|
|
110
|
+
Co.Wait(waitBeforeRetryOnTransientFailure),
|
|
111
|
+
]);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
}),
|
|
116
|
+
]),
|
|
117
|
+
]);
|
|
118
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { replaceWith } from "../fun/domains/updater/domains/replaceWith/state";
|
|
2
|
+
import { BasicUpdater, Updater } from "../fun/domains/updater/state";
|
|
3
|
+
|
|
4
|
+
export type DirtyStatus = "dirty" | "not dirty" | "dirty but being processed"
|
|
5
|
+
export type DebouncedStatus = "waiting for dirty" | "just detected dirty, starting processing"
|
|
6
|
+
| "processing finished" | "state was still dirty but being processed, resetting to not dirty"
|
|
7
|
+
| "processing shortcircuited"
|
|
8
|
+
| "state was changed underwater back to dirty, leaving the dirty flag alone"
|
|
9
|
+
| "inner call failed with transient failure"
|
|
10
|
+
export type Debounced<Value> = Value & { lastUpdated: number; dirty: DirtyStatus; status:DebouncedStatus };
|
|
11
|
+
export const Debounced = {
|
|
12
|
+
Default: <v>(initialValue: v): Debounced<v> => ({
|
|
13
|
+
...initialValue,
|
|
14
|
+
lastUpdated: 0,
|
|
15
|
+
dirty: "not dirty",
|
|
16
|
+
status: "waiting for dirty"
|
|
17
|
+
}),
|
|
18
|
+
Updaters: {
|
|
19
|
+
Core:{
|
|
20
|
+
status: <v>(_: BasicUpdater<DebouncedStatus>): Updater<Debounced<v>> => Updater<Debounced<v>>(current => ({
|
|
21
|
+
...current,
|
|
22
|
+
status: _(current.status),
|
|
23
|
+
})),
|
|
24
|
+
dirty: <v>(_: BasicUpdater<DirtyStatus>): Updater<Debounced<v>> => Updater<Debounced<v>>(current => ({
|
|
25
|
+
...current,
|
|
26
|
+
dirty: _(current.dirty),
|
|
27
|
+
})),
|
|
28
|
+
lastUpdated: <v>(_: BasicUpdater<number>): Updater<Debounced<v>> => Updater<Debounced<v>>(current => ({
|
|
29
|
+
...current,
|
|
30
|
+
lastUpdated: _(current.lastUpdated),
|
|
31
|
+
})),
|
|
32
|
+
valueWithoutDebouncing: <v>(_: BasicUpdater<v>): Updater<Debounced<v>> => Updater<Debounced<v>>(current => ({
|
|
33
|
+
...current,
|
|
34
|
+
...(_(current)),
|
|
35
|
+
})),
|
|
36
|
+
value: <v>(_: BasicUpdater<v>): Updater<Debounced<v>> => Updater<Debounced<v>>(current => ({
|
|
37
|
+
...(_(current)),
|
|
38
|
+
dirty: current.dirty,
|
|
39
|
+
lastUpdated: current.lastUpdated,
|
|
40
|
+
status: current.status
|
|
41
|
+
})),
|
|
42
|
+
},
|
|
43
|
+
Template:{
|
|
44
|
+
value: <v>(_: BasicUpdater<v>): Updater<Debounced<v>> =>
|
|
45
|
+
// Debounced.Updaters.Core.value(_).then(
|
|
46
|
+
// Debounced.Updaters.Core.dirty(replaceWith<DirtyStatus>("dirty"))
|
|
47
|
+
// ).then(
|
|
48
|
+
// Debounced.Updaters.Core.lastUpdated(replaceWith(Date.now()))
|
|
49
|
+
// )
|
|
50
|
+
Updater<Debounced<v>>(current => ({
|
|
51
|
+
...(_(current)),
|
|
52
|
+
dirty: "dirty",
|
|
53
|
+
lastUpdated: Date.now(),
|
|
54
|
+
status: current.status
|
|
55
|
+
}))
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
Operations:{
|
|
59
|
+
shouldCoroutineRun:<v>(_:Debounced<v>) => _.dirty != "not dirty"
|
|
60
|
+
}
|
|
61
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { OrderedMap } from "immutable";
|
|
2
|
+
import { v4 } from "uuid";
|
|
3
|
+
import { Guid, SimpleCallback } from "../../../../../main";
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
export const AttachmentsList = (props: { attachments: OrderedMap<Guid, File>; add: SimpleCallback<[Guid, File]>; remove: SimpleCallback<Guid>; }) => <>
|
|
8
|
+
<ul>
|
|
9
|
+
{props.attachments.map((file, fileId) => <>
|
|
10
|
+
<button onClick={() => props.remove(fileId)}>
|
|
11
|
+
{file.name} 🗑️
|
|
12
|
+
</button>
|
|
13
|
+
</>
|
|
14
|
+
).valueSeq()}
|
|
15
|
+
</ul>
|
|
16
|
+
<input
|
|
17
|
+
type="file"
|
|
18
|
+
onChange={e => {
|
|
19
|
+
if (e.currentTarget.files != null)
|
|
20
|
+
props.add([v4(), e.currentTarget.files[0]]);
|
|
21
|
+
}} />
|
|
22
|
+
</>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { simpleUpdater } from "../../../../../../main";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
export type CollectionReference = {
|
|
5
|
+
id: string;
|
|
6
|
+
displayName: string;
|
|
7
|
+
};
|
|
8
|
+
export const CollectionReference = {
|
|
9
|
+
Default: Object.assign((id: string, displayName: string): CollectionReference => ({
|
|
10
|
+
id, displayName
|
|
11
|
+
}), {
|
|
12
|
+
empty: (): CollectionReference => CollectionReference.Default("", "")
|
|
13
|
+
}),
|
|
14
|
+
Updaters: {
|
|
15
|
+
...simpleUpdater<CollectionReference>()("id"),
|
|
16
|
+
...simpleUpdater<CollectionReference>()("displayName"),
|
|
17
|
+
}
|
|
18
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Sum } from "../../../../../../main";
|
|
2
|
+
import { CollectionReference } from "../reference/state"
|
|
3
|
+
|
|
4
|
+
export type CollectionSelection<Element extends CollectionReference> = Sum<Element, "no selection">;
|
|
5
|
+
export const CollectionSelection = <Element extends CollectionReference>() => Sum<Element, "no selection">();
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { ApiErrors, AsyncState, Debounce, Debounced, CreateFormForeignMutationsExpected, CreateFormState, Synchronize, Synchronized, Unit, SimpleCallback, unit, replaceWith } from "../../../../../../../main"
|
|
2
|
+
import { CoTypedFactory } from "../../../../../../coroutines/builder"
|
|
3
|
+
import { CreateFormContext, CreateFormWritableState } from "../state"
|
|
4
|
+
|
|
5
|
+
export const createFormRunner = <E, FS>() => {
|
|
6
|
+
const Co = CoTypedFactory<CreateFormContext<E, FS> & { onSubmitted:SimpleCallback<E> }, CreateFormWritableState<E, FS>>()
|
|
7
|
+
|
|
8
|
+
const init =
|
|
9
|
+
Co.GetState().then(current =>
|
|
10
|
+
Synchronize<Unit, Synchronized<E, ApiErrors>>(() => current.api.default().then(e =>
|
|
11
|
+
Synchronized.Default<E, ApiErrors>(e)
|
|
12
|
+
), _ => "transient failure", 5, 50)
|
|
13
|
+
.embed(_ => _.entity,
|
|
14
|
+
_ => CreateFormState<E, FS>().Updaters.Core.entity(Debounced.Updaters.Core.value(_)))
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
const synchronize =
|
|
18
|
+
Co.Repeat(
|
|
19
|
+
Co.Seq([
|
|
20
|
+
Co.GetState().then(current =>
|
|
21
|
+
Debounce<Synchronized<Unit, Synchronized<E, ApiErrors>>, CreateFormContext<E, FS>>(
|
|
22
|
+
Synchronize<E, ApiErrors>(e => current.api.create(e), _ => "transient failure", 5, 50)
|
|
23
|
+
.embed(
|
|
24
|
+
_ => AsyncState.Operations.hasValue(_.sync) ? _.sync.value : undefined,
|
|
25
|
+
_ => Synchronized.Updaters.sync<Unit, Synchronized<E, ApiErrors>>(AsyncState.Operations.map(_))
|
|
26
|
+
),
|
|
27
|
+
15
|
|
28
|
+
).embed(
|
|
29
|
+
_ => ({ ..._, ..._.entity }),
|
|
30
|
+
_ => CreateFormState<E, FS>().Updaters.Core.entity(_)
|
|
31
|
+
)
|
|
32
|
+
),
|
|
33
|
+
Co.GetState().then(current =>
|
|
34
|
+
AsyncState.Operations.hasValue(current.entity.sync) && AsyncState.Operations.hasValue(current.entity.sync.value.sync) && current.notifySubmitAfterSync ?
|
|
35
|
+
Co.Seq([
|
|
36
|
+
Co.Do(() => {
|
|
37
|
+
if (AsyncState.Operations.hasValue(current.entity.sync) && AsyncState.Operations.hasValue(current.entity.sync.value.sync))
|
|
38
|
+
current.onSubmitted(current.entity.sync.value)
|
|
39
|
+
}),
|
|
40
|
+
Co.SetState(CreateFormState<E,FS>().Updaters.Core.notifySubmitAfterSync(replaceWith(false)))
|
|
41
|
+
])
|
|
42
|
+
: Co.Return(unit)
|
|
43
|
+
)
|
|
44
|
+
])
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
return Co.Template<CreateFormForeignMutationsExpected<E, FS>>(
|
|
48
|
+
init, {
|
|
49
|
+
interval: 15,
|
|
50
|
+
runFilter: props => !AsyncState.Operations.hasValue(props.context.entity.sync)
|
|
51
|
+
}
|
|
52
|
+
).any([
|
|
53
|
+
Co.Template<CreateFormForeignMutationsExpected<E, FS>>(
|
|
54
|
+
synchronize, {
|
|
55
|
+
interval: 15,
|
|
56
|
+
runFilter: props => Debounced.Operations.shouldCoroutineRun(props.context.entity) || props.context.notifySubmitAfterSync
|
|
57
|
+
}
|
|
58
|
+
)
|
|
59
|
+
])
|
|
60
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { ApiErrors, AsyncState, BasicUpdater, Debounced, ForeignMutationsInput, id, replaceWith, SimpleCallback, simpleUpdater, Synchronized, Template, unit, Unit, Updater, Value } from "../../../../../../main"
|
|
2
|
+
import { BasicFun } from "../../../../../fun/state"
|
|
3
|
+
|
|
4
|
+
export type CreateFormContext<E,FS> = {
|
|
5
|
+
entityId:string,
|
|
6
|
+
api:{
|
|
7
|
+
default:() => Promise<E>,
|
|
8
|
+
create:BasicFun<E, Promise<ApiErrors>>
|
|
9
|
+
},
|
|
10
|
+
actualForm:Template<Value<E> & FS, FS, { onChange:SimpleCallback<BasicUpdater<E>> }>
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type CreateFormState<E,FS> = {
|
|
14
|
+
// first sync is GET (returns E), second is UPDATE (accepts E)
|
|
15
|
+
entity:Debounced<Synchronized<Unit, Synchronized<E, ApiErrors>>>
|
|
16
|
+
formState:FS,
|
|
17
|
+
notifySubmitAfterSync:boolean
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const CreateFormState = <E,FS>() => ({
|
|
21
|
+
Default:(initialFormState:FS) : CreateFormState<E,FS> => ({
|
|
22
|
+
entity:Debounced.Default(
|
|
23
|
+
Synchronized.Default(unit)
|
|
24
|
+
),
|
|
25
|
+
formState:initialFormState,
|
|
26
|
+
notifySubmitAfterSync:false
|
|
27
|
+
}),
|
|
28
|
+
Updaters:{
|
|
29
|
+
Core:{
|
|
30
|
+
...simpleUpdater<CreateFormState<E,FS>>()("notifySubmitAfterSync"),
|
|
31
|
+
...simpleUpdater<CreateFormState<E,FS>>()("entity"),
|
|
32
|
+
...simpleUpdater<CreateFormState<E,FS>>()("formState"),
|
|
33
|
+
},
|
|
34
|
+
Template:{
|
|
35
|
+
entity:(_:BasicUpdater<E>) : Updater<CreateFormState<E,FS>> =>
|
|
36
|
+
CreateFormState<E,FS>().Updaters.Core.entity(
|
|
37
|
+
Debounced.Updaters.Core.value(
|
|
38
|
+
Synchronized.Updaters.sync(
|
|
39
|
+
AsyncState.Operations.map(
|
|
40
|
+
Synchronized.Updaters.value(
|
|
41
|
+
_
|
|
42
|
+
)
|
|
43
|
+
)
|
|
44
|
+
)
|
|
45
|
+
)
|
|
46
|
+
),
|
|
47
|
+
submit:() : Updater<CreateFormState<E,FS>> =>
|
|
48
|
+
CreateFormState<E,FS>().Updaters.Core.entity(
|
|
49
|
+
Debounced.Updaters.Template.value(
|
|
50
|
+
Synchronized.Updaters.sync(
|
|
51
|
+
AsyncState.Operations.map(
|
|
52
|
+
Synchronized.Updaters.value(
|
|
53
|
+
id
|
|
54
|
+
)
|
|
55
|
+
)
|
|
56
|
+
)
|
|
57
|
+
)
|
|
58
|
+
).then(
|
|
59
|
+
CreateFormState<E,FS>().Updaters.Core.notifySubmitAfterSync(replaceWith(true))
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
ForeignMutations: (_: ForeignMutationsInput<CreateFormContext<E,FS>, CreateFormWritableState<E,FS>>) => ({
|
|
65
|
+
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
export type CreateFormWritableState<E,FS> = CreateFormState<E,FS>
|
|
70
|
+
export type CreateFormForeignMutationsExposed<E,FS> = ReturnType<ReturnType<typeof CreateFormState<E,FS>>["ForeignMutations"]>
|
|
71
|
+
export type CreateFormForeignMutationsExpected<E,FS> = { onSubmitted:SimpleCallback<E> }
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { AsyncState, createFormRunner, replaceWith, SimpleCallback, unit } from "../../../../../../main";
|
|
2
|
+
import { Template, View } from "../../../../../template/state";
|
|
3
|
+
import { CreateFormContext, CreateFormForeignMutationsExpected, CreateFormState, CreateFormWritableState } from "./state";
|
|
4
|
+
|
|
5
|
+
export type CreateFormView<E, FS> =
|
|
6
|
+
Template<
|
|
7
|
+
CreateFormContext<E, FS> & CreateFormWritableState<E, FS>,
|
|
8
|
+
CreateFormWritableState<E, FS>,
|
|
9
|
+
CreateFormForeignMutationsExpected<E, FS> & { onSubmit: SimpleCallback<void> },
|
|
10
|
+
{
|
|
11
|
+
actualForm: JSX.Element | undefined
|
|
12
|
+
}>
|
|
13
|
+
export type CreateFormTemplate<E, FS> =
|
|
14
|
+
Template<
|
|
15
|
+
CreateFormContext<E, FS> & CreateFormWritableState<E, FS>,
|
|
16
|
+
CreateFormWritableState<E, FS>,
|
|
17
|
+
CreateFormForeignMutationsExpected<E, FS>,
|
|
18
|
+
CreateFormView<E, FS>>
|
|
19
|
+
export const CreateFormTemplate = <E, FS>(): CreateFormTemplate<E, FS> =>
|
|
20
|
+
Template.Default<
|
|
21
|
+
CreateFormContext<E, FS> & CreateFormWritableState<E, FS>,
|
|
22
|
+
CreateFormWritableState<E, FS>,
|
|
23
|
+
CreateFormForeignMutationsExpected<E, FS>,
|
|
24
|
+
CreateFormView<E, FS>>(props =>
|
|
25
|
+
<>
|
|
26
|
+
{
|
|
27
|
+
|
|
28
|
+
props.view({
|
|
29
|
+
...props,
|
|
30
|
+
foreignMutations: {
|
|
31
|
+
...props.foreignMutations, onSubmit: () => {
|
|
32
|
+
props.setState(CreateFormState<E, FS>().Updaters.Template.submit())
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
view: {
|
|
36
|
+
...props.view,
|
|
37
|
+
actualForm:
|
|
38
|
+
!AsyncState.Operations.hasValue(props.context.entity.sync) ? undefined :
|
|
39
|
+
props.context.actualForm({
|
|
40
|
+
context: {
|
|
41
|
+
value: props.context.entity.sync.value,
|
|
42
|
+
...props.context.formState,
|
|
43
|
+
},
|
|
44
|
+
setState: _ => {
|
|
45
|
+
props.setState(
|
|
46
|
+
CreateFormState<E, FS>().Updaters.Core.formState(_)
|
|
47
|
+
)
|
|
48
|
+
},
|
|
49
|
+
foreignMutations: {
|
|
50
|
+
onChange: (e) => {
|
|
51
|
+
props.setState(CreateFormState<E, FS>().Updaters.Template.entity(e))
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
view: unit
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
</>
|
|
61
|
+
).any([
|
|
62
|
+
createFormRunner<E, FS>().mapContextFromProps(props => ({
|
|
63
|
+
...props.context,
|
|
64
|
+
onSubmitted:props.foreignMutations.onSubmitted
|
|
65
|
+
}))
|
|
66
|
+
])
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { ApiErrors, AsyncState, Debounce, Debounced, EditFormForeignMutationsExpected, EditFormState, Synchronize, Synchronized, Unit } from "../../../../../../../main"
|
|
2
|
+
import { CoTypedFactory } from "../../../../../../coroutines/builder"
|
|
3
|
+
import { EditFormContext, EditFormWritableState } from "../state"
|
|
4
|
+
|
|
5
|
+
export const editFormRunner = <E, FS>() => {
|
|
6
|
+
const Co = CoTypedFactory<EditFormContext<E, FS>, EditFormWritableState<E, FS>>()
|
|
7
|
+
|
|
8
|
+
const init =
|
|
9
|
+
Co.GetState().then(current =>
|
|
10
|
+
Synchronize<Unit, Synchronized<E, ApiErrors>>(() => current.api.get().then(e =>
|
|
11
|
+
Synchronized.Default<E, ApiErrors>(e)
|
|
12
|
+
), _ => "transient failure", 5, 50)
|
|
13
|
+
.embed(_ => _.entity,
|
|
14
|
+
_ => EditFormState<E, FS>().Updaters.Core.entity(Debounced.Updaters.Core.value(_)))
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
const synchronize =
|
|
18
|
+
Co.Repeat(
|
|
19
|
+
Co.GetState().then(current =>
|
|
20
|
+
Debounce<Synchronized<Unit, Synchronized<E, ApiErrors>>, EditFormContext<E, FS>>(
|
|
21
|
+
Synchronize<E, ApiErrors>(e => current.api.update(e), _ => "transient failure", 5, 50)
|
|
22
|
+
.embed(
|
|
23
|
+
_ => AsyncState.Operations.hasValue(_.sync) ? _.sync.value : undefined,
|
|
24
|
+
_ => Synchronized.Updaters.sync<Unit, Synchronized<E, ApiErrors>>(AsyncState.Operations.map(_))
|
|
25
|
+
),
|
|
26
|
+
15
|
|
27
|
+
).embed(
|
|
28
|
+
_ => ({ ..._, ..._.entity }),
|
|
29
|
+
_ => EditFormState<E, FS>().Updaters.Core.entity(_)
|
|
30
|
+
)
|
|
31
|
+
)
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
return Co.Template<EditFormForeignMutationsExpected<E, FS>>(
|
|
35
|
+
init, {
|
|
36
|
+
interval: 15,
|
|
37
|
+
runFilter: props => !AsyncState.Operations.hasValue(props.context.entity.sync)
|
|
38
|
+
}
|
|
39
|
+
).any([
|
|
40
|
+
Co.Template<EditFormForeignMutationsExpected<E, FS>>(
|
|
41
|
+
synchronize, {
|
|
42
|
+
interval: 15,
|
|
43
|
+
runFilter: props => Debounced.Operations.shouldCoroutineRun(props.context.entity)
|
|
44
|
+
}
|
|
45
|
+
)
|
|
46
|
+
])
|
|
47
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { AsyncState, BasicUpdater, Debounced, ForeignMutationsInput, SimpleCallback, simpleUpdater, Synchronized, Template, unit, Unit, Updater, Value } from "../../../../../../main"
|
|
2
|
+
import { BasicFun } from "../../../../../fun/state"
|
|
3
|
+
|
|
4
|
+
export type ApiErrors = Array<string>
|
|
5
|
+
|
|
6
|
+
export type EditFormContext<E,FS> = {
|
|
7
|
+
entityId:string,
|
|
8
|
+
api:{
|
|
9
|
+
get:() => Promise<E>,
|
|
10
|
+
update:BasicFun<E, Promise<ApiErrors>>
|
|
11
|
+
},
|
|
12
|
+
actualForm:Template<Value<E> & FS, FS, { onChange:SimpleCallback<BasicUpdater<E>> }>
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type EditFormState<E,FS> = {
|
|
16
|
+
// first sync is GET (returns E), second is UPDATE (accepts E)
|
|
17
|
+
entity:Debounced<Synchronized<Unit, Synchronized<E, ApiErrors>>>
|
|
18
|
+
formState:FS,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const EditFormState = <E,FS>() => ({
|
|
22
|
+
Default:(initialFormState:FS) : EditFormState<E,FS> => ({
|
|
23
|
+
entity:Debounced.Default(
|
|
24
|
+
Synchronized.Default(unit)
|
|
25
|
+
),
|
|
26
|
+
formState:initialFormState,
|
|
27
|
+
}),
|
|
28
|
+
Updaters:{
|
|
29
|
+
Core:{
|
|
30
|
+
...simpleUpdater<EditFormState<E,FS>>()("entity"),
|
|
31
|
+
...simpleUpdater<EditFormState<E,FS>>()("formState"),
|
|
32
|
+
},
|
|
33
|
+
Template:{
|
|
34
|
+
entity:(_:BasicUpdater<E>) : Updater<EditFormState<E,FS>> =>
|
|
35
|
+
EditFormState<E,FS>().Updaters.Core.entity(
|
|
36
|
+
Debounced.Updaters.Template.value(
|
|
37
|
+
Synchronized.Updaters.sync(
|
|
38
|
+
AsyncState.Operations.map(
|
|
39
|
+
Synchronized.Updaters.value(
|
|
40
|
+
_
|
|
41
|
+
)
|
|
42
|
+
)
|
|
43
|
+
)
|
|
44
|
+
)
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
ForeignMutations: (_: ForeignMutationsInput<EditFormContext<E,FS>, EditFormWritableState<E,FS>>) => ({
|
|
49
|
+
|
|
50
|
+
})
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
export type EditFormWritableState<E,FS> = EditFormState<E,FS>
|
|
54
|
+
export type EditFormForeignMutationsExposed<E,FS> = ReturnType<ReturnType<typeof EditFormState<E,FS>>["ForeignMutations"]>
|
|
55
|
+
export type EditFormForeignMutationsExpected<E,FS> = Unit
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { AsyncState, editFormRunner, replaceWith, unit } from "../../../../../../main";
|
|
2
|
+
import { Template } from "../../../../../template/state";
|
|
3
|
+
import { EditFormContext, EditFormForeignMutationsExpected, EditFormState, EditFormWritableState } from "./state";
|
|
4
|
+
|
|
5
|
+
export type EditFormTemplate<E, FS> =
|
|
6
|
+
Template<
|
|
7
|
+
EditFormContext<E, FS> & EditFormWritableState<E, FS>,
|
|
8
|
+
EditFormWritableState<E, FS>,
|
|
9
|
+
EditFormForeignMutationsExpected<E, FS>>
|
|
10
|
+
export const EditFormTemplate = <E, FS>() : EditFormTemplate<E,FS> =>
|
|
11
|
+
Template.Default<
|
|
12
|
+
EditFormContext<E, FS> & EditFormWritableState<E, FS>,
|
|
13
|
+
EditFormWritableState<E, FS>,
|
|
14
|
+
EditFormForeignMutationsExpected<E, FS>>(props =>
|
|
15
|
+
<>
|
|
16
|
+
{
|
|
17
|
+
!AsyncState.Operations.hasValue(props.context.entity.sync) ? undefined :
|
|
18
|
+
props.context.actualForm({
|
|
19
|
+
context: {
|
|
20
|
+
value: props.context.entity.sync.value,
|
|
21
|
+
...props.context.formState,
|
|
22
|
+
},
|
|
23
|
+
setState: _ => {
|
|
24
|
+
props.setState(
|
|
25
|
+
EditFormState<E, FS>().Updaters.Core.formState(_)
|
|
26
|
+
)
|
|
27
|
+
},
|
|
28
|
+
foreignMutations: {
|
|
29
|
+
onChange: (e) => {
|
|
30
|
+
props.setState(EditFormState<E, FS>().Updaters.Template.entity(e))
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
view: unit
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
</>
|
|
37
|
+
).any([
|
|
38
|
+
editFormRunner<E, FS>()
|
|
39
|
+
])
|