@tai42/studio-sdk 21.0.0 → 22.0.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/dist/components/chip-group.d.ts +42 -0
- package/dist/components/chip-group.d.ts.map +1 -0
- package/dist/components/chip-group.js +85 -0
- package/dist/components/chip-group.js.map +1 -0
- package/dist/components/components.css +9 -3
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/schema-form/SchemaForm.d.ts +11 -1
- package/dist/schema-form/SchemaForm.d.ts.map +1 -1
- package/dist/schema-form/SchemaForm.js +2 -2
- package/dist/schema-form/SchemaForm.js.map +1 -1
- package/dist/schema-form/classify.d.ts.map +1 -1
- package/dist/schema-form/classify.js +54 -2
- package/dist/schema-form/classify.js.map +1 -1
- package/dist/schema-form/date-range-field.d.ts +44 -0
- package/dist/schema-form/date-range-field.d.ts.map +1 -0
- package/dist/schema-form/date-range-field.js +72 -0
- package/dist/schema-form/date-range-field.js.map +1 -0
- package/dist/schema-form/enum-field.d.ts +3 -1
- package/dist/schema-form/enum-field.d.ts.map +1 -1
- package/dist/schema-form/enum-field.js +8 -2
- package/dist/schema-form/enum-field.js.map +1 -1
- package/dist/schema-form/field-model.d.ts +20 -1
- package/dist/schema-form/field-model.d.ts.map +1 -1
- package/dist/schema-form/field-node.d.ts +10 -0
- package/dist/schema-form/field-node.d.ts.map +1 -1
- package/dist/schema-form/field-node.js +6 -3
- package/dist/schema-form/field-node.js.map +1 -1
- package/dist/schema-form/index.d.ts +3 -1
- package/dist/schema-form/index.d.ts.map +1 -1
- package/dist/schema-form/index.js +1 -0
- package/dist/schema-form/index.js.map +1 -1
- package/dist/schema-form/multiselect-field.d.ts.map +1 -1
- package/dist/schema-form/multiselect-field.js +11 -0
- package/dist/schema-form/multiselect-field.js.map +1 -1
- package/dist/schema-form/object-fields.d.ts +9 -1
- package/dist/schema-form/object-fields.d.ts.map +1 -1
- package/dist/schema-form/object-fields.js +16 -3
- package/dist/schema-form/object-fields.js.map +1 -1
- package/dist/schema-form/string-field.d.ts +6 -1
- package/dist/schema-form/string-field.d.ts.map +1 -1
- package/dist/schema-form/string-field.js +42 -8
- package/dist/schema-form/string-field.js.map +1 -1
- package/dist/schema-form/types.d.ts +36 -0
- package/dist/schema-form/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/components/chip-group.tsx +157 -0
- package/src/components/components.css +9 -3
- package/src/index.ts +6 -0
- package/src/schema-form/SchemaForm.tsx +10 -0
- package/src/schema-form/classify.ts +59 -3
- package/src/schema-form/date-range-field.tsx +167 -0
- package/src/schema-form/enum-field.tsx +20 -0
- package/src/schema-form/field-model.ts +24 -2
- package/src/schema-form/field-node.tsx +19 -0
- package/src/schema-form/index.ts +10 -1
- package/src/schema-form/multiselect-field.tsx +21 -0
- package/src/schema-form/object-fields.tsx +36 -2
- package/src/schema-form/string-field.tsx +72 -1
- package/src/schema-form/types.ts +38 -0
|
@@ -41,6 +41,14 @@ interface FieldNodeProps {
|
|
|
41
41
|
readonly markRequired?: boolean;
|
|
42
42
|
readonly errors: SchemaFormErrors | undefined;
|
|
43
43
|
readonly idPrefix: string;
|
|
44
|
+
/**
|
|
45
|
+
* Per-field picker placeholders keyed by property name, handed to an OBJECT node so
|
|
46
|
+
* its direct `enum`-select children draw their placeholder. Carried by the root form
|
|
47
|
+
* only; nested objects receive none.
|
|
48
|
+
*/
|
|
49
|
+
readonly fieldWording?: Readonly<Record<string, { choiceLabel?: string }>>;
|
|
50
|
+
/** This field's own picker placeholder, when it draws as an `enum` select. */
|
|
51
|
+
readonly choiceLabel?: string;
|
|
44
52
|
}
|
|
45
53
|
|
|
46
54
|
function labelText(explicit: string | undefined, label: string | undefined): string {
|
|
@@ -73,6 +81,8 @@ function FieldNodeImpl(props: FieldNodeProps): ReactNode {
|
|
|
73
81
|
required,
|
|
74
82
|
errors,
|
|
75
83
|
idPrefix,
|
|
84
|
+
fieldWording: props.fieldWording,
|
|
85
|
+
choiceLabel: props.choiceLabel,
|
|
76
86
|
});
|
|
77
87
|
}
|
|
78
88
|
|
|
@@ -90,6 +100,8 @@ interface RenderFieldArgs {
|
|
|
90
100
|
readonly required: boolean;
|
|
91
101
|
readonly errors: SchemaFormErrors | undefined;
|
|
92
102
|
readonly idPrefix: string;
|
|
103
|
+
readonly fieldWording?: Readonly<Record<string, { choiceLabel?: string }>>;
|
|
104
|
+
readonly choiceLabel?: string;
|
|
93
105
|
}
|
|
94
106
|
|
|
95
107
|
/** Render the field component matching the classified model kind. */
|
|
@@ -126,6 +138,7 @@ function renderField(args: RenderFieldArgs): ReactNode {
|
|
|
126
138
|
model={model}
|
|
127
139
|
value={value}
|
|
128
140
|
onChange={onChange}
|
|
141
|
+
choiceLabel={args.choiceLabel}
|
|
129
142
|
/>
|
|
130
143
|
);
|
|
131
144
|
case 'string':
|
|
@@ -135,6 +148,10 @@ function renderField(args: RenderFieldArgs): ReactNode {
|
|
|
135
148
|
description={description}
|
|
136
149
|
error={error}
|
|
137
150
|
format={model.format}
|
|
151
|
+
inputMode={model.inputMode}
|
|
152
|
+
multiline={model.multiline}
|
|
153
|
+
maxLength={model.maxLength}
|
|
154
|
+
minLength={model.minLength}
|
|
138
155
|
media={model.media}
|
|
139
156
|
expression={model.expression}
|
|
140
157
|
date={model.date}
|
|
@@ -187,6 +204,7 @@ function renderCompositeField(
|
|
|
187
204
|
model: Extract<FieldModel, { kind: 'array' | 'object' | 'record' | 'union' }>,
|
|
188
205
|
): ReactNode {
|
|
189
206
|
const { heading, description, error, root, value, onChange, path, errors, idPrefix } = args;
|
|
207
|
+
const { fieldWording } = args;
|
|
190
208
|
switch (model.kind) {
|
|
191
209
|
case 'array':
|
|
192
210
|
return (
|
|
@@ -216,6 +234,7 @@ function renderCompositeField(
|
|
|
216
234
|
errors={errors}
|
|
217
235
|
idPrefix={idPrefix}
|
|
218
236
|
skip={undefined}
|
|
237
|
+
fieldWording={fieldWording}
|
|
219
238
|
/>
|
|
220
239
|
</FieldGroup>
|
|
221
240
|
);
|
package/src/schema-form/index.ts
CHANGED
|
@@ -38,12 +38,21 @@ export type {
|
|
|
38
38
|
} from './context';
|
|
39
39
|
export { RecordEntryRendererContext } from './context';
|
|
40
40
|
export { ExpressionFieldContext } from './context';
|
|
41
|
+
export type { DateRangePair } from './date-range-field';
|
|
42
|
+
export { dateRangePairs } from './date-range-field';
|
|
41
43
|
export { defaultValueForSchema } from './default-value';
|
|
42
44
|
export { resolveRef } from './resolve';
|
|
43
45
|
export type { CompletionProvider, SchemaFormProps } from './SchemaForm';
|
|
44
46
|
export { SchemaForm } from './SchemaForm';
|
|
45
47
|
export type { SecretRef, SecretRefFieldProps } from './SecretRefField';
|
|
46
48
|
export { SecretRefField } from './SecretRefField';
|
|
47
|
-
export type {
|
|
49
|
+
export type {
|
|
50
|
+
Discriminator,
|
|
51
|
+
FormControl,
|
|
52
|
+
FormInputMode,
|
|
53
|
+
JsonSchema,
|
|
54
|
+
JsonSchemaType,
|
|
55
|
+
SchemaFormErrors,
|
|
56
|
+
} from './types';
|
|
48
57
|
export { validateAgainstSchema } from './validate';
|
|
49
58
|
export { isFieldVisible } from './visibility';
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import type { ReactNode } from 'react';
|
|
9
9
|
|
|
10
10
|
import { Checkbox } from '../components/checkbox';
|
|
11
|
+
import { ChipGroup } from '../components/chip-group';
|
|
11
12
|
import { Field } from '../components/field';
|
|
12
13
|
import type { FieldModel } from './field-model';
|
|
13
14
|
|
|
@@ -38,6 +39,26 @@ export function MultiSelectField({
|
|
|
38
39
|
.filter((candidate) => next.includes(candidate));
|
|
39
40
|
onChange(ordered);
|
|
40
41
|
};
|
|
42
|
+
|
|
43
|
+
// `control: chips` draws the multi-select as a toggle-chip grid in place of the
|
|
44
|
+
// checkbox group. The chips emit the selection in the OPTIONS' declared order.
|
|
45
|
+
if (model.control === 'chips') {
|
|
46
|
+
return (
|
|
47
|
+
<Field label={heading} description={description} error={error} group>
|
|
48
|
+
<ChipGroup
|
|
49
|
+
mode="multi"
|
|
50
|
+
options={model.options.map((option) => ({
|
|
51
|
+
value: String(option.value),
|
|
52
|
+
label: option.label,
|
|
53
|
+
}))}
|
|
54
|
+
value={selected}
|
|
55
|
+
onValueChange={(next) => {
|
|
56
|
+
onChange([...next]);
|
|
57
|
+
}}
|
|
58
|
+
/>
|
|
59
|
+
</Field>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
41
62
|
return (
|
|
42
63
|
<Field label={heading} description={description} error={error} group>
|
|
43
64
|
{model.options.map((option, index) => {
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import type { ReactNode } from 'react';
|
|
8
8
|
|
|
9
|
+
import { DateRangeField, dateRangePairs } from './date-range-field';
|
|
9
10
|
import { FieldNode } from './field-node';
|
|
10
11
|
import { useKeyedCallbacks } from './keyed-callbacks';
|
|
11
12
|
import type { JsonSchema, SchemaFormErrors } from './types';
|
|
@@ -26,6 +27,7 @@ export function ObjectFields({
|
|
|
26
27
|
idPrefix,
|
|
27
28
|
skip,
|
|
28
29
|
markRequired = false,
|
|
30
|
+
fieldWording,
|
|
29
31
|
}: {
|
|
30
32
|
properties: readonly (readonly [string, JsonSchema])[];
|
|
31
33
|
requiredKeys: ReadonlySet<string>;
|
|
@@ -38,6 +40,12 @@ export function ObjectFields({
|
|
|
38
40
|
skip: string | undefined;
|
|
39
41
|
/** Mark each required property's label with the DS required marker. */
|
|
40
42
|
markRequired?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Per-property picker placeholders, keyed by property name. Handed to the root form's
|
|
45
|
+
* object only; a property whose `enum` draws as a select shows its entry's
|
|
46
|
+
* `choiceLabel` as the select placeholder. Absent for nested objects.
|
|
47
|
+
*/
|
|
48
|
+
fieldWording?: Readonly<Record<string, { choiceLabel?: string }>>;
|
|
41
49
|
}): ReactNode {
|
|
42
50
|
const obj = isPlainObject(value) ? value : {};
|
|
43
51
|
const changeFor = useKeyedCallbacks<string, unknown>(
|
|
@@ -46,14 +54,39 @@ export function ObjectFields({
|
|
|
46
54
|
},
|
|
47
55
|
properties.map(([name]) => name),
|
|
48
56
|
);
|
|
57
|
+
// A drawable date-range pair renders as ONE DateRangeField at the START's slot; the
|
|
58
|
+
// END is skipped. The pairing is `dateRangePairs`, the one definition every surface
|
|
59
|
+
// shares. Properties here are one page's, so the "same page" rule holds by construction.
|
|
60
|
+
const pairs = dateRangePairs(properties);
|
|
61
|
+
const pairByStart = new Map(pairs.map((pair) => [pair.startName, pair]));
|
|
62
|
+
const skippedEnds = new Set(pairs.map((pair) => pair.endName));
|
|
63
|
+
const childPathOf = (name: string): string => (path === '' ? name : `${path}.${name}`);
|
|
49
64
|
return (
|
|
50
65
|
<>
|
|
51
66
|
{properties.map(([name, propSchema]) => {
|
|
52
67
|
if (skip !== undefined && name === skip) return null;
|
|
68
|
+
if (skippedEnds.has(name)) return null;
|
|
53
69
|
// A field hidden by its `visibleWhen` predicate (scope B) is not rendered; the
|
|
54
70
|
// validator skips it in step, so a hidden field is neither shown nor required.
|
|
55
71
|
if (!isFieldVisible(propSchema, obj)) return null;
|
|
56
|
-
const
|
|
72
|
+
const pair = pairByStart.get(name);
|
|
73
|
+
if (pair !== undefined) {
|
|
74
|
+
return (
|
|
75
|
+
<DateRangeField
|
|
76
|
+
key={name}
|
|
77
|
+
pair={pair}
|
|
78
|
+
startValue={obj[pair.startName]}
|
|
79
|
+
endValue={obj[pair.endName]}
|
|
80
|
+
startRequired={requiredKeys.has(pair.startName)}
|
|
81
|
+
endRequired={requiredKeys.has(pair.endName)}
|
|
82
|
+
markRequired={markRequired}
|
|
83
|
+
startError={errors?.[childPathOf(pair.startName)]}
|
|
84
|
+
endError={errors?.[childPathOf(pair.endName)]}
|
|
85
|
+
onChangeStart={changeFor(pair.startName)}
|
|
86
|
+
onChangeEnd={changeFor(pair.endName)}
|
|
87
|
+
/>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
57
90
|
return (
|
|
58
91
|
<FieldNode
|
|
59
92
|
key={name}
|
|
@@ -61,12 +94,13 @@ export function ObjectFields({
|
|
|
61
94
|
root={root}
|
|
62
95
|
value={obj[name]}
|
|
63
96
|
onChange={changeFor(name)}
|
|
64
|
-
path={
|
|
97
|
+
path={childPathOf(name)}
|
|
65
98
|
label={name}
|
|
66
99
|
required={requiredKeys.has(name)}
|
|
67
100
|
markRequired={markRequired}
|
|
68
101
|
errors={errors}
|
|
69
102
|
idPrefix={idPrefix}
|
|
103
|
+
choiceLabel={fieldWording?.[name]?.choiceLabel}
|
|
70
104
|
/>
|
|
71
105
|
);
|
|
72
106
|
})}
|
|
@@ -31,12 +31,17 @@ import { CompletionProviderContext, ExpressionFieldContext } from './context';
|
|
|
31
31
|
import type { DateConstraints, ExpressionAnnotation, MediaUpload } from './field-model';
|
|
32
32
|
import { MediaField } from './media-field';
|
|
33
33
|
import type { CompletionProvider } from './SchemaForm';
|
|
34
|
+
import type { FormInputMode } from './types';
|
|
34
35
|
|
|
35
36
|
export function StringField({
|
|
36
37
|
heading,
|
|
37
38
|
description,
|
|
38
39
|
error,
|
|
39
40
|
format,
|
|
41
|
+
inputMode,
|
|
42
|
+
multiline,
|
|
43
|
+
maxLength,
|
|
44
|
+
minLength,
|
|
40
45
|
media,
|
|
41
46
|
expression,
|
|
42
47
|
date,
|
|
@@ -49,6 +54,10 @@ export function StringField({
|
|
|
49
54
|
description: string | undefined;
|
|
50
55
|
error: string | undefined;
|
|
51
56
|
format: string | undefined;
|
|
57
|
+
inputMode: FormInputMode | undefined;
|
|
58
|
+
multiline: boolean;
|
|
59
|
+
maxLength: number | undefined;
|
|
60
|
+
minLength: number | undefined;
|
|
52
61
|
media: MediaUpload | undefined;
|
|
53
62
|
expression: ExpressionAnnotation | undefined;
|
|
54
63
|
date: DateConstraints | undefined;
|
|
@@ -57,6 +66,7 @@ export function StringField({
|
|
|
57
66
|
required: boolean;
|
|
58
67
|
onChange: (value: unknown) => void;
|
|
59
68
|
}): ReactNode {
|
|
69
|
+
const control = stringControl(format, inputMode);
|
|
60
70
|
const completionProvider = useContext(CompletionProviderContext);
|
|
61
71
|
const expressionField = useContext(ExpressionFieldContext);
|
|
62
72
|
const current = typeof value === 'string' ? value : '';
|
|
@@ -114,24 +124,58 @@ export function StringField({
|
|
|
114
124
|
onChange={emit}
|
|
115
125
|
provider={completionProvider}
|
|
116
126
|
/>
|
|
127
|
+
) : multiline ? (
|
|
128
|
+
// `control: textarea` draws a multi-line control; the length bounds draw
|
|
129
|
+
// natively, and a live counter shows the maximum (below).
|
|
130
|
+
<Textarea
|
|
131
|
+
maxLength={maxLength}
|
|
132
|
+
minLength={minLength}
|
|
133
|
+
value={current}
|
|
134
|
+
onChange={(event) => {
|
|
135
|
+
emit(event.target.value);
|
|
136
|
+
}}
|
|
137
|
+
/>
|
|
117
138
|
) : (
|
|
118
139
|
<TextInput
|
|
119
|
-
type={
|
|
140
|
+
type={control.type}
|
|
141
|
+
// The keyboard hint for a `numeric`/`decimal` mode (a text box with a
|
|
142
|
+
// numeric/decimal keyboard); `undefined` for every other control, which
|
|
143
|
+
// selects its kind through `type` instead.
|
|
144
|
+
inputMode={control.inputMode}
|
|
120
145
|
// A native date control enforces its inclusive bounds (scope C); on a
|
|
121
146
|
// non-date input the browser ignores these, and the validator enforces the
|
|
122
147
|
// rest (unavailable days, range span) that the control cannot draw.
|
|
123
148
|
min={date?.min}
|
|
124
149
|
max={date?.max}
|
|
150
|
+
// The length bounds draw natively (the control caps input at `maxLength`)
|
|
151
|
+
// and the validator enforces them; a counter shows the maximum (below).
|
|
152
|
+
maxLength={maxLength}
|
|
153
|
+
minLength={minLength}
|
|
125
154
|
value={current}
|
|
126
155
|
onChange={(event) => {
|
|
127
156
|
emit(event.target.value);
|
|
128
157
|
}}
|
|
129
158
|
/>
|
|
130
159
|
)}
|
|
160
|
+
{/* The live length counter, shown whenever a maximum is declared — on both the
|
|
161
|
+
text box and the text area. It counts code points, matching the validator's
|
|
162
|
+
own `maxLength` check. */}
|
|
163
|
+
{completionProvider === undefined && maxLength !== undefined ? (
|
|
164
|
+
<LengthCounter current={current} max={maxLength} />
|
|
165
|
+
) : null}
|
|
131
166
|
</Field>
|
|
132
167
|
);
|
|
133
168
|
}
|
|
134
169
|
|
|
170
|
+
/** The `n / max` length hint under a bounded string control. */
|
|
171
|
+
function LengthCounter({ current, max }: { current: string; max: number }): ReactNode {
|
|
172
|
+
return (
|
|
173
|
+
<p className="tai-field-hint" style={{ margin: 0 }} aria-live="polite">
|
|
174
|
+
{`${String(Array.from(current).length)} / ${String(max)}`}
|
|
175
|
+
</p>
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
|
|
135
179
|
/**
|
|
136
180
|
* A string field backed by argument completions. Split out so its memoised
|
|
137
181
|
* hooks stay unconditional (they cannot live behind the provider check in
|
|
@@ -360,3 +404,30 @@ function stringInputType(format: string | undefined): string {
|
|
|
360
404
|
if (format === undefined) return 'text';
|
|
361
405
|
return FORMAT_INPUT_TYPES[format] ?? 'text';
|
|
362
406
|
}
|
|
407
|
+
|
|
408
|
+
/** The native control each `inputMode` draws. `tel`/`email`/`url` select the
|
|
409
|
+
* matching input `type`; `numeric`/`decimal` keep a text box and set the HTML
|
|
410
|
+
* `inputmode` keyboard hint; `text` is a plain text box. */
|
|
411
|
+
const INPUT_MODE_CONTROLS: Record<
|
|
412
|
+
FormInputMode,
|
|
413
|
+
{ readonly type: string; readonly inputMode: 'numeric' | 'decimal' | undefined }
|
|
414
|
+
> = {
|
|
415
|
+
text: { type: 'text', inputMode: undefined },
|
|
416
|
+
numeric: { type: 'text', inputMode: 'numeric' },
|
|
417
|
+
decimal: { type: 'text', inputMode: 'decimal' },
|
|
418
|
+
tel: { type: 'tel', inputMode: undefined },
|
|
419
|
+
email: { type: 'email', inputMode: undefined },
|
|
420
|
+
url: { type: 'url', inputMode: undefined },
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
// The native control for a string field: the `inputMode` keyword OUTRANKS
|
|
424
|
+
// `format` when present; otherwise the format mapping stands as before. The value
|
|
425
|
+
// is always the typed string — `numeric`/`decimal` set only the keyboard, never a
|
|
426
|
+
// number coercion.
|
|
427
|
+
function stringControl(
|
|
428
|
+
format: string | undefined,
|
|
429
|
+
inputMode: FormInputMode | undefined,
|
|
430
|
+
): { readonly type: string; readonly inputMode: 'numeric' | 'decimal' | undefined } {
|
|
431
|
+
if (inputMode !== undefined) return INPUT_MODE_CONTROLS[inputMode];
|
|
432
|
+
return { type: stringInputType(format), inputMode: undefined };
|
|
433
|
+
}
|
package/src/schema-form/types.ts
CHANGED
|
@@ -11,6 +11,24 @@
|
|
|
11
11
|
export type JsonSchemaType =
|
|
12
12
|
'string' | 'number' | 'integer' | 'boolean' | 'object' | 'array' | 'null';
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* The input KIND a `string` field collects, drawn as the matching keyboard or
|
|
16
|
+
* native control. The vocabulary is the HTML `inputmode`/control naming of the
|
|
17
|
+
* concept: `numeric`/`decimal` set a text box's keyboard hint; `tel`/`email`/`url`
|
|
18
|
+
* select the matching native control. It draws, it does not validate.
|
|
19
|
+
*/
|
|
20
|
+
export type FormInputMode = 'text' | 'numeric' | 'decimal' | 'tel' | 'email' | 'url';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The CONTROL a surface draws for a property in place of the default for the
|
|
24
|
+
* property's shape. `chips` draws a choice (a `string` with an `enum`, or an
|
|
25
|
+
* `array` of such strings) as a tap-to-select chip grid instead of the
|
|
26
|
+
* radio/select or checkbox group. `textarea` draws a `string` as a multi-line
|
|
27
|
+
* text control instead of a single-line box. It changes what is DRAWN, never what
|
|
28
|
+
* is accepted — the value and its validation stay the property's own.
|
|
29
|
+
*/
|
|
30
|
+
export type FormControl = 'chips' | 'textarea';
|
|
31
|
+
|
|
14
32
|
/** A Pydantic-style discriminated-union tag: which property selects the variant. */
|
|
15
33
|
export interface Discriminator {
|
|
16
34
|
readonly propertyName: string;
|
|
@@ -61,6 +79,26 @@ export interface JsonSchema {
|
|
|
61
79
|
readonly minItems?: number;
|
|
62
80
|
readonly maxItems?: number;
|
|
63
81
|
readonly pattern?: string;
|
|
82
|
+
/**
|
|
83
|
+
* The input KIND a `string` field collects — a platform keyword (see {@link
|
|
84
|
+
* FormInputMode}). The renderer draws the matching keyboard or native control:
|
|
85
|
+
* `numeric`/`decimal` a text box with that keyboard hint (the value stays the
|
|
86
|
+
* typed string, so `"007"` keeps its leading zeros), `tel`/`email`/`url` the
|
|
87
|
+
* matching native control, `text` or absent a plain text box. It DRAWS, it does
|
|
88
|
+
* not validate — the answer door still enforces `type`, `pattern` and `format`.
|
|
89
|
+
* When present it OUTRANKS `format` for the control. A value outside the set is
|
|
90
|
+
* read as absent (the server ask door is the authority that refuses it).
|
|
91
|
+
*/
|
|
92
|
+
readonly inputMode?: FormInputMode;
|
|
93
|
+
/**
|
|
94
|
+
* The CONTROL to draw for this property in place of its shape's default — a
|
|
95
|
+
* platform keyword (see {@link FormControl}). `chips` draws a `string`-with-`enum`
|
|
96
|
+
* or an `array` of such strings as a chip grid; `textarea` draws a `string` as a
|
|
97
|
+
* multi-line text control. It DRAWS, it does not validate — the answer door still
|
|
98
|
+
* enforces the property's `type`, `enum`, `maxLength` and the rest. A value outside
|
|
99
|
+
* the set is read as absent (the server ask door is the authority that refuses it).
|
|
100
|
+
*/
|
|
101
|
+
readonly control?: FormControl;
|
|
64
102
|
/**
|
|
65
103
|
* Platform date-constraint keys on a `string`/`date` (or `date-time`) property. They
|
|
66
104
|
* are NOT the JSON-Schema numeric keywords (which are inert on a string): `minDate`/
|