@fieldia/core 0.9.2 → 0.11.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/README.md +199 -0
- package/package.json +1 -1
- package/schema/page.schema.json +1837 -94
- package/src/index.d.mts +1 -0
- package/src/index.d.ts +1 -0
- package/src/index.js +4 -1
- package/src/index.js.map +1 -1
- package/src/index.mjs +1 -0
- package/src/index.mjs.map +1 -1
- package/src/lib/expression/evaluator.d.mts +6 -5
- package/src/lib/expression/evaluator.d.ts +6 -5
- package/src/lib/expression/evaluator.js +37 -22
- package/src/lib/expression/evaluator.js.map +1 -1
- package/src/lib/expression/evaluator.mjs +32 -18
- package/src/lib/expression/evaluator.mjs.map +1 -1
- package/src/lib/expression/expression.d.mts +21 -0
- package/src/lib/expression/expression.d.ts +21 -0
- package/src/lib/expression/expression.js +29 -0
- package/src/lib/expression/expression.js.map +1 -0
- package/src/lib/expression/expression.mjs +26 -0
- package/src/lib/expression/expression.mjs.map +1 -0
- package/src/lib/expression/extractFieldNames.js +6 -1
- package/src/lib/expression/extractFieldNames.js.map +1 -1
- package/src/lib/expression/extractFieldNames.mjs +6 -1
- package/src/lib/expression/extractFieldNames.mjs.map +1 -1
- package/src/lib/expression/functions.d.mts +35 -0
- package/src/lib/expression/functions.d.ts +35 -0
- package/src/lib/expression/functions.js +129 -0
- package/src/lib/expression/functions.js.map +1 -0
- package/src/lib/expression/functions.mjs +124 -0
- package/src/lib/expression/functions.mjs.map +1 -0
- package/src/lib/expression/modifier.d.mts +3 -1
- package/src/lib/expression/modifier.d.ts +3 -1
- package/src/lib/expression/modifier.js +4 -27
- package/src/lib/expression/modifier.js.map +1 -1
- package/src/lib/expression/modifier.mjs +3 -26
- package/src/lib/expression/modifier.mjs.map +1 -1
- package/src/lib/expression/operations.d.mts +25 -0
- package/src/lib/expression/operations.d.ts +25 -0
- package/src/lib/expression/operations.js +81 -0
- package/src/lib/expression/operations.js.map +1 -0
- package/src/lib/expression/operations.mjs +72 -0
- package/src/lib/expression/operations.mjs.map +1 -0
- package/src/lib/expression/order.d.mts +11 -0
- package/src/lib/expression/order.d.ts +11 -0
- package/src/lib/expression/order.js +36 -0
- package/src/lib/expression/order.js.map +1 -0
- package/src/lib/expression/order.mjs +33 -0
- package/src/lib/expression/order.mjs.map +1 -0
- package/src/lib/expression/parser.d.mts +34 -4
- package/src/lib/expression/parser.d.ts +34 -4
- package/src/lib/expression/parser.js +72 -6
- package/src/lib/expression/parser.js.map +1 -1
- package/src/lib/expression/parser.mjs +72 -6
- package/src/lib/expression/parser.mjs.map +1 -1
- package/src/lib/expression/reads.d.mts +8 -0
- package/src/lib/expression/reads.d.ts +8 -0
- package/src/lib/expression/reads.js +48 -0
- package/src/lib/expression/reads.js.map +1 -0
- package/src/lib/expression/reads.mjs +44 -0
- package/src/lib/expression/reads.mjs.map +1 -0
- package/src/lib/expression/tokenizer.d.mts +7 -6
- package/src/lib/expression/tokenizer.d.ts +7 -6
- package/src/lib/expression/tokenizer.js +28 -18
- package/src/lib/expression/tokenizer.js.map +1 -1
- package/src/lib/expression/tokenizer.mjs +28 -18
- package/src/lib/expression/tokenizer.mjs.map +1 -1
- package/src/lib/format/actions.d.mts +692 -0
- package/src/lib/format/actions.d.ts +692 -0
- package/src/lib/format/actions.js +53 -0
- package/src/lib/format/actions.js.map +1 -0
- package/src/lib/format/actions.mjs +50 -0
- package/src/lib/format/actions.mjs.map +1 -0
- package/src/lib/format/field.d.mts +686 -5
- package/src/lib/format/field.d.ts +686 -5
- package/src/lib/format/field.js +117 -13
- package/src/lib/format/field.js.map +1 -1
- package/src/lib/format/field.mjs +116 -12
- package/src/lib/format/field.mjs.map +1 -1
- package/src/lib/format/layout.d.mts +1130 -22
- package/src/lib/format/layout.d.ts +1130 -22
- package/src/lib/format/layout.js +67 -5
- package/src/lib/format/layout.js.map +1 -1
- package/src/lib/format/layout.mjs +66 -4
- package/src/lib/format/layout.mjs.map +1 -1
- package/src/lib/format/layout.types.d.mts +2 -1
- package/src/lib/format/layout.types.d.ts +2 -1
- package/src/lib/format/names.d.mts +1 -1
- package/src/lib/format/names.d.ts +1 -1
- package/src/lib/format/names.js +1 -0
- package/src/lib/format/names.js.map +1 -1
- package/src/lib/format/names.mjs +1 -0
- package/src/lib/format/names.mjs.map +1 -1
- package/src/lib/format/page.d.mts +1007 -7
- package/src/lib/format/page.d.ts +1007 -7
- package/src/lib/format/page.js +28 -0
- package/src/lib/format/page.js.map +1 -1
- package/src/lib/format/page.mjs +28 -0
- package/src/lib/format/page.mjs.map +1 -1
- package/src/lib/format/part-look.d.mts +36 -0
- package/src/lib/format/part-look.d.ts +36 -0
- package/src/lib/format/part-look.js +23 -0
- package/src/lib/format/part-look.js.map +1 -0
- package/src/lib/format/part-look.mjs +20 -0
- package/src/lib/format/part-look.mjs.map +1 -0
- package/src/lib/format/references.d.mts +33 -1
- package/src/lib/format/references.d.ts +33 -1
- package/src/lib/format/references.js +227 -10
- package/src/lib/format/references.js.map +1 -1
- package/src/lib/format/references.mjs +227 -10
- package/src/lib/format/references.mjs.map +1 -1
- package/src/lib/format/translate.d.mts +20 -0
- package/src/lib/format/translate.d.ts +20 -0
- package/src/lib/format/translate.js +79 -21
- package/src/lib/format/translate.js.map +1 -1
- package/src/lib/format/translate.mjs +76 -21
- package/src/lib/format/translate.mjs.map +1 -1
- package/src/lib/record/check.d.mts +19 -1
- package/src/lib/record/check.d.ts +19 -1
- package/src/lib/record/check.js +84 -21
- package/src/lib/record/check.js.map +1 -1
- package/src/lib/record/check.mjs +82 -21
- package/src/lib/record/check.mjs.map +1 -1
- package/src/lib/record/compute.d.mts +24 -0
- package/src/lib/record/compute.d.ts +24 -0
- package/src/lib/record/compute.js +107 -0
- package/src/lib/record/compute.js.map +1 -0
- package/src/lib/record/compute.mjs +103 -0
- package/src/lib/record/compute.mjs.map +1 -0
- package/src/lib/record/data-source.d.mts +14 -2
- package/src/lib/record/data-source.d.ts +14 -2
- package/src/lib/record/data-source.js.map +1 -1
- package/src/lib/record/data-source.mjs.map +1 -1
- package/src/lib/record/env.d.mts +9 -0
- package/src/lib/record/env.d.ts +9 -0
- package/src/lib/record/env.js +28 -0
- package/src/lib/record/env.js.map +1 -0
- package/src/lib/record/env.mjs +25 -0
- package/src/lib/record/env.mjs.map +1 -0
- package/src/lib/record/form.d.mts +110 -18
- package/src/lib/record/form.d.ts +110 -18
- package/src/lib/record/form.js +497 -72
- package/src/lib/record/form.js.map +1 -1
- package/src/lib/record/form.mjs +497 -72
- package/src/lib/record/form.mjs.map +1 -1
- package/src/lib/record/inputs.d.mts +5 -0
- package/src/lib/record/inputs.d.ts +5 -0
- package/src/lib/record/inputs.js +47 -0
- package/src/lib/record/inputs.js.map +1 -0
- package/src/lib/record/inputs.mjs +44 -0
- package/src/lib/record/inputs.mjs.map +1 -0
- package/src/lib/record/limits.d.mts +13 -0
- package/src/lib/record/limits.d.ts +13 -0
- package/src/lib/record/limits.js +45 -0
- package/src/lib/record/limits.js.map +1 -0
- package/src/lib/record/limits.mjs +41 -0
- package/src/lib/record/limits.mjs.map +1 -0
- package/src/lib/record/locales/all.d.mts +7 -0
- package/src/lib/record/locales/all.d.ts +7 -0
- package/src/lib/record/locales/all.js +13 -0
- package/src/lib/record/locales/all.js.map +1 -0
- package/src/lib/record/locales/all.mjs +10 -0
- package/src/lib/record/locales/all.mjs.map +1 -0
- package/src/lib/record/locales/ar.d.mts +3 -0
- package/src/lib/record/locales/ar.d.ts +3 -0
- package/src/lib/record/locales/ar.js +55 -0
- package/src/lib/record/locales/ar.js.map +1 -0
- package/src/lib/record/locales/ar.mjs +52 -0
- package/src/lib/record/locales/ar.mjs.map +1 -0
- package/src/lib/record/locales/de.d.mts +3 -0
- package/src/lib/record/locales/de.d.ts +3 -0
- package/src/lib/record/locales/de.js +55 -0
- package/src/lib/record/locales/de.js.map +1 -0
- package/src/lib/record/locales/de.mjs +52 -0
- package/src/lib/record/locales/de.mjs.map +1 -0
- package/src/lib/record/locales/fr.d.mts +3 -0
- package/src/lib/record/locales/fr.d.ts +3 -0
- package/src/lib/record/locales/fr.js +55 -0
- package/src/lib/record/locales/fr.js.map +1 -0
- package/src/lib/record/locales/fr.mjs +52 -0
- package/src/lib/record/locales/fr.mjs.map +1 -0
- package/src/lib/record/memory-data-source.d.mts +3 -0
- package/src/lib/record/memory-data-source.d.ts +3 -0
- package/src/lib/record/memory-data-source.js +8 -0
- package/src/lib/record/memory-data-source.js.map +1 -1
- package/src/lib/record/memory-data-source.mjs +8 -0
- package/src/lib/record/memory-data-source.mjs.map +1 -1
- package/src/lib/record/messages.d.mts +46 -0
- package/src/lib/record/messages.d.ts +46 -0
- package/src/lib/record/messages.js +58 -101
- package/src/lib/record/messages.js.map +1 -1
- package/src/lib/record/messages.mjs +58 -101
- package/src/lib/record/messages.mjs.map +1 -1
- package/src/lib/record/rules.d.mts +33 -0
- package/src/lib/record/rules.d.ts +33 -0
- package/src/lib/record/rules.js +83 -0
- package/src/lib/record/rules.js.map +1 -0
- package/src/lib/record/rules.mjs +79 -0
- package/src/lib/record/rules.mjs.map +1 -0
- package/src/lib/record/run.d.mts +169 -0
- package/src/lib/record/run.d.ts +169 -0
- package/src/lib/record/run.js +350 -0
- package/src/lib/record/run.js.map +1 -0
- package/src/lib/record/run.mjs +345 -0
- package/src/lib/record/run.mjs.map +1 -0
- package/src/lib/record/score.d.mts +16 -0
- package/src/lib/record/score.d.ts +16 -0
- package/src/lib/record/score.js +35 -0
- package/src/lib/record/score.js.map +1 -0
- package/src/lib/record/score.mjs +32 -0
- package/src/lib/record/score.mjs.map +1 -0
- package/src/lib/record/set-when.d.mts +24 -0
- package/src/lib/record/set-when.d.ts +24 -0
- package/src/lib/record/set-when.js +68 -0
- package/src/lib/record/set-when.js.map +1 -0
- package/src/lib/record/set-when.mjs +65 -0
- package/src/lib/record/set-when.mjs.map +1 -0
- package/src/lib/record/structures.d.mts +14 -0
- package/src/lib/record/structures.d.ts +14 -0
- package/src/lib/record/structures.js +32 -0
- package/src/lib/record/structures.js.map +1 -0
- package/src/lib/record/structures.mjs +28 -0
- package/src/lib/record/structures.mjs.map +1 -0
- package/src/lib/record/values.d.mts +10 -5
- package/src/lib/record/values.d.ts +10 -5
- package/src/lib/record/values.js +20 -6
- package/src/lib/record/values.js.map +1 -1
- package/src/lib/record/values.mjs +19 -6
- package/src/lib/record/values.mjs.map +1 -1
- package/src/runtime.d.mts +13 -7
- package/src/runtime.d.ts +13 -7
- package/src/runtime.js +14 -1
- package/src/runtime.js.map +1 -1
- package/src/runtime.mjs +6 -2
- package/src/runtime.mjs.map +1 -1
|
@@ -1,18 +1,25 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ActionStep } from '../format/actions.mjs';
|
|
2
|
+
import type { Option } from '../format/field.mjs';
|
|
2
3
|
import type { Page } from '../format/page.mjs';
|
|
3
4
|
import { type Messages } from './messages.mjs';
|
|
4
5
|
import { type DataSource, type RecordChanges, type SaveProblem } from './data-source.mjs';
|
|
5
6
|
import { type Scheduler } from './scheduler.mjs';
|
|
7
|
+
import { type ActionHost, type ActionRequest, type ActionResult, type OnAction, type RunContext, type RunResult } from './run.mjs';
|
|
6
8
|
import { type RecordId, type RelatedRecord, type Value, type Values } from './values.mjs';
|
|
7
9
|
export type FormStatus = 'idle' | 'loading' | 'ready' | 'saving' | 'saved' | 'error';
|
|
8
10
|
export interface FormState {
|
|
9
11
|
readonly status: FormStatus;
|
|
10
12
|
readonly recordId: RecordId | null;
|
|
11
13
|
readonly values: Readonly<Values>;
|
|
12
|
-
/** Messages by field name; a line's field is `field.lineKey.subfield`. */
|
|
14
|
+
/** Messages by field name; a line's field is `field.lineKey.subfield`, a saved form's field `name.field`. */
|
|
13
15
|
readonly errors: Readonly<Record<string, string>>;
|
|
14
16
|
/** Fields that differ from the record as loaded or last saved. */
|
|
15
17
|
readonly dirty: readonly string[];
|
|
18
|
+
/**
|
|
19
|
+
* Warnings by field name, from answer rules at the warning level: shown,
|
|
20
|
+
* never blocking. Kept current as values change, for the fields people can see.
|
|
21
|
+
*/
|
|
22
|
+
readonly warnings: Readonly<Record<string, string>>;
|
|
16
23
|
/** A warning from the data source's onchange, shown without blocking. */
|
|
17
24
|
readonly warning: string | null;
|
|
18
25
|
/** The field whose change brought the warning, so it can be shown beside it. */
|
|
@@ -29,6 +36,16 @@ export interface FormState {
|
|
|
29
36
|
readonly skipped: readonly string[];
|
|
30
37
|
/** Why the last save was refused, until the next one starts or the form is reset. */
|
|
31
38
|
readonly saveProblem: SaveProblem | null;
|
|
39
|
+
/** The choices loaded from the app's lists, by field: those of a selection with `optionsFrom`, once asked for. */
|
|
40
|
+
readonly choices: Readonly<Record<string, Choices>>;
|
|
41
|
+
}
|
|
42
|
+
/** A field's choices from one of the app's lists, as they load. */
|
|
43
|
+
export interface Choices {
|
|
44
|
+
/** The choices last loaded: null until the first load ends well. */
|
|
45
|
+
readonly options: Option[] | null;
|
|
46
|
+
readonly loading?: boolean;
|
|
47
|
+
/** Why the last load failed. */
|
|
48
|
+
readonly error?: string;
|
|
32
49
|
}
|
|
33
50
|
/** What a layout element looks like right now. */
|
|
34
51
|
export interface NodeState {
|
|
@@ -42,17 +59,47 @@ export interface DraftStore {
|
|
|
42
59
|
setItem(key: string, value: string): void;
|
|
43
60
|
removeItem(key: string): void;
|
|
44
61
|
}
|
|
45
|
-
export
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
62
|
+
export type { ActionRequest } from './run.mjs';
|
|
63
|
+
/** Whose change it was: a person's (in the UI), a step's, or the app's (`setValues`, or `setValue` told so). Only a person's runs the page's `change` steps. */
|
|
64
|
+
export type ChangeBy = 'person' | 'step' | 'app';
|
|
65
|
+
/** What a form tells the app, by event: `form.on(event, listener)`. */
|
|
66
|
+
export interface FormEvents {
|
|
67
|
+
/** A field was written: by a person, a step or the app. Values worked out from it are not told. */
|
|
68
|
+
change: {
|
|
69
|
+
field: string;
|
|
70
|
+
value: Value;
|
|
71
|
+
values: Readonly<Values>;
|
|
72
|
+
by: ChangeBy;
|
|
73
|
+
};
|
|
74
|
+
/** A record was saved. */
|
|
75
|
+
save: {
|
|
76
|
+
recordId: RecordId | null;
|
|
77
|
+
values: Readonly<Values>;
|
|
78
|
+
};
|
|
79
|
+
/** A page of responses sent its answers: those sent. */
|
|
80
|
+
send: {
|
|
81
|
+
values: Values;
|
|
82
|
+
};
|
|
83
|
+
/** The app answered a call: with nothing (undefined), or an `ActionResult`. */
|
|
84
|
+
action: {
|
|
85
|
+
request: ActionRequest;
|
|
86
|
+
result: ActionResult | undefined;
|
|
87
|
+
};
|
|
88
|
+
/** A run of steps ended — a button's, a moment's, `form.run`'s — and how. */
|
|
89
|
+
run: {
|
|
90
|
+
id: string;
|
|
91
|
+
steps: readonly ActionStep[];
|
|
92
|
+
result: RunResult;
|
|
93
|
+
};
|
|
94
|
+
/** A wizard's step was entered. */
|
|
95
|
+
step: {
|
|
96
|
+
step: string;
|
|
97
|
+
};
|
|
98
|
+
/** The form has its values: loaded, or new. */
|
|
99
|
+
open: {
|
|
100
|
+
recordId: RecordId | null;
|
|
101
|
+
values: Readonly<Values>;
|
|
51
102
|
};
|
|
52
|
-
recordId: RecordId | null;
|
|
53
|
-
values: Values;
|
|
54
|
-
/** The records chosen in a list, when the button belongs to one. */
|
|
55
|
-
recordIds?: RecordId[];
|
|
56
103
|
}
|
|
57
104
|
export interface FormOptions {
|
|
58
105
|
page: Page;
|
|
@@ -61,8 +108,17 @@ export interface FormOptions {
|
|
|
61
108
|
recordId?: RecordId | null;
|
|
62
109
|
/** Values to start with, on top of each field's default. */
|
|
63
110
|
values?: Values;
|
|
64
|
-
/**
|
|
65
|
-
|
|
111
|
+
/**
|
|
112
|
+
* The app's own actions: a button's `action`, and each `call` step. Fieldia
|
|
113
|
+
* hands the request over and waits; the app may answer (`ActionResult`)
|
|
114
|
+
* with values to set, words to say, a page to open, or to stop. Nothing
|
|
115
|
+
* answered means go on.
|
|
116
|
+
*/
|
|
117
|
+
onAction?: OnAction;
|
|
118
|
+
/** The UI the core cannot draw — pages opened, words said, questions asked — given by the viewer. */
|
|
119
|
+
host?: ActionHost;
|
|
120
|
+
/** How a question is asked when there is no host. Without either, a question goes on as though answered Yes. */
|
|
121
|
+
confirm?: (message: string) => boolean | Promise<boolean>;
|
|
66
122
|
drafts?: {
|
|
67
123
|
store: DraftStore;
|
|
68
124
|
restore?: 'auto' | 'ask';
|
|
@@ -74,13 +130,20 @@ export interface FormOptions {
|
|
|
74
130
|
scheduler?: Scheduler;
|
|
75
131
|
/** Validation messages in the page's language. English by default. */
|
|
76
132
|
messages?: Messages;
|
|
133
|
+
/** The clock, for `today()` and for dates that must be past or future. The computer's own when left out. */
|
|
134
|
+
now?: () => Date;
|
|
77
135
|
}
|
|
78
136
|
export interface Form {
|
|
79
137
|
readonly page: Page;
|
|
80
138
|
getState(): FormState;
|
|
81
139
|
subscribe(listener: (state: FormState) => void): () => void;
|
|
82
140
|
load(): Promise<void>;
|
|
83
|
-
|
|
141
|
+
/** Write a field: a person's change unless told otherwise, and only a person's runs the page's `change` steps. */
|
|
142
|
+
setValue(name: string, value: Value, how?: {
|
|
143
|
+
by?: ChangeBy;
|
|
144
|
+
}): void;
|
|
145
|
+
/** Write several fields at once, worked out once: the app's change, never a person's. Throws for a field the page lacks. */
|
|
146
|
+
setValues(values: Values): void;
|
|
84
147
|
node(id: string): NodeState;
|
|
85
148
|
fieldReadonly(name: string): boolean;
|
|
86
149
|
/** What a field would be told if the form were checked now, or null when it passes. Nothing is shown. */
|
|
@@ -100,6 +163,12 @@ export interface Form {
|
|
|
100
163
|
* written: the record, its changes and its autosave stay as they are.
|
|
101
164
|
*/
|
|
102
165
|
previewLine(field: string, key: string, values: Values): Promise<Values>;
|
|
166
|
+
/**
|
|
167
|
+
* Load, or load again, the choices of a selection whose options come from
|
|
168
|
+
* the app's list (`optionsFrom`). Once loaded, they load again by themselves
|
|
169
|
+
* when a field they change with changes; the latest answer wins.
|
|
170
|
+
*/
|
|
171
|
+
loadChoices(field: string): void;
|
|
103
172
|
/** Records a many2one, many2many or reference may point to. A reference needs `options.model`. */
|
|
104
173
|
search(field: string, query: string, limit?: number, options?: {
|
|
105
174
|
model?: string;
|
|
@@ -124,14 +193,37 @@ export interface Form {
|
|
|
124
193
|
* an optional one instead.
|
|
125
194
|
*/
|
|
126
195
|
goTo(step: string): boolean;
|
|
127
|
-
/**
|
|
196
|
+
/**
|
|
197
|
+
* Press a button: ask its `confirm` first, run its `steps`, then call its
|
|
198
|
+
* `action`, when it names one, as a final `call`. A list's button passes
|
|
199
|
+
* the records chosen in it, and every call of the run carries them.
|
|
200
|
+
*/
|
|
128
201
|
runAction(id: string, chosen?: {
|
|
129
202
|
recordIds: RecordId[];
|
|
130
|
-
}): Promise<
|
|
203
|
+
}): Promise<RunResult>;
|
|
204
|
+
/** Run steps on this form, as a button would: `context.id` is what their calls carry (`run` when left out). */
|
|
205
|
+
run(steps: readonly ActionStep[], context?: RunContext): Promise<RunResult>;
|
|
206
|
+
/** A tab was shown: the viewer says so, and the page's `show` steps for it run. */
|
|
207
|
+
shown(tab: string): void;
|
|
208
|
+
/** Listen to one of the form's events; returns what stops listening. */
|
|
209
|
+
on<E extends keyof FormEvents>(event: E, listener: (event: FormEvents[E]) => void): () => void;
|
|
131
210
|
restoreDraft(): void;
|
|
132
211
|
discardDraft(): void;
|
|
133
|
-
/** Resolves once every onchange, save and
|
|
212
|
+
/** Resolves once every onchange, save, autosave and run of steps started so far has finished — a question still waiting for its answer included. */
|
|
134
213
|
settled(): Promise<void>;
|
|
214
|
+
/**
|
|
215
|
+
* Give a saved form placed on this page (a `form` part, by its id) its page,
|
|
216
|
+
* once the app has found it. From then its answers sit under the part's
|
|
217
|
+
* name as an object, checked by that page's own rules: this form refuses
|
|
218
|
+
* to save or send while one of them fails, with the error under
|
|
219
|
+
* `name.field`, and shows it on the inner form beside its field. Returns the
|
|
220
|
+
* inner form, which a view draws the part's fields from; giving the page
|
|
221
|
+
* again returns the same one. A page placed inside itself, directly or
|
|
222
|
+
* through others, is refused.
|
|
223
|
+
*/
|
|
224
|
+
embed(id: string, page: Page): Form;
|
|
225
|
+
/** The inner form of a saved form placed on this page, once it has its page; null until then. */
|
|
226
|
+
embedded(id: string): Form | null;
|
|
135
227
|
dispose(): void;
|
|
136
228
|
}
|
|
137
229
|
/**
|
package/src/lib/record/form.d.ts
CHANGED
|
@@ -1,18 +1,25 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ActionStep } from '../format/actions';
|
|
2
|
+
import type { Option } from '../format/field';
|
|
2
3
|
import type { Page } from '../format/page';
|
|
3
4
|
import { type Messages } from './messages';
|
|
4
5
|
import { type DataSource, type RecordChanges, type SaveProblem } from './data-source';
|
|
5
6
|
import { type Scheduler } from './scheduler';
|
|
7
|
+
import { type ActionHost, type ActionRequest, type ActionResult, type OnAction, type RunContext, type RunResult } from './run';
|
|
6
8
|
import { type RecordId, type RelatedRecord, type Value, type Values } from './values';
|
|
7
9
|
export type FormStatus = 'idle' | 'loading' | 'ready' | 'saving' | 'saved' | 'error';
|
|
8
10
|
export interface FormState {
|
|
9
11
|
readonly status: FormStatus;
|
|
10
12
|
readonly recordId: RecordId | null;
|
|
11
13
|
readonly values: Readonly<Values>;
|
|
12
|
-
/** Messages by field name; a line's field is `field.lineKey.subfield`. */
|
|
14
|
+
/** Messages by field name; a line's field is `field.lineKey.subfield`, a saved form's field `name.field`. */
|
|
13
15
|
readonly errors: Readonly<Record<string, string>>;
|
|
14
16
|
/** Fields that differ from the record as loaded or last saved. */
|
|
15
17
|
readonly dirty: readonly string[];
|
|
18
|
+
/**
|
|
19
|
+
* Warnings by field name, from answer rules at the warning level: shown,
|
|
20
|
+
* never blocking. Kept current as values change, for the fields people can see.
|
|
21
|
+
*/
|
|
22
|
+
readonly warnings: Readonly<Record<string, string>>;
|
|
16
23
|
/** A warning from the data source's onchange, shown without blocking. */
|
|
17
24
|
readonly warning: string | null;
|
|
18
25
|
/** The field whose change brought the warning, so it can be shown beside it. */
|
|
@@ -29,6 +36,16 @@ export interface FormState {
|
|
|
29
36
|
readonly skipped: readonly string[];
|
|
30
37
|
/** Why the last save was refused, until the next one starts or the form is reset. */
|
|
31
38
|
readonly saveProblem: SaveProblem | null;
|
|
39
|
+
/** The choices loaded from the app's lists, by field: those of a selection with `optionsFrom`, once asked for. */
|
|
40
|
+
readonly choices: Readonly<Record<string, Choices>>;
|
|
41
|
+
}
|
|
42
|
+
/** A field's choices from one of the app's lists, as they load. */
|
|
43
|
+
export interface Choices {
|
|
44
|
+
/** The choices last loaded: null until the first load ends well. */
|
|
45
|
+
readonly options: Option[] | null;
|
|
46
|
+
readonly loading?: boolean;
|
|
47
|
+
/** Why the last load failed. */
|
|
48
|
+
readonly error?: string;
|
|
32
49
|
}
|
|
33
50
|
/** What a layout element looks like right now. */
|
|
34
51
|
export interface NodeState {
|
|
@@ -42,17 +59,47 @@ export interface DraftStore {
|
|
|
42
59
|
setItem(key: string, value: string): void;
|
|
43
60
|
removeItem(key: string): void;
|
|
44
61
|
}
|
|
45
|
-
export
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
62
|
+
export type { ActionRequest } from './run';
|
|
63
|
+
/** Whose change it was: a person's (in the UI), a step's, or the app's (`setValues`, or `setValue` told so). Only a person's runs the page's `change` steps. */
|
|
64
|
+
export type ChangeBy = 'person' | 'step' | 'app';
|
|
65
|
+
/** What a form tells the app, by event: `form.on(event, listener)`. */
|
|
66
|
+
export interface FormEvents {
|
|
67
|
+
/** A field was written: by a person, a step or the app. Values worked out from it are not told. */
|
|
68
|
+
change: {
|
|
69
|
+
field: string;
|
|
70
|
+
value: Value;
|
|
71
|
+
values: Readonly<Values>;
|
|
72
|
+
by: ChangeBy;
|
|
73
|
+
};
|
|
74
|
+
/** A record was saved. */
|
|
75
|
+
save: {
|
|
76
|
+
recordId: RecordId | null;
|
|
77
|
+
values: Readonly<Values>;
|
|
78
|
+
};
|
|
79
|
+
/** A page of responses sent its answers: those sent. */
|
|
80
|
+
send: {
|
|
81
|
+
values: Values;
|
|
82
|
+
};
|
|
83
|
+
/** The app answered a call: with nothing (undefined), or an `ActionResult`. */
|
|
84
|
+
action: {
|
|
85
|
+
request: ActionRequest;
|
|
86
|
+
result: ActionResult | undefined;
|
|
87
|
+
};
|
|
88
|
+
/** A run of steps ended — a button's, a moment's, `form.run`'s — and how. */
|
|
89
|
+
run: {
|
|
90
|
+
id: string;
|
|
91
|
+
steps: readonly ActionStep[];
|
|
92
|
+
result: RunResult;
|
|
93
|
+
};
|
|
94
|
+
/** A wizard's step was entered. */
|
|
95
|
+
step: {
|
|
96
|
+
step: string;
|
|
97
|
+
};
|
|
98
|
+
/** The form has its values: loaded, or new. */
|
|
99
|
+
open: {
|
|
100
|
+
recordId: RecordId | null;
|
|
101
|
+
values: Readonly<Values>;
|
|
51
102
|
};
|
|
52
|
-
recordId: RecordId | null;
|
|
53
|
-
values: Values;
|
|
54
|
-
/** The records chosen in a list, when the button belongs to one. */
|
|
55
|
-
recordIds?: RecordId[];
|
|
56
103
|
}
|
|
57
104
|
export interface FormOptions {
|
|
58
105
|
page: Page;
|
|
@@ -61,8 +108,17 @@ export interface FormOptions {
|
|
|
61
108
|
recordId?: RecordId | null;
|
|
62
109
|
/** Values to start with, on top of each field's default. */
|
|
63
110
|
values?: Values;
|
|
64
|
-
/**
|
|
65
|
-
|
|
111
|
+
/**
|
|
112
|
+
* The app's own actions: a button's `action`, and each `call` step. Fieldia
|
|
113
|
+
* hands the request over and waits; the app may answer (`ActionResult`)
|
|
114
|
+
* with values to set, words to say, a page to open, or to stop. Nothing
|
|
115
|
+
* answered means go on.
|
|
116
|
+
*/
|
|
117
|
+
onAction?: OnAction;
|
|
118
|
+
/** The UI the core cannot draw — pages opened, words said, questions asked — given by the viewer. */
|
|
119
|
+
host?: ActionHost;
|
|
120
|
+
/** How a question is asked when there is no host. Without either, a question goes on as though answered Yes. */
|
|
121
|
+
confirm?: (message: string) => boolean | Promise<boolean>;
|
|
66
122
|
drafts?: {
|
|
67
123
|
store: DraftStore;
|
|
68
124
|
restore?: 'auto' | 'ask';
|
|
@@ -74,13 +130,20 @@ export interface FormOptions {
|
|
|
74
130
|
scheduler?: Scheduler;
|
|
75
131
|
/** Validation messages in the page's language. English by default. */
|
|
76
132
|
messages?: Messages;
|
|
133
|
+
/** The clock, for `today()` and for dates that must be past or future. The computer's own when left out. */
|
|
134
|
+
now?: () => Date;
|
|
77
135
|
}
|
|
78
136
|
export interface Form {
|
|
79
137
|
readonly page: Page;
|
|
80
138
|
getState(): FormState;
|
|
81
139
|
subscribe(listener: (state: FormState) => void): () => void;
|
|
82
140
|
load(): Promise<void>;
|
|
83
|
-
|
|
141
|
+
/** Write a field: a person's change unless told otherwise, and only a person's runs the page's `change` steps. */
|
|
142
|
+
setValue(name: string, value: Value, how?: {
|
|
143
|
+
by?: ChangeBy;
|
|
144
|
+
}): void;
|
|
145
|
+
/** Write several fields at once, worked out once: the app's change, never a person's. Throws for a field the page lacks. */
|
|
146
|
+
setValues(values: Values): void;
|
|
84
147
|
node(id: string): NodeState;
|
|
85
148
|
fieldReadonly(name: string): boolean;
|
|
86
149
|
/** What a field would be told if the form were checked now, or null when it passes. Nothing is shown. */
|
|
@@ -100,6 +163,12 @@ export interface Form {
|
|
|
100
163
|
* written: the record, its changes and its autosave stay as they are.
|
|
101
164
|
*/
|
|
102
165
|
previewLine(field: string, key: string, values: Values): Promise<Values>;
|
|
166
|
+
/**
|
|
167
|
+
* Load, or load again, the choices of a selection whose options come from
|
|
168
|
+
* the app's list (`optionsFrom`). Once loaded, they load again by themselves
|
|
169
|
+
* when a field they change with changes; the latest answer wins.
|
|
170
|
+
*/
|
|
171
|
+
loadChoices(field: string): void;
|
|
103
172
|
/** Records a many2one, many2many or reference may point to. A reference needs `options.model`. */
|
|
104
173
|
search(field: string, query: string, limit?: number, options?: {
|
|
105
174
|
model?: string;
|
|
@@ -124,14 +193,37 @@ export interface Form {
|
|
|
124
193
|
* an optional one instead.
|
|
125
194
|
*/
|
|
126
195
|
goTo(step: string): boolean;
|
|
127
|
-
/**
|
|
196
|
+
/**
|
|
197
|
+
* Press a button: ask its `confirm` first, run its `steps`, then call its
|
|
198
|
+
* `action`, when it names one, as a final `call`. A list's button passes
|
|
199
|
+
* the records chosen in it, and every call of the run carries them.
|
|
200
|
+
*/
|
|
128
201
|
runAction(id: string, chosen?: {
|
|
129
202
|
recordIds: RecordId[];
|
|
130
|
-
}): Promise<
|
|
203
|
+
}): Promise<RunResult>;
|
|
204
|
+
/** Run steps on this form, as a button would: `context.id` is what their calls carry (`run` when left out). */
|
|
205
|
+
run(steps: readonly ActionStep[], context?: RunContext): Promise<RunResult>;
|
|
206
|
+
/** A tab was shown: the viewer says so, and the page's `show` steps for it run. */
|
|
207
|
+
shown(tab: string): void;
|
|
208
|
+
/** Listen to one of the form's events; returns what stops listening. */
|
|
209
|
+
on<E extends keyof FormEvents>(event: E, listener: (event: FormEvents[E]) => void): () => void;
|
|
131
210
|
restoreDraft(): void;
|
|
132
211
|
discardDraft(): void;
|
|
133
|
-
/** Resolves once every onchange, save and
|
|
212
|
+
/** Resolves once every onchange, save, autosave and run of steps started so far has finished — a question still waiting for its answer included. */
|
|
134
213
|
settled(): Promise<void>;
|
|
214
|
+
/**
|
|
215
|
+
* Give a saved form placed on this page (a `form` part, by its id) its page,
|
|
216
|
+
* once the app has found it. From then its answers sit under the part's
|
|
217
|
+
* name as an object, checked by that page's own rules: this form refuses
|
|
218
|
+
* to save or send while one of them fails, with the error under
|
|
219
|
+
* `name.field`, and shows it on the inner form beside its field. Returns the
|
|
220
|
+
* inner form, which a view draws the part's fields from; giving the page
|
|
221
|
+
* again returns the same one. A page placed inside itself, directly or
|
|
222
|
+
* through others, is refused.
|
|
223
|
+
*/
|
|
224
|
+
embed(id: string, page: Page): Form;
|
|
225
|
+
/** The inner form of a saved form placed on this page, once it has its page; null until then. */
|
|
226
|
+
embedded(id: string): Form | null;
|
|
135
227
|
dispose(): void;
|
|
136
228
|
}
|
|
137
229
|
/**
|