@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.
Files changed (50) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +203 -0
  3. package/dist/components/SchemaEditor/AddFieldButton.js +312 -0
  4. package/dist/components/SchemaEditor/JsonSchemaEditor.js +166 -0
  5. package/dist/components/SchemaEditor/JsonSchemaVisualizer.js +96 -0
  6. package/dist/components/SchemaEditor/SchemaField.js +104 -0
  7. package/dist/components/SchemaEditor/SchemaFieldList.js +84 -0
  8. package/dist/components/SchemaEditor/SchemaPropertyEditor.js +249 -0
  9. package/dist/components/SchemaEditor/SchemaTypeSelector.js +55 -0
  10. package/dist/components/SchemaEditor/SchemaVisualEditor.js +77 -0
  11. package/dist/components/SchemaEditor/TypeDropdown.js +72 -0
  12. package/dist/components/SchemaEditor/TypeEditor.js +71 -0
  13. package/dist/components/SchemaEditor/types/ArrayEditor.js +173 -0
  14. package/dist/components/SchemaEditor/types/BooleanEditor.js +107 -0
  15. package/dist/components/SchemaEditor/types/NumberEditor.js +583 -0
  16. package/dist/components/SchemaEditor/types/ObjectEditor.js +90 -0
  17. package/dist/components/SchemaEditor/types/StringEditor.js +542 -0
  18. package/dist/components/features/JsonValidator.js +239 -0
  19. package/dist/components/features/SchemaInferencer.js +107 -0
  20. package/dist/components/ui/badge.js +25 -0
  21. package/dist/components/ui/button.js +41 -0
  22. package/dist/components/ui/dialog.js +73 -0
  23. package/dist/components/ui/input.js +11 -0
  24. package/dist/components/ui/label.js +13 -0
  25. package/dist/components/ui/select.js +90 -0
  26. package/dist/components/ui/switch.js +14 -0
  27. package/dist/components/ui/tabs.js +24 -0
  28. package/dist/components/ui/tooltip.js +15 -0
  29. package/dist/hooks/use-monaco-theme.js +197 -0
  30. package/dist/hooks/use-translation.js +14 -0
  31. package/dist/i18n/locales/de.js +143 -0
  32. package/dist/i18n/locales/en.js +143 -0
  33. package/dist/i18n/locales/es.js +143 -0
  34. package/dist/i18n/locales/fr.js +143 -0
  35. package/dist/i18n/locales/ru.js +143 -0
  36. package/dist/i18n/locales/uk.js +143 -0
  37. package/dist/i18n/locales/zh.js +143 -0
  38. package/dist/i18n/translation-context.js +4 -0
  39. package/dist/i18n/translation-keys.js +0 -0
  40. package/dist/index.css +3830 -0
  41. package/dist/index.d.ts +995 -0
  42. package/dist/index.js +10 -0
  43. package/dist/lib/schema-inference.js +266 -0
  44. package/dist/lib/schemaCompile.js +113 -0
  45. package/dist/lib/schemaEditor.js +167 -0
  46. package/dist/lib/utils.js +40 -0
  47. package/dist/types/jsonSchema.js +98 -0
  48. package/dist/types/validation.js +215 -0
  49. package/dist/utils/jsonValidator.js +162 -0
  50. package/package.json +112 -0
@@ -0,0 +1,166 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Maximize2 } from "lucide-react";
3
+ import { useRef, useState } from "react";
4
+ import { Tabs, TabsContent, TabsList, TabsTrigger } from "../ui/tabs.js";
5
+ import { useTranslation } from "../../hooks/use-translation.js";
6
+ import { cn } from "../../lib/utils.js";
7
+ import JsonSchemaVisualizer from "./JsonSchemaVisualizer.js";
8
+ import SchemaVisualEditor from "./SchemaVisualEditor.js";
9
+ const JsonSchemaEditor = ({ schema = {
10
+ type: "object"
11
+ }, readOnly = false, setSchema, className })=>{
12
+ const handleSchemaChange = (newSchema)=>{
13
+ setSchema(newSchema);
14
+ };
15
+ const t = useTranslation();
16
+ const [isFullscreen, setIsFullscreen] = useState(false);
17
+ const [leftPanelWidth, setLeftPanelWidth] = useState(50);
18
+ const resizeRef = useRef(null);
19
+ const containerRef = useRef(null);
20
+ const isDraggingRef = useRef(false);
21
+ const toggleFullscreen = ()=>{
22
+ setIsFullscreen(!isFullscreen);
23
+ };
24
+ const fullscreenClass = isFullscreen ? "fixed inset-0 z-50 bg-background" : "";
25
+ const handleMouseDown = (e)=>{
26
+ e.preventDefault();
27
+ isDraggingRef.current = true;
28
+ document.addEventListener("mousemove", handleMouseMove);
29
+ document.addEventListener("mouseup", handleMouseUp);
30
+ };
31
+ const handleMouseMove = (e)=>{
32
+ if (!isDraggingRef.current || !containerRef.current) return;
33
+ const containerRect = containerRef.current.getBoundingClientRect();
34
+ const newWidth = (e.clientX - containerRect.left) / containerRect.width * 100;
35
+ if (newWidth >= 20 && newWidth <= 80) setLeftPanelWidth(newWidth);
36
+ };
37
+ const handleMouseUp = ()=>{
38
+ isDraggingRef.current = false;
39
+ document.removeEventListener("mousemove", handleMouseMove);
40
+ document.removeEventListener("mouseup", handleMouseUp);
41
+ };
42
+ return /*#__PURE__*/ jsxs("div", {
43
+ className: cn("json-editor-container w-full", fullscreenClass, className, "jsonjoy"),
44
+ children: [
45
+ /*#__PURE__*/ jsx("div", {
46
+ className: "block lg:hidden w-full",
47
+ children: /*#__PURE__*/ jsxs(Tabs, {
48
+ defaultValue: "visual",
49
+ className: "w-full",
50
+ children: [
51
+ /*#__PURE__*/ jsxs("div", {
52
+ className: "flex items-center justify-between px-4 py-3 border-b w-full",
53
+ children: [
54
+ /*#__PURE__*/ jsx("h3", {
55
+ className: "font-medium",
56
+ children: t.schemaEditorTitle
57
+ }),
58
+ /*#__PURE__*/ jsxs("div", {
59
+ className: "flex items-center gap-2",
60
+ children: [
61
+ /*#__PURE__*/ jsx("button", {
62
+ type: "button",
63
+ onClick: toggleFullscreen,
64
+ className: "p-1.5 rounded-md hover:bg-secondary transition-colors",
65
+ "aria-label": t.schemaEditorToggleFullscreen,
66
+ children: /*#__PURE__*/ jsx(Maximize2, {
67
+ size: 16
68
+ })
69
+ }),
70
+ /*#__PURE__*/ jsxs(TabsList, {
71
+ className: "grid grid-cols-2 w-[200px]",
72
+ children: [
73
+ /*#__PURE__*/ jsx(TabsTrigger, {
74
+ value: "visual",
75
+ children: t.schemaEditorEditModeVisual
76
+ }),
77
+ /*#__PURE__*/ jsx(TabsTrigger, {
78
+ value: "json",
79
+ children: t.schemaEditorEditModeJson
80
+ })
81
+ ]
82
+ })
83
+ ]
84
+ })
85
+ ]
86
+ }),
87
+ /*#__PURE__*/ jsx(TabsContent, {
88
+ value: "visual",
89
+ className: cn("focus:outline-hidden w-full", isFullscreen ? "h-screen" : "h-[500px]"),
90
+ children: /*#__PURE__*/ jsx(SchemaVisualEditor, {
91
+ readOnly: readOnly,
92
+ schema: schema,
93
+ onChange: handleSchemaChange
94
+ })
95
+ }),
96
+ /*#__PURE__*/ jsx(TabsContent, {
97
+ value: "json",
98
+ className: cn("focus:outline-hidden w-full", isFullscreen ? "h-screen" : "h-[500px]"),
99
+ children: /*#__PURE__*/ jsx(JsonSchemaVisualizer, {
100
+ schema: schema,
101
+ onChange: handleSchemaChange
102
+ })
103
+ })
104
+ ]
105
+ })
106
+ }),
107
+ /*#__PURE__*/ jsxs("div", {
108
+ ref: containerRef,
109
+ className: cn("hidden lg:flex lg:flex-col w-full", isFullscreen ? "h-screen" : "h-[600px]"),
110
+ children: [
111
+ /*#__PURE__*/ jsxs("div", {
112
+ className: "flex items-center justify-between px-4 py-3 border-b w-full shrink-0",
113
+ children: [
114
+ /*#__PURE__*/ jsx("h3", {
115
+ className: "font-medium",
116
+ children: t.schemaEditorTitle
117
+ }),
118
+ /*#__PURE__*/ jsx("button", {
119
+ type: "button",
120
+ onClick: toggleFullscreen,
121
+ className: "p-1.5 rounded-md hover:bg-secondary transition-colors",
122
+ "aria-label": t.schemaEditorToggleFullscreen,
123
+ children: /*#__PURE__*/ jsx(Maximize2, {
124
+ size: 16
125
+ })
126
+ })
127
+ ]
128
+ }),
129
+ /*#__PURE__*/ jsxs("div", {
130
+ className: "flex flex-row w-full grow min-h-0",
131
+ children: [
132
+ /*#__PURE__*/ jsx("div", {
133
+ className: "h-full min-h-0",
134
+ style: {
135
+ width: `${leftPanelWidth}%`
136
+ },
137
+ children: /*#__PURE__*/ jsx(SchemaVisualEditor, {
138
+ readOnly: readOnly,
139
+ schema: schema,
140
+ onChange: handleSchemaChange
141
+ })
142
+ }),
143
+ /*#__PURE__*/ jsx("div", {
144
+ ref: resizeRef,
145
+ className: "w-1 bg-border hover:bg-primary cursor-col-resize shrink-0",
146
+ onMouseDown: handleMouseDown
147
+ }),
148
+ /*#__PURE__*/ jsx("div", {
149
+ className: "h-full min-h-0",
150
+ style: {
151
+ width: `${100 - leftPanelWidth}%`
152
+ },
153
+ children: /*#__PURE__*/ jsx(JsonSchemaVisualizer, {
154
+ schema: schema,
155
+ onChange: handleSchemaChange
156
+ })
157
+ })
158
+ ]
159
+ })
160
+ ]
161
+ })
162
+ ]
163
+ });
164
+ };
165
+ const SchemaEditor_JsonSchemaEditor = JsonSchemaEditor;
166
+ export { SchemaEditor_JsonSchemaEditor as default };
@@ -0,0 +1,96 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import react from "@monaco-editor/react";
3
+ import { Download, FileJson, Loader2 } from "lucide-react";
4
+ import { useRef } from "react";
5
+ import { useMonacoTheme } from "../../hooks/use-monaco-theme.js";
6
+ import { useTranslation } from "../../hooks/use-translation.js";
7
+ import { cn } from "../../lib/utils.js";
8
+ const JsonSchemaVisualizer = ({ schema, className, onChange })=>{
9
+ const editorRef = useRef(null);
10
+ const { currentTheme, defineMonacoThemes, configureJsonDefaults, defaultEditorOptions } = useMonacoTheme();
11
+ const t = useTranslation();
12
+ const handleBeforeMount = (monaco)=>{
13
+ defineMonacoThemes(monaco);
14
+ configureJsonDefaults(monaco);
15
+ };
16
+ const handleEditorDidMount = (editor)=>{
17
+ editorRef.current = editor;
18
+ editor.focus();
19
+ };
20
+ const handleEditorChange = (value)=>{
21
+ if (!value) return;
22
+ try {
23
+ const parsedJson = JSON.parse(value);
24
+ if (onChange) onChange(parsedJson);
25
+ } catch (_error) {}
26
+ };
27
+ const handleDownload = ()=>{
28
+ const content = JSON.stringify(schema, null, 2);
29
+ const blob = new Blob([
30
+ content
31
+ ], {
32
+ type: "application/json"
33
+ });
34
+ const url = URL.createObjectURL(blob);
35
+ const a = document.createElement("a");
36
+ a.href = url;
37
+ a.download = t.visualizerDownloadFileName;
38
+ document.body.appendChild(a);
39
+ a.click();
40
+ document.body.removeChild(a);
41
+ URL.revokeObjectURL(url);
42
+ };
43
+ return /*#__PURE__*/ jsxs("div", {
44
+ className: cn("relative overflow-hidden h-full flex flex-col", className, "jsonjoy"),
45
+ children: [
46
+ /*#__PURE__*/ jsxs("div", {
47
+ className: "flex items-center justify-between bg-secondary/80 backdrop-blur-xs px-4 py-2 border-b shrink-0",
48
+ children: [
49
+ /*#__PURE__*/ jsxs("div", {
50
+ className: "flex items-center gap-2",
51
+ children: [
52
+ /*#__PURE__*/ jsx(FileJson, {
53
+ size: 18
54
+ }),
55
+ /*#__PURE__*/ jsx("span", {
56
+ className: "font-medium text-sm",
57
+ children: t.visualizerSource
58
+ })
59
+ ]
60
+ }),
61
+ /*#__PURE__*/ jsx("button", {
62
+ type: "button",
63
+ onClick: handleDownload,
64
+ className: "p-1.5 hover:bg-secondary rounded-md transition-colors",
65
+ title: t.visualizerDownloadTitle,
66
+ children: /*#__PURE__*/ jsx(Download, {
67
+ size: 16
68
+ })
69
+ })
70
+ ]
71
+ }),
72
+ /*#__PURE__*/ jsx("div", {
73
+ className: "grow flex min-h-0",
74
+ children: /*#__PURE__*/ jsx(react, {
75
+ height: "100%",
76
+ defaultLanguage: "json",
77
+ value: JSON.stringify(schema, null, 2),
78
+ onChange: handleEditorChange,
79
+ beforeMount: handleBeforeMount,
80
+ onMount: handleEditorDidMount,
81
+ className: "monaco-editor-container w-full h-full",
82
+ loading: /*#__PURE__*/ jsx("div", {
83
+ className: "flex items-center justify-center h-full w-full bg-secondary/30",
84
+ children: /*#__PURE__*/ jsx(Loader2, {
85
+ className: "h-6 w-6 animate-spin"
86
+ })
87
+ }),
88
+ options: defaultEditorOptions,
89
+ theme: currentTheme
90
+ })
91
+ })
92
+ ]
93
+ });
94
+ };
95
+ const SchemaEditor_JsonSchemaVisualizer = JsonSchemaVisualizer;
96
+ export { SchemaEditor_JsonSchemaVisualizer as default };
@@ -0,0 +1,104 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import react, { Suspense } from "react";
3
+ import { useTranslation } from "../../hooks/use-translation.js";
4
+ import { asObjectSchema, getSchemaDescription, withObjectSchema } from "../../types/jsonSchema.js";
5
+ import SchemaPropertyEditor from "./SchemaPropertyEditor.js";
6
+ const SchemaField = (props)=>{
7
+ const { name, schema, required = false, onDelete, onEdit, depth = 0, readOnly = false } = props;
8
+ const handleNameChange = (newName)=>{
9
+ if (newName === name) return;
10
+ const type = withObjectSchema(schema, (s)=>s.type || "object", "object");
11
+ const description = getSchemaDescription(schema);
12
+ onEdit({
13
+ name: newName,
14
+ type: Array.isArray(type) ? type[0] : type,
15
+ description,
16
+ required,
17
+ validation: asObjectSchema(schema)
18
+ });
19
+ };
20
+ const handleRequiredChange = (isRequired)=>{
21
+ if (isRequired === required) return;
22
+ const type = withObjectSchema(schema, (s)=>s.type || "object", "object");
23
+ const description = getSchemaDescription(schema);
24
+ onEdit({
25
+ name,
26
+ type: Array.isArray(type) ? type[0] : type,
27
+ description,
28
+ required: isRequired,
29
+ validation: asObjectSchema(schema)
30
+ });
31
+ };
32
+ const handleSchemaChange = (newSchema)=>{
33
+ const type = newSchema.type || "object";
34
+ const description = newSchema.description || "";
35
+ onEdit({
36
+ name,
37
+ type: Array.isArray(type) ? type[0] : type,
38
+ description,
39
+ required,
40
+ validation: newSchema
41
+ });
42
+ };
43
+ return /*#__PURE__*/ jsx(SchemaPropertyEditor, {
44
+ name: name,
45
+ readOnly: readOnly,
46
+ schema: schema,
47
+ required: required,
48
+ onDelete: onDelete,
49
+ onNameChange: handleNameChange,
50
+ onRequiredChange: handleRequiredChange,
51
+ onSchemaChange: handleSchemaChange,
52
+ depth: depth
53
+ });
54
+ };
55
+ const SchemaEditor_SchemaField = SchemaField;
56
+ const ExpandButton = ({ expanded, onClick })=>{
57
+ const t = useTranslation();
58
+ const ChevronDown = /*#__PURE__*/ react.lazy(()=>import("lucide-react").then((mod)=>({
59
+ default: mod.ChevronDown
60
+ })));
61
+ const ChevronRight = /*#__PURE__*/ react.lazy(()=>import("lucide-react").then((mod)=>({
62
+ default: mod.ChevronRight
63
+ })));
64
+ return /*#__PURE__*/ jsx("button", {
65
+ type: "button",
66
+ className: "text-muted-foreground hover:text-foreground transition-colors",
67
+ onClick: onClick,
68
+ "aria-label": expanded ? t.collapse : t.expand,
69
+ children: /*#__PURE__*/ jsx(Suspense, {
70
+ fallback: /*#__PURE__*/ jsx("div", {
71
+ className: "w-[18px] h-[18px]"
72
+ }),
73
+ children: expanded ? /*#__PURE__*/ jsx(ChevronDown, {
74
+ size: 18
75
+ }) : /*#__PURE__*/ jsx(ChevronRight, {
76
+ size: 18
77
+ })
78
+ })
79
+ });
80
+ };
81
+ const FieldActions = ({ onDelete })=>{
82
+ const t = useTranslation();
83
+ const X = /*#__PURE__*/ react.lazy(()=>import("lucide-react").then((mod)=>({
84
+ default: mod.X
85
+ })));
86
+ return /*#__PURE__*/ jsx("div", {
87
+ className: "flex items-center gap-1 text-muted-foreground",
88
+ children: /*#__PURE__*/ jsx("button", {
89
+ type: "button",
90
+ onClick: onDelete,
91
+ className: "p-1 rounded-md hover:bg-secondary hover:text-destructive transition-colors opacity-0 group-hover:opacity-100",
92
+ "aria-label": t.fieldDelete,
93
+ children: /*#__PURE__*/ jsx(Suspense, {
94
+ fallback: /*#__PURE__*/ jsx("div", {
95
+ className: "w-[16px] h-[16px]"
96
+ }),
97
+ children: /*#__PURE__*/ jsx(X, {
98
+ size: 16
99
+ })
100
+ })
101
+ })
102
+ });
103
+ };
104
+ export { ExpandButton, FieldActions, SchemaEditor_SchemaField as default };
@@ -0,0 +1,84 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { useMemo } from "react";
3
+ import { useTranslation } from "../../hooks/use-translation.js";
4
+ import { getSchemaProperties } from "../../lib/schemaEditor.js";
5
+ import { buildValidationTree } from "../../types/validation.js";
6
+ import SchemaPropertyEditor from "./SchemaPropertyEditor.js";
7
+ const SchemaFieldList = ({ schema, onEditField, onDeleteField, onReorderField, readOnly = false })=>{
8
+ const t = useTranslation();
9
+ const properties = getSchemaProperties(schema);
10
+ const getValidSchemaType = (propSchema)=>{
11
+ if ("boolean" == typeof propSchema) return "object";
12
+ const type = propSchema.type;
13
+ if (Array.isArray(type)) return type[0] || "object";
14
+ return type || "object";
15
+ };
16
+ const handleNameChange = (oldName, newName)=>{
17
+ const property = properties.find((prop)=>prop.name === oldName);
18
+ if (!property) return;
19
+ const propSchemaObj = "boolean" == typeof property.schema ? {
20
+ type: "object"
21
+ } : property.schema;
22
+ onEditField(oldName, {
23
+ name: newName,
24
+ type: getValidSchemaType(property.schema),
25
+ description: propSchemaObj.description || "",
26
+ required: property.required,
27
+ default: propSchemaObj.default,
28
+ validation: propSchemaObj
29
+ });
30
+ };
31
+ const handleRequiredChange = (name, required)=>{
32
+ const property = properties.find((prop)=>prop.name === name);
33
+ if (!property) return;
34
+ const propSchemaObj = "boolean" == typeof property.schema ? {
35
+ type: "object"
36
+ } : property.schema;
37
+ onEditField(name, {
38
+ name,
39
+ type: getValidSchemaType(property.schema),
40
+ description: propSchemaObj.description || "",
41
+ required,
42
+ default: propSchemaObj.default,
43
+ validation: propSchemaObj
44
+ });
45
+ };
46
+ const handleSchemaChange = (name, updatedSchema)=>{
47
+ const property = properties.find((prop)=>prop.name === name);
48
+ if (!property) return;
49
+ const type = updatedSchema.type || "object";
50
+ const validType = Array.isArray(type) ? type[0] || "object" : type;
51
+ onEditField(name, {
52
+ name,
53
+ type: validType,
54
+ description: updatedSchema.description || "",
55
+ required: property.required,
56
+ default: updatedSchema.default,
57
+ validation: updatedSchema
58
+ });
59
+ };
60
+ const validationTree = useMemo(()=>buildValidationTree(schema, t), [
61
+ schema,
62
+ t
63
+ ]);
64
+ const parentSchema = "boolean" == typeof schema ? void 0 : schema;
65
+ return /*#__PURE__*/ jsx("div", {
66
+ className: "space-y-2 animate-in",
67
+ children: properties.map((property, index)=>/*#__PURE__*/ jsx(SchemaPropertyEditor, {
68
+ name: property.name,
69
+ schema: property.schema,
70
+ required: property.required,
71
+ parentSchema: parentSchema,
72
+ validationNode: validationTree.children[property.name] ?? void 0,
73
+ onDelete: ()=>onDeleteField(property.name),
74
+ onNameChange: (newName)=>handleNameChange(property.name, newName),
75
+ onRequiredChange: (required)=>handleRequiredChange(property.name, required),
76
+ onSchemaChange: (schema)=>handleSchemaChange(property.name, schema),
77
+ onMoveUp: onReorderField && index > 0 ? ()=>onReorderField(property.name, "up") : void 0,
78
+ onMoveDown: onReorderField && index < properties.length - 1 ? ()=>onReorderField(property.name, "down") : void 0,
79
+ readOnly: readOnly
80
+ }, property.name))
81
+ });
82
+ };
83
+ const SchemaEditor_SchemaFieldList = SchemaFieldList;
84
+ export { SchemaEditor_SchemaFieldList as default };