From 33f64261b6281adac049facce09972c002033108 Mon Sep 17 00:00:00 2001 From: Igor Rosliakov Date: Mon, 20 Apr 2026 22:22:48 +0300 Subject: [PATCH 1/4] add props onAddEnum and onDeleteEnum --- README.md | 44 ++++++++++++++ demo/pages/Index.tsx | 4 ++ .../SchemaEditor/JsonSchemaEditor.tsx | 9 +++ .../SchemaEditor/SchemaFieldList.tsx | 8 +++ .../SchemaEditor/SchemaPropertyEditor.tsx | 10 ++++ .../SchemaEditor/SchemaVisualEditor.tsx | 7 +++ src/components/SchemaEditor/TypeEditor.tsx | 33 ++++++++++ .../SchemaEditor/types/ArrayEditor.tsx | 7 +++ .../SchemaEditor/types/BooleanEditor.tsx | 38 ++++++++++++ .../SchemaEditor/types/CombinatorEditor.tsx | 10 ++++ .../SchemaEditor/types/NumberEditor.tsx | 28 ++++++--- .../SchemaEditor/types/ObjectEditor.tsx | 8 +++ .../SchemaEditor/types/StringEditor.tsx | 60 ++++++++++++------- 13 files changed, 238 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index d4ecbd3..0a0714a 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,50 @@ export function App() { } ``` +### Enum Change Callbacks + +You can subscribe to enum value changes in the visual editor via `onAddEnum` and `onDeleteEnum`. + +Both callbacks receive a single context object: + +```ts +type EnumChangeContext = { + value: string | number | boolean; + index: number; + schemaKey?: string; +}; +``` + +- `value`: enum value that was added/removed +- `index`: index in the enum list at the time of the change +- `schemaKey`: path-like key of the edited field (for example: `person.firstName`, `hobbies[].name`) + +Example: + +```tsx +import "jsonjoy-builder/styles.css"; +import { type JSONSchema, JsonSchemaEditor } from "jsonjoy-builder"; +import { useState } from "react"; + +export function App() { + const [schema, setSchema] = useState({ type: "object" }); + + return ( + { + console.log("enum:add", { value, index, schemaKey }); + }} + onDeleteEnum={({ value, index, schemaKey }) => { + console.log("enum:delete", { value, index, schemaKey }); + }} + /> + ); +} +``` + ### Styling To style the component, add custom CSS. For basic styling, there are some CSS custom properties ("variables") diff --git a/demo/pages/Index.tsx b/demo/pages/Index.tsx index 84289dd..f479ce7 100644 --- a/demo/pages/Index.tsx +++ b/demo/pages/Index.tsx @@ -175,6 +175,10 @@ const Index = () => { readOnly={readOnly} setSchema={setSchema} className="shadow-lg animate-in border-border/50 backdrop-blur-xs" + onAddEnum={(v: string | number | boolean, index: number) => + console.log(v, index) + } + onDeleteEnum={(v: string | number | boolean) => console.log(v)} /> diff --git a/src/components/SchemaEditor/JsonSchemaEditor.tsx b/src/components/SchemaEditor/JsonSchemaEditor.tsx index 46b868e..4f27681 100644 --- a/src/components/SchemaEditor/JsonSchemaEditor.tsx +++ b/src/components/SchemaEditor/JsonSchemaEditor.tsx @@ -16,12 +16,15 @@ import { cn } from "../../lib/utils.ts"; import type { JSONSchema } from "../../types/jsonSchema.ts"; import JsonSchemaVisualizer from "./JsonSchemaVisualizer.tsx"; import SchemaVisualEditor from "./SchemaVisualEditor.tsx"; +import type { EnumChangeContext } from "./TypeEditor.tsx"; /** @public */ export interface JsonSchemaEditorProps { schema?: JSONSchema; readOnly: boolean; setSchema?: (schema: JSONSchema) => void; + onAddEnum?: (ctx: EnumChangeContext) => void; + onDeleteEnum?: (ctx: EnumChangeContext) => void; className?: string; } @@ -30,6 +33,8 @@ const JsonSchemaEditor: FC = ({ schema = { type: "object" }, readOnly = false, setSchema, + onAddEnum, + onDeleteEnum, className, }) => { // Handle schema changes and propagate to parent if needed @@ -124,6 +129,8 @@ const JsonSchemaEditor: FC = ({ readOnly={readOnly} schema={schema} onChange={handleSchemaChange} + onAddEnum={onAddEnum} + onDeleteEnum={onDeleteEnum} /> @@ -170,6 +177,8 @@ const JsonSchemaEditor: FC = ({ readOnly={readOnly} schema={schema} onChange={handleSchemaChange} + onAddEnum={onAddEnum} + onDeleteEnum={onDeleteEnum} /> {/** biome-ignore lint/a11y/noStaticElementInteractions: What exactly does this div do? */} diff --git a/src/components/SchemaEditor/SchemaFieldList.tsx b/src/components/SchemaEditor/SchemaFieldList.tsx index f597fdb..e1831b6 100644 --- a/src/components/SchemaEditor/SchemaFieldList.tsx +++ b/src/components/SchemaEditor/SchemaFieldList.tsx @@ -14,10 +14,13 @@ import { } from "../../types/jsonSchema.ts"; import { buildValidationTree } from "../../types/validation.ts"; import SchemaPropertyEditor from "./SchemaPropertyEditor.tsx"; +import type { EnumChangeContext } from "./TypeEditor.tsx"; interface SchemaFieldListProps { schema: JSONSchemaType; readOnly: boolean; + onAddEnum?: (ctx: EnumChangeContext) => void; + onDeleteEnum?: (ctx: EnumChangeContext) => void; onAddField: (newField: NewField) => void; onEditField: (name: string, updatedField: NewField) => void; onDeleteField: (name: string) => void; @@ -27,6 +30,8 @@ const SchemaFieldList: FC = ({ schema, onEditField, onDeleteField, + onAddEnum, + onDeleteEnum, readOnly = false, }) => { const t = useTranslation(); @@ -143,9 +148,12 @@ const SchemaFieldList: FC = ({ onDeleteField(property.name)} onNameChange={(newName) => handleNameChange(property.name, newName)} onRequiredChange={(required) => diff --git a/src/components/SchemaEditor/SchemaPropertyEditor.tsx b/src/components/SchemaEditor/SchemaPropertyEditor.tsx index 9506880..6ead922 100644 --- a/src/components/SchemaEditor/SchemaPropertyEditor.tsx +++ b/src/components/SchemaEditor/SchemaPropertyEditor.tsx @@ -17,13 +17,17 @@ import type { ValidationTreeNode } from "../../types/validation.ts"; import { Badge } from "../ui/badge.tsx"; import { ButtonToggle } from "../ui/button-toggle.tsx"; import TypeDropdown from "./TypeDropdown.tsx"; +import type { EnumChangeContext } from "./TypeEditor.tsx"; import TypeEditor from "./TypeEditor.tsx"; export interface SchemaPropertyEditorProps { name: string; schema: JSONSchema; + schemaKey?: string; required: boolean; readOnly: boolean; validationNode?: ValidationTreeNode; + onAddEnum?: (ctx: EnumChangeContext) => void; + onDeleteEnum?: (ctx: EnumChangeContext) => void; onDelete: () => void; onNameChange: (newName: string) => void; onRequiredChange: (required: boolean) => void; @@ -34,9 +38,12 @@ export interface SchemaPropertyEditorProps { export const SchemaPropertyEditor: React.FC = ({ name, schema, + schemaKey, required, readOnly = false, validationNode, + onAddEnum, + onDeleteEnum, onDelete, onNameChange, onRequiredChange, @@ -254,6 +261,9 @@ export const SchemaPropertyEditor: React.FC = ({ readOnly={readOnly} validationNode={validationNode} onChange={handleSchemaUpdate} + schemaKey={schemaKey ?? name} + onAddEnum={onAddEnum} + onDeleteEnum={onDeleteEnum} depth={depth + 1} /> diff --git a/src/components/SchemaEditor/SchemaVisualEditor.tsx b/src/components/SchemaEditor/SchemaVisualEditor.tsx index 52b2ef9..093836f 100644 --- a/src/components/SchemaEditor/SchemaVisualEditor.tsx +++ b/src/components/SchemaEditor/SchemaVisualEditor.tsx @@ -10,18 +10,23 @@ import type { JSONSchema, NewField } from "../../types/jsonSchema.ts"; import { asObjectSchema, isBooleanSchema } from "../../types/jsonSchema.ts"; import AddFieldButton from "./AddFieldButton.tsx"; import SchemaFieldList from "./SchemaFieldList.tsx"; +import type { EnumChangeContext } from "./TypeEditor.tsx"; /** @public */ export interface SchemaVisualEditorProps { schema: JSONSchema; readOnly: boolean; onChange: (schema: JSONSchema) => void; + onAddEnum?: (ctx: EnumChangeContext) => void; + onDeleteEnum?: (ctx: EnumChangeContext) => void; } /** @public */ const SchemaVisualEditor: FC = ({ schema, onChange, + onAddEnum, + onDeleteEnum, readOnly = false, }) => { const t = useTranslation(); @@ -125,6 +130,8 @@ const SchemaVisualEditor: FC = ({ import("./types/ObjectEditor.tsx")); const ArrayEditor = lazy(() => import("./types/ArrayEditor.tsx")); const CombinatorEditor = lazy(() => import("./types/CombinatorEditor.tsx")); +export interface EnumChangeContext { + value: string | number | boolean; + index: number; + schemaKey?: string; +} + export interface TypeEditorProps { schema: JSONSchema; readOnly: boolean; validationNode: ValidationTreeNode | undefined; onChange: (schema: ObjectJSONSchema) => void; + schemaKey?: string; + onAddEnum?: (ctx: EnumChangeContext) => void; + onDeleteEnum?: (ctx: EnumChangeContext) => void; depth?: number; } @@ -24,6 +33,9 @@ const TypeEditor: React.FC = ({ schema, validationNode, onChange, + schemaKey, + onAddEnum, + onDeleteEnum, depth = 0, readOnly = false, }) => { @@ -37,6 +49,9 @@ const TypeEditor: React.FC = ({ readOnly={readOnly} schema={schema} onChange={onChange} + schemaKey={schemaKey} + onAddEnum={onAddEnum} + onDeleteEnum={onDeleteEnum} depth={depth} validationNode={validationNode} /> @@ -46,6 +61,9 @@ const TypeEditor: React.FC = ({ readOnly={readOnly} schema={schema} onChange={onChange} + schemaKey={schemaKey} + onAddEnum={onAddEnum} + onDeleteEnum={onDeleteEnum} depth={depth} validationNode={validationNode} /> @@ -55,6 +73,9 @@ const TypeEditor: React.FC = ({ readOnly={readOnly} schema={schema} onChange={onChange} + schemaKey={schemaKey} + onAddEnum={onAddEnum} + onDeleteEnum={onDeleteEnum} depth={depth} validationNode={validationNode} integer @@ -65,6 +86,9 @@ const TypeEditor: React.FC = ({ readOnly={readOnly} schema={schema} onChange={onChange} + schemaKey={schemaKey} + onAddEnum={onAddEnum} + onDeleteEnum={onDeleteEnum} depth={depth} validationNode={validationNode} /> @@ -74,6 +98,9 @@ const TypeEditor: React.FC = ({ readOnly={readOnly} schema={schema} onChange={onChange} + schemaKey={schemaKey} + onAddEnum={onAddEnum} + onDeleteEnum={onDeleteEnum} depth={depth} validationNode={validationNode} /> @@ -83,6 +110,9 @@ const TypeEditor: React.FC = ({ readOnly={readOnly} schema={schema} onChange={onChange} + schemaKey={schemaKey} + onAddEnum={onAddEnum} + onDeleteEnum={onDeleteEnum} depth={depth} validationNode={validationNode} /> @@ -92,6 +122,9 @@ const TypeEditor: React.FC = ({ readOnly={readOnly} schema={schema} onChange={onChange} + schemaKey={schemaKey} + onAddEnum={onAddEnum} + onDeleteEnum={onDeleteEnum} depth={depth} validationNode={validationNode} combinator={type} diff --git a/src/components/SchemaEditor/types/ArrayEditor.tsx b/src/components/SchemaEditor/types/ArrayEditor.tsx index 79ca7fa..3452a3d 100644 --- a/src/components/SchemaEditor/types/ArrayEditor.tsx +++ b/src/components/SchemaEditor/types/ArrayEditor.tsx @@ -24,6 +24,9 @@ const ArrayEditor: React.FC = ({ readOnly = false, validationNode, onChange, + schemaKey, + onAddEnum, + onDeleteEnum, depth = 0, }) => { const t = useTranslation(); @@ -43,6 +46,7 @@ const ArrayEditor: React.FC = ({ // Get the array's item schema const itemsSchema = getArrayItemsSchema(schema) || { type: "string" }; + const itemSchemaKey = schemaKey ? `${schemaKey}[]` : undefined; // Get the type of the array items const itemType = withObjectSchema( @@ -276,6 +280,9 @@ const ArrayEditor: React.FC = ({ schema={itemsSchema} validationNode={validationNode} onChange={handleItemSchemaChange} + schemaKey={itemSchemaKey} + onAddEnum={onAddEnum} + onDeleteEnum={onDeleteEnum} depth={depth + 1} /> diff --git a/src/components/SchemaEditor/types/BooleanEditor.tsx b/src/components/SchemaEditor/types/BooleanEditor.tsx index b20c314..0d01bbf 100644 --- a/src/components/SchemaEditor/types/BooleanEditor.tsx +++ b/src/components/SchemaEditor/types/BooleanEditor.tsx @@ -9,6 +9,9 @@ import type { TypeEditorProps } from "../TypeEditor.tsx"; const BooleanEditor: React.FC = ({ schema, onChange, + schemaKey, + onAddEnum, + onDeleteEnum, readOnly = false, }) => { const t = useTranslation(); @@ -30,6 +33,7 @@ const BooleanEditor: React.FC = ({ // Handle changing the allowed values const handleAllowedChange = (value: boolean, allowed: boolean) => { let newEnum: boolean[] | undefined; + let enumAction: "add" | "delete" | null = null; if (allowed) { // If allowing this value @@ -45,6 +49,7 @@ const BooleanEditor: React.FC = ({ // Add this value to enum newEnum = enumValues ? [...enumValues, value] : [value]; + enumAction = "add"; // If both are now allowed, we can remove the enum constraint if (newEnum.includes(true) && newEnum.includes(false)) { @@ -59,6 +64,7 @@ const BooleanEditor: React.FC = ({ // Create a new enum with just the opposite value newEnum = [!value]; + enumAction = "delete"; } // Create a new validation object with just the type and enum @@ -71,10 +77,42 @@ const BooleanEditor: React.FC = ({ } else { // Remove enum property if no restrictions onChange({ type: "boolean" }); + if (enumAction === "add") { + onAddEnum?.({ + value, + index: enumValues?.length ?? 0, + schemaKey, + }); + } + if (enumAction === "delete") { + const deleteIndex = + enumValues?.indexOf(value) ?? [true, false].indexOf(value); + onDeleteEnum?.({ + value, + index: Math.max(deleteIndex, 0), + schemaKey, + }); + } return; } onChange(updatedValidation); + if (enumAction === "add") { + onAddEnum?.({ + value, + index: newEnum.indexOf(value), + schemaKey, + }); + } + if (enumAction === "delete") { + const deleteIndex = + enumValues?.indexOf(value) ?? [true, false].indexOf(value); + onDeleteEnum?.({ + value, + index: Math.max(deleteIndex, 0), + schemaKey, + }); + } }; const hasEnum = enumValues && enumValues.length > 0; diff --git a/src/components/SchemaEditor/types/CombinatorEditor.tsx b/src/components/SchemaEditor/types/CombinatorEditor.tsx index 7a903d6..70be327 100644 --- a/src/components/SchemaEditor/types/CombinatorEditor.tsx +++ b/src/components/SchemaEditor/types/CombinatorEditor.tsx @@ -81,6 +81,9 @@ const CombinatorEditor: React.FC = ({ readOnly = false, validationNode, onChange, + schemaKey, + onAddEnum, + onDeleteEnum, depth = 0, combinator, }) => { @@ -219,6 +222,13 @@ const CombinatorEditor: React.FC = ({ onChange={(updatedSchema) => handleOptionSchemaChange(index, updatedSchema) } + schemaKey={ + schemaKey + ? `${schemaKey}.${combinator}[${index}]` + : `${combinator}[${index}]` + } + onAddEnum={onAddEnum} + onDeleteEnum={onDeleteEnum} depth={depth + 1} /> diff --git a/src/components/SchemaEditor/types/NumberEditor.tsx b/src/components/SchemaEditor/types/NumberEditor.tsx index f8d7460..6eb4b40 100644 --- a/src/components/SchemaEditor/types/NumberEditor.tsx +++ b/src/components/SchemaEditor/types/NumberEditor.tsx @@ -27,6 +27,9 @@ const NumberEditor: React.FC = ({ schema, validationNode, onChange, + schemaKey, + onAddEnum, + onDeleteEnum, integer = false, readOnly = false, }) => { @@ -142,6 +145,15 @@ const NumberEditor: React.FC = ({ onChange(baseProperties as ObjectJSONSchema); }; + const applyEnumValues = (values: number[]) => { + if (values.length > 0) { + handleValidationChange("enum", values); + return; + } + + handleValidationChange("enum", undefined); + }; + // Handle adding enum value const handleAddEnumValue = () => { if (!enumValue.trim()) return; @@ -153,7 +165,9 @@ const NumberEditor: React.FC = ({ const validValue = integer ? Math.floor(numValue) : numValue; if (!enumValues.includes(validValue)) { - handleValidationChange("enum", [...enumValues, validValue]); + const addedIndex = enumValues.length; + applyEnumValues([...enumValues, validValue]); + onAddEnum?.({ value: validValue, index: addedIndex, schemaKey }); } setEnumValue(""); @@ -161,15 +175,13 @@ const NumberEditor: React.FC = ({ // Handle removing enum value const handleRemoveEnumValue = (index: number) => { + const removedValue = enumValues[index]; + if (removedValue === undefined) return; + const newEnumValues = [...enumValues]; newEnumValues.splice(index, 1); - - if (newEnumValues.length === 0) { - // If empty, remove the enum property entirely by setting it to undefined - handleValidationChange("enum", undefined); - } else { - handleValidationChange("enum", newEnumValues); - } + applyEnumValues(newEnumValues); + onDeleteEnum?.({ value: removedValue, index, schemaKey }); }; const minMaxError = useMemo( diff --git a/src/components/SchemaEditor/types/ObjectEditor.tsx b/src/components/SchemaEditor/types/ObjectEditor.tsx index a5a8428..0ce512a 100644 --- a/src/components/SchemaEditor/types/ObjectEditor.tsx +++ b/src/components/SchemaEditor/types/ObjectEditor.tsx @@ -16,6 +16,9 @@ const ObjectEditor: React.FC = ({ schema, validationNode, onChange, + schemaKey, + onAddEnum, + onDeleteEnum, depth = 0, readOnly = false, }) => { @@ -136,9 +139,14 @@ const ObjectEditor: React.FC = ({ readOnly={readOnly} key={property.name} name={property.name} + schemaKey={ + schemaKey ? `${schemaKey}.${property.name}` : property.name + } schema={property.schema} required={property.required} validationNode={validationNode?.children[property.name]} + onAddEnum={onAddEnum} + onDeleteEnum={onDeleteEnum} onDelete={() => handleDeleteProperty(property.name)} onNameChange={(newName) => handlePropertyNameChange(property.name, newName) diff --git a/src/components/SchemaEditor/types/StringEditor.tsx b/src/components/SchemaEditor/types/StringEditor.tsx index 352e3a5..d4fec6a 100644 --- a/src/components/SchemaEditor/types/StringEditor.tsx +++ b/src/components/SchemaEditor/types/StringEditor.tsx @@ -24,6 +24,9 @@ const StringEditor: React.FC = ({ schema, validationNode, onChange, + schemaKey, + onAddEnum, + onDeleteEnum, readOnly = false, }) => { const t = useTranslation(); @@ -66,12 +69,41 @@ const StringEditor: React.FC = ({ onChange(updatedValidation); }; + const applyEnumValues = (values: string[]) => { + if (values.length > 0) { + const updatedSchema: ObjectJSONSchema = { + ...(isBooleanSchema(schema) + ? { type: "string" as const } + : { ...schema }), + type: "string", + enum: values, + }; + onChange(updatedSchema); + return; + } + + const baseSchema = isBooleanSchema(schema) + ? { type: "string" as const } + : { ...schema }; + + if (!isBooleanSchema(baseSchema) && "enum" in baseSchema) { + const { enum: _, ...rest } = baseSchema; + onChange(rest as ObjectJSONSchema); + return; + } + + onChange(baseSchema as ObjectJSONSchema); + }; + // Handle adding enum value const handleAddEnumValue = () => { - if (!enumValue.trim()) return; + const trimmedValue = enumValue.trim(); + if (!trimmedValue) return; - if (!enumValues.includes(enumValue)) { - handleValidationChange("enum", [...enumValues, enumValue]); + if (!enumValues.includes(trimmedValue)) { + const addedIndex = enumValues.length; + applyEnumValues([...enumValues, trimmedValue]); + onAddEnum?.({ value: trimmedValue, index: addedIndex, schemaKey }); } setEnumValue(""); @@ -79,25 +111,13 @@ const StringEditor: React.FC = ({ // Handle removing enum value const handleRemoveEnumValue = (index: number) => { + const removedValue = enumValues[index]; + if (removedValue === undefined) return; + const newEnumValues = [...enumValues]; newEnumValues.splice(index, 1); - - if (newEnumValues.length === 0) { - // If empty, remove the enum property entirely - const baseSchema = isBooleanSchema(schema) - ? { type: "string" as const } - : { ...schema }; - - // Use a type safe approach - if (!isBooleanSchema(baseSchema) && "enum" in baseSchema) { - const { enum: _, ...rest } = baseSchema; - onChange(rest as ObjectJSONSchema); - } else { - onChange(baseSchema as ObjectJSONSchema); - } - } else { - handleValidationChange("enum", newEnumValues); - } + applyEnumValues(newEnumValues); + onDeleteEnum?.({ value: removedValue, index, schemaKey }); }; const minMaxError = useMemo( From bd5621e20f230c3618d66bada3e01f6e6ba3aed7 Mon Sep 17 00:00:00 2001 From: Igor Rosliakov Date: Mon, 20 Apr 2026 22:30:14 +0300 Subject: [PATCH 2/4] clean sample --- demo/pages/Index.tsx | 66 +++++++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 35 deletions(-) diff --git a/demo/pages/Index.tsx b/demo/pages/Index.tsx index f479ce7..6168f60 100644 --- a/demo/pages/Index.tsx +++ b/demo/pages/Index.tsx @@ -9,57 +9,57 @@ import { PencilOff, RefreshCw, User, -} from "lucide-react"; -import React, { useState } from "react"; -import { exampleSchema } from "../../demo/utils/schemaExample.ts"; -import { JsonValidator } from "../../src/components/features/JsonValidator.tsx"; -import { SchemaInferencer } from "../../src/components/features/SchemaInferencer.tsx"; -import JsonSchemaEditor from "../../src/components/SchemaEditor/JsonSchemaEditor.tsx"; -import { Button } from "../../src/components/ui/button.tsx"; +} from "lucide-react" +import React, { useState } from "react" +import { exampleSchema } from "../../demo/utils/schemaExample.ts" +import { JsonValidator } from "../../src/components/features/JsonValidator.tsx" +import { SchemaInferencer } from "../../src/components/features/SchemaInferencer.tsx" +import JsonSchemaEditor from "../../src/components/SchemaEditor/JsonSchemaEditor.tsx" +import { Button } from "../../src/components/ui/button.tsx" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, -} from "../../src/components/ui/select.tsx"; -import { en } from "../../src/i18n/locales/en.ts"; -import { TranslationContext } from "../../src/i18n/translation-context.ts"; -import type { JSONSchema } from "../../src/types/jsonSchema.ts"; +} from "../../src/components/ui/select.tsx" +import { en } from "../../src/i18n/locales/en.ts" +import { TranslationContext } from "../../src/i18n/translation-context.ts" +import type { JSONSchema } from "../../src/types/jsonSchema.ts" const Index = () => { - const [schema, setSchema] = useState(exampleSchema); - const [readOnly, setReadOnly] = useState(false); - const [inferDialogOpen, setInferDialogOpen] = useState(false); - const [validateDialogOpen, setValidateDialogOpen] = useState(false); - const [language, setLanguage] = useState("en"); - const [translation, setTranslation] = useState(en); + const [schema, setSchema] = useState(exampleSchema) + const [readOnly, setReadOnly] = useState(false) + const [inferDialogOpen, setInferDialogOpen] = useState(false) + const [validateDialogOpen, setValidateDialogOpen] = useState(false) + const [language, setLanguage] = useState("en") + const [translation, setTranslation] = useState(en) - const handleReset = () => setSchema(exampleSchema); + const handleReset = () => setSchema(exampleSchema) - const handleReadOnlyToggle = () => setReadOnly(!readOnly); + const handleReadOnlyToggle = () => setReadOnly(!readOnly) const handleClear = () => setSchema({ type: "object", properties: {}, required: [], - }); + }) const handleInferSchema = () => { - setInferDialogOpen(true); - }; + setInferDialogOpen(true) + } const handleValidateJson = () => { - setValidateDialogOpen(true); - }; + setValidateDialogOpen(true) + } const handleLanguageChange = (value: string) => { - setLanguage(value); + setLanguage(value) import(`../../src/i18n/locales/${value}.ts`).then((module) => { - setTranslation(module[value]); - }); - }; + setTranslation(module[value]) + }) + } return ( @@ -175,10 +175,6 @@ const Index = () => { readOnly={readOnly} setSchema={setSchema} className="shadow-lg animate-in border-border/50 backdrop-blur-xs" - onAddEnum={(v: string | number | boolean, index: number) => - console.log(v, index) - } - onDeleteEnum={(v: string | number | boolean) => console.log(v)} /> @@ -478,7 +474,7 @@ const Index = () => { - ); -}; + ) +} -export default Index; +export default Index From 7260d5f8dea5d899fcae0a73fc8becc02fb6bf77 Mon Sep 17 00:00:00 2001 From: Igor Rosliakov Date: Mon, 20 Apr 2026 23:02:14 +0300 Subject: [PATCH 3/4] fix format --- demo/pages/Index.tsx | 62 ++++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/demo/pages/Index.tsx b/demo/pages/Index.tsx index 6168f60..84289dd 100644 --- a/demo/pages/Index.tsx +++ b/demo/pages/Index.tsx @@ -9,57 +9,57 @@ import { PencilOff, RefreshCw, User, -} from "lucide-react" -import React, { useState } from "react" -import { exampleSchema } from "../../demo/utils/schemaExample.ts" -import { JsonValidator } from "../../src/components/features/JsonValidator.tsx" -import { SchemaInferencer } from "../../src/components/features/SchemaInferencer.tsx" -import JsonSchemaEditor from "../../src/components/SchemaEditor/JsonSchemaEditor.tsx" -import { Button } from "../../src/components/ui/button.tsx" +} from "lucide-react"; +import React, { useState } from "react"; +import { exampleSchema } from "../../demo/utils/schemaExample.ts"; +import { JsonValidator } from "../../src/components/features/JsonValidator.tsx"; +import { SchemaInferencer } from "../../src/components/features/SchemaInferencer.tsx"; +import JsonSchemaEditor from "../../src/components/SchemaEditor/JsonSchemaEditor.tsx"; +import { Button } from "../../src/components/ui/button.tsx"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, -} from "../../src/components/ui/select.tsx" -import { en } from "../../src/i18n/locales/en.ts" -import { TranslationContext } from "../../src/i18n/translation-context.ts" -import type { JSONSchema } from "../../src/types/jsonSchema.ts" +} from "../../src/components/ui/select.tsx"; +import { en } from "../../src/i18n/locales/en.ts"; +import { TranslationContext } from "../../src/i18n/translation-context.ts"; +import type { JSONSchema } from "../../src/types/jsonSchema.ts"; const Index = () => { - const [schema, setSchema] = useState(exampleSchema) - const [readOnly, setReadOnly] = useState(false) - const [inferDialogOpen, setInferDialogOpen] = useState(false) - const [validateDialogOpen, setValidateDialogOpen] = useState(false) - const [language, setLanguage] = useState("en") - const [translation, setTranslation] = useState(en) + const [schema, setSchema] = useState(exampleSchema); + const [readOnly, setReadOnly] = useState(false); + const [inferDialogOpen, setInferDialogOpen] = useState(false); + const [validateDialogOpen, setValidateDialogOpen] = useState(false); + const [language, setLanguage] = useState("en"); + const [translation, setTranslation] = useState(en); - const handleReset = () => setSchema(exampleSchema) + const handleReset = () => setSchema(exampleSchema); - const handleReadOnlyToggle = () => setReadOnly(!readOnly) + const handleReadOnlyToggle = () => setReadOnly(!readOnly); const handleClear = () => setSchema({ type: "object", properties: {}, required: [], - }) + }); const handleInferSchema = () => { - setInferDialogOpen(true) - } + setInferDialogOpen(true); + }; const handleValidateJson = () => { - setValidateDialogOpen(true) - } + setValidateDialogOpen(true); + }; const handleLanguageChange = (value: string) => { - setLanguage(value) + setLanguage(value); import(`../../src/i18n/locales/${value}.ts`).then((module) => { - setTranslation(module[value]) - }) - } + setTranslation(module[value]); + }); + }; return ( @@ -474,7 +474,7 @@ const Index = () => { - ) -} + ); +}; -export default Index +export default Index; From 910511d2a32867f39330e92396b9a8fe2aae40c1 Mon Sep 17 00:00:00 2001 From: Igor Rosliakov Date: Mon, 20 Apr 2026 23:53:31 +0300 Subject: [PATCH 4/4] add export type EnumChangeContext --- src/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/index.ts b/src/index.ts index 860f5e5..a2e483f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,6 +12,7 @@ import SchemaVisualEditor, { export * from "./components/features/JsonValidator.tsx"; export * from "./components/features/SchemaInferencer.tsx"; +export type { EnumChangeContext } from "./components/SchemaEditor/TypeEditor.tsx"; export * from "./i18n/locales/de.ts"; export * from "./i18n/locales/en.ts"; export * from "./i18n/locales/es.ts";