@fieldia/core 0.10.0 → 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.
Files changed (67) hide show
  1. package/README.md +199 -0
  2. package/package.json +1 -1
  3. package/schema/page.schema.json +829 -125
  4. package/src/index.d.mts +1 -0
  5. package/src/index.d.ts +1 -0
  6. package/src/index.js +4 -1
  7. package/src/index.js.map +1 -1
  8. package/src/index.mjs +1 -0
  9. package/src/index.mjs.map +1 -1
  10. package/src/lib/format/actions.d.mts +692 -0
  11. package/src/lib/format/actions.d.ts +692 -0
  12. package/src/lib/format/actions.js +53 -0
  13. package/src/lib/format/actions.js.map +1 -0
  14. package/src/lib/format/actions.mjs +50 -0
  15. package/src/lib/format/actions.mjs.map +1 -0
  16. package/src/lib/format/field.js +4 -2
  17. package/src/lib/format/field.js.map +1 -1
  18. package/src/lib/format/field.mjs +4 -2
  19. package/src/lib/format/field.mjs.map +1 -1
  20. package/src/lib/format/layout.d.mts +745 -15
  21. package/src/lib/format/layout.d.ts +745 -15
  22. package/src/lib/format/layout.js +18 -3
  23. package/src/lib/format/layout.js.map +1 -1
  24. package/src/lib/format/layout.mjs +17 -2
  25. package/src/lib/format/layout.mjs.map +1 -1
  26. package/src/lib/format/layout.types.d.mts +2 -1
  27. package/src/lib/format/layout.types.d.ts +2 -1
  28. package/src/lib/format/page.d.mts +616 -3
  29. package/src/lib/format/page.d.ts +616 -3
  30. package/src/lib/format/page.js +17 -2
  31. package/src/lib/format/page.js.map +1 -1
  32. package/src/lib/format/page.mjs +17 -2
  33. package/src/lib/format/page.mjs.map +1 -1
  34. package/src/lib/format/part-look.d.mts +36 -0
  35. package/src/lib/format/part-look.d.ts +36 -0
  36. package/src/lib/format/part-look.js +23 -0
  37. package/src/lib/format/part-look.js.map +1 -0
  38. package/src/lib/format/part-look.mjs +20 -0
  39. package/src/lib/format/part-look.mjs.map +1 -0
  40. package/src/lib/format/references.d.mts +19 -0
  41. package/src/lib/format/references.d.ts +19 -0
  42. package/src/lib/format/references.js +131 -2
  43. package/src/lib/format/references.js.map +1 -1
  44. package/src/lib/format/references.mjs +131 -2
  45. package/src/lib/format/references.mjs.map +1 -1
  46. package/src/lib/record/data-source.d.mts +5 -1
  47. package/src/lib/record/data-source.d.ts +5 -1
  48. package/src/lib/record/data-source.js.map +1 -1
  49. package/src/lib/record/data-source.mjs.map +1 -1
  50. package/src/lib/record/form.d.mts +86 -18
  51. package/src/lib/record/form.d.ts +86 -18
  52. package/src/lib/record/form.js +343 -46
  53. package/src/lib/record/form.js.map +1 -1
  54. package/src/lib/record/form.mjs +343 -46
  55. package/src/lib/record/form.mjs.map +1 -1
  56. package/src/lib/record/run.d.mts +169 -0
  57. package/src/lib/record/run.d.ts +169 -0
  58. package/src/lib/record/run.js +350 -0
  59. package/src/lib/record/run.js.map +1 -0
  60. package/src/lib/record/run.mjs +345 -0
  61. package/src/lib/record/run.mjs.map +1 -0
  62. package/src/runtime.d.mts +5 -2
  63. package/src/runtime.d.ts +5 -2
  64. package/src/runtime.js +3 -1
  65. package/src/runtime.js.map +1 -1
  66. package/src/runtime.mjs +1 -0
  67. package/src/runtime.mjs.map +1 -1
@@ -1,16 +1,17 @@
1
+ import type { ActionStep } from '../format/actions';
1
2
  import type { Option } from '../format/field';
2
- import type { JsonValue } from '../format/json';
3
3
  import type { Page } from '../format/page';
4
4
  import { type Messages } from './messages';
5
5
  import { type DataSource, type RecordChanges, type SaveProblem } from './data-source';
6
6
  import { type Scheduler } from './scheduler';
7
+ import { type ActionHost, type ActionRequest, type ActionResult, type OnAction, type RunContext, type RunResult } from './run';
7
8
  import { type RecordId, type RelatedRecord, type Value, type Values } from './values';
8
9
  export type FormStatus = 'idle' | 'loading' | 'ready' | 'saving' | 'saved' | 'error';
9
10
  export interface FormState {
10
11
  readonly status: FormStatus;
11
12
  readonly recordId: RecordId | null;
12
13
  readonly values: Readonly<Values>;
13
- /** 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`. */
14
15
  readonly errors: Readonly<Record<string, string>>;
15
16
  /** Fields that differ from the record as loaded or last saved. */
16
17
  readonly dirty: readonly string[];
@@ -58,17 +59,47 @@ export interface DraftStore {
58
59
  setItem(key: string, value: string): void;
59
60
  removeItem(key: string): void;
60
61
  }
61
- export interface ActionRequest {
62
- /** The button or stat button pressed. */
63
- id: string;
64
- action: string;
65
- params?: {
66
- [key: string]: JsonValue;
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>;
67
102
  };
68
- recordId: RecordId | null;
69
- values: Values;
70
- /** The records chosen in a list, when the button belongs to one. */
71
- recordIds?: RecordId[];
72
103
  }
73
104
  export interface FormOptions {
74
105
  page: Page;
@@ -77,8 +108,17 @@ export interface FormOptions {
77
108
  recordId?: RecordId | null;
78
109
  /** Values to start with, on top of each field's default. */
79
110
  values?: Values;
80
- /** What a button does. Fieldia hands the press over; the app decides. */
81
- onAction?: (request: ActionRequest) => void | Promise<void>;
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>;
82
122
  drafts?: {
83
123
  store: DraftStore;
84
124
  restore?: 'auto' | 'ask';
@@ -98,7 +138,12 @@ export interface Form {
98
138
  getState(): FormState;
99
139
  subscribe(listener: (state: FormState) => void): () => void;
100
140
  load(): Promise<void>;
101
- setValue(name: string, value: Value): void;
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;
102
147
  node(id: string): NodeState;
103
148
  fieldReadonly(name: string): boolean;
104
149
  /** What a field would be told if the form were checked now, or null when it passes. Nothing is shown. */
@@ -148,14 +193,37 @@ export interface Form {
148
193
  * an optional one instead.
149
194
  */
150
195
  goTo(step: string): boolean;
151
- /** Run a button; a list's button passes the records chosen in it. */
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
+ */
152
201
  runAction(id: string, chosen?: {
153
202
  recordIds: RecordId[];
154
- }): Promise<void>;
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;
155
210
  restoreDraft(): void;
156
211
  discardDraft(): void;
157
- /** Resolves once every onchange, save and autosave started so far has finished. */
212
+ /** Resolves once every onchange, save, autosave and run of steps started so far has finished — a question still waiting for its answer included. */
158
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;
159
227
  dispose(): void;
160
228
  }
161
229
  /**