Start adding changes to address json fields

This commit is contained in:
Ola Hungerford
2025-02-26 09:50:47 -07:00
parent 717c394d3b
commit 592dacad39
2 changed files with 81 additions and 15 deletions

View File

@@ -41,9 +41,6 @@ const DynamicJsonForm = ({
onChange, onChange,
maxDepth = 3, maxDepth = 3,
}: DynamicJsonFormProps) => { }: DynamicJsonFormProps) => {
const [isJsonMode, setIsJsonMode] = useState(false);
const [jsonError, setJsonError] = useState<string>();
const generateDefaultValue = (propSchema: JsonSchemaType): JsonValue => { const generateDefaultValue = (propSchema: JsonSchemaType): JsonValue => {
switch (propSchema.type) { switch (propSchema.type) {
case "string": case "string":
@@ -69,6 +66,40 @@ const DynamicJsonForm = ({
} }
}; };
const [isJsonMode, setIsJsonMode] = useState(false);
const [jsonError, setJsonError] = useState<string>();
// Add state for storing raw JSON value
const [rawJsonValue, setRawJsonValue] = useState<string>(
JSON.stringify(value ?? generateDefaultValue(schema), null, 2)
);
const validateJsonBeforeSubmit = () => {
if (isJsonMode && rawJsonValue) {
try {
const parsed = JSON.parse(rawJsonValue);
onChange(parsed);
setJsonError(undefined);
return true;
} catch (err) {
setJsonError(err instanceof Error ? err.message : "Invalid JSON");
return false;
}
}
return true;
};
const handleSwitchToFormMode = () => {
if (isJsonMode) {
if (validateJsonBeforeSubmit()) {
setIsJsonMode(false);
}
} else {
// Update raw JSON value when switching to JSON mode
setRawJsonValue(JSON.stringify(value ?? generateDefaultValue(schema), null, 2));
setIsJsonMode(true);
}
};
const renderFormFields = ( const renderFormFields = (
propSchema: JsonSchemaType, propSchema: JsonSchemaType,
currentValue: JsonValue, currentValue: JsonValue,
@@ -329,7 +360,7 @@ const DynamicJsonForm = ({
<Button <Button
variant="outline" variant="outline"
size="sm" size="sm"
onClick={() => setIsJsonMode(!isJsonMode)} onClick={handleSwitchToFormMode}
> >
{isJsonMode ? "Switch to Form" : "Switch to JSON"} {isJsonMode ? "Switch to Form" : "Switch to JSON"}
</Button> </Button>
@@ -337,13 +368,18 @@ const DynamicJsonForm = ({
{isJsonMode ? ( {isJsonMode ? (
<JsonEditor <JsonEditor
value={JSON.stringify(value ?? generateDefaultValue(schema), null, 2)} value={rawJsonValue}
onChange={(newValue) => { onChange={(newValue) => {
setRawJsonValue(newValue);
try { try {
onChange(JSON.parse(newValue)); if (/^\s*[{[].*[}\]]\s*$/.test(newValue)) {
setJsonError(undefined); const parsed = JSON.parse(newValue);
} catch (err) { onChange(parsed);
setJsonError(err instanceof Error ? err.message : "Invalid JSON"); setJsonError(undefined);
}
} catch {
// Don't set an error during typing - that will happen when the user
// tries to save or submit the form
} }
}} }}
error={jsonError} error={jsonError}

View File

@@ -1,3 +1,4 @@
import { useState, useEffect } from "react";
import Editor from "react-simple-code-editor"; import Editor from "react-simple-code-editor";
import Prism from "prismjs"; import Prism from "prismjs";
import "prismjs/components/prism-json"; import "prismjs/components/prism-json";
@@ -10,34 +11,63 @@ interface JsonEditorProps {
error?: string; error?: string;
} }
const JsonEditor = ({ value, onChange, error }: JsonEditorProps) => { const JsonEditor = ({ value, onChange, error: externalError }: JsonEditorProps) => {
const [editorContent, setEditorContent] = useState(value);
const [internalError, setInternalError] = useState<string | undefined>(undefined);
useEffect(() => {
setEditorContent(value);
}, [value]);
const formatJson = (json: string): string => { const formatJson = (json: string): string => {
try { try {
// Handle empty arrays and objects specifically
if (json.trim() === '[]') return '[]';
if (json.trim() === '{}') return '{}';
return JSON.stringify(JSON.parse(json), null, 2); return JSON.stringify(JSON.parse(json), null, 2);
} catch { } catch {
return json; return json;
} }
}; };
const handleEditorChange = (newContent: string) => {
setEditorContent(newContent);
setInternalError(undefined);
onChange(newContent);
};
const handleFormatJson = () => {
try {
const formatted = formatJson(editorContent);
setEditorContent(formatted);
onChange(formatted);
setInternalError(undefined);
} catch (err) {
setInternalError(err instanceof Error ? err.message : "Invalid JSON");
}
};
const displayError = internalError || externalError;
return ( return (
<div className="relative space-y-2"> <div className="relative space-y-2">
<div className="flex justify-end"> <div className="flex justify-end">
<Button <Button
variant="outline" variant="outline"
size="sm" size="sm"
onClick={() => onChange(formatJson(value))} onClick={handleFormatJson}
> >
Format JSON Format JSON
</Button> </Button>
</div> </div>
<div <div
className={`border rounded-md ${ className={`border rounded-md ${
error ? "border-red-500" : "border-gray-200 dark:border-gray-800" displayError ? "border-red-500" : "border-gray-200 dark:border-gray-800"
}`} }`}
> >
<Editor <Editor
value={value} value={editorContent}
onValueChange={onChange} onValueChange={handleEditorChange}
highlight={(code) => highlight={(code) =>
Prism.highlight(code, Prism.languages.json, "json") Prism.highlight(code, Prism.languages.json, "json")
} }
@@ -51,7 +81,7 @@ const JsonEditor = ({ value, onChange, error }: JsonEditorProps) => {
className="w-full" className="w-full"
/> />
</div> </div>
{error && <p className="text-sm text-red-500 mt-1">{error}</p>} {displayError && <p className="text-sm text-red-500 mt-1">{displayError}</p>}
</div> </div>
); );
}; };