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.
Files changed (118) hide show
  1. package/main.ts +149 -0
  2. package/package.json +20 -0
  3. package/src/apiResultStatus/state.ts +7 -0
  4. package/src/async/domains/mirroring/domains/collection/coroutines/synchronizers.ts +334 -0
  5. package/src/async/domains/mirroring/domains/collection/state.ts +137 -0
  6. package/src/async/domains/mirroring/domains/entity/domains/loaded-collection/state.ts +24 -0
  7. package/src/async/domains/mirroring/domains/entity/domains/loaded-collection-entity/state.ts +27 -0
  8. package/src/async/domains/mirroring/domains/entity/domains/loaded-entities/state.ts +12 -0
  9. package/src/async/domains/mirroring/domains/entity/domains/loaded-entity/state.ts +29 -0
  10. package/src/async/domains/mirroring/domains/entity/state.ts +22 -0
  11. package/src/async/domains/mirroring/domains/singleton/coroutines/synchronizers.ts +216 -0
  12. package/src/async/domains/mirroring/domains/singleton/state.ts +57 -0
  13. package/src/async/domains/mirroring/domains/synchronization-result/state.ts +2 -0
  14. package/src/async/domains/mirroring/domains/synchronized-entities/state.ts +23 -0
  15. package/src/async/domains/promise/state.ts +13 -0
  16. package/src/async/domains/synchronized/coroutines/synchronize.ts +62 -0
  17. package/src/async/domains/synchronized/state.ts +20 -0
  18. package/src/async/state.ts +144 -0
  19. package/src/baseEntity/domains/identifiable/state.ts +29 -0
  20. package/src/collections/domains/array/state.ts +16 -0
  21. package/src/collections/domains/immutable/domains/list/state.ts +20 -0
  22. package/src/collections/domains/immutable/domains/map/state.ts +33 -0
  23. package/src/collections/domains/immutable/domains/orderedMap/state.ts +117 -0
  24. package/src/collections/domains/immutable/domains/ordereredSet/state.ts +19 -0
  25. package/src/collections/domains/immutable/domains/set/state.ts +15 -0
  26. package/src/collections/domains/maybe/state.ts +20 -0
  27. package/src/collections/domains/product/state.ts +47 -0
  28. package/src/collections/domains/sum/state.ts +82 -0
  29. package/src/coroutines/builder.tsx +66 -0
  30. package/src/coroutines/state.ts +574 -0
  31. package/src/coroutines/template.tsx +119 -0
  32. package/src/debounced/coroutines/debounce.ts +118 -0
  33. package/src/debounced/state.ts +61 -0
  34. package/src/diagnostics/domains/message-box/state.ts +4 -0
  35. package/src/foreignMutations/state.ts +4 -0
  36. package/src/forms/domains/attachments/views/attachments-view.tsx +22 -0
  37. package/src/forms/domains/collection/domains/reference/state.ts +18 -0
  38. package/src/forms/domains/collection/domains/selection/state.ts +5 -0
  39. package/src/forms/domains/launcher/domains/create/coroutines/runner.ts +60 -0
  40. package/src/forms/domains/launcher/domains/create/state.ts +71 -0
  41. package/src/forms/domains/launcher/domains/create/template.tsx +66 -0
  42. package/src/forms/domains/launcher/domains/edit/coroutines/runner.ts +47 -0
  43. package/src/forms/domains/launcher/domains/edit/state.ts +55 -0
  44. package/src/forms/domains/launcher/domains/edit/template.tsx +39 -0
  45. package/src/forms/domains/parser/domains/validator/state.ts +423 -0
  46. package/src/forms/domains/parser/state.tsx +311 -0
  47. package/src/forms/domains/primitives/domains/boolean/state.ts +13 -0
  48. package/src/forms/domains/primitives/domains/boolean/template.tsx +24 -0
  49. package/src/forms/domains/primitives/domains/date/state.ts +23 -0
  50. package/src/forms/domains/primitives/domains/date/template.tsx +31 -0
  51. package/src/forms/domains/primitives/domains/enum/state.ts +27 -0
  52. package/src/forms/domains/primitives/domains/enum/template.tsx +52 -0
  53. package/src/forms/domains/primitives/domains/enum-multiselect/state.ts +18 -0
  54. package/src/forms/domains/primitives/domains/enum-multiselect/template.tsx +56 -0
  55. package/src/forms/domains/primitives/domains/number/state.ts +8 -0
  56. package/src/forms/domains/primitives/domains/number/template.tsx +26 -0
  57. package/src/forms/domains/primitives/domains/searchable-infinite-stream/state.ts +55 -0
  58. package/src/forms/domains/primitives/domains/searchable-infinite-stream/template.tsx +114 -0
  59. package/src/forms/domains/primitives/domains/searchable-infinite-stream-multiselect/state.ts +27 -0
  60. package/src/forms/domains/primitives/domains/searchable-infinite-stream-multiselect/template.tsx +119 -0
  61. package/src/forms/domains/primitives/domains/string/state.ts +12 -0
  62. package/src/forms/domains/primitives/domains/string/template.tsx +24 -0
  63. package/src/forms/domains/singleton/domains/descriptors/entity/state.ts +9 -0
  64. package/src/forms/domains/singleton/domains/descriptors/field/domains/base/state.ts +5 -0
  65. package/src/forms/domains/singleton/domains/descriptors/field/domains/boolean/state.ts +6 -0
  66. package/src/forms/domains/singleton/domains/descriptors/field/domains/custom/state.ts +21 -0
  67. package/src/forms/domains/singleton/domains/descriptors/field/domains/date/state.ts +6 -0
  68. package/src/forms/domains/singleton/domains/descriptors/field/domains/enum/state.ts +7 -0
  69. package/src/forms/domains/singleton/domains/descriptors/field/domains/infinite-enum/state.ts +8 -0
  70. package/src/forms/domains/singleton/domains/descriptors/field/domains/number/state.ts +7 -0
  71. package/src/forms/domains/singleton/domains/descriptors/field/domains/string/state.ts +7 -0
  72. package/src/forms/domains/singleton/domains/descriptors/field/state.ts +16 -0
  73. package/src/forms/domains/singleton/domains/descriptors/form/state.ts +13 -0
  74. package/src/forms/domains/singleton/domains/form-label/state.ts +2 -0
  75. package/src/forms/domains/singleton/state.ts +87 -0
  76. package/src/forms/domains/singleton/template.tsx +92 -0
  77. package/src/forms/domains/singleton/views/field-views.ts +23 -0
  78. package/src/forms/domains/singleton/views/simple-inputs/base.ts +5 -0
  79. package/src/forms/domains/singleton/views/simple-inputs/boolean.tsx +4 -0
  80. package/src/forms/domains/singleton/views/simple-inputs/date.tsx +4 -0
  81. package/src/forms/domains/singleton/views/simple-inputs/number.tsx +4 -0
  82. package/src/forms/domains/singleton/views/simple-inputs/string.tsx +4 -0
  83. package/src/fun/domains/curry/state.ts +6 -0
  84. package/src/fun/domains/id/state.ts +2 -0
  85. package/src/fun/domains/predicate/domains/bool-expr.ts +98 -0
  86. package/src/fun/domains/predicate/state.ts +29 -0
  87. package/src/fun/domains/simpleCallback/state.ts +3 -0
  88. package/src/fun/domains/uncurry/state.ts +5 -0
  89. package/src/fun/domains/unit/state.ts +3 -0
  90. package/src/fun/domains/updater/domains/caseUpdater/state.ts +44 -0
  91. package/src/fun/domains/updater/domains/mapUpdater/state.ts +36 -0
  92. package/src/fun/domains/updater/domains/maybeUpdater/state.ts +45 -0
  93. package/src/fun/domains/updater/domains/orderedMapUpdater/state.ts +26 -0
  94. package/src/fun/domains/updater/domains/orderedSetUpdater/state.ts +23 -0
  95. package/src/fun/domains/updater/domains/replaceWith/state.ts +3 -0
  96. package/src/fun/domains/updater/domains/simpleUpdater/domains/baseSimpleUpdater/state.ts +18 -0
  97. package/src/fun/domains/updater/domains/simpleUpdater/state.ts +72 -0
  98. package/src/fun/domains/updater/state.ts +86 -0
  99. package/src/fun/state.ts +33 -0
  100. package/src/infinite-data-stream/coroutines/builder.ts +8 -0
  101. package/src/infinite-data-stream/coroutines/infiniteLoader.ts +51 -0
  102. package/src/infinite-data-stream/coroutines/runner.ts +12 -0
  103. package/src/infinite-data-stream/state.ts +175 -0
  104. package/src/infinite-data-stream/template.tsx +16 -0
  105. package/src/math/domains/DOMRect/state.ts +12 -0
  106. package/src/math/domains/number/state.ts +9 -0
  107. package/src/math/domains/rect/state.ts +22 -0
  108. package/src/math/domains/rgba/state.ts +87 -0
  109. package/src/math/domains/size2/state.ts +14 -0
  110. package/src/math/domains/vector2/state.ts +16 -0
  111. package/src/queue/state.ts +42 -0
  112. package/src/state/domains/repository/state.ts +14 -0
  113. package/src/template/state.tsx +198 -0
  114. package/src/validation/state.ts +2 -0
  115. package/src/value/domains/mutable-value/state.ts +13 -0
  116. package/src/value/state.ts +12 -0
  117. package/src/visibility/state.ts +3 -0
  118. 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,4 @@
1
+ export const messageBox = <a>(message:string, value:() => a) : a => {
2
+ alert(message)
3
+ return value()
4
+ }
@@ -0,0 +1,4 @@
1
+ import { BasicUpdater } from "../fun/domains/updater/state";
2
+ import { BasicFun } from "../fun/state";
3
+
4
+ export type ForeignMutationsInput<context, state> = { context:context & state, setState:BasicFun<BasicUpdater<state>, void> }
@@ -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
+ ])