@formancy/angular 0.1.0 → 0.2.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/fesm2022/formancy-angular.mjs +3199 -104
- package/fesm2022/formancy-angular.mjs.map +1 -1
- package/package.json +5 -4
- package/types/formancy-angular.d.ts +580 -61
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as _angular_core from '@angular/core';
|
|
2
2
|
import { InjectionToken, Provider, Signal, Type, OnChanges, OnDestroy, Injector, OnInit } from '@angular/core';
|
|
3
3
|
import * as _formancy_core from '@formancy/core';
|
|
4
4
|
import { FormEngine, FieldSnapshot, Path } from '@formancy/core';
|
|
5
|
-
import { FieldType, FieldDef } from '@formancy/spec';
|
|
5
|
+
import { FieldType, RemoteOption, FieldDef, LayoutNode, RichInline, RichBlock, RichCommand } from '@formancy/spec';
|
|
6
6
|
import * as _formancy_angular from '@formancy/angular';
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -39,6 +39,8 @@ interface RepeaterBinding {
|
|
|
39
39
|
rowIds: Signal<readonly string[]>;
|
|
40
40
|
addRow(): void;
|
|
41
41
|
removeRow(index: number): void;
|
|
42
|
+
/** Move a row. The keyboard route to reordering; a drag is a second route to this. */
|
|
43
|
+
moveRow(from: number, to: number): void;
|
|
42
44
|
}
|
|
43
45
|
/**
|
|
44
46
|
* A repeater's live row count as a signal — the Angular half of React's
|
|
@@ -110,6 +112,75 @@ interface FormancyRegistry {
|
|
|
110
112
|
declare const FORMANCY_REGISTRY: InjectionToken<FormancyRegistry>;
|
|
111
113
|
declare function provideFormancyRegistry(registry: FormancyRegistry): Provider[];
|
|
112
114
|
|
|
115
|
+
/**
|
|
116
|
+
* A field's options, from the document or from the deployment.
|
|
117
|
+
*
|
|
118
|
+
* The same contract the React binding states in `use-sourced-options.ts`, written
|
|
119
|
+
* out here rather than shared: the async lifecycle is expressed in this framework's
|
|
120
|
+
* own primitives, and a shared abstraction would report that both bindings
|
|
121
|
+
* implemented it when one had not.
|
|
122
|
+
*
|
|
123
|
+
* `optionsSource` decides which answers EXIST rather than how they are shown, so the
|
|
124
|
+
* plain `<select>` honours it as well as the typeahead — one ignoring it would render
|
|
125
|
+
* an empty chooser over a field that collects something.
|
|
126
|
+
*/
|
|
127
|
+
interface SourcedOptionsState {
|
|
128
|
+
/** The rows a source returned, empty until one does. */
|
|
129
|
+
rows: Signal<readonly RemoteOption[]>;
|
|
130
|
+
/** Names for values the form already holds, so a resumed draft is not blank. */
|
|
131
|
+
named: Signal<ReadonlyMap<string, string>>;
|
|
132
|
+
/** The document names a source this deployment does not have. */
|
|
133
|
+
unavailable: Signal<boolean>;
|
|
134
|
+
/** A request is in flight. The control says `aria-busy`; it never disables itself. */
|
|
135
|
+
busy: Signal<boolean>;
|
|
136
|
+
/** The one thing the field says out loud, or the empty string. */
|
|
137
|
+
status: Signal<string>;
|
|
138
|
+
/** Whether this field is sourced at all. False is the ordinary case. */
|
|
139
|
+
sourced: Signal<boolean>;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Where a file goes, and what the submission remembers about it.
|
|
144
|
+
*
|
|
145
|
+
* The same contract the React binding uses, with the same reasoning: this
|
|
146
|
+
* package has no opinion about the destination, so the one field works against
|
|
147
|
+
* local disk, S3 or a customer's own service without any of them becoming a
|
|
148
|
+
* dependency of a renderer.
|
|
149
|
+
*
|
|
150
|
+
* The bytes never pass through the submission. What is stored is what the file
|
|
151
|
+
* *is* and where it went, so a submission read back years later is small and
|
|
152
|
+
* says what was attached even if the object store has since been emptied.
|
|
153
|
+
*/
|
|
154
|
+
interface StoredFile {
|
|
155
|
+
/** Stable within the submission; how a row is keyed and removed. */
|
|
156
|
+
id: string;
|
|
157
|
+
name: string;
|
|
158
|
+
size: number;
|
|
159
|
+
contentType: string;
|
|
160
|
+
/** Where the bytes are, in whatever the host's storage calls a location. */
|
|
161
|
+
storageKey: string;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Uploads one file and reports what was stored.
|
|
165
|
+
*
|
|
166
|
+
* Rejecting is a real answer: the field says so out loud rather than dropping
|
|
167
|
+
* the file, because a submission somebody believes carries their evidence and
|
|
168
|
+
* does not is the worst outcome available here.
|
|
169
|
+
*/
|
|
170
|
+
type Uploader = (file: File) => Promise<StoredFile>;
|
|
171
|
+
/**
|
|
172
|
+
* Optional by design. A form with no file fields needs no uploader, and a file
|
|
173
|
+
* field without one renders read-only and says why — which beats turning a
|
|
174
|
+
* form that mostly works into a failed injection.
|
|
175
|
+
*/
|
|
176
|
+
declare const FORMANCY_UPLOADER: InjectionToken<Uploader>;
|
|
177
|
+
declare function injectUploader(): Uploader | null;
|
|
178
|
+
/** Providing one, for a host that has somewhere to put bytes. */
|
|
179
|
+
declare function provideFormancyUploader(upload: Uploader): {
|
|
180
|
+
provide: InjectionToken<Uploader>;
|
|
181
|
+
useValue: Uploader;
|
|
182
|
+
};
|
|
183
|
+
|
|
113
184
|
/**
|
|
114
185
|
* The built-in unstyled field components — the Angular rendering of the same
|
|
115
186
|
* decisions React's defaults made. Zero CSS; `data-formancy-part` is the
|
|
@@ -123,80 +194,158 @@ declare function provideFormancyRegistry(registry: FormancyRegistry): Provider[]
|
|
|
123
194
|
/** Shared unstyled shell: real label, projected control, error text as the
|
|
124
195
|
* describedby target. */
|
|
125
196
|
declare class FormancyFieldShell {
|
|
126
|
-
readonly field:
|
|
127
|
-
readonly label:
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
197
|
+
readonly field: _angular_core.InputSignal<FieldBinding>;
|
|
198
|
+
readonly label: _angular_core.InputSignal<string>;
|
|
199
|
+
/** Inert here; read by tools outside the renderer. See FormancyLayout. */
|
|
200
|
+
readonly path: _angular_core.InputSignal<string>;
|
|
201
|
+
protected readonly showError: _angular_core.Signal<boolean>;
|
|
202
|
+
protected readonly errorText: _angular_core.Signal<string>;
|
|
203
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<FormancyFieldShell, never>;
|
|
204
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<FormancyFieldShell, "formancy-field-shell", never, { "field": { "alias": "field"; "required": true; "isSignal": true; }; "label": { "alias": "label"; "required": true; "isSignal": true; }; "path": { "alias": "path"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
|
|
132
205
|
}
|
|
133
206
|
/** The state every leaf control shares; components extend it so the templates
|
|
134
207
|
* stay the only per-type code. Context comes through DI (see registry.ts). */
|
|
135
208
|
declare abstract class FieldComponentBase {
|
|
136
209
|
protected readonly context: _formancy_angular.FormancyFieldContext;
|
|
137
210
|
protected readonly field: FieldBinding;
|
|
138
|
-
protected readonly control:
|
|
139
|
-
|
|
211
|
+
protected readonly control: _angular_core.Signal<_formancy_core.ControlProps>;
|
|
212
|
+
protected readonly engine: _formancy_core.FormEngine;
|
|
140
213
|
/**
|
|
141
214
|
* Option labels resolved to strings, since a label may be a reference into
|
|
142
215
|
* the message catalogue. Falling back to the stored value keeps an
|
|
143
216
|
* untranslated option selectable rather than blank.
|
|
144
217
|
*/
|
|
145
|
-
protected readonly options:
|
|
218
|
+
protected readonly options: _angular_core.Signal<readonly {
|
|
219
|
+
value: string;
|
|
220
|
+
label: string;
|
|
221
|
+
}[]>;
|
|
222
|
+
/**
|
|
223
|
+
* What the control types into, when it has something to type into.
|
|
224
|
+
*
|
|
225
|
+
* A plain `<select>` never writes to it, so its source is asked for everything and
|
|
226
|
+
* shows what fits; the typeahead writes every keystroke.
|
|
227
|
+
*/
|
|
228
|
+
protected readonly sourceQuery: _angular_core.WritableSignal<string>;
|
|
229
|
+
/** The remote half, or an inert one for the ordinary field that lists its options. */
|
|
230
|
+
protected readonly remote: SourcedOptionsState;
|
|
231
|
+
/**
|
|
232
|
+
* Whether THIS component is the one that will render the field.
|
|
233
|
+
*
|
|
234
|
+
* True for every control but the select that hands over to the typeahead, which
|
|
235
|
+
* overrides it. Both extend this base, so without the distinction a sourced
|
|
236
|
+
* typeahead asked its source twice and read one set of answers.
|
|
237
|
+
*/
|
|
238
|
+
protected sourceEnabled(): boolean;
|
|
239
|
+
/**
|
|
240
|
+
* The options to offer: the document's, or the deployment's.
|
|
241
|
+
*
|
|
242
|
+
* The stored answer is always offerable even when the current query does not match
|
|
243
|
+
* it — a control that dropped it would show an empty box over an answer the form
|
|
244
|
+
* holds, and the next blur would look like the person cleared it.
|
|
245
|
+
*/
|
|
246
|
+
protected readonly offered: _angular_core.Signal<readonly {
|
|
146
247
|
value: string;
|
|
147
248
|
label: string;
|
|
148
249
|
}[]>;
|
|
149
250
|
}
|
|
251
|
+
/**
|
|
252
|
+
* A single-line answer, and — with `widget: "scanner"` — a camera route to the same
|
|
253
|
+
* string.
|
|
254
|
+
*
|
|
255
|
+
* The input is the control in both cases, never a second one beside it: typing is the
|
|
256
|
+
* accessibility floor and the fallback at once, so it is what is always there and the
|
|
257
|
+
* scan button is what is sometimes added. With no scanner supplied the markup is the
|
|
258
|
+
* default control exactly, because a Scan button that opens nothing is worse than no
|
|
259
|
+
* button ([0071](../../../docs/decisions/0071-a-scanner-is-supplied-not-built.md)).
|
|
260
|
+
*
|
|
261
|
+
* The React binding renders the same three elements for the same reasons.
|
|
262
|
+
*/
|
|
150
263
|
declare class FormancyTextField extends FieldComponentBase {
|
|
151
|
-
|
|
264
|
+
private readonly scan;
|
|
265
|
+
protected readonly scanning: _angular_core.WritableSignal<boolean>;
|
|
266
|
+
/** A device failure, held here rather than in the field's errors. See above. */
|
|
267
|
+
private readonly trouble;
|
|
268
|
+
protected readonly text: _angular_core.Signal<string>;
|
|
269
|
+
/**
|
|
270
|
+
* A computed over the field's SIGNAL, not over `engine.getFieldSnapshot` — the
|
|
271
|
+
* widget can arrive with a new document, and a plain method call is not a
|
|
272
|
+
* dependency an OnPush component re-runs for.
|
|
273
|
+
*/
|
|
274
|
+
protected readonly scannable: _angular_core.Signal<boolean>;
|
|
275
|
+
protected readonly status: _angular_core.Signal<string>;
|
|
152
276
|
protected onInput(event: Event): void;
|
|
153
|
-
|
|
154
|
-
|
|
277
|
+
/**
|
|
278
|
+
* The ONE place a text field's answer is written, typed or scanned.
|
|
279
|
+
*
|
|
280
|
+
* Structural rather than careful: the parameter is a `string`, so there is no path
|
|
281
|
+
* from the camera to `setValue` that could store something typing could not — the
|
|
282
|
+
* line a widget may never cross
|
|
283
|
+
* ([0065](../../../docs/decisions/0065-a-widget-is-authored-not-registered.md)).
|
|
284
|
+
*/
|
|
285
|
+
private commit;
|
|
286
|
+
protected read(): Promise<void>;
|
|
287
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<FormancyTextField, never>;
|
|
288
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<FormancyTextField, "formancy-text-field", never, {}, {}, never, never, true, never>;
|
|
155
289
|
}
|
|
156
290
|
declare class FormancyTextareaField extends FieldComponentBase {
|
|
157
|
-
protected readonly text:
|
|
291
|
+
protected readonly text: _angular_core.Signal<string>;
|
|
158
292
|
protected onInput(event: Event): void;
|
|
159
|
-
static ɵfac:
|
|
160
|
-
static ɵcmp:
|
|
293
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<FormancyTextareaField, never>;
|
|
294
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<FormancyTextareaField, "formancy-textarea-field", never, {}, {}, never, never, true, never>;
|
|
161
295
|
}
|
|
162
296
|
declare class FormancyNumberField extends FieldComponentBase {
|
|
163
|
-
protected readonly text:
|
|
297
|
+
protected readonly text: _angular_core.Signal<string>;
|
|
164
298
|
protected onInput(event: Event): void;
|
|
165
|
-
static ɵfac:
|
|
166
|
-
static ɵcmp:
|
|
299
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<FormancyNumberField, never>;
|
|
300
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<FormancyNumberField, "formancy-number-field", never, {}, {}, never, never, true, never>;
|
|
167
301
|
}
|
|
168
302
|
declare class FormancyCheckboxField extends FieldComponentBase {
|
|
169
|
-
protected readonly checked:
|
|
303
|
+
protected readonly checked: _angular_core.Signal<boolean>;
|
|
304
|
+
/**
|
|
305
|
+
* `widget: "toggle"` is a part name and NOT `role="switch"`.
|
|
306
|
+
*
|
|
307
|
+
* ARIA's switch means a control that takes effect when you operate it, and a form
|
|
308
|
+
* field sets a value submitted later or never — so announcing "switch" describes
|
|
309
|
+
* it incorrectly to the people who rely on the description. A role is also not
|
|
310
|
+
* paint: changing it would make this the first widget to change what a control
|
|
311
|
+
* claims to be, which is the line the widget mechanism exists to hold. The switch
|
|
312
|
+
* is CSS, and conformance keeps finding this by role `checkbox` either way.
|
|
313
|
+
*
|
|
314
|
+
* Null rather than absent when there is no widget, because Angular omits an
|
|
315
|
+
* attribute bound to null — which is what keeps an ordinary checkbox's markup
|
|
316
|
+
* exactly as it was.
|
|
317
|
+
*/
|
|
318
|
+
protected readonly part: _angular_core.Signal<"toggle" | null>;
|
|
170
319
|
protected onChange(event: Event): void;
|
|
171
|
-
static ɵfac:
|
|
172
|
-
static ɵcmp:
|
|
320
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<FormancyCheckboxField, never>;
|
|
321
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<FormancyCheckboxField, "formancy-checkbox-field", never, {}, {}, never, never, true, never>;
|
|
173
322
|
}
|
|
174
323
|
declare class FormancyDateField extends FieldComponentBase {
|
|
175
|
-
protected readonly text:
|
|
324
|
+
protected readonly text: _angular_core.Signal<string>;
|
|
176
325
|
protected onInput(event: Event): void;
|
|
177
|
-
static ɵfac:
|
|
178
|
-
static ɵcmp:
|
|
326
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<FormancyDateField, never>;
|
|
327
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<FormancyDateField, "formancy-date-field", never, {}, {}, never, never, true, never>;
|
|
179
328
|
}
|
|
180
329
|
declare class FormancySelectField extends FieldComponentBase {
|
|
181
|
-
protected readonly
|
|
330
|
+
protected readonly typeahead: _angular_core.Signal<boolean>;
|
|
331
|
+
/** The name the document gave, for the message when this deployment has no such source. */
|
|
332
|
+
protected readonly sourceName: _angular_core.Signal<string>;
|
|
333
|
+
/** This one hands over to the typeahead, which does its own asking. */
|
|
334
|
+
protected sourceEnabled(): boolean;
|
|
335
|
+
protected readonly selected: _angular_core.Signal<string>;
|
|
182
336
|
protected onChange(event: Event): void;
|
|
183
|
-
static ɵfac:
|
|
184
|
-
static ɵcmp:
|
|
337
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<FormancySelectField, never>;
|
|
338
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<FormancySelectField, "formancy-select-field", never, {}, {}, never, never, true, never>;
|
|
185
339
|
}
|
|
186
340
|
declare class FormancyRadioGroupField extends FieldComponentBase {
|
|
187
|
-
protected readonly showError:
|
|
188
|
-
protected readonly errorText:
|
|
341
|
+
protected readonly showError: _angular_core.Signal<boolean>;
|
|
342
|
+
protected readonly errorText: _angular_core.Signal<string>;
|
|
189
343
|
protected optionId(option: {
|
|
190
344
|
value: string;
|
|
191
345
|
}): string;
|
|
192
|
-
static ɵfac:
|
|
193
|
-
static ɵcmp:
|
|
346
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<FormancyRadioGroupField, never>;
|
|
347
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<FormancyRadioGroupField, "formancy-radio-group-field", never, {}, {}, never, never, true, never>;
|
|
194
348
|
}
|
|
195
|
-
/**
|
|
196
|
-
* The built-in unstyled components. `null` means the type renders nothing here:
|
|
197
|
-
* hidden and static are non-inputs, and the container types are laid out by
|
|
198
|
-
* their own machinery, not by a leaf slot.
|
|
199
|
-
*/
|
|
200
349
|
declare const DEFAULT_FIELD_COMPONENTS: Record<FieldType, Type<unknown> | null>;
|
|
201
350
|
|
|
202
351
|
interface SubmitOutcome {
|
|
@@ -214,13 +363,13 @@ interface SubmitOutcome {
|
|
|
214
363
|
*/
|
|
215
364
|
declare class FormancyComponentOutlet implements OnChanges, OnDestroy {
|
|
216
365
|
private readonly container;
|
|
217
|
-
readonly formancyOutlet:
|
|
218
|
-
readonly formancyOutletInjector:
|
|
366
|
+
readonly formancyOutlet: _angular_core.InputSignal<Type<unknown>>;
|
|
367
|
+
readonly formancyOutletInjector: _angular_core.InputSignal<Injector>;
|
|
219
368
|
private ref;
|
|
220
369
|
ngOnChanges(): void;
|
|
221
370
|
ngOnDestroy(): void;
|
|
222
|
-
static ɵfac:
|
|
223
|
-
static ɵdir:
|
|
371
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<FormancyComponentOutlet, never>;
|
|
372
|
+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<FormancyComponentOutlet, "[formancyOutlet]", never, { "formancyOutlet": { "alias": "formancyOutlet"; "required": true; "isSignal": true; }; "formancyOutletInjector": { "alias": "formancyOutletInjector"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
224
373
|
}
|
|
225
374
|
/**
|
|
226
375
|
* One field's slot: resolve the component through the registry (per-path beats
|
|
@@ -236,22 +385,27 @@ declare class FormancyComponentOutlet implements OnChanges, OnDestroy {
|
|
|
236
385
|
declare class FormancyFieldSlot implements OnInit {
|
|
237
386
|
private readonly registry;
|
|
238
387
|
private readonly injector;
|
|
239
|
-
readonly path:
|
|
240
|
-
readonly fallbackLabel:
|
|
388
|
+
readonly path: _angular_core.InputSignal<string>;
|
|
389
|
+
readonly fallbackLabel: _angular_core.InputSignal<string | undefined>;
|
|
241
390
|
protected state?: {
|
|
242
391
|
snapshot: Signal<FieldSnapshot>;
|
|
243
392
|
component: Type<unknown> | null;
|
|
244
393
|
injector: Injector;
|
|
245
394
|
};
|
|
246
395
|
ngOnInit(): void;
|
|
247
|
-
static ɵfac:
|
|
248
|
-
static ɵcmp:
|
|
396
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<FormancyFieldSlot, never>;
|
|
397
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<FormancyFieldSlot, "formancy-field", never, { "path": { "alias": "path"; "required": true; "isSignal": true; }; "fallbackLabel": { "alias": "fallbackLabel"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
249
398
|
}
|
|
250
399
|
interface RepeaterRow {
|
|
251
400
|
index: number;
|
|
252
401
|
/** The row's stable identity, which `@for` tracks instead of its position. */
|
|
253
402
|
id: string;
|
|
254
|
-
|
|
403
|
+
/** Each field of the row: its key within the row, and its positional wire.
|
|
404
|
+
* Tracked by `key`, bound by `wire`. */
|
|
405
|
+
children: ReadonlyArray<{
|
|
406
|
+
key: string;
|
|
407
|
+
wire: string;
|
|
408
|
+
}>;
|
|
255
409
|
}
|
|
256
410
|
/**
|
|
257
411
|
* A repeater's own chrome: a named fieldset, one row div per item with the row
|
|
@@ -262,8 +416,11 @@ interface RepeaterRow {
|
|
|
262
416
|
declare class FormancyRepeaterSection implements OnInit {
|
|
263
417
|
private readonly engine;
|
|
264
418
|
private readonly injector;
|
|
265
|
-
readonly wire:
|
|
266
|
-
readonly labels:
|
|
419
|
+
readonly wire: _angular_core.InputSignal<string>;
|
|
420
|
+
readonly labels: _angular_core.InputSignal<Record<string, string> | undefined>;
|
|
421
|
+
/** The repeater's own definition, kept because the column plan reads `widget`,
|
|
422
|
+
* `columns` and the child labels off it on every render. */
|
|
423
|
+
protected definition: FieldDef | undefined;
|
|
267
424
|
protected state?: {
|
|
268
425
|
repeater: RepeaterBinding;
|
|
269
426
|
label: string;
|
|
@@ -271,10 +428,83 @@ declare class FormancyRepeaterSection implements OnInit {
|
|
|
271
428
|
removeLabel: string;
|
|
272
429
|
rows: Signal<readonly RepeaterRow[]>;
|
|
273
430
|
};
|
|
431
|
+
/**
|
|
432
|
+
* The columns the grid shows, in the order it shows them, or an empty list when the
|
|
433
|
+
* widget was not asked for.
|
|
434
|
+
*
|
|
435
|
+
* `columns` without the widget stays inert on purpose: 0066 lets an author write the
|
|
436
|
+
* arrangement before a renderer honours it, and a repeater that silently became a grid
|
|
437
|
+
* because somebody sized its columns would be the opposite of that.
|
|
438
|
+
*
|
|
439
|
+
* The React binding computes the same list with the same helper, for the same reasons.
|
|
440
|
+
*/
|
|
441
|
+
protected readonly plan: Signal<readonly {
|
|
442
|
+
key: string;
|
|
443
|
+
heading: string;
|
|
444
|
+
align: string | null;
|
|
445
|
+
width?: number;
|
|
446
|
+
}[]>;
|
|
447
|
+
/**
|
|
448
|
+
* The authored ratios, as ONE custom property rather than as `grid-template-columns`.
|
|
449
|
+
*
|
|
450
|
+
* A property lays nothing out by itself, so a theme's narrow-screen media query
|
|
451
|
+
* replaces its own declaration and wins rather than losing to an inline one it cannot
|
|
452
|
+
* outrank. It also carries a value space no attribute could enumerate: `width` is a
|
|
453
|
+
* number with `exclusiveMinimum: 0`, so 1.5 is legal and nothing bounds it from above.
|
|
454
|
+
*
|
|
455
|
+
* Nothing at all when no column was sized, so the theme's fallback is live code.
|
|
456
|
+
*/
|
|
457
|
+
protected trackStyle(): Record<string, string>;
|
|
458
|
+
/**
|
|
459
|
+
* The controls in one cell: every leaf inside the row that belongs to that column's
|
|
460
|
+
* child field.
|
|
461
|
+
*
|
|
462
|
+
* One child, because a grid's rows are FLAT: a child holding fields of its own is
|
|
463
|
+
* refused when the document is saved (0078). This walked the whole subtree under the
|
|
464
|
+
* child while a group could be a column, and every clause that made that walk safe is
|
|
465
|
+
* gone with the arrangement it served.
|
|
466
|
+
*
|
|
467
|
+
* Still a filter over the children that EXIST rather than the wire the column implies,
|
|
468
|
+
* so a document nobody validated renders an empty cell rather than a field the engine
|
|
469
|
+
* does not have.
|
|
470
|
+
*/
|
|
471
|
+
protected cellChildren(row: RepeaterRow, key: string): readonly RepeaterRow['children'][number][];
|
|
274
472
|
ngOnInit(): void;
|
|
275
473
|
protected fallbackFor(instanceWire: string): string | undefined;
|
|
276
|
-
static ɵfac:
|
|
277
|
-
static ɵcmp:
|
|
474
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<FormancyRepeaterSection, never>;
|
|
475
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<FormancyRepeaterSection, "formancy-repeater", never, { "wire": { "alias": "wire"; "required": true; "isSignal": true; }; "labels": { "alias": "labels"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
476
|
+
}
|
|
477
|
+
declare class FormancyLayout {
|
|
478
|
+
readonly nodes: _angular_core.InputSignal<readonly LayoutNode[]>;
|
|
479
|
+
readonly labels: _angular_core.InputSignal<Record<string, string> | undefined>;
|
|
480
|
+
/** Index path of the container these nodes are the children of. */
|
|
481
|
+
readonly at: _angular_core.InputSignal<string>;
|
|
482
|
+
private readonly engine;
|
|
483
|
+
constructor();
|
|
484
|
+
/** The span as a number CSS can count with, and nothing at all for `all`.
|
|
485
|
+
*
|
|
486
|
+
* A style OBJECT rather than `[style.--fm-span]`: both set a custom property --
|
|
487
|
+
* measured, both work -- and the object form lets this return nothing for `all`
|
|
488
|
+
* without binding an empty string. */
|
|
489
|
+
protected spanStyle(node: LayoutNode): Record<string, string>;
|
|
490
|
+
/** This node's index path, as the dotted string the attribute carries. */
|
|
491
|
+
protected pathOf(index: number): string;
|
|
492
|
+
/**
|
|
493
|
+
* Headings are cached per node. Minting an id inside the template would give
|
|
494
|
+
* a different one on every change-detection pass, leaving aria-labelledby
|
|
495
|
+
* pointing at an element that no longer exists.
|
|
496
|
+
*/
|
|
497
|
+
private readonly headings;
|
|
498
|
+
private static counter;
|
|
499
|
+
/** A tabs node's own name, for the tab strip. Null when it has none. */
|
|
500
|
+
protected stripLabelFor(node: LayoutNode): string | null;
|
|
501
|
+
protected isRepeater(path: string): boolean;
|
|
502
|
+
protected headingFor(node: LayoutNode): {
|
|
503
|
+
id: string;
|
|
504
|
+
text: string;
|
|
505
|
+
} | null;
|
|
506
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<FormancyLayout, never>;
|
|
507
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<FormancyLayout, "formancy-layout", never, { "nodes": { "alias": "nodes"; "required": true; "isSignal": true; }; "labels": { "alias": "labels"; "required": false; "isSignal": true; }; "at": { "alias": "at"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
278
508
|
}
|
|
279
509
|
/**
|
|
280
510
|
* Renders the whole form from the engine: one slot per field, resolved through
|
|
@@ -294,9 +524,14 @@ declare class FormancyForm {
|
|
|
294
524
|
* `items[].name`). A `label` on the model definition wins — that is how
|
|
295
525
|
* fixture schemas carry text until the spec's i18n section lands.
|
|
296
526
|
*/
|
|
297
|
-
readonly labels:
|
|
298
|
-
|
|
299
|
-
|
|
527
|
+
readonly labels: _angular_core.InputSignal<Record<string, string> | undefined>;
|
|
528
|
+
/**
|
|
529
|
+
* Render a named entry from the schema's `layouts` instead of model order.
|
|
530
|
+
* Unknown or absent, the form falls back to model order.
|
|
531
|
+
*/
|
|
532
|
+
readonly layout: _angular_core.InputSignal<string | undefined>;
|
|
533
|
+
readonly submitLabel: _angular_core.InputSignal<string | undefined>;
|
|
534
|
+
readonly submitted: _angular_core.OutputEmitterRef<SubmitOutcome>;
|
|
300
535
|
protected readonly pages: readonly {
|
|
301
536
|
key: string;
|
|
302
537
|
def: FieldDef;
|
|
@@ -305,6 +540,11 @@ declare class FormancyForm {
|
|
|
305
540
|
protected readonly staticWires: string[];
|
|
306
541
|
protected readonly staticOnPage: Signal<string[]>;
|
|
307
542
|
protected readonly repeatersOnPage: Signal<string[]>;
|
|
543
|
+
/**
|
|
544
|
+
* The nodes of the named layout, or undefined to fall back to model order —
|
|
545
|
+
* a mistyped layout name should not produce an empty form.
|
|
546
|
+
*/
|
|
547
|
+
protected readonly arrangement: Signal<readonly LayoutNode[] | undefined>;
|
|
308
548
|
protected fallbackFor(wire: string): string | undefined;
|
|
309
549
|
protected pageLabel(page: {
|
|
310
550
|
key: string;
|
|
@@ -312,8 +552,8 @@ declare class FormancyForm {
|
|
|
312
552
|
}): string;
|
|
313
553
|
protected onNext(): void;
|
|
314
554
|
protected onSubmit(): void;
|
|
315
|
-
static ɵfac:
|
|
316
|
-
static ɵcmp:
|
|
555
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<FormancyForm, never>;
|
|
556
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<FormancyForm, "formancy-form", never, { "labels": { "alias": "labels"; "required": false; "isSignal": true; }; "layout": { "alias": "layout"; "required": false; "isSignal": true; }; "submitLabel": { "alias": "submitLabel"; "required": false; "isSignal": true; }; }, { "submitted": "submitted"; }, never, never, true, never>;
|
|
317
557
|
}
|
|
318
558
|
|
|
319
559
|
/**
|
|
@@ -326,7 +566,7 @@ declare class FormancyForm {
|
|
|
326
566
|
* problem" already means to assistive tech.
|
|
327
567
|
*/
|
|
328
568
|
declare class FormancyErrorSummary {
|
|
329
|
-
readonly labels:
|
|
569
|
+
readonly labels: _angular_core.InputSignal<Record<string, string> | undefined>;
|
|
330
570
|
private readonly engine;
|
|
331
571
|
private readonly region;
|
|
332
572
|
protected readonly errors: Signal<ReadonlyArray<{
|
|
@@ -338,9 +578,288 @@ declare class FormancyErrorSummary {
|
|
|
338
578
|
protected labelFor(path: string): string;
|
|
339
579
|
protected controlIdOf(path: string): string;
|
|
340
580
|
protected focusControl(event: Event, path: string): void;
|
|
341
|
-
static ɵfac:
|
|
342
|
-
static ɵcmp:
|
|
581
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<FormancyErrorSummary, never>;
|
|
582
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<FormancyErrorSummary, "formancy-error-summary", never, { "labels": { "alias": "labels"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* One run of inline nodes, recursing through its own selector.
|
|
587
|
+
*
|
|
588
|
+
* Angular has no fragment component, so emphasis inside emphasis is expressed
|
|
589
|
+
* by the component referring to itself — which is why it is its own
|
|
590
|
+
* declaration rather than part of the block template below.
|
|
591
|
+
*/
|
|
592
|
+
declare class FormancyRichInline {
|
|
593
|
+
readonly nodes: _angular_core.InputSignal<readonly RichInline[]>;
|
|
594
|
+
protected asText(node: RichInline): string;
|
|
595
|
+
protected childrenOf(node: RichInline): readonly RichInline[];
|
|
596
|
+
protected hrefOf(node: RichInline): string;
|
|
597
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<FormancyRichInline, never>;
|
|
598
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<FormancyRichInline, "formancy-rich-inline", never, { "nodes": { "alias": "nodes"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
601
|
+
* Showing a `richtext` answer, from the same parser the React renderer uses.
|
|
602
|
+
*
|
|
603
|
+
* No `[innerHTML]`, and no `DomSanitizer` — there is nothing to sanitise. The
|
|
604
|
+
* stored answer is parsed into a typed tree in `@formancy/spec`, and every
|
|
605
|
+
* element here is created by Angular from that tree, so the characters
|
|
606
|
+
* somebody typed arrive as interpolated text and cannot become markup however
|
|
607
|
+
* they are spelled ([0052](../../../docs/decisions/0052-richtext-is-not-html.md)).
|
|
608
|
+
*
|
|
609
|
+
* Both renderers building the same tree into the same elements is the promise
|
|
610
|
+
* the engine makes across browser and server, applied to presentation: two
|
|
611
|
+
* implementations, one meaning.
|
|
612
|
+
*/
|
|
613
|
+
declare class FormancyRichText {
|
|
614
|
+
readonly source: _angular_core.InputSignal<string>;
|
|
615
|
+
protected readonly blocks: _angular_core.Signal<RichBlock[]>;
|
|
616
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<FormancyRichText, never>;
|
|
617
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<FormancyRichText, "formancy-rich-text", never, { "source": { "alias": "source"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
343
618
|
}
|
|
344
619
|
|
|
345
|
-
|
|
346
|
-
|
|
620
|
+
/**
|
|
621
|
+
* Telling somebody their draft came back changed.
|
|
622
|
+
*
|
|
623
|
+
* The same component the React binding has, word for word in what it says,
|
|
624
|
+
* because the two renderers agreeing about what a form TELLS somebody matters as
|
|
625
|
+
* much as them agreeing about what it collects.
|
|
626
|
+
*
|
|
627
|
+
* The server already does the careful half: a republished form migrates a draft
|
|
628
|
+
* lazily on resume, and answers whose field no longer exists move to
|
|
629
|
+
* `data.__orphaned` rather than being deleted
|
|
630
|
+
* ([0027](../../../docs/decisions/0027-lazy-draft-migration.md)).
|
|
631
|
+
*
|
|
632
|
+
* **Nothing showed that to the person.** They resumed a draft, some of their
|
|
633
|
+
* answers were no longer on the form, and they submitted believing everything
|
|
634
|
+
* they had typed was in it. The answers are not lost from storage — they are
|
|
635
|
+
* lost from the submission, and nobody was told.
|
|
636
|
+
*
|
|
637
|
+
* The semantics follow `formancy-error-summary`, which solved the same shape of
|
|
638
|
+
* problem: the container takes focus through `tabindex="-1"` and is deliberately
|
|
639
|
+
* NOT `role="alert"`, because focusing it already makes a screen reader announce
|
|
640
|
+
* it and doing both announces it twice.
|
|
641
|
+
*/
|
|
642
|
+
interface ResumeMigration {
|
|
643
|
+
readonly severity: 'lossy' | 'breaking';
|
|
644
|
+
readonly changes: ReadonlyArray<{
|
|
645
|
+
readonly kind: string;
|
|
646
|
+
readonly path?: string;
|
|
647
|
+
}>;
|
|
648
|
+
}
|
|
649
|
+
declare class FormancyResumeNotice {
|
|
650
|
+
/** Omitted when the draft came back unchanged. */
|
|
651
|
+
readonly migration: _angular_core.InputSignal<ResumeMigration | undefined>;
|
|
652
|
+
/** Question wording for a field key, since a key is not what the form asked. */
|
|
653
|
+
readonly labels: _angular_core.InputSignal<Readonly<Record<string, string>>>;
|
|
654
|
+
protected readonly setAside: _angular_core.Signal<string[]>;
|
|
655
|
+
protected readonly summary: _angular_core.Signal<string>;
|
|
656
|
+
private readonly region;
|
|
657
|
+
private readonly focused;
|
|
658
|
+
protected labelFor(path: string): string;
|
|
659
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<FormancyResumeNotice, never>;
|
|
660
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<FormancyResumeNotice, "formancy-resume-notice", never, { "migration": { "alias": "migration"; "required": false; "isSignal": true; }; "labels": { "alias": "labels"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
/**
|
|
664
|
+
* A rich-text editing surface, supplied by the host.
|
|
665
|
+
*
|
|
666
|
+
* The same contract the React binding uses, deliberately to the letter: one
|
|
667
|
+
* interface, one factory, one set of attributes. A renderer that invented its own
|
|
668
|
+
* shape here would be the drift the conformance suite exists to prevent, except
|
|
669
|
+
* in the one place the suite cannot see — what the HOST passes in.
|
|
670
|
+
*
|
|
671
|
+
* Why the host and not this package: a contenteditable editor means ProseMirror,
|
|
672
|
+
* which is larger than this entire package, and most forms have no rich-text
|
|
673
|
+
* field. Without a factory the field is a textarea with a toolbar, which is a
|
|
674
|
+
* working editor and not a degraded mode
|
|
675
|
+
* ([0061](../../../docs/decisions/0061-tiptap-over-the-closed-grammar.md)).
|
|
676
|
+
*
|
|
677
|
+
* What crosses this boundary is **the stored grammar in both directions, never
|
|
678
|
+
* markup**. That is the whole reason an editor is admissible: nothing on either
|
|
679
|
+
* side holds a string of HTML, so no consumer of an answer becomes a sanitiser
|
|
680
|
+
* ([0052](../../../docs/decisions/0052-richtext-is-not-html.md)).
|
|
681
|
+
*/
|
|
682
|
+
interface RichTextEditorHandle {
|
|
683
|
+
/** The stored answer, in the grammar. */
|
|
684
|
+
value: () => string;
|
|
685
|
+
/** Replace the content when the form's value changes underneath the editor. */
|
|
686
|
+
setValue: (value: string) => void;
|
|
687
|
+
/**
|
|
688
|
+
* Run a formatting command, so the field's own toolbar keeps working.
|
|
689
|
+
*
|
|
690
|
+
* Needed because an editor library brings keyboard shortcuts and no toolbar
|
|
691
|
+
* UI. Leaving the field's toolbar out made Bold reachable by Ctrl+B and by no
|
|
692
|
+
* visible control, which is a regression against the `<textarea>` it replaced
|
|
693
|
+
* and unusable for anybody who does not know the shortcut.
|
|
694
|
+
*
|
|
695
|
+
* The commands are the grammar's whole surface — the same `RichCommand`
|
|
696
|
+
* values the textarea's toolbar uses — so one toolbar drives either surface
|
|
697
|
+
* and the two cannot offer different things.
|
|
698
|
+
*/
|
|
699
|
+
run: (command: RichCommand, href?: string) => void;
|
|
700
|
+
/** Whether the command is on at the caret, for the toolbar's pressed state. */
|
|
701
|
+
isActive: (command: RichCommand) => boolean;
|
|
702
|
+
destroy: () => void;
|
|
703
|
+
}
|
|
704
|
+
interface RichTextEditorMount {
|
|
705
|
+
/** Where to mount. The host's factory owns what it puts inside. */
|
|
706
|
+
readonly element: Element;
|
|
707
|
+
/** The stored answer to open with, in the grammar. */
|
|
708
|
+
readonly value: string;
|
|
709
|
+
/** Called with the new stored answer, in the grammar. */
|
|
710
|
+
readonly onChange: (value: string) => void;
|
|
711
|
+
readonly editable: boolean;
|
|
712
|
+
/**
|
|
713
|
+
* Attributes for the editing surface itself, so the ENGINE keeps owning the
|
|
714
|
+
* accessibility wiring.
|
|
715
|
+
*
|
|
716
|
+
* These ids are minted by `@formancy/core` and composed centrally, which is
|
|
717
|
+
* what makes `aria-describedby` identical in both renderers rather than
|
|
718
|
+
* correct in one of them. An editor package that labelled its own surface
|
|
719
|
+
* would be a third implementation, and the one nobody tests.
|
|
720
|
+
*/
|
|
721
|
+
readonly attributes: Readonly<Record<string, string>>;
|
|
722
|
+
}
|
|
723
|
+
type RichTextEditorFactory = (mount: RichTextEditorMount) => RichTextEditorHandle;
|
|
724
|
+
/**
|
|
725
|
+
* Optional by design, and its absence costs less than the uploader's.
|
|
726
|
+
*
|
|
727
|
+
* A missing uploader makes a file field read-only, because there is nowhere to
|
|
728
|
+
* put the bytes. A missing editor costs only the WYSIWYG surface: the answer is
|
|
729
|
+
* still editable, still valid, and still the same grammar.
|
|
730
|
+
*/
|
|
731
|
+
declare const FORMANCY_RICH_TEXT_EDITOR: InjectionToken<RichTextEditorFactory>;
|
|
732
|
+
declare function injectRichTextEditorFactory(): RichTextEditorFactory | null;
|
|
733
|
+
/** Providing one, for a host that wants the contenteditable surface. */
|
|
734
|
+
declare function provideFormancyRichTextEditor(make: RichTextEditorFactory): {
|
|
735
|
+
provide: InjectionToken<RichTextEditorFactory>;
|
|
736
|
+
useValue: RichTextEditorFactory;
|
|
737
|
+
};
|
|
738
|
+
|
|
739
|
+
/**
|
|
740
|
+
* What a source is asked for.
|
|
741
|
+
*
|
|
742
|
+
* Declared here as well as in `@formancy/react` and deliberately not shared, exactly
|
|
743
|
+
* as `ScanRequest` and `Scanner` are declared twice. A host contract each binding
|
|
744
|
+
* states in its own words is a contract each binding can be read on its own.
|
|
745
|
+
*
|
|
746
|
+
* `kind` is a flat discriminator rather than a union of two interfaces: a host
|
|
747
|
+
* written in plain JavaScript must be harmless, and a shape it can read with one
|
|
748
|
+
* `if` is a shape it gets right.
|
|
749
|
+
*/
|
|
750
|
+
interface OptionsRequest {
|
|
751
|
+
/**
|
|
752
|
+
* `search` — somebody is typing and wants matching rows.
|
|
753
|
+
* `labels` — the form holds these values already and needs their names.
|
|
754
|
+
*/
|
|
755
|
+
kind: 'search' | 'labels';
|
|
756
|
+
/** The name the document gave, which the deployment resolves. */
|
|
757
|
+
source: string;
|
|
758
|
+
/** The field's data path, e.g. `canton` or `people[1].canton`. */
|
|
759
|
+
path: string;
|
|
760
|
+
/** What was typed. Empty on a `labels` request. */
|
|
761
|
+
query: string;
|
|
762
|
+
/** The stored values to name. Empty on a `search`. */
|
|
763
|
+
values: readonly string[];
|
|
764
|
+
/**
|
|
765
|
+
* The locale the form is being resolved in — the engine's, which is the host's own
|
|
766
|
+
* `locale` when it passed one and the document's default otherwise.
|
|
767
|
+
*
|
|
768
|
+
* A remote label is a plain string, never a `{$t}` reference: nothing can check a
|
|
769
|
+
* reference that arrives at runtime, and an unchecked one resolves to nothing and
|
|
770
|
+
* shows an opaque identifier. So the source answers in the right language instead.
|
|
771
|
+
*/
|
|
772
|
+
locale: string;
|
|
773
|
+
/** How many rows the control can show. A hint: the control caps what arrives anyway. */
|
|
774
|
+
limit: number;
|
|
775
|
+
/** Aborted when the answer stops being wanted — a newer keystroke, or a destroy. */
|
|
776
|
+
signal: AbortSignal;
|
|
777
|
+
}
|
|
778
|
+
/**
|
|
779
|
+
* Where one named list of options comes from.
|
|
780
|
+
*
|
|
781
|
+
* The third instance of the inversion the uploader and the scanner already are: the
|
|
782
|
+
* document names a list, the deployment says what that name means, and nothing in
|
|
783
|
+
* formancy ever makes a request of its own. A URL in a form document would be a
|
|
784
|
+
* deployment detail in a portable format, unfixable once published, and an SSRF
|
|
785
|
+
* surface on a self-hosted instance.
|
|
786
|
+
*/
|
|
787
|
+
interface OptionsSource {
|
|
788
|
+
resolve(request: OptionsRequest): Promise<readonly RemoteOption[]>;
|
|
789
|
+
/** How long to wait after a keystroke before asking. The control has a default. */
|
|
790
|
+
debounceMs?: number;
|
|
791
|
+
/** Below this many characters, do not ask at all. The control says so on screen. */
|
|
792
|
+
minQueryLength?: number;
|
|
793
|
+
/** How many rows to show at once. */
|
|
794
|
+
maxRows?: number;
|
|
795
|
+
}
|
|
796
|
+
/**
|
|
797
|
+
* Every source this deployment has, by the name a document would use.
|
|
798
|
+
*
|
|
799
|
+
* A MAP rather than one resolver function, and that is the one deliberate difference
|
|
800
|
+
* from `Scanner`. The control has to know **synchronously** whether a name resolves,
|
|
801
|
+
* because absence here is the *file field's* branch and not the scanner's: a text
|
|
802
|
+
* field with no scanner still collects the answer by typing, but a select whose
|
|
803
|
+
* options come only from a source collects nothing at all, so it must say so instead
|
|
804
|
+
* of rendering an empty chooser.
|
|
805
|
+
*/
|
|
806
|
+
type OptionsSources = Readonly<Record<string, OptionsSource>>;
|
|
807
|
+
/**
|
|
808
|
+
* Optional, and what its absence costs depends on the document. A form with no
|
|
809
|
+
* sourced field never needs one; a field that names a source renders a message where
|
|
810
|
+
* its chooser would be, exactly as the file field does without an uploader.
|
|
811
|
+
*/
|
|
812
|
+
declare const FORMANCY_OPTIONS_SOURCES: InjectionToken<Readonly<Record<string, OptionsSource>>>;
|
|
813
|
+
declare function injectOptionsSources(): OptionsSources | null;
|
|
814
|
+
/** Providing them, for a host that has the lists a document names. */
|
|
815
|
+
declare function provideFormancyOptionsSources(sources: OptionsSources): {
|
|
816
|
+
provide: InjectionToken<Readonly<Record<string, OptionsSource>>>;
|
|
817
|
+
useValue: Readonly<Record<string, OptionsSource>>;
|
|
818
|
+
};
|
|
819
|
+
|
|
820
|
+
/**
|
|
821
|
+
* What a scanner is asked to read.
|
|
822
|
+
*
|
|
823
|
+
* The same contract the React binding declares: the field's resolved label, so a host's
|
|
824
|
+
* camera sheet can say what it is looking for, and its data path, so a host can meter
|
|
825
|
+
* one field's scans.
|
|
826
|
+
*
|
|
827
|
+
* **Nothing about the format is here.** `pattern` is the field's and the engine checks
|
|
828
|
+
* it; a scanner told to pre-filter would be a second validator drifting from the first.
|
|
829
|
+
*/
|
|
830
|
+
interface ScanRequest {
|
|
831
|
+
/** The field's label, resolved through the message catalogue. */
|
|
832
|
+
label: string;
|
|
833
|
+
/** The field's data path, e.g. `serial` or `items[1].serial`. */
|
|
834
|
+
path: string;
|
|
835
|
+
}
|
|
836
|
+
/**
|
|
837
|
+
* Reads a code and reports the text on it.
|
|
838
|
+
*
|
|
839
|
+
* The camera, the permission prompt, the viewfinder and the decoding all belong to
|
|
840
|
+
* whoever mounted the form ([0071](../../../docs/decisions/0071-a-scanner-is-supplied-not-built.md)).
|
|
841
|
+
*
|
|
842
|
+
* **Resolving with `null` means nobody scanned anything** — the sheet was closed. Not a
|
|
843
|
+
* failure, and the field says nothing about it. **Rejecting means the device did not
|
|
844
|
+
* work**, and the field says so in its status region rather than its error region,
|
|
845
|
+
* because a hardware problem is not a wrong answer.
|
|
846
|
+
*
|
|
847
|
+
* **A scanner may not pre-validate**: a value the field's `pattern` refuses is still
|
|
848
|
+
* what the camera read, and dropping it would leave the field looking empty.
|
|
849
|
+
*/
|
|
850
|
+
type Scanner = (request: ScanRequest) => Promise<string | null>;
|
|
851
|
+
/**
|
|
852
|
+
* Optional by design. A `scanner` widget with no scanner behind it renders the ordinary
|
|
853
|
+
* text input and no button — typing was always the field's primary route, so there is
|
|
854
|
+
* nothing to disable and a Scan button that opened nothing would be worse.
|
|
855
|
+
*/
|
|
856
|
+
declare const FORMANCY_SCANNER: InjectionToken<Scanner>;
|
|
857
|
+
declare function injectScanner(): Scanner | null;
|
|
858
|
+
/** Providing one, for a host that has a camera and a decoder. */
|
|
859
|
+
declare function provideFormancyScanner(scan: Scanner): {
|
|
860
|
+
provide: InjectionToken<Scanner>;
|
|
861
|
+
useValue: Scanner;
|
|
862
|
+
};
|
|
863
|
+
|
|
864
|
+
export { DEFAULT_FIELD_COMPONENTS, FORMANCY_ENGINE, FORMANCY_FIELD_CONTEXT, FORMANCY_OPTIONS_SOURCES, FORMANCY_REGISTRY, FORMANCY_RICH_TEXT_EDITOR, FORMANCY_SCANNER, FORMANCY_UPLOADER, FormancyCheckboxField, FormancyComponentOutlet, FormancyDateField, FormancyErrorSummary, FormancyFieldShell, FormancyFieldSlot, FormancyForm, FormancyLayout, FormancyNumberField, FormancyRadioGroupField, FormancyRepeaterSection, FormancyResumeNotice, FormancyRichInline, FormancyRichText, FormancySelectField, FormancyTextField, FormancyTextareaField, injectEngine, injectField, injectFieldContext, injectOptionsSources, injectRepeater, injectRichTextEditorFactory, injectScanner, injectSubmit, injectUploader, injectWizard, provideFormancy, provideFormancyOptionsSources, provideFormancyRegistry, provideFormancyRichTextEditor, provideFormancyScanner, provideFormancyUploader };
|
|
865
|
+
export type { FieldBinding, FormancyFieldContext, FormancyRegistry, OptionsRequest, OptionsSource, OptionsSources, RepeaterBinding, ResumeMigration, RichTextEditorFactory, RichTextEditorHandle, RichTextEditorMount, ScanRequest, Scanner, StoredFile, SubmitOutcome, Uploader, WizardBinding };
|