@fieldia/core 0.11.0 → 0.13.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 +5 -0
- package/package.json +1 -1
- package/schema/page.schema.json +3998 -893
- package/src/index.d.mts +1 -1
- package/src/index.d.ts +1 -1
- package/src/index.js +4 -1
- package/src/index.js.map +1 -1
- package/src/index.mjs +1 -1
- package/src/index.mjs.map +1 -1
- package/src/lib/expression/built-ins.d.mts +4 -0
- package/src/lib/expression/built-ins.d.ts +4 -0
- package/src/lib/expression/built-ins.js +8 -0
- package/src/lib/expression/built-ins.js.map +1 -0
- package/src/lib/expression/built-ins.mjs +5 -0
- package/src/lib/expression/built-ins.mjs.map +1 -0
- package/src/lib/expression/evaluator.js +17 -7
- package/src/lib/expression/evaluator.js.map +1 -1
- package/src/lib/expression/evaluator.mjs +17 -7
- package/src/lib/expression/evaluator.mjs.map +1 -1
- package/src/lib/expression/expression.d.mts +7 -0
- package/src/lib/expression/expression.d.ts +7 -0
- package/src/lib/expression/expression.js +2 -0
- package/src/lib/expression/expression.js.map +1 -1
- package/src/lib/expression/expression.mjs +3 -1
- package/src/lib/expression/expression.mjs.map +1 -1
- package/src/lib/expression/functions.d.mts +2 -0
- package/src/lib/expression/functions.d.ts +2 -0
- package/src/lib/expression/functions.js +38 -5
- package/src/lib/expression/functions.js.map +1 -1
- package/src/lib/expression/functions.mjs +38 -5
- package/src/lib/expression/functions.mjs.map +1 -1
- package/src/lib/expression/modifier.d.mts +7 -0
- package/src/lib/expression/modifier.d.ts +7 -0
- package/src/lib/expression/modifier.js +3 -1
- package/src/lib/expression/modifier.js.map +1 -1
- package/src/lib/expression/modifier.mjs +4 -2
- package/src/lib/expression/modifier.mjs.map +1 -1
- package/src/lib/expression/parser.d.mts +8 -1
- package/src/lib/expression/parser.d.ts +8 -1
- package/src/lib/expression/parser.js +30 -2
- package/src/lib/expression/parser.js.map +1 -1
- package/src/lib/expression/parser.mjs +30 -2
- package/src/lib/expression/parser.mjs.map +1 -1
- package/src/lib/expression/reads.d.mts +7 -0
- package/src/lib/expression/reads.d.ts +7 -0
- package/src/lib/expression/reads.js +23 -0
- package/src/lib/expression/reads.js.map +1 -1
- package/src/lib/expression/reads.mjs +21 -0
- package/src/lib/expression/reads.mjs.map +1 -1
- package/src/lib/format/actions.d.mts +486 -2
- package/src/lib/format/actions.d.ts +486 -2
- package/src/lib/format/actions.js +9 -0
- package/src/lib/format/actions.js.map +1 -1
- package/src/lib/format/actions.mjs +9 -0
- package/src/lib/format/actions.mjs.map +1 -1
- package/src/lib/format/check-page.d.mts +7 -2
- package/src/lib/format/check-page.d.ts +7 -2
- package/src/lib/format/check-page.js +6 -3
- package/src/lib/format/check-page.js.map +1 -1
- package/src/lib/format/check-page.mjs +6 -3
- package/src/lib/format/check-page.mjs.map +1 -1
- package/src/lib/format/field.d.mts +3285 -214
- package/src/lib/format/field.d.ts +3285 -214
- package/src/lib/format/field.js +101 -14
- package/src/lib/format/field.js.map +1 -1
- package/src/lib/format/field.mjs +100 -13
- package/src/lib/format/field.mjs.map +1 -1
- package/src/lib/format/hotkeys.d.mts +6 -0
- package/src/lib/format/hotkeys.d.ts +6 -0
- package/src/lib/format/hotkeys.js +11 -0
- package/src/lib/format/hotkeys.js.map +1 -0
- package/src/lib/format/hotkeys.mjs +7 -0
- package/src/lib/format/hotkeys.mjs.map +1 -0
- package/src/lib/format/layout.d.mts +4185 -352
- package/src/lib/format/layout.d.ts +4185 -352
- package/src/lib/format/layout.js +131 -20
- package/src/lib/format/layout.js.map +1 -1
- package/src/lib/format/layout.mjs +130 -19
- package/src/lib/format/layout.mjs.map +1 -1
- package/src/lib/format/page.d.mts +2938 -196
- package/src/lib/format/page.d.ts +2938 -196
- package/src/lib/format/page.js +7 -1
- package/src/lib/format/page.js.map +1 -1
- package/src/lib/format/page.mjs +6 -1
- package/src/lib/format/page.mjs.map +1 -1
- package/src/lib/format/references.d.mts +35 -2
- package/src/lib/format/references.d.ts +35 -2
- package/src/lib/format/references.js +321 -43
- package/src/lib/format/references.js.map +1 -1
- package/src/lib/format/references.mjs +321 -43
- package/src/lib/format/references.mjs.map +1 -1
- package/src/lib/format/themes.d.mts +9 -0
- package/src/lib/format/themes.d.ts +9 -0
- package/src/lib/format/themes.js +13 -0
- package/src/lib/format/themes.js.map +1 -0
- package/src/lib/format/themes.mjs +10 -0
- package/src/lib/format/themes.mjs.map +1 -0
- package/src/lib/format/translate.js +5 -3
- package/src/lib/format/translate.js.map +1 -1
- package/src/lib/format/translate.mjs +5 -3
- package/src/lib/format/translate.mjs.map +1 -1
- package/src/lib/format/words.d.mts +9 -0
- package/src/lib/format/words.d.ts +9 -0
- package/src/lib/format/words.js +35 -0
- package/src/lib/format/words.js.map +1 -0
- package/src/lib/format/words.mjs +30 -0
- package/src/lib/format/words.mjs.map +1 -0
- package/src/lib/record/cells.d.mts +37 -0
- package/src/lib/record/cells.d.ts +37 -0
- package/src/lib/record/cells.js +59 -0
- package/src/lib/record/cells.js.map +1 -0
- package/src/lib/record/cells.mjs +54 -0
- package/src/lib/record/cells.mjs.map +1 -0
- package/src/lib/record/compute.d.mts +5 -4
- package/src/lib/record/compute.d.ts +5 -4
- package/src/lib/record/compute.js +11 -10
- package/src/lib/record/compute.js.map +1 -1
- package/src/lib/record/compute.mjs +12 -11
- package/src/lib/record/compute.mjs.map +1 -1
- package/src/lib/record/data-source.d.mts +32 -2
- package/src/lib/record/data-source.d.ts +32 -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 +38 -1
- package/src/lib/record/env.d.ts +38 -1
- package/src/lib/record/env.js +41 -3
- package/src/lib/record/env.js.map +1 -1
- package/src/lib/record/env.mjs +38 -3
- package/src/lib/record/env.mjs.map +1 -1
- package/src/lib/record/filter.d.mts +10 -0
- package/src/lib/record/filter.d.ts +10 -0
- package/src/lib/record/filter.js +49 -0
- package/src/lib/record/filter.js.map +1 -1
- package/src/lib/record/filter.mjs +48 -0
- package/src/lib/record/filter.mjs.map +1 -1
- package/src/lib/record/form.d.mts +143 -8
- package/src/lib/record/form.d.ts +143 -8
- package/src/lib/record/form.js +495 -58
- package/src/lib/record/form.js.map +1 -1
- package/src/lib/record/form.mjs +496 -60
- package/src/lib/record/form.mjs.map +1 -1
- package/src/lib/record/locales/ar.js +3 -0
- package/src/lib/record/locales/ar.js.map +1 -1
- package/src/lib/record/locales/ar.mjs +3 -0
- package/src/lib/record/locales/ar.mjs.map +1 -1
- package/src/lib/record/locales/de.js +3 -0
- package/src/lib/record/locales/de.js.map +1 -1
- package/src/lib/record/locales/de.mjs +3 -0
- package/src/lib/record/locales/de.mjs.map +1 -1
- package/src/lib/record/locales/fr.js +3 -0
- package/src/lib/record/locales/fr.js.map +1 -1
- package/src/lib/record/locales/fr.mjs +3 -0
- package/src/lib/record/locales/fr.mjs.map +1 -1
- package/src/lib/record/memory-data-source.d.mts +26 -2
- package/src/lib/record/memory-data-source.d.ts +26 -2
- package/src/lib/record/memory-data-source.js +98 -7
- package/src/lib/record/memory-data-source.js.map +1 -1
- package/src/lib/record/memory-data-source.mjs +98 -7
- package/src/lib/record/memory-data-source.mjs.map +1 -1
- package/src/lib/record/messages.d.mts +5 -0
- package/src/lib/record/messages.d.ts +5 -0
- package/src/lib/record/messages.js +3 -0
- package/src/lib/record/messages.js.map +1 -1
- package/src/lib/record/messages.mjs +3 -0
- package/src/lib/record/messages.mjs.map +1 -1
- package/src/lib/record/roles.d.mts +10 -0
- package/src/lib/record/roles.d.ts +10 -0
- package/src/lib/record/roles.js +21 -0
- package/src/lib/record/roles.js.map +1 -0
- package/src/lib/record/roles.mjs +18 -0
- package/src/lib/record/roles.mjs.map +1 -0
- package/src/lib/record/rules.js +30 -0
- package/src/lib/record/rules.js.map +1 -1
- package/src/lib/record/rules.mjs +31 -1
- package/src/lib/record/rules.mjs.map +1 -1
- package/src/lib/record/run.d.mts +79 -0
- package/src/lib/record/run.d.ts +79 -0
- package/src/lib/record/run.js +105 -0
- package/src/lib/record/run.js.map +1 -1
- package/src/lib/record/run.mjs +105 -0
- package/src/lib/record/run.mjs.map +1 -1
- package/src/lib/record/set-when.d.mts +5 -2
- package/src/lib/record/set-when.d.ts +5 -2
- package/src/lib/record/set-when.js +28 -12
- package/src/lib/record/set-when.js.map +1 -1
- package/src/lib/record/set-when.mjs +30 -14
- package/src/lib/record/set-when.mjs.map +1 -1
- package/src/lib/record/value-map.d.mts +41 -0
- package/src/lib/record/value-map.d.ts +41 -0
- package/src/lib/record/value-map.js +80 -0
- package/src/lib/record/value-map.js.map +1 -0
- package/src/lib/record/value-map.mjs +73 -0
- package/src/lib/record/value-map.mjs.map +1 -0
- package/src/lib/record/values.d.mts +10 -0
- package/src/lib/record/values.d.ts +10 -0
- package/src/lib/record/values.js.map +1 -1
- package/src/lib/record/values.mjs.map +1 -1
- package/src/runtime.d.mts +11 -6
- package/src/runtime.d.ts +11 -6
- package/src/runtime.js +13 -1
- package/src/runtime.js.map +1 -1
- package/src/runtime.mjs +4 -0
- package/src/runtime.mjs.map +1 -1
package/src/lib/record/filter.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.matchesFilter = matchesFilter;
|
|
4
|
+
exports.resolveFilter = resolveFilter;
|
|
4
5
|
/**
|
|
5
6
|
* Whether a record passes a filter, the way a search should judge it: every
|
|
6
7
|
* item of the list holds, an `any` group when one of its items holds, an `all`
|
|
@@ -20,6 +21,12 @@ function holds(values, item) {
|
|
|
20
21
|
function isSet(value) {
|
|
21
22
|
return value !== null && value !== undefined && value !== '' && value !== false && !(Array.isArray(value) && !value.length);
|
|
22
23
|
}
|
|
24
|
+
/** The ids a list of links holds: records by their id, ids as they are. */
|
|
25
|
+
function idsIn(value) {
|
|
26
|
+
if (!Array.isArray(value))
|
|
27
|
+
return [];
|
|
28
|
+
return value.map((item) => (item !== null && typeof item === 'object' && 'id' in item ? item.id : item));
|
|
29
|
+
}
|
|
23
30
|
function meets(values, condition) {
|
|
24
31
|
const raw = values[condition.field];
|
|
25
32
|
// A link compares by its id.
|
|
@@ -30,6 +37,15 @@ function meets(values, condition) {
|
|
|
30
37
|
switch (condition.op) {
|
|
31
38
|
case '=':
|
|
32
39
|
return actual === expected;
|
|
40
|
+
case '=?':
|
|
41
|
+
return !isSet(expected) || actual === expected;
|
|
42
|
+
case 'contains':
|
|
43
|
+
case 'not contains': {
|
|
44
|
+
const held = idsIn(raw);
|
|
45
|
+
const wanted = Array.isArray(expected) ? expected : [expected];
|
|
46
|
+
const found = wanted.some((value) => held.includes(value));
|
|
47
|
+
return condition.op === 'contains' ? found : !found;
|
|
48
|
+
}
|
|
33
49
|
case '!=':
|
|
34
50
|
return actual !== expected;
|
|
35
51
|
case '<':
|
|
@@ -64,4 +80,37 @@ function meets(values, condition) {
|
|
|
64
80
|
}
|
|
65
81
|
}
|
|
66
82
|
}
|
|
83
|
+
/** What a condition left out stands for: it always holds. */
|
|
84
|
+
const ALWAYS = null;
|
|
85
|
+
/**
|
|
86
|
+
* A filter as a search or a list receives it: each `valueFrom` — a field, or a
|
|
87
|
+
* path such as `parent.company_id` or `user.id` — replaced by its value in
|
|
88
|
+
* `context`. A `=?` whose value is empty is left out, and so is a group it
|
|
89
|
+
* makes always hold: an `any` group with one item always holding, an `all`
|
|
90
|
+
* group left with none. A `=?` with a value goes as `=`, so a data source
|
|
91
|
+
* never sees `=?`.
|
|
92
|
+
*/
|
|
93
|
+
function resolveFilter(items, context) {
|
|
94
|
+
const resolve = (item) => {
|
|
95
|
+
if ('any' in item) {
|
|
96
|
+
const inner = item.any.map(resolve);
|
|
97
|
+
return inner.includes(ALWAYS) ? ALWAYS : { any: inner };
|
|
98
|
+
}
|
|
99
|
+
if ('all' in item) {
|
|
100
|
+
const inner = item.all.map(resolve).filter((r) => r !== ALWAYS);
|
|
101
|
+
return inner.length ? { all: inner } : ALWAYS;
|
|
102
|
+
}
|
|
103
|
+
const value = (item.valueFrom !== undefined ? valueAt(context, item.valueFrom) : (item.value ?? null));
|
|
104
|
+
if (item.op === '=?')
|
|
105
|
+
return isSet(value) ? { field: item.field, op: '=', value } : ALWAYS;
|
|
106
|
+
return { field: item.field, op: item.op, value };
|
|
107
|
+
};
|
|
108
|
+
return items.map(resolve).filter((r) => r !== ALWAYS);
|
|
109
|
+
}
|
|
110
|
+
function valueAt(context, path) {
|
|
111
|
+
let at = context;
|
|
112
|
+
for (const part of path.split('.'))
|
|
113
|
+
at = at === null || typeof at !== 'object' ? undefined : at[part];
|
|
114
|
+
return at ?? null;
|
|
115
|
+
}
|
|
67
116
|
//# sourceMappingURL=filter.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filter.js","sourceRoot":"","sources":["../../../../../../libs/core/src/lib/record/filter.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"filter.js","sourceRoot":"","sources":["../../../../../../libs/core/src/lib/record/filter.ts"],"names":[],"mappings":"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"}
|
|
@@ -17,6 +17,12 @@ function holds(values, item) {
|
|
|
17
17
|
function isSet(value) {
|
|
18
18
|
return value !== null && value !== undefined && value !== '' && value !== false && !(Array.isArray(value) && !value.length);
|
|
19
19
|
}
|
|
20
|
+
/** The ids a list of links holds: records by their id, ids as they are. */
|
|
21
|
+
function idsIn(value) {
|
|
22
|
+
if (!Array.isArray(value))
|
|
23
|
+
return [];
|
|
24
|
+
return value.map((item) => (item !== null && typeof item === 'object' && 'id' in item ? item.id : item));
|
|
25
|
+
}
|
|
20
26
|
function meets(values, condition) {
|
|
21
27
|
const raw = values[condition.field];
|
|
22
28
|
// A link compares by its id.
|
|
@@ -27,6 +33,15 @@ function meets(values, condition) {
|
|
|
27
33
|
switch (condition.op) {
|
|
28
34
|
case '=':
|
|
29
35
|
return actual === expected;
|
|
36
|
+
case '=?':
|
|
37
|
+
return !isSet(expected) || actual === expected;
|
|
38
|
+
case 'contains':
|
|
39
|
+
case 'not contains': {
|
|
40
|
+
const held = idsIn(raw);
|
|
41
|
+
const wanted = Array.isArray(expected) ? expected : [expected];
|
|
42
|
+
const found = wanted.some((value) => held.includes(value));
|
|
43
|
+
return condition.op === 'contains' ? found : !found;
|
|
44
|
+
}
|
|
30
45
|
case '!=':
|
|
31
46
|
return actual !== expected;
|
|
32
47
|
case '<':
|
|
@@ -61,4 +76,37 @@ function meets(values, condition) {
|
|
|
61
76
|
}
|
|
62
77
|
}
|
|
63
78
|
}
|
|
79
|
+
/** What a condition left out stands for: it always holds. */
|
|
80
|
+
const ALWAYS = null;
|
|
81
|
+
/**
|
|
82
|
+
* A filter as a search or a list receives it: each `valueFrom` — a field, or a
|
|
83
|
+
* path such as `parent.company_id` or `user.id` — replaced by its value in
|
|
84
|
+
* `context`. A `=?` whose value is empty is left out, and so is a group it
|
|
85
|
+
* makes always hold: an `any` group with one item always holding, an `all`
|
|
86
|
+
* group left with none. A `=?` with a value goes as `=`, so a data source
|
|
87
|
+
* never sees `=?`.
|
|
88
|
+
*/
|
|
89
|
+
export function resolveFilter(items, context) {
|
|
90
|
+
const resolve = (item) => {
|
|
91
|
+
if ('any' in item) {
|
|
92
|
+
const inner = item.any.map(resolve);
|
|
93
|
+
return inner.includes(ALWAYS) ? ALWAYS : { any: inner };
|
|
94
|
+
}
|
|
95
|
+
if ('all' in item) {
|
|
96
|
+
const inner = item.all.map(resolve).filter((r) => r !== ALWAYS);
|
|
97
|
+
return inner.length ? { all: inner } : ALWAYS;
|
|
98
|
+
}
|
|
99
|
+
const value = (item.valueFrom !== undefined ? valueAt(context, item.valueFrom) : (item.value ?? null));
|
|
100
|
+
if (item.op === '=?')
|
|
101
|
+
return isSet(value) ? { field: item.field, op: '=', value } : ALWAYS;
|
|
102
|
+
return { field: item.field, op: item.op, value };
|
|
103
|
+
};
|
|
104
|
+
return items.map(resolve).filter((r) => r !== ALWAYS);
|
|
105
|
+
}
|
|
106
|
+
function valueAt(context, path) {
|
|
107
|
+
let at = context;
|
|
108
|
+
for (const part of path.split('.'))
|
|
109
|
+
at = at === null || typeof at !== 'object' ? undefined : at[part];
|
|
110
|
+
return at ?? null;
|
|
111
|
+
}
|
|
64
112
|
//# sourceMappingURL=filter.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filter.mjs","sourceRoot":"","sources":["../../../../../../libs/core/src/lib/record/filter.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"filter.mjs","sourceRoot":"","sources":["../../../../../../libs/core/src/lib/record/filter.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type { ActionStep } from '../format/actions.mjs';
|
|
2
|
-
import type { Option } from '../format/field.mjs';
|
|
2
|
+
import type { Field, LineField, Option, PropertyDefinition } from '../format/field.mjs';
|
|
3
3
|
import type { Page } from '../format/page.mjs';
|
|
4
|
+
import type { JsonValue } from '../format/json.mjs';
|
|
5
|
+
import { type FieldTone, type LineState } from './cells.mjs';
|
|
4
6
|
import { type Messages } from './messages.mjs';
|
|
5
7
|
import { type DataSource, type RecordChanges, type SaveProblem } from './data-source.mjs';
|
|
6
8
|
import { type Scheduler } from './scheduler.mjs';
|
|
7
|
-
import { type ActionHost, type ActionRequest, type ActionResult, type OnAction, type RunContext, type RunResult } from './run.mjs';
|
|
9
|
+
import { type ActionHost, type ActionRequest, type ActionResult, type OnAction, type PostedMessage, type RunContext, type RunResult } from './run.mjs';
|
|
8
10
|
import { type RecordId, type RelatedRecord, type Value, type Values } from './values.mjs';
|
|
9
11
|
export type FormStatus = 'idle' | 'loading' | 'ready' | 'saving' | 'saved' | 'error';
|
|
10
12
|
export interface FormState {
|
|
@@ -38,6 +40,16 @@ export interface FormState {
|
|
|
38
40
|
readonly saveProblem: SaveProblem | null;
|
|
39
41
|
/** The choices loaded from the app's lists, by field: those of a selection with `optionsFrom`, once asked for. */
|
|
40
42
|
readonly choices: Readonly<Record<string, Choices>>;
|
|
43
|
+
/** The definitions loaded for a properties field with `definitionsFrom`, by field, once asked for. */
|
|
44
|
+
readonly definitions: Readonly<Record<string, Definitions>>;
|
|
45
|
+
}
|
|
46
|
+
/** A properties field's definitions from the app, as they load. */
|
|
47
|
+
export interface Definitions {
|
|
48
|
+
/** The definitions last loaded: null until the first load ends well. */
|
|
49
|
+
readonly definitions: PropertyDefinition[] | null;
|
|
50
|
+
readonly loading?: boolean;
|
|
51
|
+
/** Why the last load failed. */
|
|
52
|
+
readonly error?: string;
|
|
41
53
|
}
|
|
42
54
|
/** A field's choices from one of the app's lists, as they load. */
|
|
43
55
|
export interface Choices {
|
|
@@ -100,6 +112,28 @@ export interface FormEvents {
|
|
|
100
112
|
recordId: RecordId | null;
|
|
101
113
|
values: Readonly<Values>;
|
|
102
114
|
};
|
|
115
|
+
/** The record was archived (`archived` true) or brought back, by a step. */
|
|
116
|
+
archive: {
|
|
117
|
+
recordId: RecordId;
|
|
118
|
+
archived: boolean;
|
|
119
|
+
};
|
|
120
|
+
/** The record was copied, by a step: the form shows the copy now. */
|
|
121
|
+
duplicate: {
|
|
122
|
+
from: RecordId;
|
|
123
|
+
recordId: RecordId;
|
|
124
|
+
};
|
|
125
|
+
/**
|
|
126
|
+
* The record was deleted, by a step. A listener may show another record
|
|
127
|
+
* (`openRecord`), as a pager moves on; else the form starts a new one.
|
|
128
|
+
*/
|
|
129
|
+
delete: {
|
|
130
|
+
recordId: RecordId;
|
|
131
|
+
};
|
|
132
|
+
/** Words to post in the record's conversation, from a `post` step or the app's answer: the chatter beside it listens. */
|
|
133
|
+
post: {
|
|
134
|
+
recordId: RecordId | null;
|
|
135
|
+
message: PostedMessage;
|
|
136
|
+
};
|
|
103
137
|
}
|
|
104
138
|
export interface FormOptions {
|
|
105
139
|
page: Page;
|
|
@@ -132,6 +166,40 @@ export interface FormOptions {
|
|
|
132
166
|
messages?: Messages;
|
|
133
167
|
/** The clock, for `today()` and for dates that must be past or future. The computer's own when left out. */
|
|
134
168
|
now?: () => Date;
|
|
169
|
+
/**
|
|
170
|
+
* The person using the form: conditions read it as `user` (`user.id`,
|
|
171
|
+
* `user.name`, `user.roles`), and a part with `roles` shows only to people
|
|
172
|
+
* in them. Left out, the person has no id and no roles.
|
|
173
|
+
*/
|
|
174
|
+
user?: FormUser;
|
|
175
|
+
/** Whether the record starts being edited, as conditions read `editing`; true unless said. A view locking it says so with `setEditing`. */
|
|
176
|
+
editing?: boolean;
|
|
177
|
+
/**
|
|
178
|
+
* Values the app passes in, as Flectra's context: read by conditions and
|
|
179
|
+
* expressions as `context.name`, and by a filter's `valueFrom:
|
|
180
|
+
* "context.name"` — a transfer opened from Receipts restricting its
|
|
181
|
+
* operation types. They are the app's, never the page's: nothing saves
|
|
182
|
+
* them, and a page's field of that name comes first.
|
|
183
|
+
*/
|
|
184
|
+
context?: Readonly<Record<string, JsonValue>>;
|
|
185
|
+
/**
|
|
186
|
+
* The record this form's values are a line of, when the form edits one
|
|
187
|
+
* table's line in a form of its own (a node's `lineForm`): its values and
|
|
188
|
+
* fields, read by conditions and filters as `parent`, as a line's are —
|
|
189
|
+
* `parent.state`, a filter's `valueFrom: "parent.company_id"`.
|
|
190
|
+
*/
|
|
191
|
+
parent?: {
|
|
192
|
+
values: Readonly<Values>;
|
|
193
|
+
fields: Readonly<Record<string, Field | LineField>>;
|
|
194
|
+
id?: RecordId | null;
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
/** The person using a form, as the app knows them. */
|
|
198
|
+
export interface FormUser {
|
|
199
|
+
id: RecordId | null;
|
|
200
|
+
name?: string;
|
|
201
|
+
/** The roles they hold, by the names the page's `roles` use: Flectra's groups, such as `sales_team.group_sale_manager`. */
|
|
202
|
+
roles?: readonly string[];
|
|
135
203
|
}
|
|
136
204
|
export interface Form {
|
|
137
205
|
readonly page: Page;
|
|
@@ -145,6 +213,8 @@ export interface Form {
|
|
|
145
213
|
/** Write several fields at once, worked out once: the app's change, never a person's. Throws for a field the page lacks. */
|
|
146
214
|
setValues(values: Values): void;
|
|
147
215
|
node(id: string): NodeState;
|
|
216
|
+
/** Whether the record is being edited, as conditions read `editing`: a view that locks it to be read says false, and parts follow. */
|
|
217
|
+
setEditing(editing: boolean): void;
|
|
148
218
|
fieldReadonly(name: string): boolean;
|
|
149
219
|
/** What a field would be told if the form were checked now, or null when it passes. Nothing is shown. */
|
|
150
220
|
problem(field: string): string | null;
|
|
@@ -163,25 +233,68 @@ export interface Form {
|
|
|
163
233
|
* written: the record, its changes and its autosave stay as they are.
|
|
164
234
|
*/
|
|
165
235
|
previewLine(field: string, key: string, values: Values): Promise<Values>;
|
|
236
|
+
/**
|
|
237
|
+
* Show another record of the page's model in this form, loaded from the
|
|
238
|
+
* data source as a pager moves to it, or a new one for null. Changes not
|
|
239
|
+
* saved are dropped: save first. The page's `open` steps run for it.
|
|
240
|
+
*/
|
|
241
|
+
openRecord(id: RecordId | null): Promise<void>;
|
|
242
|
+
/**
|
|
243
|
+
* Post words in the record's conversation, as a `post` step does: through
|
|
244
|
+
* the form's host when it has a way to, else told to the form's own
|
|
245
|
+
* listeners (`post`). False when nothing heard them.
|
|
246
|
+
*/
|
|
247
|
+
post(message: PostedMessage): Promise<boolean>;
|
|
166
248
|
/**
|
|
167
249
|
* Load, or load again, the choices of a selection whose options come from
|
|
168
250
|
* the app's list (`optionsFrom`). Once loaded, they load again by themselves
|
|
169
251
|
* when a field they change with changes; the latest answer wins.
|
|
170
252
|
*/
|
|
171
253
|
loadChoices(field: string): void;
|
|
172
|
-
/**
|
|
254
|
+
/**
|
|
255
|
+
* Load, or load again, the definitions of a properties field whose
|
|
256
|
+
* definitions come from a linked record (`definitionsFrom`). Once loaded,
|
|
257
|
+
* they load again by themselves when a field they change with changes.
|
|
258
|
+
*/
|
|
259
|
+
loadDefinitions(field: string): void;
|
|
260
|
+
/** A property added in place to such a field: kept in its definitions, and handed to the data source's `saveDefinitions`. */
|
|
261
|
+
addDefinition(field: string, definition: PropertyDefinition): Promise<void>;
|
|
262
|
+
/**
|
|
263
|
+
* Records a many2one, many2many or reference may point to. A reference needs
|
|
264
|
+
* `options.model`, and so does structured data naming records of a model (a
|
|
265
|
+
* json field, such as an analytic distribution's accounts); `options.ids`
|
|
266
|
+
* finds those records alone, for their names.
|
|
267
|
+
*/
|
|
173
268
|
search(field: string, query: string, limit?: number, options?: {
|
|
174
269
|
model?: string;
|
|
270
|
+
ids?: RecordId[];
|
|
175
271
|
}): Promise<RelatedRecord[]>;
|
|
176
|
-
/** The same, for a relation inside a one2many line, filtered by that line's values. */
|
|
177
|
-
searchLine(field: string, key: string, subfield: string, query: string, limit?: number
|
|
272
|
+
/** The same, for a relation inside a one2many line, filtered by that line's values — or structured data of a line naming records of a model (`options.model`). */
|
|
273
|
+
searchLine(field: string, key: string, subfield: string, query: string, limit?: number, options?: {
|
|
274
|
+
model?: string;
|
|
275
|
+
ids?: RecordId[];
|
|
276
|
+
}): Promise<RelatedRecord[]>;
|
|
277
|
+
/**
|
|
278
|
+
* The values of records a many2many links to, for a table of them: `fields`
|
|
279
|
+
* of the records with these ids, in the order asked, read with the data
|
|
280
|
+
* source's `list`. None without one.
|
|
281
|
+
*/
|
|
282
|
+
linkedValues(field: string, ids: readonly RecordId[], fields: readonly string[]): Promise<{
|
|
283
|
+
id: RecordId;
|
|
284
|
+
values: Values;
|
|
285
|
+
}[]>;
|
|
178
286
|
/** Whether a link field can make a record from a typed name: the data source must be able to. */
|
|
179
287
|
canCreate(field: string): boolean;
|
|
180
|
-
/** Make a record from a typed name, for the link field to point to. */
|
|
288
|
+
/** Make a record from a typed name, for the link field to point to, with what its `createValues` hand on. */
|
|
181
289
|
quickCreate(field: string, name: string): Promise<RelatedRecord>;
|
|
182
|
-
/** The same, for a link inside a one2many's lines. */
|
|
290
|
+
/** The same, for a link inside a one2many's lines; with the line's key, its `createValues` are read on that line. */
|
|
183
291
|
canCreateLine(field: string, subfield: string): boolean;
|
|
184
|
-
quickCreateLine(field: string, subfield: string, name: string): Promise<RelatedRecord>;
|
|
292
|
+
quickCreateLine(field: string, subfield: string, name: string, key?: string): Promise<RelatedRecord>;
|
|
293
|
+
/** What a record made from a link starts with besides its name: its `createValues`, read now — on a line, on that line. */
|
|
294
|
+
createValues(field: string, line?: {
|
|
295
|
+
lines: string;
|
|
296
|
+
key: string;
|
|
297
|
+
}): Values;
|
|
185
298
|
steps(): string[];
|
|
186
299
|
next(): boolean;
|
|
187
300
|
back(): boolean;
|
|
@@ -201,6 +314,26 @@ export interface Form {
|
|
|
201
314
|
runAction(id: string, chosen?: {
|
|
202
315
|
recordIds: RecordId[];
|
|
203
316
|
}): Promise<RunResult>;
|
|
317
|
+
/** A field's own value as its tones have it now: its tone and whether it is bold, read on the record. */
|
|
318
|
+
fieldTone(nodeId: string): FieldTone;
|
|
319
|
+
/** A table's line as its rules have it now — its tone, each ruled column's cells, which of its buttons show — by the table's field node. */
|
|
320
|
+
lineState(nodeId: string, key: string): LineState;
|
|
321
|
+
/** Whether a table's column is hidden now by its `hidden` rule, read on the record. */
|
|
322
|
+
columnHidden(nodeId: string, column: string): boolean;
|
|
323
|
+
/**
|
|
324
|
+
* The words a field's empty box shows now, by its node (`#title` for a
|
|
325
|
+
* sheet's title): its first `placeholderWhen` that holds, else its
|
|
326
|
+
* `placeholder`; undefined when it has neither.
|
|
327
|
+
*/
|
|
328
|
+
placeholder(nodeId: string): string | undefined;
|
|
329
|
+
/** Press a button on a table's line: its confirmation, its steps, then its action, each call carrying the line. Hidden on that line, it does not run. */
|
|
330
|
+
runRowAction(nodeId: string, buttonId: string, key: string): Promise<RunResult>;
|
|
331
|
+
/**
|
|
332
|
+
* Press a button for the lines chosen in a table (its `selectedButtons`),
|
|
333
|
+
* by their keys: its confirmation, its steps, then its action, each call
|
|
334
|
+
* carrying the lines. Hidden, or with no line chosen, it does not run.
|
|
335
|
+
*/
|
|
336
|
+
runLinesAction(nodeId: string, buttonId: string, keys: readonly string[]): Promise<RunResult>;
|
|
204
337
|
/** Run steps on this form, as a button would: `context.id` is what their calls carry (`run` when left out). */
|
|
205
338
|
run(steps: readonly ActionStep[], context?: RunContext): Promise<RunResult>;
|
|
206
339
|
/** A tab was shown: the viewer says so, and the page's `show` steps for it run. */
|
|
@@ -232,3 +365,5 @@ export interface Form {
|
|
|
232
365
|
* binding renders from `getState()` and calls these methods.
|
|
233
366
|
*/
|
|
234
367
|
export declare function createForm(options: FormOptions): Form;
|
|
368
|
+
/** Words held where a number goes ("4.001234567.5"): a box the person typed in and the browser could not read as one. */
|
|
369
|
+
export declare function notANumber(def: Field | undefined, value: unknown): boolean;
|
package/src/lib/record/form.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type { ActionStep } from '../format/actions';
|
|
2
|
-
import type { Option } from '../format/field';
|
|
2
|
+
import type { Field, LineField, Option, PropertyDefinition } from '../format/field';
|
|
3
3
|
import type { Page } from '../format/page';
|
|
4
|
+
import type { JsonValue } from '../format/json';
|
|
5
|
+
import { type FieldTone, type LineState } from './cells';
|
|
4
6
|
import { type Messages } from './messages';
|
|
5
7
|
import { type DataSource, type RecordChanges, type SaveProblem } from './data-source';
|
|
6
8
|
import { type Scheduler } from './scheduler';
|
|
7
|
-
import { type ActionHost, type ActionRequest, type ActionResult, type OnAction, type RunContext, type RunResult } from './run';
|
|
9
|
+
import { type ActionHost, type ActionRequest, type ActionResult, type OnAction, type PostedMessage, type RunContext, type RunResult } from './run';
|
|
8
10
|
import { type RecordId, type RelatedRecord, type Value, type Values } from './values';
|
|
9
11
|
export type FormStatus = 'idle' | 'loading' | 'ready' | 'saving' | 'saved' | 'error';
|
|
10
12
|
export interface FormState {
|
|
@@ -38,6 +40,16 @@ export interface FormState {
|
|
|
38
40
|
readonly saveProblem: SaveProblem | null;
|
|
39
41
|
/** The choices loaded from the app's lists, by field: those of a selection with `optionsFrom`, once asked for. */
|
|
40
42
|
readonly choices: Readonly<Record<string, Choices>>;
|
|
43
|
+
/** The definitions loaded for a properties field with `definitionsFrom`, by field, once asked for. */
|
|
44
|
+
readonly definitions: Readonly<Record<string, Definitions>>;
|
|
45
|
+
}
|
|
46
|
+
/** A properties field's definitions from the app, as they load. */
|
|
47
|
+
export interface Definitions {
|
|
48
|
+
/** The definitions last loaded: null until the first load ends well. */
|
|
49
|
+
readonly definitions: PropertyDefinition[] | null;
|
|
50
|
+
readonly loading?: boolean;
|
|
51
|
+
/** Why the last load failed. */
|
|
52
|
+
readonly error?: string;
|
|
41
53
|
}
|
|
42
54
|
/** A field's choices from one of the app's lists, as they load. */
|
|
43
55
|
export interface Choices {
|
|
@@ -100,6 +112,28 @@ export interface FormEvents {
|
|
|
100
112
|
recordId: RecordId | null;
|
|
101
113
|
values: Readonly<Values>;
|
|
102
114
|
};
|
|
115
|
+
/** The record was archived (`archived` true) or brought back, by a step. */
|
|
116
|
+
archive: {
|
|
117
|
+
recordId: RecordId;
|
|
118
|
+
archived: boolean;
|
|
119
|
+
};
|
|
120
|
+
/** The record was copied, by a step: the form shows the copy now. */
|
|
121
|
+
duplicate: {
|
|
122
|
+
from: RecordId;
|
|
123
|
+
recordId: RecordId;
|
|
124
|
+
};
|
|
125
|
+
/**
|
|
126
|
+
* The record was deleted, by a step. A listener may show another record
|
|
127
|
+
* (`openRecord`), as a pager moves on; else the form starts a new one.
|
|
128
|
+
*/
|
|
129
|
+
delete: {
|
|
130
|
+
recordId: RecordId;
|
|
131
|
+
};
|
|
132
|
+
/** Words to post in the record's conversation, from a `post` step or the app's answer: the chatter beside it listens. */
|
|
133
|
+
post: {
|
|
134
|
+
recordId: RecordId | null;
|
|
135
|
+
message: PostedMessage;
|
|
136
|
+
};
|
|
103
137
|
}
|
|
104
138
|
export interface FormOptions {
|
|
105
139
|
page: Page;
|
|
@@ -132,6 +166,40 @@ export interface FormOptions {
|
|
|
132
166
|
messages?: Messages;
|
|
133
167
|
/** The clock, for `today()` and for dates that must be past or future. The computer's own when left out. */
|
|
134
168
|
now?: () => Date;
|
|
169
|
+
/**
|
|
170
|
+
* The person using the form: conditions read it as `user` (`user.id`,
|
|
171
|
+
* `user.name`, `user.roles`), and a part with `roles` shows only to people
|
|
172
|
+
* in them. Left out, the person has no id and no roles.
|
|
173
|
+
*/
|
|
174
|
+
user?: FormUser;
|
|
175
|
+
/** Whether the record starts being edited, as conditions read `editing`; true unless said. A view locking it says so with `setEditing`. */
|
|
176
|
+
editing?: boolean;
|
|
177
|
+
/**
|
|
178
|
+
* Values the app passes in, as Flectra's context: read by conditions and
|
|
179
|
+
* expressions as `context.name`, and by a filter's `valueFrom:
|
|
180
|
+
* "context.name"` — a transfer opened from Receipts restricting its
|
|
181
|
+
* operation types. They are the app's, never the page's: nothing saves
|
|
182
|
+
* them, and a page's field of that name comes first.
|
|
183
|
+
*/
|
|
184
|
+
context?: Readonly<Record<string, JsonValue>>;
|
|
185
|
+
/**
|
|
186
|
+
* The record this form's values are a line of, when the form edits one
|
|
187
|
+
* table's line in a form of its own (a node's `lineForm`): its values and
|
|
188
|
+
* fields, read by conditions and filters as `parent`, as a line's are —
|
|
189
|
+
* `parent.state`, a filter's `valueFrom: "parent.company_id"`.
|
|
190
|
+
*/
|
|
191
|
+
parent?: {
|
|
192
|
+
values: Readonly<Values>;
|
|
193
|
+
fields: Readonly<Record<string, Field | LineField>>;
|
|
194
|
+
id?: RecordId | null;
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
/** The person using a form, as the app knows them. */
|
|
198
|
+
export interface FormUser {
|
|
199
|
+
id: RecordId | null;
|
|
200
|
+
name?: string;
|
|
201
|
+
/** The roles they hold, by the names the page's `roles` use: Flectra's groups, such as `sales_team.group_sale_manager`. */
|
|
202
|
+
roles?: readonly string[];
|
|
135
203
|
}
|
|
136
204
|
export interface Form {
|
|
137
205
|
readonly page: Page;
|
|
@@ -145,6 +213,8 @@ export interface Form {
|
|
|
145
213
|
/** Write several fields at once, worked out once: the app's change, never a person's. Throws for a field the page lacks. */
|
|
146
214
|
setValues(values: Values): void;
|
|
147
215
|
node(id: string): NodeState;
|
|
216
|
+
/** Whether the record is being edited, as conditions read `editing`: a view that locks it to be read says false, and parts follow. */
|
|
217
|
+
setEditing(editing: boolean): void;
|
|
148
218
|
fieldReadonly(name: string): boolean;
|
|
149
219
|
/** What a field would be told if the form were checked now, or null when it passes. Nothing is shown. */
|
|
150
220
|
problem(field: string): string | null;
|
|
@@ -163,25 +233,68 @@ export interface Form {
|
|
|
163
233
|
* written: the record, its changes and its autosave stay as they are.
|
|
164
234
|
*/
|
|
165
235
|
previewLine(field: string, key: string, values: Values): Promise<Values>;
|
|
236
|
+
/**
|
|
237
|
+
* Show another record of the page's model in this form, loaded from the
|
|
238
|
+
* data source as a pager moves to it, or a new one for null. Changes not
|
|
239
|
+
* saved are dropped: save first. The page's `open` steps run for it.
|
|
240
|
+
*/
|
|
241
|
+
openRecord(id: RecordId | null): Promise<void>;
|
|
242
|
+
/**
|
|
243
|
+
* Post words in the record's conversation, as a `post` step does: through
|
|
244
|
+
* the form's host when it has a way to, else told to the form's own
|
|
245
|
+
* listeners (`post`). False when nothing heard them.
|
|
246
|
+
*/
|
|
247
|
+
post(message: PostedMessage): Promise<boolean>;
|
|
166
248
|
/**
|
|
167
249
|
* Load, or load again, the choices of a selection whose options come from
|
|
168
250
|
* the app's list (`optionsFrom`). Once loaded, they load again by themselves
|
|
169
251
|
* when a field they change with changes; the latest answer wins.
|
|
170
252
|
*/
|
|
171
253
|
loadChoices(field: string): void;
|
|
172
|
-
/**
|
|
254
|
+
/**
|
|
255
|
+
* Load, or load again, the definitions of a properties field whose
|
|
256
|
+
* definitions come from a linked record (`definitionsFrom`). Once loaded,
|
|
257
|
+
* they load again by themselves when a field they change with changes.
|
|
258
|
+
*/
|
|
259
|
+
loadDefinitions(field: string): void;
|
|
260
|
+
/** A property added in place to such a field: kept in its definitions, and handed to the data source's `saveDefinitions`. */
|
|
261
|
+
addDefinition(field: string, definition: PropertyDefinition): Promise<void>;
|
|
262
|
+
/**
|
|
263
|
+
* Records a many2one, many2many or reference may point to. A reference needs
|
|
264
|
+
* `options.model`, and so does structured data naming records of a model (a
|
|
265
|
+
* json field, such as an analytic distribution's accounts); `options.ids`
|
|
266
|
+
* finds those records alone, for their names.
|
|
267
|
+
*/
|
|
173
268
|
search(field: string, query: string, limit?: number, options?: {
|
|
174
269
|
model?: string;
|
|
270
|
+
ids?: RecordId[];
|
|
175
271
|
}): Promise<RelatedRecord[]>;
|
|
176
|
-
/** The same, for a relation inside a one2many line, filtered by that line's values. */
|
|
177
|
-
searchLine(field: string, key: string, subfield: string, query: string, limit?: number
|
|
272
|
+
/** The same, for a relation inside a one2many line, filtered by that line's values — or structured data of a line naming records of a model (`options.model`). */
|
|
273
|
+
searchLine(field: string, key: string, subfield: string, query: string, limit?: number, options?: {
|
|
274
|
+
model?: string;
|
|
275
|
+
ids?: RecordId[];
|
|
276
|
+
}): Promise<RelatedRecord[]>;
|
|
277
|
+
/**
|
|
278
|
+
* The values of records a many2many links to, for a table of them: `fields`
|
|
279
|
+
* of the records with these ids, in the order asked, read with the data
|
|
280
|
+
* source's `list`. None without one.
|
|
281
|
+
*/
|
|
282
|
+
linkedValues(field: string, ids: readonly RecordId[], fields: readonly string[]): Promise<{
|
|
283
|
+
id: RecordId;
|
|
284
|
+
values: Values;
|
|
285
|
+
}[]>;
|
|
178
286
|
/** Whether a link field can make a record from a typed name: the data source must be able to. */
|
|
179
287
|
canCreate(field: string): boolean;
|
|
180
|
-
/** Make a record from a typed name, for the link field to point to. */
|
|
288
|
+
/** Make a record from a typed name, for the link field to point to, with what its `createValues` hand on. */
|
|
181
289
|
quickCreate(field: string, name: string): Promise<RelatedRecord>;
|
|
182
|
-
/** The same, for a link inside a one2many's lines. */
|
|
290
|
+
/** The same, for a link inside a one2many's lines; with the line's key, its `createValues` are read on that line. */
|
|
183
291
|
canCreateLine(field: string, subfield: string): boolean;
|
|
184
|
-
quickCreateLine(field: string, subfield: string, name: string): Promise<RelatedRecord>;
|
|
292
|
+
quickCreateLine(field: string, subfield: string, name: string, key?: string): Promise<RelatedRecord>;
|
|
293
|
+
/** What a record made from a link starts with besides its name: its `createValues`, read now — on a line, on that line. */
|
|
294
|
+
createValues(field: string, line?: {
|
|
295
|
+
lines: string;
|
|
296
|
+
key: string;
|
|
297
|
+
}): Values;
|
|
185
298
|
steps(): string[];
|
|
186
299
|
next(): boolean;
|
|
187
300
|
back(): boolean;
|
|
@@ -201,6 +314,26 @@ export interface Form {
|
|
|
201
314
|
runAction(id: string, chosen?: {
|
|
202
315
|
recordIds: RecordId[];
|
|
203
316
|
}): Promise<RunResult>;
|
|
317
|
+
/** A field's own value as its tones have it now: its tone and whether it is bold, read on the record. */
|
|
318
|
+
fieldTone(nodeId: string): FieldTone;
|
|
319
|
+
/** A table's line as its rules have it now — its tone, each ruled column's cells, which of its buttons show — by the table's field node. */
|
|
320
|
+
lineState(nodeId: string, key: string): LineState;
|
|
321
|
+
/** Whether a table's column is hidden now by its `hidden` rule, read on the record. */
|
|
322
|
+
columnHidden(nodeId: string, column: string): boolean;
|
|
323
|
+
/**
|
|
324
|
+
* The words a field's empty box shows now, by its node (`#title` for a
|
|
325
|
+
* sheet's title): its first `placeholderWhen` that holds, else its
|
|
326
|
+
* `placeholder`; undefined when it has neither.
|
|
327
|
+
*/
|
|
328
|
+
placeholder(nodeId: string): string | undefined;
|
|
329
|
+
/** Press a button on a table's line: its confirmation, its steps, then its action, each call carrying the line. Hidden on that line, it does not run. */
|
|
330
|
+
runRowAction(nodeId: string, buttonId: string, key: string): Promise<RunResult>;
|
|
331
|
+
/**
|
|
332
|
+
* Press a button for the lines chosen in a table (its `selectedButtons`),
|
|
333
|
+
* by their keys: its confirmation, its steps, then its action, each call
|
|
334
|
+
* carrying the lines. Hidden, or with no line chosen, it does not run.
|
|
335
|
+
*/
|
|
336
|
+
runLinesAction(nodeId: string, buttonId: string, keys: readonly string[]): Promise<RunResult>;
|
|
204
337
|
/** Run steps on this form, as a button would: `context.id` is what their calls carry (`run` when left out). */
|
|
205
338
|
run(steps: readonly ActionStep[], context?: RunContext): Promise<RunResult>;
|
|
206
339
|
/** A tab was shown: the viewer says so, and the page's `show` steps for it run. */
|
|
@@ -232,3 +365,5 @@ export interface Form {
|
|
|
232
365
|
* binding renders from `getState()` and calls these methods.
|
|
233
366
|
*/
|
|
234
367
|
export declare function createForm(options: FormOptions): Form;
|
|
368
|
+
/** Words held where a number goes ("4.001234567.5"): a box the person typed in and the browser could not read as one. */
|
|
369
|
+
export declare function notANumber(def: Field | undefined, value: unknown): boolean;
|