@ht-rnd/json-schema-editor 2.0.1 → 2.0.3

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/README.md CHANGED
@@ -1,13 +1,19 @@
1
1
  # @ht-rnd/json-schema-editor
2
2
 
3
- A headless JSON Schema editor library for React. This package provides the core logic, hooks, and utilities for building JSON Schema editors, while allowing complete control over the UI.
3
+ A powerful **headless** JSON Schema editor for React. Build fully customized editors with complete UI control while leveraging battle-tested logic, validation, and state management.
4
4
 
5
- ## Architecture
5
+ > **Headless Architecture**: Core logic via npm + optional copy-paste UI components. Use our pre-built Shadcn/ui components or build your own interface.
6
6
 
7
- This library follows the **headless UI pattern**:
7
+ ## Features
8
8
 
9
- - **NPM Package**: Contains only the headless core - hooks, types, validation, and utilities
10
- - **Components Folder**: Contains copy-paste shadcn-style React components for the UI
9
+ - **JSON Schema Draft 2020-12** - Full specification support
10
+ - **All Types** - string, number, integer, boolean, object, array, $ref
11
+ - **Nested Schemas** - Unlimited depth for objects and arrays
12
+ - **$defs Support** - Reusable definitions with `$ref`
13
+ - **Validation** - AJV-powered real-time validation
14
+ - **Combinators** - allOf, anyOf, oneOf, not
15
+ - **TypeScript** - Full type safety
16
+ - **Headless** - Zero UI dependencies in core
11
17
 
12
18
  ## Installation
13
19
 
@@ -15,170 +21,47 @@ This library follows the **headless UI pattern**:
15
21
  npm install @ht-rnd/json-schema-editor
16
22
  ```
17
23
 
18
- ### For UI Components
19
-
20
- Copy the `components/json-schema-editor/` folder from this repository into your project. These components are designed to be customized and follow [shadcn/ui](https://ui.shadcn.com/) principles.
21
-
22
- **Required peer dependencies for UI components:**
23
-
24
- ```bash
25
- npm install @radix-ui/react-alert-dialog @radix-ui/react-checkbox @radix-ui/react-dialog \
26
- @radix-ui/react-label @radix-ui/react-radio-group @radix-ui/react-select \
27
- @radix-ui/react-separator @radix-ui/react-slot @radix-ui/react-tooltip \
28
- class-variance-authority clsx lucide-react tailwind-merge tailwindcss-animate
29
- ```
30
-
31
- ## Features
32
-
33
- ### Core Capabilities
34
-
35
- - **JSON Schema 2020-12** - Full support for draft 2020-12 specification
36
- - **Multiple Data Types** - string, number, integer, boolean, object, array, and $ref
37
- - **Nested Schemas** - Support for nested objects and arrays with unlimited depth
38
- - **Definitions ($defs)** - Create reusable schema definitions with $ref support
39
- - **Validation Constraints** - min/max, length, format, pattern, enum, and more
40
- - **Boolean Combinators** - allOf, anyOf, oneOf, not for complex schema logic
41
- - **Real-time Validation** - AJV-based validation with inline error messages
42
- - **Type Safety** - Full TypeScript support with comprehensive type definitions
43
-
44
- ### UI Features (Pre-built Components)
45
-
46
- - **Responsive Layout** - Configurable form and output positioning (top/bottom/left/right)
47
- - **Customizable Sizing** - Flexible width and height options (sm/md/lg/full)
48
- - **Dark Mode Ready** - Theme prop support for dark/light mode
49
- - **Read-only Mode** - View-only mode for displaying schemas
50
- - **Settings Dialogs** - Rich settings for each field type with type-specific options
51
- - **Inline Editing** - Direct field manipulation with instant feedback
52
-
53
- ## Usage
54
-
55
- ### Option 1: Using the Headless Hook (Full Control)
56
-
57
- Use the `useJsonSchemaEditor` hook directly for complete control over the UI:
58
-
59
- ```tsx
60
- import { useJsonSchemaEditor } from "@ht-rnd/json-schema-editor";
61
- import { FormProvider } from "react-hook-form";
62
-
63
- function MyCustomEditor() {
64
- const editor = useJsonSchemaEditor({
65
- rootType: "object",
66
- onChange: (schema) => console.log("Schema changed:", schema),
67
- });
68
-
69
- return (
70
- <FormProvider {...editor.form}>
71
- <div>
72
- {/* Your custom UI here */}
73
- <pre>{JSON.stringify(editor.schema, null, 2)}</pre>
74
-
75
- <button onClick={editor.addField}>Add Field</button>
76
- <button onClick={editor.addDefinition}>Add Definition</button>
77
-
78
- {editor.fields.map((field, index) => (
79
- <div key={field.id}>
80
- {/* Render your custom field UI */}
81
- <input value={field.key} />
82
- <button onClick={() => editor.removeField(index)}>Remove</button>
83
- <button onClick={() => editor.openSettings(`properties.${index}`)}>Settings</button>
84
- </div>
85
- ))}
86
-
87
- {editor.definitions.map((def, index) => (
88
- <div key={def.id}>
89
- <input value={def.key} />
90
- <button onClick={() => editor.removeDefinition(index)}>Remove</button>
91
- </div>
92
- ))}
93
-
94
- {editor.errors && (
95
- <div>
96
- {editor.errors.map((error, i) => (
97
- <p key={i}>{error.message}</p>
98
- ))}
99
- </div>
100
- )}
101
- </div>
102
- </FormProvider>
103
- );
104
- }
105
- ```
24
+ **Optional UI components**: Copy `components/ui/` from this repo to your project. Requires Radix UI and Tailwind CSS.
106
25
 
107
- ### Option 2: Using the Pre-built Components
26
+ ## Quick Start
108
27
 
109
- Copy the components from `components/json-schema-editor/` and use them directly:
28
+ ### Option 1: Pre-built Components
110
29
 
111
30
  ```tsx
112
- import { JsonSchemaEditor } from "@/components/json-schema-editor";
31
+ import { JsonSchemaEditor } from "@/components/ui/json-schema-editor";
113
32
 
114
33
  function App() {
115
34
  return (
116
35
  <JsonSchemaEditor
117
36
  rootType="object"
118
- theme="light"
119
- readOnly={false}
120
- showOutput={true}
121
37
  onChange={(schema) => console.log(schema)}
122
- styles={{
123
- form: { width: "full", height: "md" },
124
- output: { position: "bottom", showJson: true, width: "full", height: "md" },
125
- settings: { width: "md" },
126
- spacing: "md",
127
- }}
128
38
  />
129
39
  );
130
40
  }
131
41
  ```
132
42
 
133
- ### Option 3: Mix and Match
134
-
135
- Use the headless hook with some pre-built components:
43
+ ### Option 2: Headless Hook
136
44
 
137
45
  ```tsx
138
46
  import { useJsonSchemaEditor } from "@ht-rnd/json-schema-editor";
139
- import { Root, FieldList, SettingsDialog } from "@/components/json-schema-editor";
140
47
  import { FormProvider } from "react-hook-form";
141
48
 
142
- function HybridEditor() {
143
- const editor = useJsonSchemaEditor({ rootType: "object" });
49
+ function App() {
50
+ const editor = useJsonSchemaEditor({
51
+ rootType: "object",
52
+ onChange: (schema) => console.log(schema),
53
+ });
144
54
 
145
55
  return (
146
56
  <FormProvider {...editor.form}>
147
- <div className="space-y-4">
148
- <Root
149
- rootType="object"
150
- onAddField={editor.addField}
151
- onOpenSettings={editor.openSettings}
152
- />
153
-
154
- <FieldList
155
- fields={editor.fields}
156
- onRemove={editor.removeField}
157
- onOpenSettings={editor.openSettings}
158
- />
159
-
160
- {/* Add definitions support */}
161
- {editor.definitions.length > 0 && (
162
- <div className="mt-4">
163
- <h3>Definitions ($defs)</h3>
164
- {editor.definitions.map((def, index) => (
165
- <div key={def.id}>
166
- <span>{def.key}</span>
167
- <button onClick={() => editor.removeDefinition(index)}>Remove</button>
168
- </div>
169
- ))}
170
- </div>
171
- )}
172
-
173
- {/* Your custom JSON output */}
174
- <textarea value={JSON.stringify(editor.schema, null, 2)} readOnly />
175
-
176
- <SettingsDialog
177
- isOpen={editor.settingsState.isOpen}
178
- fieldPath={editor.settingsState.fieldPath}
179
- onClose={editor.closeSettings}
180
- />
181
- </div>
57
+ <button onClick={editor.addField}>Add Field</button>
58
+ {editor.fields.map((field, index) => (
59
+ <div key={field.id}>
60
+ <input value={field.key} />
61
+ <button onClick={() => editor.removeField(index)}>Remove</button>
62
+ </div>
63
+ ))}
64
+ <pre>{JSON.stringify(editor.schema, null, 2)}</pre>
182
65
  </FormProvider>
183
66
  );
184
67
  }
@@ -186,71 +69,53 @@ function HybridEditor() {
186
69
 
187
70
  ## API Reference
188
71
 
189
- ### `JsonSchemaEditor` Component (Pre-built)
190
-
191
- The pre-built React component with full UI implementation.
192
-
193
- #### Props
194
-
195
- | Property | Type | Default | Description |
196
- |----------|------|---------|-------------|
197
- | `rootType` | `"object" \| "array"` | `"object"` | Root schema type |
198
- | `defaultValue` | `JSONSchema` | - | Initial schema value |
199
- | `onChange` | `(schema: JSONSchema) => void` | - | Callback when schema changes |
200
- | `readOnly` | `boolean` | `false` | Make all inputs read-only |
201
- | `showOutput` | `boolean` | `true` | Show/hide output panel |
202
- | `theme` | `string` | `"light"` | Theme class for styling |
203
- | `styles` | `Partial<Styles>` | - | Layout and sizing configuration |
204
- | `className` | `string` | - | Additional CSS classes |
72
+ ### `useJsonSchemaEditor(options)`
205
73
 
206
- #### Styles Configuration
74
+ Main hook for editor state management.
207
75
 
76
+ **Options:**
208
77
  ```typescript
209
- interface Styles {
210
- form: { width: "sm" | "md" | "lg" | "full"; height: "sm" | "md" | "lg" | "full" };
211
- output: {
212
- position: "top" | "bottom" | "left" | "right";
213
- showJson: boolean;
214
- width: "sm" | "md" | "lg" | "full";
215
- height: "sm" | "md" | "lg" | "full";
216
- };
217
- settings: { width: "sm" | "md" | "lg" | "full" };
218
- spacing: "sm" | "md" | "lg";
78
+ {
79
+ rootType?: "object" | "array"; // Default: "object"
80
+ defaultValue?: JSONSchema; // Load existing schema
81
+ onChange?: (schema: JSONSchema) => void;
219
82
  }
220
83
  ```
221
84
 
222
- ### `useJsonSchemaEditor(options)`
223
-
224
- The main headless hook for managing JSON Schema editor state.
85
+ **Returns:**
86
+ ```typescript
87
+ {
88
+ // State
89
+ schema: JSONSchema;
90
+ errors: ErrorObject[] | null;
91
+ fields: FieldItem[];
92
+ definitions: DefinitionItem[];
93
+ form: UseFormReturn;
94
+ settingsState: { isOpen: boolean; fieldPath: string | null };
95
+
96
+ // Actions
97
+ addField: () => void;
98
+ removeField: (index: number) => void;
99
+ addDefinition: () => void;
100
+ removeDefinition: (index: number) => void;
101
+ openSettings: (path: string) => void;
102
+ closeSettings: () => void;
103
+ handleTypeChange: (path: string, type: string) => void;
104
+ addNestedField: (parentPath: string) => void;
105
+ reset: () => void;
106
+ }
107
+ ```
225
108
 
226
- #### Options
109
+ ### `JsonSchemaEditor` Component Props
227
110
 
228
- | Property | Type | Default | Description |
229
- |----------|------|---------|-------------|
111
+ | Prop | Type | Default | Description |
112
+ |------|------|---------|-------------|
230
113
  | `rootType` | `"object" \| "array"` | `"object"` | Root schema type |
231
- | `defaultValue` | `JSONSchema` | - | Initial schema value |
232
- | `onChange` | `(schema: JSONSchema) => void` | - | Callback when schema changes |
233
-
234
- #### Returns
235
-
236
- | Property | Type | Description |
237
- |----------|------|-------------|
238
- | `schema` | `JSONSchema` | Current JSON Schema output |
239
- | `errors` | `ErrorObject[] \| null` | AJV validation errors |
240
- | `fields` | `FieldItem[]` | Field array for iteration |
241
- | `definitions` | `DefinitionItem[]` | Definition array for $defs |
242
- | `form` | `UseFormReturn` | React Hook Form methods |
243
- | `settingsState` | `{ isOpen, fieldPath }` | Settings dialog state |
244
- | `addField()` | `() => void` | Add a new field |
245
- | `removeField(index)` | `(index: number) => void` | Remove field by index |
246
- | `addDefinition()` | `() => void` | Add a new definition to $defs |
247
- | `removeDefinition(index)` | `(index: number) => void` | Remove definition by index |
248
- | `updateReferences(oldKey, newKey)` | `(oldKey: string, newKey: string \| null) => void` | Update $ref references when definition key changes |
249
- | `openSettings(path)` | `(path: string) => void` | Open settings for a field |
250
- | `closeSettings()` | `() => void` | Close settings dialog |
251
- | `handleTypeChange(path, type)` | `(path: string, type: string) => void` | Change field type |
252
- | `addNestedField(parentPath)` | `(parentPath: string) => void` | Add a nested field to an object |
253
- | `reset()` | `() => void` | Reset to default state |
114
+ | `defaultValue` | `JSONSchema` | - | Initial schema to load |
115
+ | `onChange` | `(schema) => void` | - | Callback on schema change |
116
+ | `readOnly` | `boolean` | `false` | View-only mode |
117
+ | `showOutput` | `boolean` | `true` | Show/hide JSON output |
118
+ | `className` | `string` | - | Additional CSS classes |
254
119
 
255
120
  ### Exports
256
121
 
@@ -259,124 +124,70 @@ The main headless hook for managing JSON Schema editor state.
259
124
  export { useJsonSchemaEditor } from "@ht-rnd/json-schema-editor";
260
125
 
261
126
  // Types
262
- export type {
263
- JSONSchema,
264
- FormSchema,
265
- JsonSchemaEditorOptions,
266
- SettingsState,
267
- FieldItem,
268
- DefinitionItem,
269
- SchemaType,
270
- SchemaTypeValue,
271
- SchemaTypeWithRefValue,
272
- UseJsonSchemaEditorReturn,
273
- } from "@ht-rnd/json-schema-editor";
274
-
275
- // Validation
276
- export { validateSchema } from "@ht-rnd/json-schema-editor";
277
- export { jsonSchemaZod } from "@ht-rnd/json-schema-editor";
127
+ export type { JSONSchema, FieldItem, DefinitionItem, UseJsonSchemaEditorReturn };
128
+
129
+ // Utilities
130
+ export { validateSchema, formToSchema, schemaToForm };
278
131
 
279
132
  // Constants
280
- export {
281
- SCHEMA_TYPES,
282
- SCHEMA_TYPES_WITH_REF,
283
- INTEGER_FORMATS,
284
- NUMBER_FORMATS,
285
- STRING_FORMATS,
286
- DEFAULT_SCHEMA_URI,
287
- } from "@ht-rnd/json-schema-editor";
288
-
289
- // Transforms and utilities
290
- export { formToSchema, schemaToForm } from "@ht-rnd/json-schema-editor";
291
- export {
292
- createDefaultObjectSchema,
293
- createDefaultArraySchema,
294
- createDefaultField,
295
- } from "@ht-rnd/json-schema-editor";
133
+ export { SCHEMA_TYPES, STRING_FORMATS, NUMBER_FORMATS, INTEGER_FORMATS };
296
134
  ```
297
135
 
298
- ## Component Architecture
136
+ ## Examples
299
137
 
300
- ### Available Components
138
+ ### Load Existing Schema
301
139
 
302
- The `components/json-schema-editor/` folder includes these ready-to-use components:
303
-
304
- #### Form Components
305
- - `JsonSchemaEditor` - Complete editor with form and output
306
- - `Root` - Root field component with type selector
307
- - `FieldList` - List of field rows
308
- - `FieldRow` - Individual field row with controls
309
- - `Field` - Recursive field component for nested schemas
310
-
311
- #### Settings Components
312
- - `SettingsDialog` - Modal dialog for field settings
313
- - `Settings` - Main settings router component
314
- - `StringSettings`, `NumberSettings`, `IntegerSettings`, `BooleanSettings` - Type-specific settings
315
- - `ObjectSettings`, `ArraySettings` - Complex type settings
316
- - `BoolCombSettings` - allOf/anyOf/oneOf/not settings
317
- - `DefinitionsSettings` - $defs management
318
- - `RootSettings` - Root schema settings
319
-
320
- #### UI Primitives
321
- All based on [shadcn/ui](https://ui.shadcn.com/): `Button`, `Input`, `Select`, `Checkbox`, `Dialog`, `Badge`, `Tooltip`, etc.
322
-
323
- ## Customizing Components
140
+ ```tsx
141
+ const editor = useJsonSchemaEditor({
142
+ rootType: "object",
143
+ defaultValue: {
144
+ type: "object",
145
+ properties: {
146
+ username: { type: "string", minLength: 3 },
147
+ email: { type: "string", format: "email" },
148
+ age: { type: "integer", minimum: 18 }
149
+ },
150
+ required: ["username", "email"]
151
+ }
152
+ });
153
+ ```
324
154
 
325
- The components in `components/json-schema-editor/` follow shadcn/ui patterns:
155
+ ### Validate Data Against Schema
326
156
 
327
- 1. **`cn()` utility**: All components use the `cn()` function to merge Tailwind classes
328
- 2. **`className` prop**: Every component accepts a `className` prop for customization
329
- 3. **`forwardRef`**: Components forward refs for DOM access
330
- 4. **Composable**: Use individual components or compose your own
331
- 5. **Copy & Modify**: Copy components into your project and customize as needed
157
+ ```tsx
158
+ import Ajv from "ajv";
332
159
 
333
- ### Example: Customizing the FieldRow
160
+ const ajv = new Ajv();
161
+ const validate = ajv.compile(editor.schema);
162
+ const valid = validate({ username: "john", email: "john@example.com" });
334
163
 
335
- ```tsx
336
- import { FieldRow } from "@/components/json-schema-editor";
337
-
338
- <FieldRow
339
- className="bg-slate-50 dark:bg-slate-900 rounded-lg"
340
- fieldPath="properties.0"
341
- theme="dark"
342
- // ... other props
343
- />
164
+ if (!valid) console.log(validate.errors);
344
165
  ```
345
166
 
346
- ### Example: Creating a Custom Field Component
167
+ ### Persist Schema Changes
347
168
 
348
169
  ```tsx
349
- import * as React from "react";
350
- import { Controller, useFormContext } from "react-hook-form";
351
- import { cn } from "./lib/utils";
352
- import { Input } from "./ui/input";
170
+ const editor = useJsonSchemaEditor({
171
+ onChange: (schema) => {
172
+ localStorage.setItem("schema", JSON.stringify(schema));
173
+ // Or send to API
174
+ }
175
+ });
176
+ ```
353
177
 
354
- interface MyCustomFieldProps extends React.HTMLAttributes<HTMLDivElement> {
355
- fieldPath: string;
356
- }
178
+ ## Contributing
357
179
 
358
- const MyCustomField = React.forwardRef<HTMLDivElement, MyCustomFieldProps>(
359
- ({ className, fieldPath, ...props }, ref) => {
360
- const { control } = useFormContext();
361
-
362
- return (
363
- <div ref={ref} className={cn("flex gap-2", className)} {...props}>
364
- <Controller
365
- control={control}
366
- name={`${fieldPath}.key`}
367
- render={({ field }) => (
368
- <Input {...field} placeholder="Field name" />
369
- )}
370
- />
371
- </div>
372
- );
373
- }
374
- );
375
- MyCustomField.displayName = "MyCustomField";
180
+ Contributions welcome! See [GitHub](https://github.com/ht-rnd/json-schema-editor) for issues and PRs.
376
181
 
377
- export { MyCustomField };
182
+ ```bash
183
+ git clone https://github.com/ht-rnd/json-schema-editor.git
184
+ npm install
185
+ npm test
186
+ npm run demo
378
187
  ```
379
188
 
380
189
  ## License
381
190
 
382
- Apache-2.0
191
+ Apache-2.0 © [HT-RND]
192
+
193
+ See [LICENSE](./LICENSE) for more details.