@formhaus/core 0.12.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/{check-definition-B-YU_XN6.d.ts → check-definition-DuLExofm.d.ts} +2 -2
- package/dist/chunk-4IHQINIH.js +3 -0
- package/dist/chunk-4IHQINIH.js.map +1 -0
- package/dist/chunk-5KV7BDCS.js +2 -0
- package/dist/chunk-5KV7BDCS.js.map +1 -0
- package/dist/chunk-FQ3GLJTI.js +5 -0
- package/dist/chunk-FQ3GLJTI.js.map +1 -0
- package/dist/chunk-PZEBYRGE.js +2 -0
- package/dist/chunk-PZEBYRGE.js.map +1 -0
- package/dist/{chunk-KXLWZLOO.js → chunk-UFYD5LVW.js} +3 -3
- package/dist/{chunk-KXLWZLOO.js.map → chunk-UFYD5LVW.js.map} +1 -1
- package/dist/chunk-Y33LPJXU.js +2 -0
- package/dist/chunk-Y33LPJXU.js.map +1 -0
- package/dist/cloud.d.ts +3 -3
- package/dist/cloud.js +1 -1
- package/dist/{controller-DXb698eH.d.ts → controller-Ds_kbXSU.d.ts} +1 -1
- package/dist/default-messages-yS4QtrYi.d.ts +23 -0
- package/dist/{definition-PdtKhxBh.d.ts → definition-B4OIy_wH.d.ts} +44 -1
- package/dist/i18n.d.ts +60 -0
- package/dist/i18n.js +2 -0
- package/dist/i18n.js.map +1 -0
- package/dist/index.d.ts +11 -12
- package/dist/index.js +1 -1
- package/dist/internal.d.ts +8 -5
- package/dist/internal.js +1 -1
- package/dist/internal.js.map +1 -1
- package/dist/{runtime-Dn2_UIxt.d.ts → runtime-BzYG6cit.d.ts} +5 -2
- package/dist/server.d.ts +6 -4
- package/dist/server.iife.js +4 -4
- package/dist/server.iife.js.map +1 -1
- package/dist/server.js +1 -1
- package/dist/server.js.map +1 -1
- package/dist/validate-field-ND_UxoJU.d.ts +7 -0
- package/fixtures/submissions/messages.json +155 -0
- package/package.json +6 -1
- package/schema/form-definition.schema.json +236 -0
- package/schema/form-translation.schema.json +222 -0
- package/dist/chunk-OBVVPPVA.js +0 -3
- package/dist/chunk-OBVVPPVA.js.map +0 -1
- package/dist/chunk-OZ352IOK.js +0 -5
- package/dist/chunk-OZ352IOK.js.map +0 -1
- package/dist/validate-field-dZd5yZAp.d.ts +0 -19
package/dist/server.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {c,b}from'./chunk-Y33LPJXU.js';import {r}from'./chunk-4IHQINIH.js';import {k}from'./chunk-FQ3GLJTI.js';export{a as FormDefinitionError,j as checkDefinition,k as parseDefinition}from'./chunk-FQ3GLJTI.js';import {g,d as d$1,p,q}from'./chunk-5KV7BDCS.js';var V='Pass the values object as the second argument, not the request body with "values" and "skippedSteps".',h=()=>null,d=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);function D(e,t,s){let o=s!==null&&typeof s=="object"&&!Array.isArray(s)?s:{},n=b(e),i=[...new Set(t.map(({key:l})=>l))].filter(l=>d(o,l)),a={values:{},active:new Map};for(let l=0;l<=t.length;l++){let c={};for(let r of i)c[r]=p(a.active.get(r)??t.find(p=>p.key===r),o[r]);let u=new Map;for(let r of i){let p=n.activeField(r,c);p&&u.set(r,p);}let m=i.every(r=>u.get(r)===a.active.get(r));if(a={values:c,active:u},m)break}return a}function A({values:e,active:t},s){let o=new Map;for(let[n,i]of t){let a=q(i,e[n],s);a&&o.set(n,a);}return o}function M(e,t){let s=n=>n.map(i=>t.has(i.key)?{...i,validation:void 0}:i),o=e.steps?.map(n=>({...n,fields:s(n.fields)}));return {...e,...e.fields&&{fields:s(e.fields)},...o&&{steps:o}}}function E(e,t={}){let s={};for(let{validation:o}of e)o?.validator&&(s[o.validator]=h);return {...s,...t}}function O(e,t){return t===null||typeof t!="object"||!d(t,"values")?false:!e.some(s=>s.key==="values"||s.key==="skippedSteps")}function x(e,t){if(!Array.isArray(t))return;let s=new Set(t);for(let o=0;o<e.visibleSteps.length;o++){let n=e.visibleSteps[o];n.skip&&s.has(n.id)&&r(e,o);}}function j(e,t,s={}){k(e);let o=g(e),n=D(e,o,t),i=d$1(s.messages,e.messages),a=A(n,i),l={validators:E(o,s.validators),messages:i,onWarning:()=>{}},c$1=new c(M(e,a),n.values,l);x(c$1,s.skippedSteps);let u=c$1.validate(),m=c$1.getSubmitValues();for(let[r,p]of a)d(m,r)&&(u[r]=p);return O(o,t)&&(u.values=V),{values:m,errors:u}}export{j as validateSubmission};//# sourceMappingURL=server.js.map
|
|
2
2
|
//# sourceMappingURL=server.js.map
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server/validate-submission.ts"],"names":["BODY_MESSAGE","allow","hasOwn","source","key","activeValues","definition","fields","input","visibility","createVisibility","keys","result","round","values","normalizeValue","field","active","settled","typeErrors","errors","error","typeError","withoutRules","invalid","strip","steps","step","ignoredValidators","given","validators","validation","isBody","applySkips","engine","skippedSteps","requested","index","skipStepAt","validateSubmission","options","parseDefinition","getAllFields","resolved","engineOptions","FormEngine","submitted"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/server/validate-submission.ts"],"names":["BODY_MESSAGE","allow","hasOwn","source","key","activeValues","definition","fields","input","visibility","createVisibility","keys","result","round","values","normalizeValue","field","active","settled","typeErrors","resolve","errors","error","typeError","withoutRules","invalid","strip","steps","step","ignoredValidators","given","validators","validation","isBody","applySkips","engine","skippedSteps","requested","index","skipStepAt","validateSubmission","options","parseDefinition","getAllFields","resolved","messages","messageResolver","engineOptions","FormEngine","submitted"],"mappings":"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","file":"server.js","sourcesContent":["import type { FormDefinition, FormField } from '../types';\nimport type { MessageSource, ValidatorFn } from '../validation';\nimport { messageResolver, type ResolveMessage } from '../validation/default-messages';\nimport { parseDefinition } from '../definition-validation';\nimport { getAllFields } from '../engine/engine-utils';\nimport type { EngineInternals } from '../engine/runtime-internals';\nimport { FormEngine } from '../engine/runtime';\nimport { skipStepAt } from '../engine/step-skip';\nimport { createVisibility } from '../engine/visibility-state';\nimport { normalizeValue, typeError } from '../validation/type-checks';\n\nexport interface SubmissionOptions {\n skippedSteps?: string[];\n validators?: Record<string, ValidatorFn>;\n messages?: MessageSource;\n}\n\nconst BODY_MESSAGE = 'Pass the values object as the second argument, not the request body with \"values\" and \"skippedSteps\".';\n\nexport interface SubmissionResult {\n values: Record<string, unknown>;\n errors: Record<string, string>;\n}\n\nconst allow: ValidatorFn = () => null;\n\nconst hasOwn = (source: object, key: string) => Object.prototype.hasOwnProperty.call(source, key);\n\ninterface ActiveValues {\n values: Record<string, unknown>;\n active: Map<string, FormField>;\n}\n\nfunction activeValues(definition: FormDefinition, fields: FormField[], input: unknown): ActiveValues {\n const source = input !== null && typeof input === 'object' && !Array.isArray(input) ? input as Record<string, unknown> : {};\n const visibility = createVisibility(definition);\n const keys = [...new Set(fields.map(({ key }) => key))].filter((key) => hasOwn(source, key));\n let result: ActiveValues = { values: {}, active: new Map() };\n for (let round = 0; round <= fields.length; round++) {\n const values: Record<string, unknown> = {};\n for (const key of keys) values[key] = normalizeValue(result.active.get(key) ?? fields.find((field) => field.key === key)!, source[key]);\n const active = new Map<string, FormField>();\n for (const key of keys) {\n const field = visibility.activeField(key, values);\n if (field) active.set(key, field);\n }\n const settled = keys.every((key) => active.get(key) === result.active.get(key));\n result = { values, active };\n if (settled) break;\n }\n return result;\n}\n\nfunction typeErrors({ values, active }: ActiveValues, resolve: ResolveMessage): Map<string, string> {\n const errors = new Map<string, string>();\n for (const [key, field] of active) {\n const error = typeError(field, values[key], resolve);\n if (error) errors.set(key, error);\n }\n return errors;\n}\n\nfunction withoutRules(definition: FormDefinition, invalid: Map<string, string>): FormDefinition {\n const strip = (fields: FormField[]) => fields.map((field) => (invalid.has(field.key) ? { ...field, validation: undefined } : field));\n const steps = definition.steps?.map((step) => ({ ...step, fields: strip(step.fields) }));\n return { ...definition, ...(definition.fields && { fields: strip(definition.fields) }), ...(steps && { steps }) };\n}\n\nfunction ignoredValidators(fields: FormField[], given: Record<string, ValidatorFn> = {}): Record<string, ValidatorFn> {\n const validators: Record<string, ValidatorFn> = {};\n for (const { validation } of fields) if (validation?.validator) validators[validation.validator] = allow;\n return { ...validators, ...given };\n}\n\nfunction isBody(fields: FormField[], input: unknown): boolean {\n if (input === null || typeof input !== 'object' || !hasOwn(input, 'values')) return false;\n return !fields.some((field) => field.key === 'values' || field.key === 'skippedSteps');\n}\n\nfunction applySkips(engine: FormEngine, skippedSteps: unknown): void {\n if (!Array.isArray(skippedSteps)) return;\n const requested = new Set(skippedSteps);\n for (let index = 0; index < engine.visibleSteps.length; index++) {\n const step = engine.visibleSteps[index];\n if (step.skip && requested.has(step.id)) skipStepAt(engine as unknown as EngineInternals, index);\n }\n}\n\nexport function validateSubmission(\n definition: FormDefinition,\n values: unknown,\n options: SubmissionOptions = {},\n): SubmissionResult {\n parseDefinition(definition);\n const fields = getAllFields(definition);\n const resolved = activeValues(definition, fields, values);\n const messages = messageResolver(options.messages, definition.messages);\n const invalid = typeErrors(resolved, messages);\n const engineOptions = { validators: ignoredValidators(fields, options.validators), messages, onWarning: () => {} };\n const engine = new FormEngine(withoutRules(definition, invalid), resolved.values, engineOptions);\n applySkips(engine, options.skippedSteps);\n const errors = engine.validate();\n const submitted = engine.getSubmitValues();\n for (const [key, error] of invalid) if (hasOwn(submitted, key)) errors[key] = error;\n if (isBody(fields, values)) errors.values = BODY_MESSAGE;\n return { values: submitted, errors };\n}\n"]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { F as FormField } from './definition-B4OIy_wH.js';
|
|
2
|
+
import { M as MessageSource } from './default-messages-yS4QtrYi.js';
|
|
3
|
+
|
|
4
|
+
type ValidatorFn = (value: unknown, allValues: Record<string, unknown>) => string | null;
|
|
5
|
+
declare function validateFieldValue(field: FormField, input: unknown, allValues: Record<string, unknown>, validators?: Record<string, ValidatorFn>, messages?: MessageSource): string | null;
|
|
6
|
+
|
|
7
|
+
export { type ValidatorFn as V, validateFieldValue as v };
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "messages",
|
|
3
|
+
"definition": {
|
|
4
|
+
"id": "anketa",
|
|
5
|
+
"lang": "ru",
|
|
6
|
+
"title": "Анкета",
|
|
7
|
+
"submit": {
|
|
8
|
+
"label": "Отправить"
|
|
9
|
+
},
|
|
10
|
+
"messages": {
|
|
11
|
+
"required": "Обязательное поле",
|
|
12
|
+
"minLength": "Минимум {min} символов",
|
|
13
|
+
"maxItems": "Не более {max}",
|
|
14
|
+
"email": "Введите корректный email",
|
|
15
|
+
"min": "Не меньше {min}"
|
|
16
|
+
},
|
|
17
|
+
"i18n": {
|
|
18
|
+
"messages": {
|
|
19
|
+
"required": "errors.required"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"fields": [
|
|
23
|
+
{
|
|
24
|
+
"key": "name",
|
|
25
|
+
"type": "text",
|
|
26
|
+
"label": "Имя",
|
|
27
|
+
"i18n": {
|
|
28
|
+
"label": "form.name"
|
|
29
|
+
},
|
|
30
|
+
"validation": {
|
|
31
|
+
"required": true,
|
|
32
|
+
"minLength": 2
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"key": "email",
|
|
37
|
+
"type": "email",
|
|
38
|
+
"label": "Email",
|
|
39
|
+
"validation": {
|
|
40
|
+
"required": "Укажите email"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"key": "age",
|
|
45
|
+
"type": "number",
|
|
46
|
+
"label": "Возраст",
|
|
47
|
+
"validation": {
|
|
48
|
+
"min": 18
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"key": "topics",
|
|
53
|
+
"type": "multiselect",
|
|
54
|
+
"label": "Темы",
|
|
55
|
+
"options": [
|
|
56
|
+
{
|
|
57
|
+
"value": "news",
|
|
58
|
+
"label": "Новости"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"value": "events",
|
|
62
|
+
"label": "События"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"value": "jobs",
|
|
66
|
+
"label": "Вакансии"
|
|
67
|
+
}
|
|
68
|
+
],
|
|
69
|
+
"validation": {
|
|
70
|
+
"maxLength": 2
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
]
|
|
74
|
+
},
|
|
75
|
+
"cases": [
|
|
76
|
+
{
|
|
77
|
+
"name": "reports catalog messages with params",
|
|
78
|
+
"input": {
|
|
79
|
+
"values": {
|
|
80
|
+
"name": "А",
|
|
81
|
+
"email": "",
|
|
82
|
+
"age": 16,
|
|
83
|
+
"topics": [
|
|
84
|
+
"news",
|
|
85
|
+
"events",
|
|
86
|
+
"jobs"
|
|
87
|
+
]
|
|
88
|
+
},
|
|
89
|
+
"skippedSteps": []
|
|
90
|
+
},
|
|
91
|
+
"expected": {
|
|
92
|
+
"values": {
|
|
93
|
+
"name": "А",
|
|
94
|
+
"email": "",
|
|
95
|
+
"age": 16,
|
|
96
|
+
"topics": [
|
|
97
|
+
"news",
|
|
98
|
+
"events",
|
|
99
|
+
"jobs"
|
|
100
|
+
]
|
|
101
|
+
},
|
|
102
|
+
"errors": {
|
|
103
|
+
"name": "Минимум 2 символов",
|
|
104
|
+
"email": "Укажите email",
|
|
105
|
+
"age": "Не меньше 18",
|
|
106
|
+
"topics": "Не более 2"
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"name": "reports a format error from the catalog",
|
|
112
|
+
"input": {
|
|
113
|
+
"values": {
|
|
114
|
+
"name": "Аня",
|
|
115
|
+
"email": "nope"
|
|
116
|
+
},
|
|
117
|
+
"skippedSteps": []
|
|
118
|
+
},
|
|
119
|
+
"expected": {
|
|
120
|
+
"values": {
|
|
121
|
+
"name": "Аня",
|
|
122
|
+
"email": "nope"
|
|
123
|
+
},
|
|
124
|
+
"errors": {
|
|
125
|
+
"email": "Введите корректный email"
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"name": "accepts valid answers",
|
|
131
|
+
"input": {
|
|
132
|
+
"values": {
|
|
133
|
+
"name": "Аня",
|
|
134
|
+
"email": "anya@example.com",
|
|
135
|
+
"age": 30,
|
|
136
|
+
"topics": [
|
|
137
|
+
"news"
|
|
138
|
+
]
|
|
139
|
+
},
|
|
140
|
+
"skippedSteps": []
|
|
141
|
+
},
|
|
142
|
+
"expected": {
|
|
143
|
+
"values": {
|
|
144
|
+
"name": "Аня",
|
|
145
|
+
"email": "anya@example.com",
|
|
146
|
+
"age": 30,
|
|
147
|
+
"topics": [
|
|
148
|
+
"news"
|
|
149
|
+
]
|
|
150
|
+
},
|
|
151
|
+
"errors": {}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
]
|
|
155
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@formhaus/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "JSON form definition engine: validation, conditional visibility, multi-step and branching routes. Zero dependencies, works in React, Vue, Svelte or vanilla.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": [
|
|
@@ -27,9 +27,14 @@
|
|
|
27
27
|
"types": "./dist/internal.d.ts",
|
|
28
28
|
"import": "./dist/internal.js"
|
|
29
29
|
},
|
|
30
|
+
"./i18n": {
|
|
31
|
+
"types": "./dist/i18n.d.ts",
|
|
32
|
+
"import": "./dist/i18n.js"
|
|
33
|
+
},
|
|
30
34
|
"./server.iife.js": "./dist/server.iife.js",
|
|
31
35
|
"./style.css": "./style.css",
|
|
32
36
|
"./schema.json": "./schema/form-definition.schema.json",
|
|
37
|
+
"./translation.schema.json": "./schema/form-translation.schema.json",
|
|
33
38
|
"./fixtures/submissions/*": "./fixtures/submissions/*",
|
|
34
39
|
"./fixtures/cloud/*": "./fixtures/cloud/*"
|
|
35
40
|
},
|
|
@@ -16,6 +16,10 @@
|
|
|
16
16
|
"type": "string",
|
|
17
17
|
"description": "Unique form identifier."
|
|
18
18
|
},
|
|
19
|
+
"lang": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"description": "Language of the texts, a BCP 47 tag such as \"en\" or \"pt-BR\". Renderers set it as the lang attribute of the form."
|
|
22
|
+
},
|
|
19
23
|
"title": {
|
|
20
24
|
"type": "string",
|
|
21
25
|
"description": "Form title for host UI and Figma output. Renderers do not display it."
|
|
@@ -25,6 +29,18 @@
|
|
|
25
29
|
"description": "Plain text shown under the title by the host UI and Figma output. Renderers do not display it. Up to 1000 characters.",
|
|
26
30
|
"maxLength": 1000
|
|
27
31
|
},
|
|
32
|
+
"messages": {
|
|
33
|
+
"$ref": "#/definitions/MessageCatalog",
|
|
34
|
+
"description": "Validation message templates by message key. {min} and {max} are replaced. Field-level messages win over these."
|
|
35
|
+
},
|
|
36
|
+
"labels": {
|
|
37
|
+
"$ref": "#/definitions/LabelCatalog",
|
|
38
|
+
"description": "Renderer label templates: continue, back, stepOf ({current}, {total}) and errorSummary."
|
|
39
|
+
},
|
|
40
|
+
"i18n": {
|
|
41
|
+
"$ref": "#/definitions/FormI18n",
|
|
42
|
+
"description": "Translation keys for the form texts and the messages and labels catalogs, resolved by the app translate function."
|
|
43
|
+
},
|
|
28
44
|
"submit": {
|
|
29
45
|
"$ref": "#/definitions/FormAction",
|
|
30
46
|
"description": "Submit button on the last step."
|
|
@@ -56,6 +72,130 @@
|
|
|
56
72
|
"additionalProperties": false,
|
|
57
73
|
"description": "A Formhaus form. Use fields for a single-step form or steps for a multi-step form, not both.",
|
|
58
74
|
"definitions": {
|
|
75
|
+
"MessageCatalog": {
|
|
76
|
+
"type": "object",
|
|
77
|
+
"properties": {
|
|
78
|
+
"required": {
|
|
79
|
+
"type": "string",
|
|
80
|
+
"minLength": 1
|
|
81
|
+
},
|
|
82
|
+
"minLength": {
|
|
83
|
+
"type": "string",
|
|
84
|
+
"minLength": 1
|
|
85
|
+
},
|
|
86
|
+
"maxLength": {
|
|
87
|
+
"type": "string",
|
|
88
|
+
"minLength": 1
|
|
89
|
+
},
|
|
90
|
+
"minItems": {
|
|
91
|
+
"type": "string",
|
|
92
|
+
"minLength": 1
|
|
93
|
+
},
|
|
94
|
+
"maxItems": {
|
|
95
|
+
"type": "string",
|
|
96
|
+
"minLength": 1
|
|
97
|
+
},
|
|
98
|
+
"min": {
|
|
99
|
+
"type": "string",
|
|
100
|
+
"minLength": 1
|
|
101
|
+
},
|
|
102
|
+
"max": {
|
|
103
|
+
"type": "string",
|
|
104
|
+
"minLength": 1
|
|
105
|
+
},
|
|
106
|
+
"pattern": {
|
|
107
|
+
"type": "string",
|
|
108
|
+
"minLength": 1
|
|
109
|
+
},
|
|
110
|
+
"matchField": {
|
|
111
|
+
"type": "string",
|
|
112
|
+
"minLength": 1
|
|
113
|
+
},
|
|
114
|
+
"email": {
|
|
115
|
+
"type": "string",
|
|
116
|
+
"minLength": 1
|
|
117
|
+
},
|
|
118
|
+
"number": {
|
|
119
|
+
"type": "string",
|
|
120
|
+
"minLength": 1
|
|
121
|
+
},
|
|
122
|
+
"date": {
|
|
123
|
+
"type": "string",
|
|
124
|
+
"minLength": 1
|
|
125
|
+
},
|
|
126
|
+
"datetime": {
|
|
127
|
+
"type": "string",
|
|
128
|
+
"minLength": 1
|
|
129
|
+
},
|
|
130
|
+
"option": {
|
|
131
|
+
"type": "string",
|
|
132
|
+
"minLength": 1
|
|
133
|
+
},
|
|
134
|
+
"options": {
|
|
135
|
+
"type": "string",
|
|
136
|
+
"minLength": 1
|
|
137
|
+
},
|
|
138
|
+
"boolean": {
|
|
139
|
+
"type": "string",
|
|
140
|
+
"minLength": 1
|
|
141
|
+
},
|
|
142
|
+
"string": {
|
|
143
|
+
"type": "string",
|
|
144
|
+
"minLength": 1
|
|
145
|
+
},
|
|
146
|
+
"file": {
|
|
147
|
+
"type": "string",
|
|
148
|
+
"minLength": 1
|
|
149
|
+
},
|
|
150
|
+
"value": {
|
|
151
|
+
"type": "string",
|
|
152
|
+
"minLength": 1
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
"additionalProperties": false
|
|
156
|
+
},
|
|
157
|
+
"LabelCatalog": {
|
|
158
|
+
"type": "object",
|
|
159
|
+
"properties": {
|
|
160
|
+
"continue": {
|
|
161
|
+
"type": "string",
|
|
162
|
+
"minLength": 1
|
|
163
|
+
},
|
|
164
|
+
"back": {
|
|
165
|
+
"type": "string",
|
|
166
|
+
"minLength": 1
|
|
167
|
+
},
|
|
168
|
+
"stepOf": {
|
|
169
|
+
"type": "string",
|
|
170
|
+
"minLength": 1
|
|
171
|
+
},
|
|
172
|
+
"errorSummary": {
|
|
173
|
+
"type": "string",
|
|
174
|
+
"minLength": 1
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
"additionalProperties": false
|
|
178
|
+
},
|
|
179
|
+
"FormI18n": {
|
|
180
|
+
"type": "object",
|
|
181
|
+
"properties": {
|
|
182
|
+
"title": {
|
|
183
|
+
"type": "string",
|
|
184
|
+
"minLength": 1
|
|
185
|
+
},
|
|
186
|
+
"description": {
|
|
187
|
+
"type": "string",
|
|
188
|
+
"minLength": 1
|
|
189
|
+
},
|
|
190
|
+
"messages": {
|
|
191
|
+
"$ref": "#/definitions/MessageCatalog"
|
|
192
|
+
},
|
|
193
|
+
"labels": {
|
|
194
|
+
"$ref": "#/definitions/LabelCatalog"
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
"additionalProperties": false
|
|
198
|
+
},
|
|
59
199
|
"FormAction": {
|
|
60
200
|
"type": "object",
|
|
61
201
|
"properties": {
|
|
@@ -75,6 +215,10 @@
|
|
|
75
215
|
"items": {
|
|
76
216
|
"$ref": "#/definitions/ShowCondition"
|
|
77
217
|
}
|
|
218
|
+
},
|
|
219
|
+
"i18n": {
|
|
220
|
+
"$ref": "#/definitions/LabelI18n",
|
|
221
|
+
"description": "Translation key for the button label."
|
|
78
222
|
}
|
|
79
223
|
},
|
|
80
224
|
"required": [
|
|
@@ -154,6 +298,16 @@
|
|
|
154
298
|
"minProperties": 2,
|
|
155
299
|
"maxProperties": 2
|
|
156
300
|
},
|
|
301
|
+
"LabelI18n": {
|
|
302
|
+
"type": "object",
|
|
303
|
+
"properties": {
|
|
304
|
+
"label": {
|
|
305
|
+
"type": "string",
|
|
306
|
+
"minLength": 1
|
|
307
|
+
}
|
|
308
|
+
},
|
|
309
|
+
"additionalProperties": false
|
|
310
|
+
},
|
|
157
311
|
"FormField": {
|
|
158
312
|
"type": "object",
|
|
159
313
|
"properties": {
|
|
@@ -263,6 +417,10 @@
|
|
|
263
417
|
"url",
|
|
264
418
|
"search"
|
|
265
419
|
]
|
|
420
|
+
},
|
|
421
|
+
"i18n": {
|
|
422
|
+
"$ref": "#/definitions/FieldI18n",
|
|
423
|
+
"description": "Translation keys for label, placeholder and helperText. The literal texts stay the fallback."
|
|
266
424
|
}
|
|
267
425
|
},
|
|
268
426
|
"required": [
|
|
@@ -350,6 +508,44 @@
|
|
|
350
508
|
},
|
|
351
509
|
"validator": {
|
|
352
510
|
"type": "string"
|
|
511
|
+
},
|
|
512
|
+
"i18n": {
|
|
513
|
+
"$ref": "#/definitions/ValidationI18n",
|
|
514
|
+
"description": "Translation keys for the messages of the validation rules, by rule name."
|
|
515
|
+
}
|
|
516
|
+
},
|
|
517
|
+
"additionalProperties": false
|
|
518
|
+
},
|
|
519
|
+
"ValidationI18n": {
|
|
520
|
+
"type": "object",
|
|
521
|
+
"properties": {
|
|
522
|
+
"required": {
|
|
523
|
+
"type": "string",
|
|
524
|
+
"minLength": 1
|
|
525
|
+
},
|
|
526
|
+
"minLength": {
|
|
527
|
+
"type": "string",
|
|
528
|
+
"minLength": 1
|
|
529
|
+
},
|
|
530
|
+
"maxLength": {
|
|
531
|
+
"type": "string",
|
|
532
|
+
"minLength": 1
|
|
533
|
+
},
|
|
534
|
+
"min": {
|
|
535
|
+
"type": "string",
|
|
536
|
+
"minLength": 1
|
|
537
|
+
},
|
|
538
|
+
"max": {
|
|
539
|
+
"type": "string",
|
|
540
|
+
"minLength": 1
|
|
541
|
+
},
|
|
542
|
+
"pattern": {
|
|
543
|
+
"type": "string",
|
|
544
|
+
"minLength": 1
|
|
545
|
+
},
|
|
546
|
+
"matchField": {
|
|
547
|
+
"type": "string",
|
|
548
|
+
"minLength": 1
|
|
353
549
|
}
|
|
354
550
|
},
|
|
355
551
|
"additionalProperties": false
|
|
@@ -362,6 +558,10 @@
|
|
|
362
558
|
},
|
|
363
559
|
"label": {
|
|
364
560
|
"type": "string"
|
|
561
|
+
},
|
|
562
|
+
"i18n": {
|
|
563
|
+
"$ref": "#/definitions/LabelI18n",
|
|
564
|
+
"description": "Translation key for the option label."
|
|
365
565
|
}
|
|
366
566
|
},
|
|
367
567
|
"required": [
|
|
@@ -370,6 +570,24 @@
|
|
|
370
570
|
],
|
|
371
571
|
"additionalProperties": false
|
|
372
572
|
},
|
|
573
|
+
"FieldI18n": {
|
|
574
|
+
"type": "object",
|
|
575
|
+
"properties": {
|
|
576
|
+
"label": {
|
|
577
|
+
"type": "string",
|
|
578
|
+
"minLength": 1
|
|
579
|
+
},
|
|
580
|
+
"placeholder": {
|
|
581
|
+
"type": "string",
|
|
582
|
+
"minLength": 1
|
|
583
|
+
},
|
|
584
|
+
"helperText": {
|
|
585
|
+
"type": "string",
|
|
586
|
+
"minLength": 1
|
|
587
|
+
}
|
|
588
|
+
},
|
|
589
|
+
"additionalProperties": false
|
|
590
|
+
},
|
|
373
591
|
"FormStep": {
|
|
374
592
|
"type": "object",
|
|
375
593
|
"properties": {
|
|
@@ -437,6 +655,10 @@
|
|
|
437
655
|
"skip": {
|
|
438
656
|
"$ref": "#/definitions/FormAction",
|
|
439
657
|
"description": "Shows a Skip button that resets the step fields and moves forward without validation."
|
|
658
|
+
},
|
|
659
|
+
"i18n": {
|
|
660
|
+
"$ref": "#/definitions/StepI18n",
|
|
661
|
+
"description": "Translation keys for the step title and description. The literal texts stay the fallback."
|
|
440
662
|
}
|
|
441
663
|
},
|
|
442
664
|
"required": [
|
|
@@ -476,6 +698,20 @@
|
|
|
476
698
|
],
|
|
477
699
|
"additionalProperties": false,
|
|
478
700
|
"description": "A forward destination for a step."
|
|
701
|
+
},
|
|
702
|
+
"StepI18n": {
|
|
703
|
+
"type": "object",
|
|
704
|
+
"properties": {
|
|
705
|
+
"title": {
|
|
706
|
+
"type": "string",
|
|
707
|
+
"minLength": 1
|
|
708
|
+
},
|
|
709
|
+
"description": {
|
|
710
|
+
"type": "string",
|
|
711
|
+
"minLength": 1
|
|
712
|
+
}
|
|
713
|
+
},
|
|
714
|
+
"additionalProperties": false
|
|
479
715
|
}
|
|
480
716
|
}
|
|
481
717
|
}
|