@petrarca/sonnet-schema-designer 0.8.0 → 0.9.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 +7 -0
- package/dist/chunk-MVPNMAVL.js +3178 -0
- package/dist/chunk-MVPNMAVL.js.map +1 -0
- package/dist/index-Cu6JZdYj.d.ts +875 -0
- package/dist/index.d.ts +126 -355
- package/dist/index.js +2506 -1761
- package/dist/index.js.map +1 -1
- package/dist/questionnaire/index.d.ts +3 -0
- package/dist/questionnaire/index.js +153 -0
- package/dist/questionnaire/index.js.map +1 -0
- package/package.json +14 -10
|
@@ -0,0 +1,875 @@
|
|
|
1
|
+
import { JsonSchemaProperty, JsonSchema, UISchema, UILayoutConfig, UISchemaOptions, UILayoutGroupConfig, UILayoutRowConfig, ValueType, Binding } from '@petrarca/sonnet-core/schema';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Path navigation over a JsonSchema tree.
|
|
6
|
+
*
|
|
7
|
+
* A path is a list of typed segments from the root down to a property. Most
|
|
8
|
+
* segments are property keys (`{kind:"prop"}`) -- object children nest through
|
|
9
|
+
* `properties`, array-of-object children through `items.properties`, and the
|
|
10
|
+
* navigator descends through `items` transparently so an array and its item
|
|
11
|
+
* object share one editing surface.
|
|
12
|
+
*
|
|
13
|
+
* Logic constructs introduce non-property segments: `{kind:"branch"}` for a
|
|
14
|
+
* `oneOf`/`allOf` element, and `{kind:"slot"}` for a fixed schema slot
|
|
15
|
+
* (`then`/`else`/`items`). The discriminated union keeps the distinction
|
|
16
|
+
* explicit and type-checked; a single codec (pathKey/parsePathKey) owns all
|
|
17
|
+
* conversion to/from strings.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/** A fixed schema slot that holds a single sub-schema. */
|
|
21
|
+
type PathSlot = "then" | "else" | "items";
|
|
22
|
+
/**
|
|
23
|
+
* The array a conditional (if/then/else) member lives in. `allOf` = every
|
|
24
|
+
* condition must hold (AND); `anyOf` = any condition suffices (OR). Maps to
|
|
25
|
+
* FHIR `enableBehavior` "all" / "any".
|
|
26
|
+
*/
|
|
27
|
+
type ConditionCombinator = "allOf" | "anyOf";
|
|
28
|
+
/** One step in a property path. */
|
|
29
|
+
type PathSegment = {
|
|
30
|
+
kind: "prop";
|
|
31
|
+
key: string;
|
|
32
|
+
} | {
|
|
33
|
+
kind: "branch";
|
|
34
|
+
index: number;
|
|
35
|
+
} | {
|
|
36
|
+
kind: "condition";
|
|
37
|
+
combinator: ConditionCombinator;
|
|
38
|
+
index: number;
|
|
39
|
+
} | {
|
|
40
|
+
kind: "slot";
|
|
41
|
+
slot: PathSlot;
|
|
42
|
+
} | {
|
|
43
|
+
kind: "def";
|
|
44
|
+
name: string;
|
|
45
|
+
};
|
|
46
|
+
type PropertyPath = PathSegment[];
|
|
47
|
+
/**
|
|
48
|
+
* Whether a property key is reserved by the path codec. Keys matching
|
|
49
|
+
* `/^#(branch|condition|def|slot):/` would corrupt the pathKey/parsePathKey
|
|
50
|
+
* round-trip. The designer guards against these at rename/add time.
|
|
51
|
+
*/
|
|
52
|
+
declare function isReservedKey(key: string): boolean;
|
|
53
|
+
/** Construct a property-key segment. */
|
|
54
|
+
declare function prop(key: string): PathSegment;
|
|
55
|
+
/** Construct a branch (oneOf element) segment. */
|
|
56
|
+
declare function branch(index: number): PathSegment;
|
|
57
|
+
/** Construct a condition (if/then/else member) segment for a combinator array. */
|
|
58
|
+
declare function condition(index: number, combinator?: ConditionCombinator): PathSegment;
|
|
59
|
+
/** Construct a fixed-slot segment. */
|
|
60
|
+
declare function slot(name: PathSlot): PathSegment;
|
|
61
|
+
/** Construct a definition ($defs entry) segment. Root-only in practice. */
|
|
62
|
+
declare function def(name: string): PathSegment;
|
|
63
|
+
/** Serialize a path to a stable string (React key, selection id, scope value). */
|
|
64
|
+
declare function pathKey(path: PropertyPath): string;
|
|
65
|
+
/** Parse a string produced by {@link pathKey} back into a path. */
|
|
66
|
+
declare function parsePathKey(key: string): PropertyPath;
|
|
67
|
+
/** Whether two path segments refer to the same position. */
|
|
68
|
+
declare function sameSegment(a: PathSegment, b: PathSegment): boolean;
|
|
69
|
+
/** Whether two paths refer to the same node (structural comparison). */
|
|
70
|
+
declare function samePath(a: PropertyPath | null, b: PropertyPath | null): boolean;
|
|
71
|
+
/** How a child slot is presented and mutated. */
|
|
72
|
+
type SlotKind = "property" | "branch" | "conditional" | "definition";
|
|
73
|
+
/** A single editable child of a property (or the root). */
|
|
74
|
+
interface ChildSlot {
|
|
75
|
+
/** Segment appended to the parent path to reach this child. */
|
|
76
|
+
segment: PathSegment;
|
|
77
|
+
/** Display label for the tree row. */
|
|
78
|
+
label: string;
|
|
79
|
+
/** The child schema. */
|
|
80
|
+
property: JsonSchemaProperty;
|
|
81
|
+
kind: SlotKind;
|
|
82
|
+
removable: boolean;
|
|
83
|
+
reorderable: boolean;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* The properties map that holds a property's named children, or undefined when
|
|
87
|
+
* it has none. Object children live in `properties`; array-of-object children
|
|
88
|
+
* live in `items.properties`. (Logic constructs use branch/slot children and
|
|
89
|
+
* are surfaced via {@link childSlots}, not here.)
|
|
90
|
+
*/
|
|
91
|
+
declare function childContainer(prop: JsonSchemaProperty): Record<string, JsonSchemaProperty> | undefined;
|
|
92
|
+
/**
|
|
93
|
+
* The reusable definitions map of a schema. Canonically 2020-12 `$defs`; also
|
|
94
|
+
* reads Draft-7 `definitions` so both dialects surface as editable nodes. New
|
|
95
|
+
* definitions are always authored into `$defs` (see mutate.addDefinition).
|
|
96
|
+
*/
|
|
97
|
+
declare function definitionsOf(property: JsonSchemaProperty): Record<string, JsonSchemaProperty> | undefined;
|
|
98
|
+
/**
|
|
99
|
+
* The ordered child slots of a property (the single extension point for the
|
|
100
|
+
* tree/inspector/mutations). Surfaces object/array property children, oneOf
|
|
101
|
+
* branches, if/then/else conditionals (allOf/anyOf members), and $defs
|
|
102
|
+
* definitions (root-only in practice).
|
|
103
|
+
*/
|
|
104
|
+
declare function childSlots(property: JsonSchemaProperty): ChildSlot[];
|
|
105
|
+
/**
|
|
106
|
+
* Wrap a JsonSchema root as an object-shaped JsonSchemaProperty so the path
|
|
107
|
+
* navigation machinery can traverse it with the same stepInto logic as any
|
|
108
|
+
* nested property. Pass the keys needed by the caller; unused fields default
|
|
109
|
+
* to undefined.
|
|
110
|
+
*/
|
|
111
|
+
declare function schemaAsRootProp(schema: JsonSchema): JsonSchemaProperty;
|
|
112
|
+
/** Resolve a path to its property definition, or undefined if not found. */
|
|
113
|
+
declare function getProperty(schema: JsonSchema, path: PropertyPath): JsonSchemaProperty | undefined;
|
|
114
|
+
/** Whether a property has (or can have) child slots (properties, branches, conditionals). */
|
|
115
|
+
declare function isContainer(prop: JsonSchemaProperty): boolean;
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Pure edit operations over { schema, uiSchema }.
|
|
119
|
+
*
|
|
120
|
+
* Every function returns a new pair; nothing mutates in place. Operations keep
|
|
121
|
+
* the schema and uiSchema consistent -- renaming a property rewrites the property
|
|
122
|
+
* key, the uiSchema entry, the required entry, and any x-ui-order reference at
|
|
123
|
+
* the same level. Mirrors the graph-model/mutate.ts discipline.
|
|
124
|
+
*/
|
|
125
|
+
|
|
126
|
+
type UiSchemaMap = Record<string, UISchema>;
|
|
127
|
+
interface DesignerModel {
|
|
128
|
+
schema: JsonSchema;
|
|
129
|
+
uiSchema: UiSchemaMap;
|
|
130
|
+
}
|
|
131
|
+
/** Kinds that can be created as a new property. */
|
|
132
|
+
type NewPropertyKind = "scalar" | "object" | "array" | "oneOf";
|
|
133
|
+
/** A default property for a newly created property of a given kind. */
|
|
134
|
+
declare function newProperty(kind: NewPropertyKind): JsonSchemaProperty;
|
|
135
|
+
/** A default oneOf branch: an object with a const-valued `kind` discriminator. */
|
|
136
|
+
declare function newBranch(value: string): JsonSchemaProperty;
|
|
137
|
+
/** Add a property under a parent path (root when empty). Returns the new model. */
|
|
138
|
+
declare function addProperty(model: DesignerModel, parentPath: PropertyPath, kind: NewPropertyKind): {
|
|
139
|
+
model: DesignerModel;
|
|
140
|
+
path: PropertyPath;
|
|
141
|
+
};
|
|
142
|
+
/** Add a branch to the oneOf property at `path`. Returns the new model + branch path. */
|
|
143
|
+
declare function addBranch(model: DesignerModel, path: PropertyPath): {
|
|
144
|
+
model: DesignerModel;
|
|
145
|
+
path: PropertyPath;
|
|
146
|
+
};
|
|
147
|
+
/** Remove the branch at `path` (a branch segment). Returns the new model. */
|
|
148
|
+
declare function removeBranch(model: DesignerModel, path: PropertyPath): DesignerModel;
|
|
149
|
+
/** Move the branch at `path` up (-1) or down (+1) among its siblings. */
|
|
150
|
+
declare function moveBranch(model: DesignerModel, path: PropertyPath, direction: -1 | 1): DesignerModel;
|
|
151
|
+
/**
|
|
152
|
+
* A seed conditional member: an empty `if` (no field yet) plus an empty `then`.
|
|
153
|
+
* The condition is filled in by setCondition; `then` fields are added like any
|
|
154
|
+
* object's properties.
|
|
155
|
+
*/
|
|
156
|
+
declare function newCondition(): JsonSchemaProperty;
|
|
157
|
+
/**
|
|
158
|
+
* Add a conditional (if/then/else) to the object at `objectPath` (root when
|
|
159
|
+
* empty), appended to the given combinator array (`allOf` = AND, default;
|
|
160
|
+
* `anyOf` = OR). Returns the new model + the condition path.
|
|
161
|
+
*/
|
|
162
|
+
declare function addConditional(model: DesignerModel, objectPath: PropertyPath, combinator?: ConditionCombinator): {
|
|
163
|
+
model: DesignerModel;
|
|
164
|
+
path: PropertyPath;
|
|
165
|
+
};
|
|
166
|
+
/** Remove the conditional at `path` (a condition segment). Returns the new model. */
|
|
167
|
+
declare function removeConditional(model: DesignerModel, path: PropertyPath): DesignerModel;
|
|
168
|
+
/** Write the `if` of the conditional at `path` from a condition triple. */
|
|
169
|
+
declare function setCondition(model: DesignerModel, path: PropertyPath, ifSchema: JsonSchemaProperty): DesignerModel;
|
|
170
|
+
/** Add an empty `else` branch to the conditional at `path`. */
|
|
171
|
+
declare function addElse(model: DesignerModel, path: PropertyPath): DesignerModel;
|
|
172
|
+
/** Remove the `else` branch from the conditional at `path`. */
|
|
173
|
+
declare function removeElse(model: DesignerModel, path: PropertyPath): DesignerModel;
|
|
174
|
+
/**
|
|
175
|
+
* Switch the combinator (AND/OR) for the conditionals of the object at
|
|
176
|
+
* `objectPath`. Migrates every conditional member from the current combinator
|
|
177
|
+
* array to `next`. Non-conditional members (structural allOf/anyOf entries)
|
|
178
|
+
* stay put. No-op when already using `next` or when there are no conditionals.
|
|
179
|
+
*/
|
|
180
|
+
declare function setConditionalBehavior(model: DesignerModel, objectPath: PropertyPath, next: ConditionCombinator): DesignerModel;
|
|
181
|
+
/** Delete the property at path. Returns the new model. */
|
|
182
|
+
declare function deleteProperty(model: DesignerModel, path: PropertyPath): DesignerModel;
|
|
183
|
+
/** Rename the property at path. Rewrites key, order, required, and uiSchema. */
|
|
184
|
+
declare function renameProperty(model: DesignerModel, path: PropertyPath, nextKey: string): {
|
|
185
|
+
model: DesignerModel;
|
|
186
|
+
path: PropertyPath;
|
|
187
|
+
};
|
|
188
|
+
/** Move the property at path up (-1) or down (+1) among its siblings. */
|
|
189
|
+
declare function moveProperty(model: DesignerModel, path: PropertyPath, direction: -1 | 1): DesignerModel;
|
|
190
|
+
/** Toggle whether the property at path is required among its siblings. */
|
|
191
|
+
declare function setRequired(model: DesignerModel, path: PropertyPath, required: boolean): DesignerModel;
|
|
192
|
+
/** Replace the property definition at path (merges the given partial). */
|
|
193
|
+
declare function updateProperty(model: DesignerModel, path: PropertyPath, patch: Partial<JsonSchemaProperty>): DesignerModel;
|
|
194
|
+
/** The kind a property can be changed to: a JSON Schema type, or a "oneOf" union. */
|
|
195
|
+
type PropertyTypeChoice = NonNullable<JsonSchemaProperty["type"]> | "oneOf";
|
|
196
|
+
/**
|
|
197
|
+
* Change the kind of the property at path, reshaping structural keys: object
|
|
198
|
+
* adds an empty `properties`; array adds `items`; oneOf adds branches + a
|
|
199
|
+
* discriminator; leaving a kind strips its keys. Type-specific validation keys
|
|
200
|
+
* that no longer apply are dropped.
|
|
201
|
+
*/
|
|
202
|
+
declare function changePropertyType(model: DesignerModel, path: PropertyPath, next: PropertyTypeChoice): DesignerModel;
|
|
203
|
+
/**
|
|
204
|
+
* Point the property at path to a definition by name, writing a local `$ref`
|
|
205
|
+
* (`#/$defs/<name>`) and stripping type-specific keys. Turns a plain property
|
|
206
|
+
* into a reference usage. No-op for the root or an unknown target.
|
|
207
|
+
*/
|
|
208
|
+
declare function setReference(model: DesignerModel, path: PropertyPath, name: string): DesignerModel;
|
|
209
|
+
/**
|
|
210
|
+
* Add a new empty object definition under `$defs`. Returns the new model and the
|
|
211
|
+
* path to the created definition for selection.
|
|
212
|
+
*/
|
|
213
|
+
declare function addDefinition(model: DesignerModel, name?: string): {
|
|
214
|
+
model: DesignerModel;
|
|
215
|
+
path: PropertyPath;
|
|
216
|
+
};
|
|
217
|
+
/**
|
|
218
|
+
* Rename a `$defs` entry, preserving order, and rewrite every local `$ref` that
|
|
219
|
+
* targets it (both `#/$defs/` and `#/definitions/` forms) to the new pointer.
|
|
220
|
+
* No-op on collision, reserved, or empty names.
|
|
221
|
+
*/
|
|
222
|
+
declare function renameDefinition(model: DesignerModel, oldName: string, newName: string): {
|
|
223
|
+
model: DesignerModel;
|
|
224
|
+
path: PropertyPath;
|
|
225
|
+
};
|
|
226
|
+
/**
|
|
227
|
+
* Remove a `$defs` entry. Blocked (no-op) while any local `$ref` still targets
|
|
228
|
+
* it; the UI surfaces the usage count. Drops the `$defs` map when it empties.
|
|
229
|
+
*/
|
|
230
|
+
declare function removeDefinition(model: DesignerModel, name: string): DesignerModel;
|
|
231
|
+
/** Whether a definition is referenced anywhere (for UI delete-guard messaging). */
|
|
232
|
+
declare function definitionUsageCount(schema: JsonSchema, name: string): number;
|
|
233
|
+
/** One enum choice: a value and an optional display label. */
|
|
234
|
+
interface EnumChoice {
|
|
235
|
+
value: string;
|
|
236
|
+
label?: string;
|
|
237
|
+
}
|
|
238
|
+
declare function setEnum(model: DesignerModel, path: PropertyPath, choices: EnumChoice[]): DesignerModel;
|
|
239
|
+
/** Read the current enum choices for a property + its uiSchema entry. */
|
|
240
|
+
declare function readEnumChoices(prop: JsonSchemaProperty, uiEntry: UISchema | undefined): EnumChoice[];
|
|
241
|
+
/** Table presentation hints for a property (embedded in the schema). */
|
|
242
|
+
interface TableHints {
|
|
243
|
+
cell?: string;
|
|
244
|
+
cellOptions?: Record<string, unknown>;
|
|
245
|
+
column?: NonNullable<JsonSchemaProperty["x-ui-column"]>;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Set (or clear) a property's table hints: x-ui-cell, x-ui-cell-options, and
|
|
249
|
+
* x-ui-column. These live embedded on the property (EntityTable reads them
|
|
250
|
+
* there). Empty values clear the corresponding key.
|
|
251
|
+
*/
|
|
252
|
+
declare function setTableHints(model: DesignerModel, path: PropertyPath, hints: TableHints): DesignerModel;
|
|
253
|
+
/** Read a property's current table hints. */
|
|
254
|
+
declare function readTableHints(prop: JsonSchemaProperty): TableHints;
|
|
255
|
+
/** Read the UISchema entry for a property path, or undefined. */
|
|
256
|
+
declare function getUiEntryAtPath(uiSchema: UiSchemaMap, path: PropertyPath): UISchema | undefined;
|
|
257
|
+
/** Set (or clear) the UISchema entry at a property path (any nesting depth). */
|
|
258
|
+
declare function setUiEntry(model: DesignerModel, path: PropertyPath, entry: UISchema | undefined): DesignerModel;
|
|
259
|
+
/**
|
|
260
|
+
* Set (or clear) the x-ui-layout of an object/array property at path. Object
|
|
261
|
+
* layout lives on the property; array layout lives on `items`.
|
|
262
|
+
*/
|
|
263
|
+
declare function setPropertyLayout(model: DesignerModel, path: PropertyPath, layout: UILayoutConfig | undefined): DesignerModel;
|
|
264
|
+
/** Set (or clear) the root form layout (uiSchema["x-ui-layout"]). */
|
|
265
|
+
declare function setRootLayout(model: DesignerModel, layout: UILayoutConfig | undefined): DesignerModel;
|
|
266
|
+
/**
|
|
267
|
+
* Set (or clear) the root layout rows (uiSchema["x-ui-layout"].rows),
|
|
268
|
+
* preserving other x-ui-layout keys. Clearing rows to empty removes the key;
|
|
269
|
+
* if x-ui-layout then has no keys, it is removed entirely.
|
|
270
|
+
*/
|
|
271
|
+
declare function setRootLayoutRows(model: DesignerModel, rows: UILayoutConfig["rows"]): DesignerModel;
|
|
272
|
+
/**
|
|
273
|
+
* Set the layout rows for a scope: the root form (path []) or an object/array
|
|
274
|
+
* container (its property path). Root rows live in uiSchema["x-ui-layout"];
|
|
275
|
+
* a container's rows live on the property's x-ui-layout (array -> items).
|
|
276
|
+
* Preserves other x-ui-layout keys.
|
|
277
|
+
*/
|
|
278
|
+
declare function setScopedLayoutRows(model: DesignerModel, scopePath: PropertyPath, rows: UILayoutConfig["rows"]): DesignerModel;
|
|
279
|
+
/** Read the current x-ui-layout for a scope (root or a container property). */
|
|
280
|
+
declare function getScopedLayout(schema: JsonSchema, uiSchema: UiSchemaMap, scopePath: PropertyPath): UILayoutConfig | undefined;
|
|
281
|
+
/**
|
|
282
|
+
* Merge scalar layout options (direction/gap/labels/compact) into a scope's
|
|
283
|
+
* x-ui-layout, preserving rows and other keys. Undefined values clear the key.
|
|
284
|
+
*/
|
|
285
|
+
declare function setScopedLayoutOptions(model: DesignerModel, scopePath: PropertyPath, options: Pick<UILayoutConfig, "direction" | "gap" | "labels" | "compact">): DesignerModel;
|
|
286
|
+
/**
|
|
287
|
+
* Set the group definitions for a scope, preserving other x-ui-layout keys.
|
|
288
|
+
* Passing an empty list (or undefined) clears groups, returning the scope to the
|
|
289
|
+
* flat row layout. Root groups live in uiSchema["x-ui-layout"]; a container's
|
|
290
|
+
* live on the property's x-ui-layout (array -> items).
|
|
291
|
+
*
|
|
292
|
+
* Groups and flat rows are mutually exclusive (the renderer gives groups
|
|
293
|
+
* precedence), so setting groups drops `rows` -- otherwise the orphaned list
|
|
294
|
+
* would be invisible in both the form and the designer.
|
|
295
|
+
*/
|
|
296
|
+
declare function setScopedLayoutGroups(model: DesignerModel, scopePath: PropertyPath, groups: UILayoutConfig["groups"]): DesignerModel;
|
|
297
|
+
/** Append an empty group. The label defaults to the next ordinal ("Group 3"). */
|
|
298
|
+
declare function addLayoutGroup(model: DesignerModel, scopePath: PropertyPath, label?: string): DesignerModel;
|
|
299
|
+
/** Rename one group. No-op for an unknown index or a blank label. */
|
|
300
|
+
declare function renameLayoutGroup(model: DesignerModel, scopePath: PropertyPath, groupIndex: number, label: string): DesignerModel;
|
|
301
|
+
/**
|
|
302
|
+
* Place a property into one group's row, removing it from wherever it currently
|
|
303
|
+
* sits across *all* groups. Passing a null target returns it to the pool.
|
|
304
|
+
*
|
|
305
|
+
* Needed because each group owns its own rows: without a cross-group operation a
|
|
306
|
+
* field could only be moved between groups by visiting its old group first.
|
|
307
|
+
*/
|
|
308
|
+
declare function placeFieldInGroup(model: DesignerModel, scopePath: PropertyPath, key: string, target: {
|
|
309
|
+
groupIndex: number;
|
|
310
|
+
rowIndex: number;
|
|
311
|
+
} | null): DesignerModel;
|
|
312
|
+
/** Move one group left (-1) or right (+1). No-op at the ends. */
|
|
313
|
+
declare function moveLayoutGroup(model: DesignerModel, scopePath: PropertyPath, groupIndex: number, direction: -1 | 1): DesignerModel;
|
|
314
|
+
/**
|
|
315
|
+
* Set (or clear) a group's show/hide rule. Passing undefined makes the group
|
|
316
|
+
* unconditional.
|
|
317
|
+
*/
|
|
318
|
+
declare function setLayoutGroupRule(model: DesignerModel, scopePath: PropertyPath, groupIndex: number, rule: UILayoutGroupConfig["rule"]): DesignerModel;
|
|
319
|
+
/**
|
|
320
|
+
* Set the group presentation for a scope ("groups", "stepper", or a custom
|
|
321
|
+
* template name). Passing undefined restores the default group presentation.
|
|
322
|
+
*/
|
|
323
|
+
declare function setScopedLayoutVariant(model: DesignerModel, scopePath: PropertyPath, variant: UILayoutConfig["variant"]): DesignerModel;
|
|
324
|
+
/**
|
|
325
|
+
* Remove one group. Its rows are not discarded: removing the last remaining group
|
|
326
|
+
* flattens them back into the scope's flat row layout, so a single click can
|
|
327
|
+
* never destroy every field placement.
|
|
328
|
+
*/
|
|
329
|
+
declare function removeLayoutGroup(model: DesignerModel, scopePath: PropertyPath, groupIndex: number): DesignerModel;
|
|
330
|
+
/** Replace the rows of one group in a scope. No-op for an unknown group index. */
|
|
331
|
+
declare function setScopedGroupRows(model: DesignerModel, scopePath: PropertyPath, groupIndex: number, rows: UILayoutRowConfig[]): DesignerModel;
|
|
332
|
+
/**
|
|
333
|
+
* Switch a scope between flat rows and groups, carrying the existing layout over
|
|
334
|
+
* so no field placement is lost: rows become the first group's rows, and leaving
|
|
335
|
+
* group mode flattens every group's rows back into one list.
|
|
336
|
+
*/
|
|
337
|
+
declare function setScopedLayoutMode(model: DesignerModel, scopePath: PropertyPath, mode: "rows" | "groups"): DesignerModel;
|
|
338
|
+
/** Set (or clear) the root global options (uiSchema["x-ui-globalOptions"]). */
|
|
339
|
+
declare function setGlobalOptions(model: DesignerModel, options: UISchemaOptions | undefined): DesignerModel;
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Value types as answer types -- a value type contributed to the questionnaire
|
|
343
|
+
* designer: a question whose `$ref` is the type's id is that answer type, and
|
|
344
|
+
* its settings are fields the panel shows, read from and written to the
|
|
345
|
+
* question through the contribution. Sonnet contributes the core types the
|
|
346
|
+
* same way an application contributes its own. Pure, no React. See
|
|
347
|
+
* docs/design/value-types.md (workspace).
|
|
348
|
+
*/
|
|
349
|
+
|
|
350
|
+
/** A value type's settings: the panel's fields, read from and written to the question. */
|
|
351
|
+
interface ValueTypeSettings {
|
|
352
|
+
/** The fields, by name, as JSON Schema properties of the panel's form. */
|
|
353
|
+
fields: Record<string, JsonSchemaProperty>;
|
|
354
|
+
/** The settings' values, read from the question's property and hints. */
|
|
355
|
+
read(prop: JsonSchemaProperty, ui: UISchema): Record<string, unknown>;
|
|
356
|
+
/** What the values write: keywords beside the `$ref`, and `x-ui-options` keys -- undefined removes one. */
|
|
357
|
+
write(values: Record<string, unknown>): {
|
|
358
|
+
prop: Partial<JsonSchemaProperty>;
|
|
359
|
+
options: Record<string, unknown>;
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
/** A value type contributed as an answer type of the questionnaire designer. */
|
|
363
|
+
interface ValueTypeAnswer {
|
|
364
|
+
type: ValueType;
|
|
365
|
+
/** The keywords it narrows beside its `$ref` (the quantity's `properties`). */
|
|
366
|
+
keywords?: string[];
|
|
367
|
+
/** The `x-ui-options` keys its settings use (the quantity's `units`). */
|
|
368
|
+
options?: string[];
|
|
369
|
+
/** Whether a property of the type is one the designer understands; default: any. */
|
|
370
|
+
accepts?(prop: JsonSchemaProperty): boolean;
|
|
371
|
+
settings?: ValueTypeSettings;
|
|
372
|
+
}
|
|
373
|
+
/** The quantity as an answer type: narrowed at most to its units, each in its range. */
|
|
374
|
+
declare const QUANTITY_ANSWER: ValueTypeAnswer;
|
|
375
|
+
/** The binding a coded value's settings write; none without a list. */
|
|
376
|
+
declare function codingBindingOf(values: Record<string, unknown>): Binding | undefined;
|
|
377
|
+
/** The coded value as an answer type: a code from the list its binding names. */
|
|
378
|
+
declare const CODING_ANSWER: ValueTypeAnswer;
|
|
379
|
+
/** The codeable concept as an answer type: a concept coded in its first list and in further ones, or its text. */
|
|
380
|
+
declare const CODEABLE_CONCEPT_ANSWER: ValueTypeAnswer;
|
|
381
|
+
/** The value types sonnet contributes: the core types. */
|
|
382
|
+
declare const DEFAULT_VALUE_TYPES: readonly ValueTypeAnswer[];
|
|
383
|
+
/** The value type a property is, among those contributed -- by its `$ref`, or that or a text; undefined when none. */
|
|
384
|
+
declare function valueTypeOf(prop: JsonSchemaProperty, types: readonly ValueTypeAnswer[]): ValueTypeAnswer | undefined;
|
|
385
|
+
/** The contribution of an answer type, when it is a value type. */
|
|
386
|
+
declare function valueTypeAnswer(answerType: string, types: readonly ValueTypeAnswer[]): ValueTypeAnswer | undefined;
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Answer types -- a question's kind of answer, as a pattern of schema keywords
|
|
390
|
+
* and form hints. Pure, no React.
|
|
391
|
+
*
|
|
392
|
+
* An answer type is never stored: `recognizeAnswerType` finds it in a property
|
|
393
|
+
* and its effective hints, and names the keywords the pattern does not account
|
|
394
|
+
* for (the question's extra settings, kept untouched). See
|
|
395
|
+
* docs/design/questionnaires.md, "Answer types".
|
|
396
|
+
*/
|
|
397
|
+
|
|
398
|
+
/** The answer types the questionnaire designer knows itself. */
|
|
399
|
+
type BuiltinAnswerType = "yesNo" | "shortText" | "longText" | "integer" | "decimal" | "date" | "time" | "dateTime" | "singleChoice" | "multipleChoice" | "choiceWithText" | "listOfText" | "parts";
|
|
400
|
+
/**
|
|
401
|
+
* An answer type: a built-in one, or a contributed value type -- by its id
|
|
402
|
+
* (see valueTypes.ts).
|
|
403
|
+
*/
|
|
404
|
+
type AnswerType = BuiltinAnswerType | (string & {});
|
|
405
|
+
/** What each built-in answer type is called where an author sees it. */
|
|
406
|
+
declare const ANSWER_TYPE_LABELS: Record<BuiltinAnswerType, string>;
|
|
407
|
+
/** Whether an answer type is one the designer knows itself, not a contributed value type. */
|
|
408
|
+
declare function isBuiltinAnswerType(type: string): type is BuiltinAnswerType;
|
|
409
|
+
/** What an answer type is called: its label, or its contribution's. */
|
|
410
|
+
declare function answerTypeLabel(type: AnswerType, types?: readonly ValueTypeAnswer[]): string;
|
|
411
|
+
/**
|
|
412
|
+
* The container widget a choice with text renders with: it arranges its two
|
|
413
|
+
* children, `choice` and `text`, each rendered by its own widget -- a select,
|
|
414
|
+
* radio buttons, a terminology picker -- resolved as for any field.
|
|
415
|
+
*/
|
|
416
|
+
declare const CHOICE_WITH_TEXT_WIDGET = "choice-with-text";
|
|
417
|
+
/** Whether an answer type is a container -- its children answers of their own, it no answer itself. */
|
|
418
|
+
declare function isContainerType(type: AnswerType): boolean;
|
|
419
|
+
/** A recognized answer: its type, and the keywords the pattern leaves unexplained. */
|
|
420
|
+
interface RecognizedAnswer {
|
|
421
|
+
type: AnswerType;
|
|
422
|
+
/** Schema keywords and hints beyond the pattern -- shown as extra settings, kept as they are. */
|
|
423
|
+
extras: string[];
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* The option of a choice with text whose choice asks for the text ("other,
|
|
427
|
+
* please describe"); none for the variant whose text is an optional note.
|
|
428
|
+
*
|
|
429
|
+
* The note variant has `choice` and `text` as properties. The other variant
|
|
430
|
+
* has only `choice`, and one conditional of its own: if the choice is that
|
|
431
|
+
* option, then `text`, required.
|
|
432
|
+
*/
|
|
433
|
+
declare function choiceTextWhen(prop: JsonSchemaProperty): string | undefined;
|
|
434
|
+
/** The conditional of a choice with text's "other" variant: if the choice is `when`, then the text, required. */
|
|
435
|
+
declare function otherTextMember(when: string, text: JsonSchemaProperty): JsonSchemaProperty;
|
|
436
|
+
/** A choice with text's choice and text, wherever its variant keeps the text -- unchecked. */
|
|
437
|
+
declare function rawChoiceAndText(prop: JsonSchemaProperty): {
|
|
438
|
+
choice?: JsonSchemaProperty;
|
|
439
|
+
text?: JsonSchemaProperty;
|
|
440
|
+
};
|
|
441
|
+
/**
|
|
442
|
+
* The answer type a property follows, with its extra settings; null when the
|
|
443
|
+
* property follows no answer type -- the questionnaire designer then does not
|
|
444
|
+
* understand it. A widget the type does not know is an extra setting, not a
|
|
445
|
+
* reason: the type comes from the schema's shape.
|
|
446
|
+
*/
|
|
447
|
+
declare function recognizeAnswerType(prop: JsonSchemaProperty, ui: UISchema, types?: readonly ValueTypeAnswer[]): RecognizedAnswer | null;
|
|
448
|
+
/** The hints a property carries on itself (embedded x-ui-* keys), as a UISchema entry. */
|
|
449
|
+
declare function embeddedHints(prop: JsonSchemaProperty): UISchema;
|
|
450
|
+
/** A new answer's schema and hints: what writing an answer type sets, as one unit. */
|
|
451
|
+
interface AnswerFragment {
|
|
452
|
+
prop: JsonSchemaProperty;
|
|
453
|
+
ui: UISchema;
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* The schema and hints of an answer type -- the reverse of recognizeAnswerType:
|
|
457
|
+
* `recognizeAnswerType(fragment.prop, fragment.ui)` is the type, with no extras.
|
|
458
|
+
*/
|
|
459
|
+
declare function answerTypeFragment(type: AnswerType, options?: string[]): AnswerFragment;
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Condition builder: the bridge between a friendly (property, operator, value)
|
|
463
|
+
* triple and the JSON Schema `if` sub-schema the renderer evaluates.
|
|
464
|
+
*
|
|
465
|
+
* An `if` tests a sibling property. Operators map to standard schema keywords
|
|
466
|
+
* (mirrors the FHIR operator table in packages/sonnet-fhir/docs/design/fhir-questionnaire-adapter.md, tier 1):
|
|
467
|
+
*
|
|
468
|
+
* = -> { properties: { field: { const: value } }, required: [field] }
|
|
469
|
+
* != -> { properties: { field: { not: { const: value } } }, required: [field] }
|
|
470
|
+
* > >= < <= -> exclusiveMinimum / minimum / exclusiveMaximum / maximum
|
|
471
|
+
* exists -> { required: [field] }
|
|
472
|
+
*
|
|
473
|
+
* A value operator always adds `required: [field]` so an absent field does not
|
|
474
|
+
* vacuously satisfy the condition.
|
|
475
|
+
*/
|
|
476
|
+
|
|
477
|
+
type ConditionOperator = "=" | "!=" | ">" | ">=" | "<" | "<=" | "exists";
|
|
478
|
+
/** A condition as edited in the inspector. */
|
|
479
|
+
interface Condition {
|
|
480
|
+
field: string;
|
|
481
|
+
operator: ConditionOperator;
|
|
482
|
+
/** Raw string value from the inspector; coerced to number where numeric. */
|
|
483
|
+
value: string;
|
|
484
|
+
/** On a coded question that also takes a text: the value is that text, not a code. */
|
|
485
|
+
asText?: boolean;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* Show only if -- when a question is asked: conditions on the answers to other
|
|
490
|
+
* questions of its section, all of them or any. Pure, no React.
|
|
491
|
+
*
|
|
492
|
+
* In the schema a conditional item is an `allOf` member of its section,
|
|
493
|
+
* `{ if, then: { properties: { <item> }, required } }`: the renderer adds the
|
|
494
|
+
* item to the form while the `if` holds and removes it -- with its answer --
|
|
495
|
+
* when it does not. "All" is one `if` testing every condition; "any" is an `if`
|
|
496
|
+
* of `anyOf` single conditions. Each condition is the schema designer's
|
|
497
|
+
* (model/conditions.ts). A member is understood only when writing back what
|
|
498
|
+
* was read gives exactly the member: what the questionnaire designer cannot
|
|
499
|
+
* show as conditions stays locked. See docs/design/questionnaires.md.
|
|
500
|
+
*/
|
|
501
|
+
|
|
502
|
+
/** When an item is shown: all of its conditions hold, or any of them. */
|
|
503
|
+
interface ShowIf {
|
|
504
|
+
behavior: "all" | "any";
|
|
505
|
+
conditions: Condition[];
|
|
506
|
+
}
|
|
507
|
+
/** A section's field, by key -- the comparand is written as its type takes it. */
|
|
508
|
+
type FieldType = (field: string) => JsonSchemaProperty | undefined;
|
|
509
|
+
/** The `if` a show-if is written as. */
|
|
510
|
+
declare function showIfToIf(showIf: ShowIf, typeOf: FieldType): JsonSchemaProperty;
|
|
511
|
+
/** A conditional member's `then`: the items it shows and which of them are required. */
|
|
512
|
+
interface ConditionalItems {
|
|
513
|
+
showIf: ShowIf;
|
|
514
|
+
properties: Record<string, JsonSchemaProperty>;
|
|
515
|
+
required: string[];
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* The show-if of a section's `allOf` member and the items it shows, or null
|
|
519
|
+
* when the member is not one the questionnaire designer understands -- its `if`
|
|
520
|
+
* is more than conditions on the section's fields, or its `then` more than items.
|
|
521
|
+
*/
|
|
522
|
+
declare function readConditional(member: JsonSchemaProperty, sectionKeys: ReadonlySet<string>, typeOf: FieldType): ConditionalItems | null;
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* Limits -- what an answer may be: a text's length and pattern, a number's
|
|
526
|
+
* range, a date's or a time's (`formatMinimum` / `formatMaximum`), how many a
|
|
527
|
+
* list holds. Plain JSON Schema keywords on the question,
|
|
528
|
+
* which the form validates; each answer type takes its own. Pure, no React.
|
|
529
|
+
*/
|
|
530
|
+
|
|
531
|
+
type LimitKeyword = "minLength" | "maxLength" | "pattern" | "minimum" | "maximum" | "minItems" | "maxItems" | "formatMinimum" | "formatMaximum";
|
|
532
|
+
/** A question's limits, by keyword: counts and bounds are numbers, a pattern its text. */
|
|
533
|
+
interface Limits {
|
|
534
|
+
minLength?: number;
|
|
535
|
+
maxLength?: number;
|
|
536
|
+
pattern?: string;
|
|
537
|
+
minimum?: number;
|
|
538
|
+
maximum?: number;
|
|
539
|
+
minItems?: number;
|
|
540
|
+
maxItems?: number;
|
|
541
|
+
/** The earliest date, time or datetime, as the answer is written. */
|
|
542
|
+
formatMinimum?: string;
|
|
543
|
+
formatMaximum?: string;
|
|
544
|
+
}
|
|
545
|
+
/** The limits each answer type takes; one not listed takes none. */
|
|
546
|
+
declare const ANSWER_TYPE_LIMITS: Partial<Record<AnswerType, LimitKeyword[]>>;
|
|
547
|
+
/** What each limit is called where an author sees it. */
|
|
548
|
+
declare const LIMIT_LABELS: Record<LimitKeyword, string>;
|
|
549
|
+
/** The limits an answer type takes. */
|
|
550
|
+
declare function limitsFor(type: AnswerType): LimitKeyword[];
|
|
551
|
+
/** The limits a question has, of those its answer type takes. */
|
|
552
|
+
declare function limitsOf(prop: JsonSchemaProperty, type: AnswerType): Limits;
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* classifyQuestionnaire -- a schema (with its uiSchema) read as a questionnaire:
|
|
556
|
+
* its sections, texts and questions, and in their place whatever the
|
|
557
|
+
* questionnaire designer does not understand. Pure, no React.
|
|
558
|
+
*
|
|
559
|
+
* Every part is sorted into one of three classes: understood (an item), partly
|
|
560
|
+
* understood (an item with `extras` -- the keywords it keeps untouched) and not
|
|
561
|
+
* understood (a `locked` item, with the reason). Nothing is dropped: the
|
|
562
|
+
* classification only reads. See docs/design/questionnaires.md, "The boundary
|
|
563
|
+
* with the schema designer".
|
|
564
|
+
*/
|
|
565
|
+
|
|
566
|
+
interface ItemBase {
|
|
567
|
+
key: string;
|
|
568
|
+
path: PropertyPath;
|
|
569
|
+
/** When the item is shown; none: always. */
|
|
570
|
+
showIf?: ShowIf;
|
|
571
|
+
}
|
|
572
|
+
/** A section: a heading, an introduction, its items; repeating when it is a list of them. */
|
|
573
|
+
interface SectionItem extends ItemBase {
|
|
574
|
+
kind: "section";
|
|
575
|
+
title: string;
|
|
576
|
+
description?: string;
|
|
577
|
+
repeats: boolean;
|
|
578
|
+
required: boolean;
|
|
579
|
+
/** A repeating section's count: how many entries at least, at most. */
|
|
580
|
+
limits?: {
|
|
581
|
+
minItems?: number;
|
|
582
|
+
maxItems?: number;
|
|
583
|
+
};
|
|
584
|
+
items: QuestionnaireItem[];
|
|
585
|
+
extras: string[];
|
|
586
|
+
}
|
|
587
|
+
/** Static text between questions. */
|
|
588
|
+
interface TextItem extends ItemBase {
|
|
589
|
+
kind: "text";
|
|
590
|
+
title?: string;
|
|
591
|
+
text?: string;
|
|
592
|
+
extras: string[];
|
|
593
|
+
}
|
|
594
|
+
/** One option of a choice: the value stored, the label shown. */
|
|
595
|
+
interface ChoiceOption {
|
|
596
|
+
value: string;
|
|
597
|
+
label: string;
|
|
598
|
+
}
|
|
599
|
+
/** A question: its text, help, example, and its answer type. */
|
|
600
|
+
interface QuestionItem extends ItemBase {
|
|
601
|
+
kind: "question";
|
|
602
|
+
title: string;
|
|
603
|
+
help?: string;
|
|
604
|
+
placeholder?: string;
|
|
605
|
+
required: boolean;
|
|
606
|
+
readOnly: boolean;
|
|
607
|
+
answerType: AnswerType;
|
|
608
|
+
/** How the question is shown, when it names a widget its answer type knows (`radio`, `select`, ...). */
|
|
609
|
+
presentation?: string;
|
|
610
|
+
/** A choice's options, in order (single, multiple, the choice of a choice with text). */
|
|
611
|
+
options?: ChoiceOption[];
|
|
612
|
+
/** The sub-answers of a container answer type (parts; a choice with text's choice and text). */
|
|
613
|
+
parts?: QuestionItem[];
|
|
614
|
+
/** A choice with text: the option that asks for the text ("other"); none: the text is a note. */
|
|
615
|
+
textWhen?: string;
|
|
616
|
+
/** What the answer may be: its length, pattern, range, count -- as its answer type takes them. */
|
|
617
|
+
limits?: Limits;
|
|
618
|
+
/** A value type's settings, read through its contribution (a quantity's units). */
|
|
619
|
+
settings?: Record<string, unknown>;
|
|
620
|
+
/** The answer given until the person answers (`default`); none for a container. */
|
|
621
|
+
defaultAnswer?: unknown;
|
|
622
|
+
extras: string[];
|
|
623
|
+
}
|
|
624
|
+
/** A part the questionnaire designer does not understand: kept, shown locked, edited in the schema designer. */
|
|
625
|
+
interface LockedItem extends ItemBase {
|
|
626
|
+
kind: "locked";
|
|
627
|
+
title: string;
|
|
628
|
+
reason: string;
|
|
629
|
+
}
|
|
630
|
+
type QuestionnaireItem = SectionItem | TextItem | QuestionItem | LockedItem;
|
|
631
|
+
/** A schema read as a questionnaire. */
|
|
632
|
+
interface QuestionnaireView {
|
|
633
|
+
title?: string;
|
|
634
|
+
description?: string;
|
|
635
|
+
items: QuestionnaireItem[];
|
|
636
|
+
/** The root's keywords and hints the questionnaire designer keeps untouched (a layout, definitions). */
|
|
637
|
+
extras: string[];
|
|
638
|
+
/** The reasons a question may be left unanswered for; none declared: every question is answered. */
|
|
639
|
+
reasons?: ChoiceOption[];
|
|
640
|
+
}
|
|
641
|
+
/** A choice's options: its enum values with their labels (x-ui-options.enumNames); none for another type. */
|
|
642
|
+
declare function optionsOf(prop: JsonSchemaProperty, ui: UISchema, type: AnswerType): ChoiceOption[] | undefined;
|
|
643
|
+
/** What holds a section's items: the root schema, an object, or a repeating section's items. */
|
|
644
|
+
type Container = Pick<JsonSchemaProperty, "properties" | "required" | "x-ui-order" | "allOf" | "anyOf">;
|
|
645
|
+
/** Every item of a container by key: its properties, and those its conditionals show -- what a show-if may test. */
|
|
646
|
+
declare function containerFields(container: Container): Record<string, JsonSchemaProperty>;
|
|
647
|
+
/** The keys of a container's items in the order the form shows them -- conditional ones included. */
|
|
648
|
+
declare function containerOrder(container: Container): string[];
|
|
649
|
+
/** Every item of a questionnaire, at any depth: each section before what it holds. */
|
|
650
|
+
declare function itemsOf(items: QuestionnaireItem[]): QuestionnaireItem[];
|
|
651
|
+
/** The schema read as a questionnaire: every part an item, nothing dropped. */
|
|
652
|
+
declare function classifyQuestionnaire(model: DesignerModel, types?: readonly ValueTypeAnswer[]): QuestionnaireView;
|
|
653
|
+
|
|
654
|
+
/**
|
|
655
|
+
* Questionnaire edits -- what the questionnaire designer does to the schema,
|
|
656
|
+
* in the author's terms. Pure, no React; built on the schema designer's own
|
|
657
|
+
* operations (model/mutate.ts), so both designers edit the same way.
|
|
658
|
+
*
|
|
659
|
+
* Every edit changes only what it touches: the rest of the schema, its
|
|
660
|
+
* uiSchema and every keyword the designer does not understand stay as they
|
|
661
|
+
* are. Changing an answer type is the one edit that can drop something; it
|
|
662
|
+
* returns what it dropped, so the designer asks before applying it. See
|
|
663
|
+
* docs/design/questionnaires.md, "The boundary with the schema designer".
|
|
664
|
+
*/
|
|
665
|
+
|
|
666
|
+
/** What an author adds: a question of an answer type, a text, or a section. */
|
|
667
|
+
type NewItem = {
|
|
668
|
+
kind: "question";
|
|
669
|
+
answerType: AnswerType;
|
|
670
|
+
} | {
|
|
671
|
+
kind: "text";
|
|
672
|
+
} | {
|
|
673
|
+
kind: "section";
|
|
674
|
+
};
|
|
675
|
+
/** A key from a question's text: lower case, words joined by `_`, unique among `taken`. */
|
|
676
|
+
declare function keyFromText(text: string, taken: Iterable<string>): string;
|
|
677
|
+
/** Whether a key may be set: lower case letters, digits and `_`, starting with a letter. */
|
|
678
|
+
declare function isValidKey(key: string): boolean;
|
|
679
|
+
/** Add a question, a text or a section at the end of a container; its key from its text. */
|
|
680
|
+
declare function addItem(model: DesignerModel, parentPath: PropertyPath, item: NewItem, text: string): {
|
|
681
|
+
model: DesignerModel;
|
|
682
|
+
path: PropertyPath;
|
|
683
|
+
};
|
|
684
|
+
/** Set a question's, a section's or a text's heading. */
|
|
685
|
+
declare function setItemText(model: DesignerModel, path: PropertyPath, text: string): DesignerModel;
|
|
686
|
+
/** Set a question's help, a section's introduction, a text's body. */
|
|
687
|
+
declare function setHelp(model: DesignerModel, path: PropertyPath, help: string): DesignerModel;
|
|
688
|
+
/** Set whether a question is shown, not entered. */
|
|
689
|
+
declare function setReadOnly(model: DesignerModel, path: PropertyPath, readOnly: boolean): DesignerModel;
|
|
690
|
+
/** Set how a question is shown -- a widget its answer type knows (`radio`, `select`, ...); undefined: its default. */
|
|
691
|
+
declare function setPresentation(model: DesignerModel, path: PropertyPath, widget: string | undefined): DesignerModel;
|
|
692
|
+
/**
|
|
693
|
+
* Set a question's default answer (`default`): given until the person
|
|
694
|
+
* answers, which they may change. An empty one removes it.
|
|
695
|
+
*/
|
|
696
|
+
declare function setDefaultAnswer(model: DesignerModel, path: PropertyPath, value: unknown): DesignerModel;
|
|
697
|
+
/** Set a question's example: shown in the empty field, never stored. */
|
|
698
|
+
declare function setPlaceholder(model: DesignerModel, path: PropertyPath, example: string): DesignerModel;
|
|
699
|
+
/** Set an item's key; refused (the model unchanged) when the key is invalid or taken. */
|
|
700
|
+
declare function setKey(model: DesignerModel, path: PropertyPath, key: string): {
|
|
701
|
+
model: DesignerModel;
|
|
702
|
+
path: PropertyPath;
|
|
703
|
+
};
|
|
704
|
+
/** Set a choice's options, in order: their values in the schema, their labels as hints. */
|
|
705
|
+
declare function setOptions(model: DesignerModel, path: PropertyPath, options: ChoiceOption[]): DesignerModel;
|
|
706
|
+
/** The result of changing an answer type: the new model, and what it dropped. */
|
|
707
|
+
interface AnswerTypeChange {
|
|
708
|
+
model: DesignerModel;
|
|
709
|
+
/** Settings the new type does not take -- a `pattern`, an unknown widget, the options. */
|
|
710
|
+
lost: string[];
|
|
711
|
+
}
|
|
712
|
+
/**
|
|
713
|
+
* Change a question's answer type. Its text, help, example and read-only flag
|
|
714
|
+
* stay; options move from one kind of choice to another. What the new type does
|
|
715
|
+
* not take is dropped and named in `lost` -- the designer asks before applying.
|
|
716
|
+
*/
|
|
717
|
+
declare function changeAnswerType(model: DesignerModel, path: PropertyPath, type: AnswerType, types?: readonly ValueTypeAnswer[], keep?: readonly string[]): AnswerTypeChange;
|
|
718
|
+
/**
|
|
719
|
+
* Set keywords the questionnaire designer keeps but does not edit itself -- a
|
|
720
|
+
* converter's annotations, say -- on an item, or (an empty path) on the
|
|
721
|
+
* questionnaire; an undefined value removes one.
|
|
722
|
+
*/
|
|
723
|
+
declare function setKeywords(model: DesignerModel, path: PropertyPath, keywords: Record<string, unknown>): DesignerModel;
|
|
724
|
+
/**
|
|
725
|
+
* Set whether a section repeats -- a list of entries, each with the section's
|
|
726
|
+
* items. Its items keep their paths: the list's entries are its container.
|
|
727
|
+
*/
|
|
728
|
+
declare function setSectionRepeats(model: DesignerModel, path: PropertyPath, repeats: boolean): DesignerModel;
|
|
729
|
+
/** Set one of a question's limits -- its length, pattern, range or count; undefined or empty removes it. */
|
|
730
|
+
declare function setLimit(model: DesignerModel, path: PropertyPath, keyword: LimitKeyword, value: number | string | undefined): DesignerModel;
|
|
731
|
+
/** Set the questionnaire's own title or introduction (the root's title and description). */
|
|
732
|
+
declare function setQuestionnaireText(model: DesignerModel, text: {
|
|
733
|
+
title?: string;
|
|
734
|
+
description?: string;
|
|
735
|
+
}): DesignerModel;
|
|
736
|
+
/** The path of the section an item is in -- the same for its conditional siblings. */
|
|
737
|
+
declare function sectionPathOf(path: PropertyPath): PropertyPath;
|
|
738
|
+
/**
|
|
739
|
+
* Show an item only if conditions hold, or (null) always. The item keeps its
|
|
740
|
+
* key, its place in the section's order and its hints; it moves into a
|
|
741
|
+
* conditional of its own -- or back among the section's properties.
|
|
742
|
+
*/
|
|
743
|
+
declare function setShowIf(model: DesignerModel, path: PropertyPath, showIf: ShowIf | null): {
|
|
744
|
+
model: DesignerModel;
|
|
745
|
+
path: PropertyPath;
|
|
746
|
+
};
|
|
747
|
+
/** Move an item up (-1) or down (+1) among its section's items -- conditional ones included. */
|
|
748
|
+
declare function moveItem(model: DesignerModel, path: PropertyPath, direction: -1 | 1): DesignerModel;
|
|
749
|
+
/** Put an item before another of its section (by key), or last (null) -- dragging it there. */
|
|
750
|
+
declare function reorderItem(model: DesignerModel, path: PropertyPath, beforeKey: string | null): DesignerModel;
|
|
751
|
+
/** Delete an item: its definition, its hints, its place in the order -- and its conditional, once empty. */
|
|
752
|
+
declare function deleteItem(model: DesignerModel, path: PropertyPath): DesignerModel;
|
|
753
|
+
/**
|
|
754
|
+
* Set a value type's settings on its question -- the quantity's units -- as its
|
|
755
|
+
* contribution writes them: keywords beside the `$ref`, `x-ui-options` keys.
|
|
756
|
+
*/
|
|
757
|
+
declare function setValueTypeSettings(model: DesignerModel, path: PropertyPath, answer: ValueTypeAnswer, values: Record<string, unknown>): DesignerModel;
|
|
758
|
+
/** The option a choice with text's "other" variant adds when its options have none. */
|
|
759
|
+
declare const OTHER_OPTION: {
|
|
760
|
+
value: string;
|
|
761
|
+
label: string;
|
|
762
|
+
};
|
|
763
|
+
/**
|
|
764
|
+
* Set what a choice with text's text is: a note, always offered (null), or
|
|
765
|
+
* asked for only when the choice is one option -- "other, please describe" --
|
|
766
|
+
* and then required. Switching to the latter adds an "other" option when the
|
|
767
|
+
* choice has none; the text keeps its definition either way.
|
|
768
|
+
*/
|
|
769
|
+
declare function setChoiceTextWhen(model: DesignerModel, path: PropertyPath, when: string | null): DesignerModel;
|
|
770
|
+
/** The reasons the questionnaire declares (its root's `absent`); none: every question is answered. */
|
|
771
|
+
declare function declaredReasons(model: DesignerModel): ChoiceOption[] | undefined;
|
|
772
|
+
/**
|
|
773
|
+
* Set whether a question must be answered. With reasons declared, an always
|
|
774
|
+
* asked question is required unless a reason is given for it; a conditional
|
|
775
|
+
* one, and any question without reasons, is plainly required.
|
|
776
|
+
*/
|
|
777
|
+
declare function setQuestionRequired(model: DesignerModel, path: PropertyPath, required: boolean): DesignerModel;
|
|
778
|
+
/**
|
|
779
|
+
* Declare the reasons a question may be left unanswered for -- or none (null),
|
|
780
|
+
* so every question is answered. Every section gets (or loses) its `absent`,
|
|
781
|
+
* and its required questions become required unless a reason is given (or
|
|
782
|
+
* plainly required again).
|
|
783
|
+
*/
|
|
784
|
+
declare function setReasons(model: DesignerModel, reasons: ChoiceOption[] | null): DesignerModel;
|
|
785
|
+
|
|
786
|
+
/**
|
|
787
|
+
* Reasons for no answer -- *unknown*, *not applicable*: a statement about a
|
|
788
|
+
* question instead of its answer. Pure, no React.
|
|
789
|
+
*
|
|
790
|
+
* A questionnaire declares its reasons once; every section then carries them
|
|
791
|
+
* in its own `absent` property, an object of question keys whose values are
|
|
792
|
+
* the declared reasons, so an answer keeps its type and a reason sits beside
|
|
793
|
+
* it. A required question is required *unless* the section's `absent` names
|
|
794
|
+
* it -- one conditional per question, `{ if: { not: <absent names it> }, then:
|
|
795
|
+
* { required: [key] } }` -- so the form asks for an answer or a reason, and
|
|
796
|
+
* the renderer enforces it. See docs/design/questionnaires.md, "Reasons for no
|
|
797
|
+
* answer".
|
|
798
|
+
*/
|
|
799
|
+
|
|
800
|
+
/** The property of a section that holds its reasons. */
|
|
801
|
+
declare const ABSENT_KEY = "absent";
|
|
802
|
+
/** The widget a section's reasons are entered with. */
|
|
803
|
+
declare const ABSENT_WIDGET = "absent-reasons";
|
|
804
|
+
/** The `absent` property of a section: question keys to one of the reasons. */
|
|
805
|
+
declare function absentProperty(reasons: ChoiceOption[]): JsonSchemaProperty;
|
|
806
|
+
/** The reasons an `absent` property declares; none when the property is not of that shape. */
|
|
807
|
+
declare function readReasons(prop: JsonSchemaProperty | undefined): ChoiceOption[] | undefined;
|
|
808
|
+
/** The conditional that makes a question required unless a reason is given for it. */
|
|
809
|
+
declare function unlessReasonMember(key: string): JsonSchemaProperty;
|
|
810
|
+
/** The question an "unless a reason" conditional makes required; none for another member. */
|
|
811
|
+
declare function unlessReasonKey(member: JsonSchemaProperty): string | undefined;
|
|
812
|
+
/** The answers of one section: its values, and its reasons in `absent`. */
|
|
813
|
+
type SectionAnswers = Record<string, unknown> & {
|
|
814
|
+
[ABSENT_KEY]?: Record<string, string>;
|
|
815
|
+
};
|
|
816
|
+
/** Whether a question has an answer or a reason in its section's answers. */
|
|
817
|
+
declare function answeredOrExcused(answers: SectionAnswers | undefined, key: string): boolean;
|
|
818
|
+
|
|
819
|
+
/**
|
|
820
|
+
* Completion -- whether a set of answers is complete: every question asked has
|
|
821
|
+
* an answer or a reason for none. A question shown only if conditions hold is
|
|
822
|
+
* asked while they hold. Distinct from `required`, which says an answer must
|
|
823
|
+
* be given. Pure, no React. See docs/design/questionnaires.md, "Reasons for no
|
|
824
|
+
* answer".
|
|
825
|
+
*/
|
|
826
|
+
|
|
827
|
+
type Answers = Record<string, unknown>;
|
|
828
|
+
/** Whether the answers are complete, and the questions that still lack an answer or a reason. */
|
|
829
|
+
interface Completion {
|
|
830
|
+
complete: boolean;
|
|
831
|
+
/** The missing questions, by key path (`company.vat_id`; a repeating section's entry by index). */
|
|
832
|
+
missing: string[];
|
|
833
|
+
}
|
|
834
|
+
/** Whether an item is asked, given its section's answers: always, or while its show-if holds. */
|
|
835
|
+
declare function isAsked(showIf: ShowIf | undefined, answers: Answers): boolean;
|
|
836
|
+
/** Whether a set of answers is complete: every question asked has an answer or a reason. */
|
|
837
|
+
declare function isComplete(view: QuestionnaireView, answers: Answers): Completion;
|
|
838
|
+
|
|
839
|
+
/**
|
|
840
|
+
* QuestionnaireDesigner capabilities -- a profile that limits what an author
|
|
841
|
+
* may build. As the schema designer's (../capabilities.ts): the Zod schema is
|
|
842
|
+
* the single source, every field optional and defaulting to the most
|
|
843
|
+
* permissive value, so adding a capability never tightens a profile already
|
|
844
|
+
* written. A profile limits authoring; it never hides what a schema holds --
|
|
845
|
+
* a question of a type the profile does not offer is still shown and edited.
|
|
846
|
+
*/
|
|
847
|
+
|
|
848
|
+
declare const questionnaireCapabilitiesSchema: z.ZodObject<{
|
|
849
|
+
answerTypes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
850
|
+
sections: z.ZodDefault<z.ZodBoolean>;
|
|
851
|
+
logic: z.ZodDefault<z.ZodBoolean>;
|
|
852
|
+
reasons: z.ZodDefault<z.ZodBoolean>;
|
|
853
|
+
switchToDesigner: z.ZodDefault<z.ZodBoolean>;
|
|
854
|
+
previews: z.ZodDefault<z.ZodArray<z.ZodEnum<{
|
|
855
|
+
form: "form";
|
|
856
|
+
json: "json";
|
|
857
|
+
capabilities: "capabilities";
|
|
858
|
+
}>>>;
|
|
859
|
+
}, z.core.$strip>;
|
|
860
|
+
type QuestionnaireDesignerCapabilities = z.input<typeof questionnaireCapabilitiesSchema>;
|
|
861
|
+
/** The resolved profile: every field set, the answer types listed. */
|
|
862
|
+
type ResolvedQuestionnaireCapabilities = Required<z.output<typeof questionnaireCapabilitiesSchema>>;
|
|
863
|
+
/** A profile checked: its value, or what is wrong with it, by path. */
|
|
864
|
+
declare function validateQuestionnaireCapabilities(input: unknown): {
|
|
865
|
+
ok: true;
|
|
866
|
+
value: QuestionnaireDesignerCapabilities;
|
|
867
|
+
} | {
|
|
868
|
+
ok: false;
|
|
869
|
+
errors: string[];
|
|
870
|
+
};
|
|
871
|
+
/** Resolve a profile: defaults applied; an invalid one warns and falls back to the defaults. */
|
|
872
|
+
declare function resolveQuestionnaireCapabilities(capabilities: QuestionnaireDesignerCapabilities | undefined, valueTypes?: readonly ValueTypeAnswer[]): ResolvedQuestionnaireCapabilities;
|
|
873
|
+
|
|
874
|
+
export { addLayoutGroup as $, ABSENT_KEY as A, LIMIT_LABELS as L, absentProperty as M, OTHER_OPTION as O, addBranch as W, addConditional as X, addDefinition as Y, addElse as Z, addItem as _, renameProperty as a$, addProperty as a0, answerTypeFragment as a1, answerTypeLabel as a2, answeredOrExcused as a3, branch as a4, changeAnswerType as a5, changePropertyType as a6, childContainer as a7, childSlots as a8, choiceTextWhen as a9, moveBranch as aA, moveItem as aB, moveLayoutGroup as aC, moveProperty as aD, newBranch as aE, newCondition as aF, newProperty as aG, optionsOf as aH, otherTextMember as aI, parsePathKey as aJ, pathKey as aK, placeFieldInGroup as aL, prop as aM, questionnaireCapabilitiesSchema as aN, rawChoiceAndText as aO, readConditional as aP, readEnumChoices as aQ, readReasons as aR, readTableHints as aS, recognizeAnswerType as aT, removeBranch as aU, removeConditional as aV, removeDefinition as aW, removeElse as aX, removeLayoutGroup as aY, renameDefinition as aZ, renameLayoutGroup as a_, classifyQuestionnaire as aa, codingBindingOf as ab, condition as ac, containerFields as ad, containerOrder as ae, declaredReasons as af, def as ag, definitionUsageCount as ah, definitionsOf as ai, deleteItem as aj, deleteProperty as ak, embeddedHints as al, getProperty as am, getScopedLayout as an, getUiEntryAtPath as ao, isAsked as ap, isBuiltinAnswerType as aq, isComplete as ar, isContainer as as, isContainerType as at, isReservedKey as au, isValidKey as av, itemsOf as aw, keyFromText as ax, limitsFor as ay, limitsOf as az, ABSENT_WIDGET as b, reorderItem as b0, resolveQuestionnaireCapabilities as b1, samePath as b2, sameSegment as b3, schemaAsRootProp as b4, sectionPathOf as b5, setChoiceTextWhen as b6, setCondition as b7, setConditionalBehavior as b8, setDefaultAnswer as b9, setSectionRepeats as bA, setShowIf as bB, setTableHints as bC, setUiEntry as bD, setValueTypeSettings as bE, showIfToIf as bF, slot as bG, unlessReasonKey as bH, unlessReasonMember as bI, updateProperty as bJ, validateQuestionnaireCapabilities as bK, valueTypeAnswer as bL, valueTypeOf as bM, setEnum as ba, setGlobalOptions as bb, setHelp as bc, setItemText as bd, setKey as be, setKeywords as bf, setLayoutGroupRule as bg, setLimit as bh, setOptions as bi, setPlaceholder as bj, setPresentation as bk, setPropertyLayout as bl, setQuestionRequired as bm, setQuestionnaireText as bn, setReadOnly as bo, setReasons as bp, setReference as bq, setRequired as br, setRootLayout as bs, setRootLayoutRows as bt, setScopedGroupRows as bu, setScopedLayoutGroups as bv, setScopedLayoutMode as bw, setScopedLayoutOptions as bx, setScopedLayoutRows as by, setScopedLayoutVariant as bz, ANSWER_TYPE_LABELS as c, ANSWER_TYPE_LIMITS as d, CHOICE_WITH_TEXT_WIDGET as h, CODEABLE_CONCEPT_ANSWER as i, CODING_ANSWER as j, DEFAULT_VALUE_TYPES as p, QUANTITY_ANSWER as w };
|
|
875
|
+
export type { BuiltinAnswerType as B, ConditionCombinator as C, DesignerModel as D, EnumChoice as E, FieldType as F, ResolvedQuestionnaireCapabilities as G, SectionItem as H, ShowIf as I, TextItem as J, ValueTypeSettings as K, NewPropertyKind as N, PropertyPath as P, QuestionnaireDesignerCapabilities as Q, RecognizedAnswer as R, SlotKind as S, TableHints as T, UiSchemaMap as U, ValueTypeAnswer as V, PropertyTypeChoice as a, AnswerFragment as e, AnswerType as f, AnswerTypeChange as g, ChildSlot as k, ChoiceOption as l, Completion as m, Condition as n, ConditionalItems as o, LimitKeyword as q, Limits as r, LockedItem as s, NewItem as t, PathSegment as u, PathSlot as v, QuestionItem as x, QuestionnaireItem as y, QuestionnaireView as z };
|