Developing Visual Editors for UI Component Type Elements
After completing Extend Your Own UI Component Type Elements, components can be used in pages, but their configuration parameters must be manually modified in the scheme.json file. This is not user-friendly for business experts who lack coding skills.
This guide demonstrates how to develop a visual configuration editor for counter components, providing the same graphical configuration experience as JitAi's official components.
Preview of the editor interface
Once development is complete, selecting a counter component in the visual development tool displays a user-friendly configuration interface in the right property panel:
Editor architecture
| Element Level | fullName | Main Responsibilities |
|---|---|---|
| Editor Element | components.CounterType.Editor | Type points to editors.React; provides a visual configuration interface for CounterType |
| Target Component | components.CounterType | The target component being edited; completed in previous chapters |
Editor directory structure
components/
└── CounterType/
├── e.json # Component declaration file
├── index.ts # Component PC entry
├── index.mobile.ts # Component mobile entry
├── CounterComponent.ts # Component business logic
├── render/ # Component render layer
│ ├── pc/
│ └── mobile/
└── Editor/ # Editor directory (new)
├── e.json # Editor element definition file
├── index.ts # Editor entry file
└── Editor.tsx # Editor implementation file
Implementation guide
Creating the editor directory
Create an Editor subdirectory under the CounterType directory:
# Execute in the CounterType directory
mkdir -p Editor
Implementing editor files
- Element Definition File
- Entry File
- Editor Implementation
Create the editor element definition file components/CounterType/Editor/e.json:
In JitAi, editors are also elements with their own e.json definition files.
{
"title": "Counter Component Editor",
"type":"editors.React",
"tag": "config",
"targetType": "components.CounterType",
"frontBundleEntry": "index.ts"
}
Editor element configuration properties:
title: Display name of the editor elementtype: Fixed aseditors.Reactto indicate a React-based editor elementtag: Fixed asconfigto indicate a configuration editortargetType: The fullName of the target componentfrontBundleEntry: Entry file for the editor
Create the editor entry file components/CounterType/Editor/index.ts:
import CounterEditor from "./Editor";
export const Editor = CounterEditor;
Editors must export a component named Editor. This is a fixed convention that JitAi tools use to recognize editors.
Create the editor implementation file components/CounterType/Editor/Editor.tsx:
import type { FC } from 'react';
import type { CompEditorProps } from 'components/common/types';
import { useState, useEffect, useRef } from 'react';
import { Form, InputNumber } from 'antd';
const CounterEditor: FC<CompEditorProps> = (props) => {
const { onChangeCompConfig } = props;
const didMountRef = useRef(false); // Prevent triggering callback during initialization
// 1. Manage component configuration state
const [compConfig, setCompConfig] = useState(props.compConfig);
// 2. Configuration update function
const updateConfig = (updates: Record<string, any>) => {
const newConfig = {
...compConfig,
config: {
...compConfig.config,
...updates,
},
};
setCompConfig(newConfig);
};
// 3. Notify parent component when configuration changes (skip initialization)
useEffect(() => {
if (!didMountRef.current) {
didMountRef.current = true;
return; // Skip callback during component initialization
}
onChangeCompConfig?.(compConfig);
}, [compConfig, onChangeCompConfig]);
// 4. Render configuration interface
return (
<div style={{ padding: '16px' }}>
<Form layout="vertical">
<Form.Item label="Initial Value">
<InputNumber
value={compConfig.config?.initialValue || 0}
onChange={(value) => updateConfig({ initialValue: value || 0 })}
placeholder="Please enter counter initial value"
/>
</Form.Item>
</Form>
</div>
);
};
export default CounterEditor;
How the editor works
Data flow mechanism
- Configuration reception: The editor component receives the current component configuration through
props.compConfig - State management: Uses
useStateto manage the configuration state within the editor - Configuration update: The
updateConfigfunction merges and updates the configuration object - Change monitoring:
useEffectmonitors configuration changes - Callback notification: Notifies the tool that the configuration has changed through the
onChangeCompConfigcallback
Core interface specifications
CompEditorProps interface
CompEditorProps is a standard interface provided by JitAi IDEApp. Developers can directly import it from components/common/types without defining it themselves.
// Import from JitAi—no need to define yourself
import type { CompEditorProps } from 'components/common/types';
interface CompEditorProps {
compConfig: {
name: string; // Component instance name
title: string; // Component display title
showTitle: boolean; // Whether to show title
config: { // Component custom configuration
initialValue?: number;
// Other configuration items...
};
// Other system configurations...
};
onChangeCompConfig?: (newConfig: any) => void; // Configuration change callback
}
Configuration update best practices
// ✅ Correct configuration update approach
const updateConfig = (updates: Record<string, any>) => {
const newConfig = {
...compConfig, // Preserve existing configuration
config: {
...compConfig.config, // Preserve existing config
...updates, // Merge new configuration
},
};
setCompConfig(newConfig);
};
// ❌ Incorrect configuration update approach
const updateConfig = (updates: Record<string, any>) => {
setCompConfig({ config: updates }); // This will lose other configurations
};
Testing
Making the editor take effect
- Clear cache: Delete the
distdirectory in the application directory - Restart service: Restart the desktop client
- Trigger repackaging: Access the application page; the system will automatically repackage
Verifying editor functionality
- Open the page editor: Open a page containing counter components in the JitAi development tool
- Select the component: Click the counter component in the page
- View the property panel: The right property panel should display the "Initial Value" configuration item
- Modify configuration: Try modifying the initial value and observe whether the component updates in real-time
- Verify persistence: Save the page and reopen it to confirm the configuration has been persisted
Common issue troubleshooting
- Editor not displaying: Check whether
targetTypeine.jsoncorrectly points to the component - Configuration not saving: Confirm whether the
onChangeCompConfigcallback is being called correctly - Initialization error: Check whether
didMountRefcorrectly prevents the initial callback
Advanced extensions
To add more configuration options for the counter component, simply add the corresponding form controls to the editor:
<Form layout="vertical">
<Form.Item label="Initial Value">
<InputNumber
value={compConfig.config?.initialValue || 0}
onChange={(value) => updateConfig({ initialValue: value || 0 })}
/>
</Form.Item>
<Form.Item label="Step">
<InputNumber
value={compConfig.config?.step || 1}
min={1}
onChange={(value) => updateConfig({ step: value || 1 })}
/>
</Form.Item>
<Form.Item label="Maximum Value">
<InputNumber
value={compConfig.config?.max}
onChange={(value) => updateConfig({ max: value })}
/>
</Form.Item>
</Form>
Supported Ant Design components include: InputNumber, Input, Switch, Select, DatePicker, and more.
Summary
Core steps for developing visual editors for UI components:
- Create the Editor directory and configure the editor element definition file
e.jsonwithtype: "editors.React" - Implement the Editor component: Accept
CompEditorPropsand render the configuration form - Export correctly: Export a component named
Editor - Synchronize configuration: Update configuration through the
onChangeCompConfigcallback
Key points:
- The export name
Editormust not be changed CompEditorPropsis provided by JitAi—import it directly- Configuration objects must be merged and updated correctly
- Use
didMountRefto prevent invalid callbacks during initialization