@datenlotse/jsonjoy-builder 0.4.0 → 0.5.1

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.
@@ -1,6 +1,6 @@
1
1
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
2
  import { CirclePlus, HelpCircle, Info } from "lucide-react";
3
- import { useId, useState } from "react";
3
+ import { useEffect, useId, useState } from "react";
4
4
  import { Badge } from "../ui/badge.js";
5
5
  import { Button } from "../ui/button.js";
6
6
  import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "../ui/dialog.js";
@@ -8,30 +8,64 @@ import { Input } from "../ui/input.js";
8
8
  import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../ui/tooltip.js";
9
9
  import { useTranslation } from "../../hooks/use-translation.js";
10
10
  import SchemaTypeSelector from "./SchemaTypeSelector.js";
11
- const AddFieldButton = ({ onAddField, variant = "primary" })=>{
11
+ import TypeEditor from "./TypeEditor.js";
12
+ const ENUM_TYPES = [
13
+ "string",
14
+ "number",
15
+ "integer"
16
+ ];
17
+ function createEmptyDraftSchema(type) {
18
+ return {
19
+ type
20
+ };
21
+ }
22
+ const AddFieldButton = ({ parentSchema, onAddField, variant = "primary" })=>{
12
23
  const [dialogOpen, setDialogOpen] = useState(false);
13
24
  const [fieldName, setFieldName] = useState("");
14
25
  const [fieldType, setFieldType] = useState("string");
15
26
  const [fieldDesc, setFieldDesc] = useState("");
16
27
  const [fieldRequired, setFieldRequired] = useState(false);
28
+ const [draftSchema, setDraftSchema] = useState(()=>createEmptyDraftSchema("string"));
17
29
  const fieldNameId = useId();
18
30
  const fieldDescId = useId();
19
31
  const fieldRequiredId = useId();
20
32
  const fieldTypeId = useId();
21
33
  const t = useTranslation();
34
+ useEffect(()=>{
35
+ setDraftSchema((prev)=>{
36
+ const nextType = fieldType;
37
+ const prevType = prev.type;
38
+ if (prevType === nextType) return prev;
39
+ return createEmptyDraftSchema(nextType);
40
+ });
41
+ }, [
42
+ fieldType
43
+ ]);
44
+ useEffect(()=>{
45
+ if (dialogOpen) setDraftSchema(createEmptyDraftSchema(fieldType));
46
+ }, [
47
+ dialogOpen
48
+ ]);
22
49
  const handleSubmit = (e)=>{
23
50
  e.preventDefault();
24
51
  if (!fieldName.trim()) return;
52
+ const validation = ENUM_TYPES.includes(fieldType) && Object.keys(draftSchema).length > 1 ? {
53
+ ...draftSchema,
54
+ type: fieldType
55
+ } : void 0;
25
56
  onAddField({
26
- name: fieldName,
57
+ name: fieldName.trim(),
27
58
  type: fieldType,
28
59
  description: fieldDesc,
29
- required: fieldRequired
60
+ required: fieldRequired,
61
+ default: draftSchema.default,
62
+ validation
30
63
  });
31
64
  setFieldName("");
32
65
  setFieldType("string");
33
66
  setFieldDesc("");
34
67
  setFieldRequired(false);
68
+ setDraftSchema(createEmptyDraftSchema("string"));
35
69
  setDialogOpen(false);
36
70
  };
37
71
  return /*#__PURE__*/ jsxs(Fragment, {
@@ -283,6 +317,27 @@ const AddFieldButton = ({ onAddField, variant = "primary" })=>{
283
317
  })
284
318
  ]
285
319
  }),
320
+ ENUM_TYPES.includes(fieldType) && /*#__PURE__*/ jsxs("details", {
321
+ className: "group rounded-lg border bg-muted/30",
322
+ children: [
323
+ /*#__PURE__*/ jsx("summary", {
324
+ className: "cursor-pointer list-none px-4 py-3 text-sm font-medium",
325
+ children: t.stringAllowedValuesEnumLabel
326
+ }),
327
+ /*#__PURE__*/ jsx("div", {
328
+ className: "border-t px-4 pb-4 pt-2",
329
+ children: /*#__PURE__*/ jsx(TypeEditor, {
330
+ schema: draftSchema,
331
+ readOnly: false,
332
+ parentSchema: parentSchema,
333
+ propertyName: fieldName.trim() || void 0,
334
+ validationNode: void 0,
335
+ onChange: setDraftSchema,
336
+ depth: 1
337
+ })
338
+ })
339
+ ]
340
+ }),
286
341
  /*#__PURE__*/ jsxs(DialogFooter, {
287
342
  className: "mt-6 gap-2 flex-wrap",
288
343
  children: [
@@ -2,9 +2,10 @@ import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { ChevronDown, ChevronRight, ChevronUp, X } from "lucide-react";
3
3
  import { useEffect, useMemo, useState } from "react";
4
4
  import { Input } from "../ui/input.js";
5
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../ui/select.js";
5
6
  import { useTranslation } from "../../hooks/use-translation.js";
6
7
  import { cn } from "../../lib/utils.js";
7
- import { asObjectSchema, getSchemaDescription, withObjectSchema } from "../../types/jsonSchema.js";
8
+ import { asObjectSchema, getSchemaDescription, isBooleanSchema, withObjectSchema } from "../../types/jsonSchema.js";
8
9
  import { Badge } from "../ui/badge.js";
9
10
  import TypeDropdown from "./TypeDropdown.js";
10
11
  import TypeEditor from "./TypeEditor.js";
@@ -23,6 +24,33 @@ const SchemaPropertyEditor = ({ name, schema, required, readOnly = false, parent
23
24
  schema
24
25
  ]);
25
26
  const [tempDefault, setTempDefault] = useState(defaultValue);
27
+ const defaultValueOptions = useMemo(()=>{
28
+ if ("string" !== type && "number" !== type && "integer" !== type) return null;
29
+ const objSchema = asObjectSchema(schema);
30
+ const enumValues = objSchema.enum;
31
+ const dependentEnum = objSchema.$dependentEnum;
32
+ if (Array.isArray(enumValues) && enumValues.length > 0) return enumValues.map((v)=>String(v));
33
+ if (dependentEnum && parentSchema) {
34
+ const controllingPropertyName = dependentEnum.property;
35
+ const controllingPropertySchema = parentSchema.properties?.[controllingPropertyName];
36
+ if (controllingPropertySchema && "object" == typeof controllingPropertySchema && !isBooleanSchema(controllingPropertySchema)) {
37
+ const controllingDefault = controllingPropertySchema.default;
38
+ if (void 0 !== controllingDefault) {
39
+ const ctrlDefaultStr = String(controllingDefault);
40
+ const allowedValues = dependentEnum.values[ctrlDefaultStr];
41
+ if (Array.isArray(allowedValues) && allowedValues.length > 0) return allowedValues.map((v)=>String(v));
42
+ }
43
+ }
44
+ }
45
+ return null;
46
+ }, [
47
+ schema,
48
+ type,
49
+ parentSchema
50
+ ]);
51
+ const defaultError = useMemo(()=>validationNode?.validation.errors?.find((err)=>"default" === err.path[0])?.message, [
52
+ validationNode
53
+ ]);
26
54
  useEffect(()=>{
27
55
  setTempName(name);
28
56
  setTempDesc(getSchemaDescription(schema));
@@ -78,6 +106,26 @@ const SchemaPropertyEditor = ({ name, schema, required, readOnly = false, parent
78
106
  });
79
107
  }
80
108
  };
109
+ const handleDefaultSelectChange = (value)=>{
110
+ const objSchema = asObjectSchema(schema);
111
+ const currentDefault = objSchema.default;
112
+ if ("__none__" === value || "" === value) {
113
+ if (void 0 !== currentDefault) {
114
+ const { default: _, ...rest } = objSchema;
115
+ onSchemaChange(rest);
116
+ }
117
+ } else {
118
+ let parsedValue = value;
119
+ if ("number" === type || "integer" === type) {
120
+ parsedValue = Number(value);
121
+ if (Number.isNaN(parsedValue)) parsedValue = value;
122
+ }
123
+ if (JSON.stringify(currentDefault) !== JSON.stringify(parsedValue)) onSchemaChange({
124
+ ...objSchema,
125
+ default: parsedValue
126
+ });
127
+ }
128
+ };
81
129
  return /*#__PURE__*/ jsxs("div", {
82
130
  className: cn("mb-2 animate-in rounded-lg border transition-all duration-200", depth > 0 && "ml-0 sm:ml-4 border-l border-l-border/40"),
83
131
  children: [
@@ -221,13 +269,40 @@ const SchemaPropertyEditor = ({ name, schema, required, readOnly = false, parent
221
269
  className: "text-xs font-medium text-muted-foreground",
222
270
  children: t.propertyDefaultLabel
223
271
  }),
224
- /*#__PURE__*/ jsx(Input, {
272
+ defaultValueOptions && defaultValueOptions.length > 0 ? /*#__PURE__*/ jsxs(Select, {
273
+ value: "" !== defaultValue && defaultValueOptions.includes(defaultValue) ? defaultValue : "__none__",
274
+ onValueChange: handleDefaultSelectChange,
275
+ children: [
276
+ /*#__PURE__*/ jsx(SelectTrigger, {
277
+ className: "h-8 text-sm",
278
+ children: /*#__PURE__*/ jsx(SelectValue, {
279
+ placeholder: t.propertyDefaultPlaceholder
280
+ })
281
+ }),
282
+ /*#__PURE__*/ jsxs(SelectContent, {
283
+ children: [
284
+ /*#__PURE__*/ jsx(SelectItem, {
285
+ value: "__none__",
286
+ children: t.propertyDefaultNone
287
+ }),
288
+ defaultValueOptions.map((option)=>/*#__PURE__*/ jsx(SelectItem, {
289
+ value: option,
290
+ children: option
291
+ }, option))
292
+ ]
293
+ })
294
+ ]
295
+ }) : /*#__PURE__*/ jsx(Input, {
225
296
  value: tempDefault,
226
297
  onChange: (e)=>setTempDefault(e.target.value),
227
298
  onBlur: handleDefaultSubmit,
228
299
  onKeyDown: (e)=>"Enter" === e.key && handleDefaultSubmit(),
229
300
  placeholder: t.propertyDefaultPlaceholder,
230
301
  className: "h-8 text-sm"
302
+ }),
303
+ defaultError && /*#__PURE__*/ jsx("div", {
304
+ className: "text-xs text-destructive italic",
305
+ children: defaultError
231
306
  })
232
307
  ]
233
308
  }),
@@ -44,6 +44,7 @@ const SchemaVisualEditor = ({ schema, onChange, readOnly = false })=>{
44
44
  !readOnly && /*#__PURE__*/ jsx("div", {
45
45
  className: "mb-6 shrink-0",
46
46
  children: /*#__PURE__*/ jsx(AddFieldButton, {
47
+ parentSchema: asObjectSchema(schema),
47
48
  onAddField: handleAddField
48
49
  })
49
50
  }),
@@ -79,6 +79,7 @@ const ObjectEditor = ({ schema, validationNode, onChange, depth = 0, readOnly =
79
79
  !readOnly && /*#__PURE__*/ jsx("div", {
80
80
  className: "mt-4",
81
81
  children: /*#__PURE__*/ jsx(AddFieldButton, {
82
+ parentSchema: normalizedSchema,
82
83
  onAddField: handleAddProperty,
83
84
  variant: "secondary"
84
85
  })
@@ -30,7 +30,7 @@ const de = {
30
30
  fieldTypeBooleanDescription: "Für Wahr/Falsch-Werte",
31
31
  fieldTypeObjectLabel: "Gruppe",
32
32
  fieldTypeObjectDescription: "Zum Gruppieren verwandter Felder",
33
- fieldTypeArrayLabel: "Liste",
33
+ fieldTypeArrayLabel: "Mehrfach-Auswahl",
34
34
  fieldTypeArrayDescription: "Für Sammlungen von Elementen",
35
35
  propertyDescriptionPlaceholder: "Beschreibung hinzufügen...",
36
36
  propertyDescriptionButton: "Beschreibung hinzufügen...",
@@ -41,6 +41,9 @@ const de = {
41
41
  propertyMoveDown: "Feld nach unten verschieben",
42
42
  propertyDefaultLabel: "Standardwert",
43
43
  propertyDefaultPlaceholder: "Standardwert eingeben...",
44
+ propertyDefaultMustBeInEnum: "Der Standardwert muss einer der erlaubten Werte sein.",
45
+ propertyDefaultRequiresDependentDefault: "Ein Standardwert kann nur gesetzt werden, wenn die abhängige Eigenschaft einen Standardwert hat.",
46
+ propertyDefaultNone: "Kein Standardwert",
44
47
  schemaEditorTitle: "JSON-Schema-Editor",
45
48
  schemaEditorToggleFullscreen: "Vollbild umschalten",
46
49
  schemaEditorEditModeVisual: "Visuell",
@@ -109,10 +112,10 @@ const de = {
109
112
  whenPropertyEquals: "Wenn {property} = {value}",
110
113
  stringFormatSelectPlaceholder: "Format auswählen",
111
114
  stringValidationErrorLengthRange: "'Minimale Länge' darf nicht größer als 'Maximale Länge' sein.",
112
- schemaTypeArray: "Liste",
115
+ schemaTypeArray: "Mehrfach-Auswahl",
113
116
  schemaTypeBoolean: "Ja/Nein",
114
117
  schemaTypeNumber: "Zahl",
115
- schemaTypeObject: "Objekt",
118
+ schemaTypeObject: "Gruppe",
116
119
  schemaTypeString: "Text",
117
120
  schemaTypeNull: "Leer",
118
121
  inferrerTitle: "JSON-Schema ableiten",
@@ -41,6 +41,9 @@ const en = {
41
41
  propertyMoveDown: "Move field down",
42
42
  propertyDefaultLabel: "Default Value",
43
43
  propertyDefaultPlaceholder: "Enter default value...",
44
+ propertyDefaultMustBeInEnum: "Default value must be one of the allowed values.",
45
+ propertyDefaultRequiresDependentDefault: "Default can only be set when the dependent property has a default.",
46
+ propertyDefaultNone: "No default",
44
47
  schemaEditorTitle: "JSON Schema Editor",
45
48
  schemaEditorToggleFullscreen: "Toggle fullscreen",
46
49
  schemaEditorEditModeVisual: "Visual",
@@ -41,6 +41,9 @@ const es = {
41
41
  propertyMoveDown: "Mover campo hacia abajo",
42
42
  propertyDefaultLabel: "Valor por defecto",
43
43
  propertyDefaultPlaceholder: "Ingresar valor por defecto...",
44
+ propertyDefaultMustBeInEnum: "El valor por defecto debe ser uno de los valores permitidos.",
45
+ propertyDefaultRequiresDependentDefault: "El valor por defecto solo puede establecerse cuando la propiedad dependiente tiene un valor por defecto.",
46
+ propertyDefaultNone: "Sin valor por defecto",
44
47
  schemaEditorTitle: "Editor de JSON Schema",
45
48
  schemaEditorToggleFullscreen: "Cambiar a pantalla completa",
46
49
  schemaEditorEditModeVisual: "Visual",
@@ -41,6 +41,9 @@ const fr = {
41
41
  propertyMoveDown: "Déplacer le champ vers le bas",
42
42
  propertyDefaultLabel: "Valeur par défaut",
43
43
  propertyDefaultPlaceholder: "Entrer la valeur par défaut...",
44
+ propertyDefaultMustBeInEnum: "La valeur par défaut doit être l'une des valeurs autorisées.",
45
+ propertyDefaultRequiresDependentDefault: "La valeur par défaut ne peut être définie que lorsque la propriété dépendante a une valeur par défaut.",
46
+ propertyDefaultNone: "Aucune valeur par défaut",
44
47
  schemaEditorTitle: "Éditeur de schéma JSON",
45
48
  schemaEditorToggleFullscreen: "Basculer en plein écran",
46
49
  schemaEditorEditModeVisual: "Visuel",
@@ -41,6 +41,9 @@ const ru = {
41
41
  propertyMoveDown: "Переместить поле вниз",
42
42
  propertyDefaultLabel: "Значение по умолчанию",
43
43
  propertyDefaultPlaceholder: "Введите значение по умолчанию...",
44
+ propertyDefaultMustBeInEnum: "Значение по умолчанию должно быть одним из разрешенных значений.",
45
+ propertyDefaultRequiresDependentDefault: "Значение по умолчанию может быть установлено только тогда, когда зависимое свойство имеет значение по умолчанию.",
46
+ propertyDefaultNone: "Нет значения по умолчанию",
44
47
  schemaEditorTitle: "Редактор JSON схем",
45
48
  schemaEditorToggleFullscreen: "Переключить полноэкранный режим",
46
49
  schemaEditorEditModeVisual: "Визуальный",
@@ -41,6 +41,9 @@ const uk = {
41
41
  propertyMoveDown: "Перемістити поле вниз",
42
42
  propertyDefaultLabel: "Значення за замовчуванням",
43
43
  propertyDefaultPlaceholder: "Введіть значення за замовчуванням...",
44
+ propertyDefaultMustBeInEnum: "Значення за замовчуванням повинно бути одним з дозволених значень.",
45
+ propertyDefaultRequiresDependentDefault: "Значення за замовчуванням може бути встановлено лише тоді, коли залежна властивість має значення за замовчуванням.",
46
+ propertyDefaultNone: "Немає значення за замовчуванням",
44
47
  schemaEditorTitle: "Редактор JSON-схем",
45
48
  schemaEditorToggleFullscreen: "Перемкнути повноекранний режим",
46
49
  schemaEditorEditModeVisual: "Візуальний",
@@ -41,6 +41,9 @@ const zh = {
41
41
  propertyMoveDown: "向下移动字段",
42
42
  propertyDefaultLabel: "默认值",
43
43
  propertyDefaultPlaceholder: "输入默认值...",
44
+ propertyDefaultMustBeInEnum: "默认值必须是允许的值之一。",
45
+ propertyDefaultRequiresDependentDefault: "只有在依赖属性具有默认值时才能设置默认值。",
46
+ propertyDefaultNone: "无默认值",
44
47
  schemaEditorTitle: "JSON Schema 编辑器",
45
48
  schemaEditorToggleFullscreen: "切换全屏",
46
49
  schemaEditorEditModeVisual: "可视化",
package/dist/index.css CHANGED
@@ -1356,6 +1356,10 @@
1356
1356
  cursor: text;
1357
1357
  }
1358
1358
 
1359
+ .jsonjoy .list-none, .jsonjoy.list-none {
1360
+ list-style-type: none;
1361
+ }
1362
+
1359
1363
  .jsonjoy .grid-cols-1, .jsonjoy.grid-cols-1 {
1360
1364
  grid-template-columns: repeat(1, minmax(0, 1fr));
1361
1365
  }
@@ -1719,7 +1723,17 @@
1719
1723
  background-color: var(--jsonjoy-color-green-50);
1720
1724
  }
1721
1725
 
1722
- .jsonjoy .bg-muted, .jsonjoy.bg-muted, .jsonjoy .bg-muted\/40, .jsonjoy.bg-muted\/40 {
1726
+ .jsonjoy .bg-muted, .jsonjoy.bg-muted, .jsonjoy .bg-muted\/30, .jsonjoy.bg-muted\/30 {
1727
+ background-color: var(--jsonjoy-color-muted);
1728
+ }
1729
+
1730
+ @supports (color: color-mix(in lab, red, red)) {
1731
+ .jsonjoy .bg-muted\/30, .jsonjoy.bg-muted\/30 {
1732
+ background-color: color-mix(in oklab, var(--jsonjoy-color-muted) 30%, transparent);
1733
+ }
1734
+ }
1735
+
1736
+ .jsonjoy .bg-muted\/40, .jsonjoy.bg-muted\/40 {
1723
1737
  background-color: var(--jsonjoy-color-muted);
1724
1738
  }
1725
1739
 
@@ -1968,6 +1982,10 @@
1968
1982
  padding-bottom: calc(var(--jsonjoy-spacing) * 2);
1969
1983
  }
1970
1984
 
1985
+ .jsonjoy .pb-4, .jsonjoy.pb-4 {
1986
+ padding-bottom: calc(var(--jsonjoy-spacing) * 4);
1987
+ }
1988
+
1971
1989
  .jsonjoy .pb-24, .jsonjoy.pb-24 {
1972
1990
  padding-bottom: calc(var(--jsonjoy-spacing) * 24);
1973
1991
  }
package/dist/index.d.ts CHANGED
@@ -400,6 +400,24 @@ export declare interface Translation {
400
400
  * > Enter default value...
401
401
  */
402
402
  readonly propertyDefaultPlaceholder: string;
403
+ /**
404
+ * The translation for the key `propertyDefaultMustBeInEnum`. English default is:
405
+ *
406
+ * > Default value must be one of the allowed values.
407
+ */
408
+ readonly propertyDefaultMustBeInEnum: string;
409
+ /**
410
+ * The translation for the key `propertyDefaultRequiresDependentDefault`. English default is:
411
+ *
412
+ * > Default can only be set when the dependent property has a default.
413
+ */
414
+ readonly propertyDefaultRequiresDependentDefault: string;
415
+ /**
416
+ * The translation for the key `propertyDefaultNone`. English default is:
417
+ *
418
+ * > No default
419
+ */
420
+ readonly propertyDefaultNone: string;
403
421
  /**
404
422
  * The translation for the key `arrayNoConstraint`. English default is:
405
423
  *
@@ -129,7 +129,9 @@ function renameObjectProperty(schema, oldName, newName) {
129
129
  const propertyOrder = newSchema.$propertyOrder || Object.keys(newSchema.properties);
130
130
  for (const key of propertyOrder)if (key === oldName) newProperties[newName] = newSchema.properties[oldName];
131
131
  else if (key in newSchema.properties) newProperties[key] = newSchema.properties[key];
132
- for (const [key, value] of Object.entries(newSchema.properties))if (!(key in newProperties)) newProperties[key] = value;
132
+ for (const [key, value] of Object.entries(newSchema.properties))if (key !== oldName) {
133
+ if (!(key in newProperties)) newProperties[key] = value;
134
+ }
133
135
  newSchema.properties = newProperties;
134
136
  if (newSchema.required) newSchema.required = newSchema.required.map((field)=>field === oldName ? newName : field);
135
137
  if (newSchema.$propertyOrder) newSchema.$propertyOrder = newSchema.$propertyOrder.map((name)=>name === oldName ? newName : name);
@@ -138,7 +138,75 @@ function validateSchemaByType(schema, type, t) {
138
138
  errors: result.error.issues
139
139
  };
140
140
  }
141
- function buildValidationTree(schema, t) {
141
+ function validateDefaultInEnum(schema, type, t, parentSchema) {
142
+ if ("string" !== type && "number" !== type && "integer" !== type) return null;
143
+ const defaultValue = schema.default;
144
+ if (void 0 === defaultValue) return null;
145
+ const enumValues = schema.enum;
146
+ if (Array.isArray(enumValues) && enumValues.length > 0) {
147
+ const isInEnum = enumValues.some((val)=>{
148
+ if ("number" === type || "integer" === type) return val === defaultValue || "number" == typeof val && "number" == typeof defaultValue && val === defaultValue;
149
+ return String(val) === String(defaultValue);
150
+ });
151
+ if (!isInEnum) return {
152
+ code: "custom",
153
+ message: t.propertyDefaultMustBeInEnum,
154
+ path: [
155
+ "default"
156
+ ]
157
+ };
158
+ }
159
+ const dependentEnum = schema.$dependentEnum;
160
+ if (dependentEnum) {
161
+ const controllingPropertyName = dependentEnum.property;
162
+ const dependentValuesMap = dependentEnum.values;
163
+ if (!parentSchema) return {
164
+ code: "custom",
165
+ message: t.propertyDefaultRequiresDependentDefault,
166
+ path: [
167
+ "default"
168
+ ]
169
+ };
170
+ const controllingPropertySchema = parentSchema.properties?.[controllingPropertyName];
171
+ if (!controllingPropertySchema || "object" != typeof controllingPropertySchema) return {
172
+ code: "custom",
173
+ message: t.propertyDefaultRequiresDependentDefault,
174
+ path: [
175
+ "default"
176
+ ]
177
+ };
178
+ const controllingDefault = controllingPropertySchema.default;
179
+ if (void 0 === controllingDefault) return {
180
+ code: "custom",
181
+ message: t.propertyDefaultRequiresDependentDefault,
182
+ path: [
183
+ "default"
184
+ ]
185
+ };
186
+ const ctrlDefaultStr = String(controllingDefault);
187
+ const allowedValues = dependentValuesMap[ctrlDefaultStr];
188
+ if (!Array.isArray(allowedValues) || 0 === allowedValues.length) return {
189
+ code: "custom",
190
+ message: t.propertyDefaultRequiresDependentDefault,
191
+ path: [
192
+ "default"
193
+ ]
194
+ };
195
+ const isInAllowedValues = allowedValues.some((val)=>{
196
+ if ("number" === type || "integer" === type) return val === defaultValue || "number" == typeof val && "number" == typeof defaultValue && val === defaultValue;
197
+ return String(val) === String(defaultValue);
198
+ });
199
+ if (!isInAllowedValues) return {
200
+ code: "custom",
201
+ message: t.propertyDefaultMustBeInEnum,
202
+ path: [
203
+ "default"
204
+ ]
205
+ };
206
+ }
207
+ return null;
208
+ }
209
+ function buildValidationTree(schema, t, parentSchema) {
142
210
  const deriveType = (sch)=>{
143
211
  if (!sch || "object" != typeof sch) return;
144
212
  const declared = sch.type;
@@ -169,20 +237,33 @@ function buildValidationTree(schema, t) {
169
237
  const sch = schema;
170
238
  const currentType = deriveType(sch);
171
239
  const validation = validateSchemaByType(schema, currentType, t);
240
+ if ("string" === currentType || "number" === currentType || "integer" === currentType) {
241
+ const defaultError = validateDefaultInEnum(schema, currentType, t, parentSchema);
242
+ if (defaultError) if (validation.success) {
243
+ validation.success = false;
244
+ validation.errors = [
245
+ defaultError
246
+ ];
247
+ } else validation.errors = [
248
+ ...validation.errors || [],
249
+ defaultError
250
+ ];
251
+ }
172
252
  const children = {};
173
253
  if ("object" === currentType) {
254
+ const currentObjectSchema = schema;
174
255
  const properties = sch.properties;
175
- if (properties && "object" == typeof properties) for (const [propName, propSchema] of Object.entries(properties))children[propName] = buildValidationTree(propSchema, t);
176
- if (sch.patternProperties && "object" == typeof sch.patternProperties) for (const [patternName, patternSchema] of Object.entries(sch.patternProperties))children[`pattern:${patternName}`] = buildValidationTree(patternSchema, t);
256
+ if (properties && "object" == typeof properties) for (const [propName, propSchema] of Object.entries(properties))children[propName] = buildValidationTree(propSchema, t, currentObjectSchema);
257
+ if (sch.patternProperties && "object" == typeof sch.patternProperties) for (const [patternName, patternSchema] of Object.entries(sch.patternProperties))children[`pattern:${patternName}`] = buildValidationTree(patternSchema, t, currentObjectSchema);
177
258
  }
178
259
  if ("array" === currentType) {
179
260
  const items = sch.items;
180
261
  if (Array.isArray(items)) items.forEach((it, idx)=>{
181
- children[`items[${idx}]`] = buildValidationTree(it, t);
262
+ children[`items[${idx}]`] = buildValidationTree(it, t, parentSchema);
182
263
  });
183
- else if (items) children.items = buildValidationTree(items, t);
264
+ else if (items) children.items = buildValidationTree(items, t, parentSchema);
184
265
  if (Array.isArray(sch.prefixItems)) sch.prefixItems.forEach((it, idx)=>{
185
- children[`prefixItems[${idx}]`] = buildValidationTree(it, t);
266
+ children[`prefixItems[${idx}]`] = buildValidationTree(it, t, parentSchema);
186
267
  });
187
268
  }
188
269
  const combinators = [
@@ -196,13 +277,13 @@ function buildValidationTree(schema, t) {
196
277
  children[[
197
278
  comb,
198
279
  idx
199
- ].join(":")] = buildValidationTree(subSchema, t);
280
+ ].join(":")] = buildValidationTree(subSchema, t, parentSchema);
200
281
  });
201
282
  }
202
- if (sch.not) children.not = buildValidationTree(sch.not, t);
203
- if (sch.$defs && "object" == typeof sch.$defs) for (const [defName, defSchema] of Object.entries(sch.$defs))children[`$defs:${defName}`] = buildValidationTree(defSchema, t);
283
+ if (sch.not) children.not = buildValidationTree(sch.not, t, parentSchema);
284
+ if (sch.$defs && "object" == typeof sch.$defs) for (const [defName, defSchema] of Object.entries(sch.$defs))children[`$defs:${defName}`] = buildValidationTree(defSchema, t, parentSchema);
204
285
  const definitions = sch.definitions;
205
- if (definitions && "object" == typeof definitions) for (const [defName, defSchema] of Object.entries(definitions))children[`definitions:${defName}`] = buildValidationTree(defSchema, t);
286
+ if (definitions && "object" == typeof definitions) for (const [defName, defSchema] of Object.entries(definitions))children[`definitions:${defName}`] = buildValidationTree(defSchema, t, parentSchema);
206
287
  const ownErrors = validation.success ? 0 : validation.errors?.length ?? 0;
207
288
  const childrenErrors = Object.values(children).reduce((sum, child)=>sum + child.cumulativeChildrenErrors, 0);
208
289
  return {
package/package.json CHANGED
@@ -14,7 +14,7 @@
14
14
  "react"
15
15
  ],
16
16
  "private": false,
17
- "version": "0.4.0",
17
+ "version": "0.5.1",
18
18
  "type": "module",
19
19
  "sideEffects": false,
20
20
  "repository": {