A widget can be simple like a button, or complex like intractive form.
Conceptally a widget is a React component with customize settings. To simplify settings(eg. color, sliding, padding) we provide some setting component(eg. color
) which can be configured and the setting will work automatially, so the developer can focus on the widget rendering.
Click this to see our widget sample.
A typical widget(eg. heading
) needs below information:
- A definition object, which defines widget's name, type, settings, etc
- An entity(optional), which defines data model of this widget. If not defined, the default data model will be used.
- A render component which is a react component rendering the widget's data
You will need to register 1. and 3. using registerWidget
function.
In addition, you may need to create your customized setting component if our built-in setting components don't fit your need.
A definition defines 'meta data' of a widget, and other information like settings, methods (eg, return data after user create a block from this widget). Below is the meta data information:
{
// Name of the widget
name: 'Heading',
// Type of the widget, unique identifier for this widget.
// Only allow small case, -, numbers, starting from letter
type: 'heading',
category: 'basic',
icon: 'TextFormatOutlined',
settings: [
{
name: 'Background Color',
settingComponent: 'color',
category: 'style',
property: 'settings.backgroundColor',
},
{
name: 'Width',
settingComponent: 'setting_input',
category: 'style',
property: 'settings.width',
},
],
}
Note: setting_input
is a customized setting component, see Customized setting for implementation.
See our definition reference for full properties and explaination.
An entity defines data model. It's recommanded to define a model so the widget data manipulation is easier. The saved json will be same as entity definition.
export interface EntitySampleWidget {
settings: {
width: number,
backgroundColor?: string,
};
}
Below is a render component sample to render:
Note EntitySampleWidget
is the Entity definition.
export const SampleWidget = (props: DME.WidgetRenderProps<EntitySampleWidget>) => {
const {
blockNode: {
data: { settings },
},
} = props;
...
return (
<div>
<div
style={{ width: width }}
className={css`
height: 300px;
background: ${settings.backgroundColor ?? '#ffe3e3'};
`}
>
</div>
</div>
}
const { updateSelectedBlock } = useEditorStore();
const updateWidth = (e, v) => {
//update data with entity
updateSelectedBlock<EntitySampleWidget>((data) => {
data.settings.width = v as number;
});
};
registerWidget(
{definition:
{
type: 'sample',
name: 'Sample widget',
...
},
render:SampleWidget}
)
Implementation:
const SettingInput = (props: DME.SettingComponentProps) => {
const { property, value } = props;
const { getSelectedBlock, updateSelectedBlockProps } = useEditorStore();
//Get block data
const blockData = getSelectedBlock<EntitySampleWidget>();
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
//update data with dynamic property
updateSelectedBlockProps(property, e.target.value);
};
return (
<div>
<TextField onChange={handleChange} value={value} />
{/* Show background color instead */}
Color: {blockData?.data.settings.backgroundColor}
</div>
);
};
Registration the setting component:
registerSettingComponent("setting_input", SettingInput);
Sometimes widget needs to convert data before Server Side Rendering. For example, a widget shows top 10 article list under a folder. The article list data needs to fetched dynamically when requesting the page.
Below is an example of getting image url, while the widget only stores image id. Implement the converting and register the function on onServerSideLoad
.
const onServerLoad = async (blockData, context)=>{
const imageID = blockData.settings.imageID;
fetch('/api/image/'+imageID).then((res)=>res.json()).then((data)=>{
blockData.url = data.url;
})
}
registerWidget(
{
type: 'sample',
name: 'Sample widget',
...
},
{render:SampleWidget,
onServerSideLoad: onServerLoad
}
)