@datenlotse/jsonjoy-builder 0.4.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/LICENSE +21 -0
- package/README.md +203 -0
- package/dist/components/SchemaEditor/AddFieldButton.js +312 -0
- package/dist/components/SchemaEditor/JsonSchemaEditor.js +166 -0
- package/dist/components/SchemaEditor/JsonSchemaVisualizer.js +96 -0
- package/dist/components/SchemaEditor/SchemaField.js +104 -0
- package/dist/components/SchemaEditor/SchemaFieldList.js +84 -0
- package/dist/components/SchemaEditor/SchemaPropertyEditor.js +249 -0
- package/dist/components/SchemaEditor/SchemaTypeSelector.js +55 -0
- package/dist/components/SchemaEditor/SchemaVisualEditor.js +77 -0
- package/dist/components/SchemaEditor/TypeDropdown.js +72 -0
- package/dist/components/SchemaEditor/TypeEditor.js +71 -0
- package/dist/components/SchemaEditor/types/ArrayEditor.js +173 -0
- package/dist/components/SchemaEditor/types/BooleanEditor.js +107 -0
- package/dist/components/SchemaEditor/types/NumberEditor.js +583 -0
- package/dist/components/SchemaEditor/types/ObjectEditor.js +90 -0
- package/dist/components/SchemaEditor/types/StringEditor.js +542 -0
- package/dist/components/features/JsonValidator.js +239 -0
- package/dist/components/features/SchemaInferencer.js +107 -0
- package/dist/components/ui/badge.js +25 -0
- package/dist/components/ui/button.js +41 -0
- package/dist/components/ui/dialog.js +73 -0
- package/dist/components/ui/input.js +11 -0
- package/dist/components/ui/label.js +13 -0
- package/dist/components/ui/select.js +90 -0
- package/dist/components/ui/switch.js +14 -0
- package/dist/components/ui/tabs.js +24 -0
- package/dist/components/ui/tooltip.js +15 -0
- package/dist/hooks/use-monaco-theme.js +197 -0
- package/dist/hooks/use-translation.js +14 -0
- package/dist/i18n/locales/de.js +143 -0
- package/dist/i18n/locales/en.js +143 -0
- package/dist/i18n/locales/es.js +143 -0
- package/dist/i18n/locales/fr.js +143 -0
- package/dist/i18n/locales/ru.js +143 -0
- package/dist/i18n/locales/uk.js +143 -0
- package/dist/i18n/locales/zh.js +143 -0
- package/dist/i18n/translation-context.js +4 -0
- package/dist/i18n/translation-keys.js +0 -0
- package/dist/index.css +3830 -0
- package/dist/index.d.ts +995 -0
- package/dist/index.js +10 -0
- package/dist/lib/schema-inference.js +266 -0
- package/dist/lib/schemaCompile.js +113 -0
- package/dist/lib/schemaEditor.js +167 -0
- package/dist/lib/utils.js +40 -0
- package/dist/types/jsonSchema.js +98 -0
- package/dist/types/validation.js +215 -0
- package/dist/utils/jsonValidator.js +162 -0
- package/package.json +112 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ChevronDown, ChevronRight, ChevronUp, X } from "lucide-react";
|
|
3
|
+
import { useEffect, useMemo, useState } from "react";
|
|
4
|
+
import { Input } from "../ui/input.js";
|
|
5
|
+
import { useTranslation } from "../../hooks/use-translation.js";
|
|
6
|
+
import { cn } from "../../lib/utils.js";
|
|
7
|
+
import { asObjectSchema, getSchemaDescription, withObjectSchema } from "../../types/jsonSchema.js";
|
|
8
|
+
import { Badge } from "../ui/badge.js";
|
|
9
|
+
import TypeDropdown from "./TypeDropdown.js";
|
|
10
|
+
import TypeEditor from "./TypeEditor.js";
|
|
11
|
+
const SchemaPropertyEditor = ({ name, schema, required, readOnly = false, parentSchema, validationNode, onDelete, onNameChange, onRequiredChange, onSchemaChange, onMoveUp, onMoveDown, depth = 0 })=>{
|
|
12
|
+
const t = useTranslation();
|
|
13
|
+
const [expanded, setExpanded] = useState(false);
|
|
14
|
+
const [isEditingName, setIsEditingName] = useState(false);
|
|
15
|
+
const [isEditingDesc, setIsEditingDesc] = useState(false);
|
|
16
|
+
const [tempName, setTempName] = useState(name);
|
|
17
|
+
const [tempDesc, setTempDesc] = useState(getSchemaDescription(schema));
|
|
18
|
+
const type = withObjectSchema(schema, (s)=>s.type || "object", "object");
|
|
19
|
+
const defaultValue = useMemo(()=>{
|
|
20
|
+
const objSchema = asObjectSchema(schema);
|
|
21
|
+
return void 0 !== objSchema.default ? String(objSchema.default) : "";
|
|
22
|
+
}, [
|
|
23
|
+
schema
|
|
24
|
+
]);
|
|
25
|
+
const [tempDefault, setTempDefault] = useState(defaultValue);
|
|
26
|
+
useEffect(()=>{
|
|
27
|
+
setTempName(name);
|
|
28
|
+
setTempDesc(getSchemaDescription(schema));
|
|
29
|
+
setTempDefault(defaultValue);
|
|
30
|
+
}, [
|
|
31
|
+
name,
|
|
32
|
+
schema,
|
|
33
|
+
defaultValue
|
|
34
|
+
]);
|
|
35
|
+
const handleNameSubmit = ()=>{
|
|
36
|
+
const trimmedName = tempName.trim();
|
|
37
|
+
if (trimmedName && trimmedName !== name) onNameChange(trimmedName);
|
|
38
|
+
else setTempName(name);
|
|
39
|
+
setIsEditingName(false);
|
|
40
|
+
};
|
|
41
|
+
const handleDescSubmit = ()=>{
|
|
42
|
+
const trimmedDesc = tempDesc.trim();
|
|
43
|
+
if (trimmedDesc !== getSchemaDescription(schema)) onSchemaChange({
|
|
44
|
+
...asObjectSchema(schema),
|
|
45
|
+
description: trimmedDesc || void 0
|
|
46
|
+
});
|
|
47
|
+
else setTempDesc(getSchemaDescription(schema));
|
|
48
|
+
setIsEditingDesc(false);
|
|
49
|
+
};
|
|
50
|
+
const handleSchemaUpdate = (updatedSchema)=>{
|
|
51
|
+
const description = getSchemaDescription(schema);
|
|
52
|
+
const currentDefault = asObjectSchema(schema).default;
|
|
53
|
+
onSchemaChange({
|
|
54
|
+
...updatedSchema,
|
|
55
|
+
description: description || void 0,
|
|
56
|
+
default: currentDefault
|
|
57
|
+
});
|
|
58
|
+
};
|
|
59
|
+
const handleDefaultSubmit = ()=>{
|
|
60
|
+
const trimmedDefault = tempDefault.trim();
|
|
61
|
+
const objSchema = asObjectSchema(schema);
|
|
62
|
+
const currentDefault = objSchema.default;
|
|
63
|
+
if ("" === trimmedDefault) {
|
|
64
|
+
if (void 0 !== currentDefault) {
|
|
65
|
+
const { default: _, ...rest } = objSchema;
|
|
66
|
+
onSchemaChange(rest);
|
|
67
|
+
}
|
|
68
|
+
} else {
|
|
69
|
+
let parsedValue = trimmedDefault;
|
|
70
|
+
if ("number" === type || "integer" === type) {
|
|
71
|
+
parsedValue = Number(trimmedDefault);
|
|
72
|
+
if (Number.isNaN(parsedValue)) parsedValue = trimmedDefault;
|
|
73
|
+
} else if ("boolean" === type) parsedValue = "true" === trimmedDefault.toLowerCase() ? true : "false" === trimmedDefault.toLowerCase() ? false : trimmedDefault;
|
|
74
|
+
else if ("null" === type) parsedValue = null;
|
|
75
|
+
if (JSON.stringify(currentDefault) !== JSON.stringify(parsedValue)) onSchemaChange({
|
|
76
|
+
...objSchema,
|
|
77
|
+
default: parsedValue
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
return /*#__PURE__*/ jsxs("div", {
|
|
82
|
+
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
|
+
children: [
|
|
84
|
+
/*#__PURE__*/ jsxs("div", {
|
|
85
|
+
className: "relative json-field-row justify-between group",
|
|
86
|
+
children: [
|
|
87
|
+
/*#__PURE__*/ jsxs("div", {
|
|
88
|
+
className: "flex items-center gap-2 grow min-w-0",
|
|
89
|
+
children: [
|
|
90
|
+
/*#__PURE__*/ jsx("button", {
|
|
91
|
+
type: "button",
|
|
92
|
+
className: "text-muted-foreground hover:text-foreground transition-colors",
|
|
93
|
+
onClick: ()=>setExpanded(!expanded),
|
|
94
|
+
"aria-label": expanded ? t.collapse : t.expand,
|
|
95
|
+
children: expanded ? /*#__PURE__*/ jsx(ChevronDown, {
|
|
96
|
+
size: 18
|
|
97
|
+
}) : /*#__PURE__*/ jsx(ChevronRight, {
|
|
98
|
+
size: 18
|
|
99
|
+
})
|
|
100
|
+
}),
|
|
101
|
+
/*#__PURE__*/ jsxs("div", {
|
|
102
|
+
className: "flex items-center gap-2 grow min-w-0 overflow-visible",
|
|
103
|
+
children: [
|
|
104
|
+
/*#__PURE__*/ jsxs("div", {
|
|
105
|
+
className: "flex items-center gap-2 min-w-0 grow overflow-visible",
|
|
106
|
+
children: [
|
|
107
|
+
!readOnly && isEditingName ? /*#__PURE__*/ jsx(Input, {
|
|
108
|
+
value: tempName,
|
|
109
|
+
onChange: (e)=>setTempName(e.target.value),
|
|
110
|
+
onBlur: handleNameSubmit,
|
|
111
|
+
onKeyDown: (e)=>"Enter" === e.key && handleNameSubmit(),
|
|
112
|
+
className: "h-8 text-sm font-medium min-w-[120px] max-w-full z-10",
|
|
113
|
+
autoFocus: true,
|
|
114
|
+
onFocus: (e)=>e.target.select()
|
|
115
|
+
}) : /*#__PURE__*/ jsx("button", {
|
|
116
|
+
type: "button",
|
|
117
|
+
onClick: ()=>setIsEditingName(true),
|
|
118
|
+
onKeyDown: (e)=>"Enter" === e.key && setIsEditingName(true),
|
|
119
|
+
className: "json-field-label font-medium cursor-text px-2 py-0.5 -mx-0.5 rounded-sm hover:bg-secondary/30 hover:shadow-xs hover:ring-1 hover:ring-ring/20 transition-all text-left truncate min-w-[80px] max-w-[50%]",
|
|
120
|
+
children: name
|
|
121
|
+
}),
|
|
122
|
+
!readOnly && isEditingDesc ? /*#__PURE__*/ jsx(Input, {
|
|
123
|
+
value: tempDesc,
|
|
124
|
+
onChange: (e)=>setTempDesc(e.target.value),
|
|
125
|
+
onBlur: handleDescSubmit,
|
|
126
|
+
onKeyDown: (e)=>"Enter" === e.key && handleDescSubmit(),
|
|
127
|
+
placeholder: t.propertyDescriptionPlaceholder,
|
|
128
|
+
className: "h-8 text-xs text-muted-foreground italic flex-1 min-w-[150px] z-10",
|
|
129
|
+
autoFocus: true,
|
|
130
|
+
onFocus: (e)=>e.target.select()
|
|
131
|
+
}) : tempDesc ? /*#__PURE__*/ jsx("button", {
|
|
132
|
+
type: "button",
|
|
133
|
+
onClick: ()=>setIsEditingDesc(true),
|
|
134
|
+
onKeyDown: (e)=>"Enter" === e.key && setIsEditingDesc(true),
|
|
135
|
+
className: "text-xs text-muted-foreground italic cursor-text px-2 py-0.5 -mx-0.5 rounded-sm hover:bg-secondary/30 hover:shadow-xs hover:ring-1 hover:ring-ring/20 transition-all text-left truncate flex-1 max-w-[40%] mr-2",
|
|
136
|
+
children: tempDesc
|
|
137
|
+
}) : /*#__PURE__*/ jsx("button", {
|
|
138
|
+
type: "button",
|
|
139
|
+
onClick: ()=>setIsEditingDesc(true),
|
|
140
|
+
onKeyDown: (e)=>"Enter" === e.key && setIsEditingDesc(true),
|
|
141
|
+
className: "text-xs text-muted-foreground/50 italic cursor-text px-2 py-0.5 -mx-0.5 rounded-sm hover:bg-secondary/30 hover:shadow-xs hover:ring-1 hover:ring-ring/20 transition-all opacity-0 group-hover:opacity-100 text-left truncate flex-1 max-w-[40%] mr-2",
|
|
142
|
+
children: t.propertyDescriptionButton
|
|
143
|
+
})
|
|
144
|
+
]
|
|
145
|
+
}),
|
|
146
|
+
/*#__PURE__*/ jsxs("div", {
|
|
147
|
+
className: "flex items-center gap-2 justify-end shrink-0",
|
|
148
|
+
children: [
|
|
149
|
+
/*#__PURE__*/ jsx(TypeDropdown, {
|
|
150
|
+
value: type,
|
|
151
|
+
readOnly: readOnly,
|
|
152
|
+
onChange: (newType)=>{
|
|
153
|
+
onSchemaChange({
|
|
154
|
+
...asObjectSchema(schema),
|
|
155
|
+
type: newType
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}),
|
|
159
|
+
/*#__PURE__*/ jsx("button", {
|
|
160
|
+
type: "button",
|
|
161
|
+
onClick: ()=>!readOnly && onRequiredChange(!required),
|
|
162
|
+
className: cn("text-xs px-2 py-1 rounded-md font-medium min-w-[80px] text-center cursor-pointer hover:shadow-xs hover:ring-2 hover:ring-ring/30 active:scale-95 transition-all whitespace-nowrap", required ? "bg-red-50 text-red-500" : "bg-secondary text-muted-foreground"),
|
|
163
|
+
children: required ? t.propertyRequired : t.propertyOptional
|
|
164
|
+
})
|
|
165
|
+
]
|
|
166
|
+
})
|
|
167
|
+
]
|
|
168
|
+
})
|
|
169
|
+
]
|
|
170
|
+
}),
|
|
171
|
+
validationNode?.cumulativeChildrenErrors > 0 && /*#__PURE__*/ jsx(Badge, {
|
|
172
|
+
className: "h-5 min-w-5 rounded-full px-1 font-mono tabular-nums justify-center",
|
|
173
|
+
variant: "destructive",
|
|
174
|
+
children: validationNode.cumulativeChildrenErrors
|
|
175
|
+
}),
|
|
176
|
+
!readOnly && /*#__PURE__*/ jsxs("div", {
|
|
177
|
+
className: "flex items-center gap-1 text-muted-foreground",
|
|
178
|
+
children: [
|
|
179
|
+
onMoveUp && /*#__PURE__*/ jsx("button", {
|
|
180
|
+
type: "button",
|
|
181
|
+
onClick: onMoveUp,
|
|
182
|
+
className: "p-1 rounded-md hover:bg-secondary hover:text-foreground transition-colors opacity-0 group-hover:opacity-100",
|
|
183
|
+
"aria-label": t.propertyMoveUp,
|
|
184
|
+
children: /*#__PURE__*/ jsx(ChevronUp, {
|
|
185
|
+
size: 16
|
|
186
|
+
})
|
|
187
|
+
}),
|
|
188
|
+
onMoveDown && /*#__PURE__*/ jsx("button", {
|
|
189
|
+
type: "button",
|
|
190
|
+
onClick: onMoveDown,
|
|
191
|
+
className: "p-1 rounded-md hover:bg-secondary hover:text-foreground transition-colors opacity-0 group-hover:opacity-100",
|
|
192
|
+
"aria-label": t.propertyMoveDown,
|
|
193
|
+
children: /*#__PURE__*/ jsx(ChevronDown, {
|
|
194
|
+
size: 16
|
|
195
|
+
})
|
|
196
|
+
}),
|
|
197
|
+
/*#__PURE__*/ jsx("button", {
|
|
198
|
+
type: "button",
|
|
199
|
+
onClick: onDelete,
|
|
200
|
+
className: "p-1 rounded-md hover:bg-secondary hover:text-destructive transition-colors opacity-0 group-hover:opacity-100",
|
|
201
|
+
"aria-label": t.propertyDelete,
|
|
202
|
+
children: /*#__PURE__*/ jsx(X, {
|
|
203
|
+
size: 16
|
|
204
|
+
})
|
|
205
|
+
})
|
|
206
|
+
]
|
|
207
|
+
})
|
|
208
|
+
]
|
|
209
|
+
}),
|
|
210
|
+
expanded && /*#__PURE__*/ jsxs("div", {
|
|
211
|
+
className: "pt-1 pb-2 px-2 sm:px-3 animate-in space-y-3",
|
|
212
|
+
children: [
|
|
213
|
+
readOnly && tempDesc && /*#__PURE__*/ jsx("p", {
|
|
214
|
+
className: "pb-2",
|
|
215
|
+
children: tempDesc
|
|
216
|
+
}),
|
|
217
|
+
!readOnly && "object" !== type && /*#__PURE__*/ jsxs("div", {
|
|
218
|
+
className: "space-y-1",
|
|
219
|
+
children: [
|
|
220
|
+
/*#__PURE__*/ jsx("label", {
|
|
221
|
+
className: "text-xs font-medium text-muted-foreground",
|
|
222
|
+
children: t.propertyDefaultLabel
|
|
223
|
+
}),
|
|
224
|
+
/*#__PURE__*/ jsx(Input, {
|
|
225
|
+
value: tempDefault,
|
|
226
|
+
onChange: (e)=>setTempDefault(e.target.value),
|
|
227
|
+
onBlur: handleDefaultSubmit,
|
|
228
|
+
onKeyDown: (e)=>"Enter" === e.key && handleDefaultSubmit(),
|
|
229
|
+
placeholder: t.propertyDefaultPlaceholder,
|
|
230
|
+
className: "h-8 text-sm"
|
|
231
|
+
})
|
|
232
|
+
]
|
|
233
|
+
}),
|
|
234
|
+
/*#__PURE__*/ jsx(TypeEditor, {
|
|
235
|
+
schema: schema,
|
|
236
|
+
readOnly: readOnly,
|
|
237
|
+
parentSchema: parentSchema,
|
|
238
|
+
propertyName: name,
|
|
239
|
+
validationNode: validationNode,
|
|
240
|
+
onChange: handleSchemaUpdate,
|
|
241
|
+
depth: depth + 1
|
|
242
|
+
})
|
|
243
|
+
]
|
|
244
|
+
})
|
|
245
|
+
]
|
|
246
|
+
});
|
|
247
|
+
};
|
|
248
|
+
const SchemaEditor_SchemaPropertyEditor = SchemaPropertyEditor;
|
|
249
|
+
export { SchemaPropertyEditor, SchemaEditor_SchemaPropertyEditor as default };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useTranslation } from "../../hooks/use-translation.js";
|
|
3
|
+
import { cn } from "../../lib/utils.js";
|
|
4
|
+
const typeOptions = [
|
|
5
|
+
{
|
|
6
|
+
id: "string",
|
|
7
|
+
label: "fieldTypeTextLabel",
|
|
8
|
+
description: "fieldTypeTextDescription"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
id: "number",
|
|
12
|
+
label: "fieldTypeNumberLabel",
|
|
13
|
+
description: "fieldTypeNumberDescription"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
id: "boolean",
|
|
17
|
+
label: "fieldTypeBooleanLabel",
|
|
18
|
+
description: "fieldTypeBooleanDescription"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
id: "object",
|
|
22
|
+
label: "fieldTypeObjectLabel",
|
|
23
|
+
description: "fieldTypeObjectDescription"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: "array",
|
|
27
|
+
label: "fieldTypeArrayLabel",
|
|
28
|
+
description: "fieldTypeArrayDescription"
|
|
29
|
+
}
|
|
30
|
+
];
|
|
31
|
+
const SchemaTypeSelector = ({ id, value, onChange })=>{
|
|
32
|
+
const t = useTranslation();
|
|
33
|
+
return /*#__PURE__*/ jsx("div", {
|
|
34
|
+
id: id,
|
|
35
|
+
className: "grid grid-cols-1 xs:grid-cols-2 md:grid-cols-3 gap-2",
|
|
36
|
+
children: typeOptions.map((type)=>/*#__PURE__*/ jsxs("button", {
|
|
37
|
+
type: "button",
|
|
38
|
+
title: t[type.description],
|
|
39
|
+
className: cn("p-2.5 rounded-lg border-2 text-left transition-all duration-200", value === type.id ? "border-primary bg-primary/5 shadow-xs" : "border-border hover:border-primary/30 hover:bg-secondary"),
|
|
40
|
+
onClick: ()=>onChange(type.id),
|
|
41
|
+
children: [
|
|
42
|
+
/*#__PURE__*/ jsx("div", {
|
|
43
|
+
className: "font-medium text-sm",
|
|
44
|
+
children: t[type.label]
|
|
45
|
+
}),
|
|
46
|
+
/*#__PURE__*/ jsx("div", {
|
|
47
|
+
className: "text-xs text-muted-foreground line-clamp-1",
|
|
48
|
+
children: t[type.description]
|
|
49
|
+
})
|
|
50
|
+
]
|
|
51
|
+
}, type.id))
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
const SchemaEditor_SchemaTypeSelector = SchemaTypeSelector;
|
|
55
|
+
export { SchemaEditor_SchemaTypeSelector as default };
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useTranslation } from "../../hooks/use-translation.js";
|
|
3
|
+
import { createFieldSchema, renameObjectProperty, reorderObjectProperty, updateObjectProperty, updatePropertyRequired } from "../../lib/schemaEditor.js";
|
|
4
|
+
import { asObjectSchema, isBooleanSchema } from "../../types/jsonSchema.js";
|
|
5
|
+
import AddFieldButton from "./AddFieldButton.js";
|
|
6
|
+
import SchemaFieldList from "./SchemaFieldList.js";
|
|
7
|
+
const SchemaVisualEditor = ({ schema, onChange, readOnly = false })=>{
|
|
8
|
+
const t = useTranslation();
|
|
9
|
+
const handleAddField = (newField)=>{
|
|
10
|
+
const fieldSchema = createFieldSchema(newField);
|
|
11
|
+
let newSchema = updateObjectProperty(asObjectSchema(schema), newField.name, fieldSchema);
|
|
12
|
+
if (newField.required) newSchema = updatePropertyRequired(newSchema, newField.name, true);
|
|
13
|
+
onChange(newSchema);
|
|
14
|
+
};
|
|
15
|
+
const handleEditField = (name, updatedField)=>{
|
|
16
|
+
const fieldSchema = createFieldSchema(updatedField);
|
|
17
|
+
let newSchema = asObjectSchema(schema);
|
|
18
|
+
if (name !== updatedField.name) {
|
|
19
|
+
newSchema = renameObjectProperty(newSchema, name, updatedField.name);
|
|
20
|
+
newSchema = updateObjectProperty(newSchema, updatedField.name, fieldSchema);
|
|
21
|
+
} else newSchema = updateObjectProperty(newSchema, name, fieldSchema);
|
|
22
|
+
newSchema = updatePropertyRequired(newSchema, updatedField.name, updatedField.required || false);
|
|
23
|
+
onChange(newSchema);
|
|
24
|
+
};
|
|
25
|
+
const handleDeleteField = (name)=>{
|
|
26
|
+
if (isBooleanSchema(schema) || !schema.properties) return;
|
|
27
|
+
const { [name]: _, ...remainingProps } = schema.properties;
|
|
28
|
+
const newSchema = {
|
|
29
|
+
...schema,
|
|
30
|
+
properties: remainingProps
|
|
31
|
+
};
|
|
32
|
+
if (newSchema.required) newSchema.required = newSchema.required.filter((field)=>field !== name);
|
|
33
|
+
if (newSchema.$propertyOrder) newSchema.$propertyOrder = newSchema.$propertyOrder.filter((field)=>field !== name);
|
|
34
|
+
onChange(newSchema);
|
|
35
|
+
};
|
|
36
|
+
const handleReorderField = (name, direction)=>{
|
|
37
|
+
const newSchema = reorderObjectProperty(asObjectSchema(schema), name, direction);
|
|
38
|
+
onChange(newSchema);
|
|
39
|
+
};
|
|
40
|
+
const hasFields = !isBooleanSchema(schema) && schema.properties && Object.keys(schema.properties).length > 0;
|
|
41
|
+
return /*#__PURE__*/ jsxs("div", {
|
|
42
|
+
className: "p-4 h-full flex flex-col overflow-auto jsonjoy",
|
|
43
|
+
children: [
|
|
44
|
+
!readOnly && /*#__PURE__*/ jsx("div", {
|
|
45
|
+
className: "mb-6 shrink-0",
|
|
46
|
+
children: /*#__PURE__*/ jsx(AddFieldButton, {
|
|
47
|
+
onAddField: handleAddField
|
|
48
|
+
})
|
|
49
|
+
}),
|
|
50
|
+
/*#__PURE__*/ jsx("div", {
|
|
51
|
+
className: "grow overflow-auto",
|
|
52
|
+
children: hasFields ? /*#__PURE__*/ jsx(SchemaFieldList, {
|
|
53
|
+
schema: schema,
|
|
54
|
+
readOnly: readOnly,
|
|
55
|
+
onAddField: handleAddField,
|
|
56
|
+
onEditField: handleEditField,
|
|
57
|
+
onDeleteField: handleDeleteField,
|
|
58
|
+
onReorderField: handleReorderField
|
|
59
|
+
}) : /*#__PURE__*/ jsxs("div", {
|
|
60
|
+
className: "text-center py-10 text-muted-foreground",
|
|
61
|
+
children: [
|
|
62
|
+
/*#__PURE__*/ jsx("p", {
|
|
63
|
+
className: "mb-3",
|
|
64
|
+
children: t.visualEditorNoFieldsHint1
|
|
65
|
+
}),
|
|
66
|
+
/*#__PURE__*/ jsx("p", {
|
|
67
|
+
className: "text-sm",
|
|
68
|
+
children: t.visualEditorNoFieldsHint2
|
|
69
|
+
})
|
|
70
|
+
]
|
|
71
|
+
})
|
|
72
|
+
})
|
|
73
|
+
]
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
const SchemaEditor_SchemaVisualEditor = SchemaVisualEditor;
|
|
77
|
+
export { SchemaEditor_SchemaVisualEditor as default };
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Check, ChevronDown } from "lucide-react";
|
|
3
|
+
import { useEffect, useRef, useState } from "react";
|
|
4
|
+
import { useTranslation } from "../../hooks/use-translation.js";
|
|
5
|
+
import { cn, getTypeColor, getTypeLabel } from "../../lib/utils.js";
|
|
6
|
+
const typeOptions = [
|
|
7
|
+
"string",
|
|
8
|
+
"number",
|
|
9
|
+
"boolean",
|
|
10
|
+
"object",
|
|
11
|
+
"array",
|
|
12
|
+
"null"
|
|
13
|
+
];
|
|
14
|
+
const TypeDropdown = ({ value, onChange, className, readOnly })=>{
|
|
15
|
+
const t = useTranslation();
|
|
16
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
17
|
+
const dropdownRef = useRef(null);
|
|
18
|
+
useEffect(()=>{
|
|
19
|
+
const handleClickOutside = (event)=>{
|
|
20
|
+
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) setIsOpen(false);
|
|
21
|
+
};
|
|
22
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
23
|
+
return ()=>{
|
|
24
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
25
|
+
};
|
|
26
|
+
}, []);
|
|
27
|
+
return /*#__PURE__*/ jsxs("div", {
|
|
28
|
+
className: "relative",
|
|
29
|
+
ref: dropdownRef,
|
|
30
|
+
children: [
|
|
31
|
+
/*#__PURE__*/ jsxs("button", {
|
|
32
|
+
type: "button",
|
|
33
|
+
className: cn("text-xs px-3.5 py-1.5 rounded-md font-medium text-center flex items-center justify-between", getTypeColor(value), "hover:shadow-xs hover:ring-1 hover:ring-ring/30 active:scale-95 transition-all", readOnly ? "" : "w-[92px]", className),
|
|
34
|
+
onClick: ()=>!readOnly && setIsOpen(!isOpen),
|
|
35
|
+
children: [
|
|
36
|
+
/*#__PURE__*/ jsx("span", {
|
|
37
|
+
children: getTypeLabel(t, value)
|
|
38
|
+
}),
|
|
39
|
+
!readOnly && /*#__PURE__*/ jsx(ChevronDown, {
|
|
40
|
+
size: 14,
|
|
41
|
+
className: "ml-1"
|
|
42
|
+
})
|
|
43
|
+
]
|
|
44
|
+
}),
|
|
45
|
+
isOpen && /*#__PURE__*/ jsx("div", {
|
|
46
|
+
className: "absolute z-50 mt-1 w-[140px] rounded-md border bg-popover shadow-lg animate-in fade-in-50 zoom-in-95",
|
|
47
|
+
children: /*#__PURE__*/ jsx("div", {
|
|
48
|
+
className: "py-1",
|
|
49
|
+
children: typeOptions.map((type)=>/*#__PURE__*/ jsxs("button", {
|
|
50
|
+
type: "button",
|
|
51
|
+
className: cn("w-full text-left px-3 py-1.5 text-xs flex items-center justify-between", "hover:bg-muted/50 transition-colors", value === type && "font-medium"),
|
|
52
|
+
onClick: ()=>{
|
|
53
|
+
onChange(type);
|
|
54
|
+
setIsOpen(false);
|
|
55
|
+
},
|
|
56
|
+
children: [
|
|
57
|
+
/*#__PURE__*/ jsx("span", {
|
|
58
|
+
className: cn("px-2 py-0.5 rounded", getTypeColor(type)),
|
|
59
|
+
children: getTypeLabel(t, type)
|
|
60
|
+
}),
|
|
61
|
+
value === type && /*#__PURE__*/ jsx(Check, {
|
|
62
|
+
size: 14
|
|
63
|
+
})
|
|
64
|
+
]
|
|
65
|
+
}, type))
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
]
|
|
69
|
+
});
|
|
70
|
+
};
|
|
71
|
+
const SchemaEditor_TypeDropdown = TypeDropdown;
|
|
72
|
+
export { TypeDropdown, SchemaEditor_TypeDropdown as default };
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Suspense, lazy } from "react";
|
|
3
|
+
import { useTranslation } from "../../hooks/use-translation.js";
|
|
4
|
+
import { withObjectSchema } from "../../types/jsonSchema.js";
|
|
5
|
+
const StringEditor = /*#__PURE__*/ lazy(()=>import("./types/StringEditor.js"));
|
|
6
|
+
const NumberEditor = /*#__PURE__*/ lazy(()=>import("./types/NumberEditor.js"));
|
|
7
|
+
const BooleanEditor = /*#__PURE__*/ lazy(()=>import("./types/BooleanEditor.js"));
|
|
8
|
+
const ObjectEditor = /*#__PURE__*/ lazy(()=>import("./types/ObjectEditor.js"));
|
|
9
|
+
const ArrayEditor = /*#__PURE__*/ lazy(()=>import("./types/ArrayEditor.js"));
|
|
10
|
+
const TypeEditor = ({ schema, validationNode, onChange, depth = 0, readOnly = false, parentSchema, propertyName })=>{
|
|
11
|
+
const t = useTranslation();
|
|
12
|
+
const type = withObjectSchema(schema, (s)=>s.type || "object", "string");
|
|
13
|
+
return /*#__PURE__*/ jsxs(Suspense, {
|
|
14
|
+
fallback: /*#__PURE__*/ jsx("div", {
|
|
15
|
+
children: t.schemaEditorLoading
|
|
16
|
+
}),
|
|
17
|
+
children: [
|
|
18
|
+
"string" === type && /*#__PURE__*/ jsx(StringEditor, {
|
|
19
|
+
readOnly: readOnly,
|
|
20
|
+
schema: schema,
|
|
21
|
+
onChange: onChange,
|
|
22
|
+
depth: depth,
|
|
23
|
+
parentSchema: parentSchema,
|
|
24
|
+
propertyName: propertyName,
|
|
25
|
+
validationNode: validationNode
|
|
26
|
+
}),
|
|
27
|
+
"number" === type && /*#__PURE__*/ jsx(NumberEditor, {
|
|
28
|
+
readOnly: readOnly,
|
|
29
|
+
schema: schema,
|
|
30
|
+
onChange: onChange,
|
|
31
|
+
depth: depth,
|
|
32
|
+
parentSchema: parentSchema,
|
|
33
|
+
propertyName: propertyName,
|
|
34
|
+
validationNode: validationNode
|
|
35
|
+
}),
|
|
36
|
+
"integer" === type && /*#__PURE__*/ jsx(NumberEditor, {
|
|
37
|
+
readOnly: readOnly,
|
|
38
|
+
schema: schema,
|
|
39
|
+
onChange: onChange,
|
|
40
|
+
depth: depth,
|
|
41
|
+
parentSchema: parentSchema,
|
|
42
|
+
propertyName: propertyName,
|
|
43
|
+
validationNode: validationNode,
|
|
44
|
+
integer: true
|
|
45
|
+
}),
|
|
46
|
+
"boolean" === type && /*#__PURE__*/ jsx(BooleanEditor, {
|
|
47
|
+
readOnly: readOnly,
|
|
48
|
+
schema: schema,
|
|
49
|
+
onChange: onChange,
|
|
50
|
+
depth: depth,
|
|
51
|
+
validationNode: validationNode
|
|
52
|
+
}),
|
|
53
|
+
"object" === type && /*#__PURE__*/ jsx(ObjectEditor, {
|
|
54
|
+
readOnly: readOnly,
|
|
55
|
+
schema: schema,
|
|
56
|
+
onChange: onChange,
|
|
57
|
+
depth: depth,
|
|
58
|
+
validationNode: validationNode
|
|
59
|
+
}),
|
|
60
|
+
"array" === type && /*#__PURE__*/ jsx(ArrayEditor, {
|
|
61
|
+
readOnly: readOnly,
|
|
62
|
+
schema: schema,
|
|
63
|
+
onChange: onChange,
|
|
64
|
+
depth: depth,
|
|
65
|
+
validationNode: validationNode
|
|
66
|
+
})
|
|
67
|
+
]
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
const SchemaEditor_TypeEditor = TypeEditor;
|
|
71
|
+
export { SchemaEditor_TypeEditor as default };
|