17 KiB
title, toc, order
title | toc | order |
---|---|---|
dooringx lib plug-in development | menu | 3 |
Plug in import
The dooringx lib plug-in requires an object of type 'partial < initconfig >'. For multiple plug-ins, you need to use the 'userconfigmerge' exported by dooringx lib to merge. Not all properties of userconfigmerge will be merged, and some properties will be overwritten.
* 部分无法合并属性如果b传了会以b为准
* initstore不合并
* leftallregistmap合并
* leftRenderListCategory合并
* rightRenderListCategory合并
* rightGlobalCustom 不合并
* initComponentCache合并
* initFunctionMap合并
* initDataCenterMap合并
* initCommandModule合并
* initFormComponents合并
Config supports partial configuration of asynchronous import, such as classification on the left. This is an experimental function, so it is not recommended.
Left panel
On the left panel, enter leftrenderlistcategory.
leftRenderListCategory: [
{
type: 'basic',
icon: <HighlightOutlined />,
displayName: '基础组件',
},
{
type: 'xxc',
icon: <ContainerOutlined />,
custom: true,
customRender: <div>我是自定义渲染</div>,
},
],
Type is the category. The category displayed by the component on the left is determined by this field. Icon is a small icon on the left. When custom is true, you can use customrender to customize the rendering.
Left side assembly
Plug in import
The left component is placed in the leftregistmap of the object. The left component supports synchronous import or asynchronous import.
const LeftRegistMap: LeftRegistComponentMapItem[] = [
{
type: 'basic',
component: 'button',
img: 'icon-anniu',
displayName: '按钮',
urlFn: () => import('./registComponents/button'),
},
];
If you need to import components asynchronously, you need to fill in urlfn and a function that returns promise. Remote loading of components can also be supported, as long as webpack is equipped with it. If you need to import components synchronously, you need to put the components into the initcomponentcache of the configuration item, so that they will be registered in the componentregister when loaded.
initComponentCache: {
modalMask: { component: MmodalMask },
},
Component writing
Component needs to export an object generated by componentitemfactory.
const MButton = new ComponentItemFactory(
'button',
'按钮',
{
style: [
createPannelOptions<FormMap, 'input'>('input', {
receive: 'text',
label: '文字',
}),
],
animate: [createPannelOptions<FormMap, 'animateControl'>('animateControl', {})],
actions: [createPannelOptions<FormMap, 'actionButton'>('actionButton', {})],
},
{
props: {
...
text:'yehuozhili'// input配置项组件接收的初始值
},
},
(data, context, store, config) => {
return <ButtonTemp data={data} store={store} context={context} config={config}></ButtonTemp>;
},
true
);
export default MButton;
The first parameter is the component registration name, and the second parameter is used to display the usage. The third parameter is used to configure the configuration item component of the right panel. The key is the classification of the right panel, and the value is the configuration item component array. The fourth parameter will configure the initial value of the component. In particular, the manufacturing component must have the initial width and height (not supported by the content), otherwise there will be a problem when selecting all during adaptation. There are many useful properties in this initial value. For example, fixed represents fixed positioning. You can change this value in combination with configuration items so that components can be fixed positioning. Also, candrag is similar to the lock command. Locked elements cannot be dragged. The rotate in the initial value needs an object. Value represents the rotation angle and canrotate represents whether the rotation can be operated. (supported from version 0.7.0) The fifth parameter is a function. You will get the configuration from the receive attribute in the configuration item (the default configuration is receive for the time being). For example, if the received in the above example is text, the field will be received in the data in the function. Context generally has only preview and edit for environment judgment. Config can get all the data for use when making events. The sixth parameter resize is used to determine whether scaling can be performed. When it is false, scaling cannot be performed. The seventh parameter is needposition. After some components are moved into the canvas, the drop point will be adopted by default. This configuration item is true by default, that is, the position to be dragged. When it is false, the component's top and left positioning will be used to place it.
Event registration
Timing registration
As mentioned earlier, events have timing and functions, so hook can be used to register timing in the component:
useDynamicAddEventCenter(pr, `${pr.data.id}-init`, '初始渲染时机'); //注册名必须带id 约定!
useDynamicAddEventCenter(pr, `${pr.data.id}-click`, '点击执行时机');
The first parameter of usedynamicaddevencenter is an object composed of four parameters of render. The second parameter is the registered time name, which must be related to the ID, which is a convention. Otherwise, multiple components may cause name conflicts, and it is convenient to find the time. After registering the timing, you need to put the timing into the corresponding trigger position. For example, the click execution timing of this button is put into onclick:
<Button
onClick={() => {
eventCenter.runEventQueue(`${pr.data.id}-click`, pr.config);
}}
>
yehuozhili
</Button>
The first parameter is the registered time name, and the second parameter is the last parameter config in the render function
Function registration
Functions are thrown by components and can be loaded into the event chain. For example, if I register a function to change the text, I can call the function at the time of any component, so as to trigger the component to change the text. The function registration needs to be put into useeffect, and the function needs to be unloaded when the component is unloaded! Otherwise, there will be more and more functions. Note that the component ID should be taken with the component ID, because a component can drag n components to generate n functions.
useEffect(() => {
const functionCenter = eventCenter.getFunctionCenter();
const unregist = functionCenter.register(
`${pr.data.id}+改变文本函数`,
async (ctx, next, config, args, _eventList, iname) => {
const userSelect = iname.data;
const ctxVal = changeUserValue(
userSelect['改变文本数据源'],
args,
'_changeval',
config,
ctx
);
const text = ctxVal[0];
setText(text);
next();
},
[
{
name: '改变文本数据源',
data: ['ctx', 'input', 'dataSource'],
options: {
receive: '_changeval',
multi: false,
},
},
],
`${pr.data.id}+改变文本函数`
);
return () => {
unregist();
};
}, []);
See later function development for parameters and configuration in the function.
Right side panel
The configuration of the right panel is the same as that of the left panel:
export interface RightMapRenderListPropsItemCategory {
type: string;
icon: ReactNode;
custom?: boolean;
customRender?: (type: string, current: IBlockType) => ReactNode;
}
Type will affect the key name of the third parameter of the left component during development. The key name represents the type displayed on the right. Icon can put text or icons for panel switching. If custom is true, the display under this panel can be customized through customrender.
Right side assembly
Right side component import
When importing, just match the developed components into an object and put it into initformcomponents.
initFormComponents: Formmodules,
Right side component development
First, for a good development experience, you need to define a formmap type:
export interface FormBaseType {
receive?: string;
}
export interface FormInputType extends FormBaseType {
label: string;
}
export interface FormActionButtonType {}
export interface FormAnimateControlType {}
export interface FormMap {
input: FormInputType;
actionButton: FormActionButtonType;
animateControl: FormAnimateControlType;
}
The key name of formmap is the key name of initformcomponents. The value of formmap corresponds to the value that the component needs to receive. Take the input component as an example. At this time, forminputtype has two attributes, label and receive. When developing this component, props will receive:
interface MInputProps {
data: CreateOptionsRes<FormMap, 'input'>;
current: IBlockType;
config: UserConfig;
}
That is, where data is the formmap type and current is the currently clicked component, needless to say config. Remember the third parameter in component development on the left? So it's all related:
style: [
createPannelOptions<FormMap, 'input'>('input', {
receive: 'text',
label: '文字',
}),
],
The generics of the createpanneloptions function are filled with the corresponding components, which will give a good prompt to the received configuration items. All you need to do in the configuration item component is to receive the configuration item from the component, and then modify the property of current:
function MInput(props: MInputProps) {
const option = useMemo(() => {
return props.data?.option || {};
}, [props.data]);
return (
<Row style={{ padding: '10px 20px' }}>
<Col span={6} style={{ lineHeight: '30px' }}>
{(option as any)?.label || '文字'}:
</Col>
<Col span={18}>
<Input
value={props.current.props[(option as any).receive] || ''}
onChange={(e) => {
const receive = (option as any).receive;
const clonedata = deepCopy(store.getData());
const newblock = clonedata.block.map((v: IBlockType) => {
if (v.id === props.current.id) {
v.props[receive] = e.target.value;
}
return v;
});
store.setData({ ...clonedata, block: [...newblock] });
}}
></Input>
</Col>
</Row>
);
}
Because you can easily get the store, you can modify the data anywhere. Associate the value of the component with the property of current, and onchange to modify the store, thus completing a two-way binding. Note: if your right component needs attributes other than block, you may need to judge whether it is in pop-up mode. ####Import of commands The command object can be imported into the initcommandmodule of the plug-in
initCommandModule: commandModules,
Command development
The command needs to export an object generated by commanderitemfactory.
import { CommanderItemFactory } from 'dooringx-lib';
const undo = new CommanderItemFactory(
'redo',
'Control+Shift+z',
(store) => {
store.redo();
},
'redo '
);
export default undo;
The first parameter is the registration name. The second parameter is the shortcut key name, and the shortcut key mapping is the key value of the keyboard event:
Cancel: 3,
Help: 6,
Backspace: 8,
Tab: 9,
Clear: 12,
Enter: 13,
Shift: 16,
Control: 17,
Alt: 18,
Pause: 19,
CapsLock: 20,
Escape: 27,
Convert: 28,
NonConvert: 29,
Accept: 30,
ModeChange: 31,
' ': 32,
PageUp: 33,
PageDown: 34,
End: 35,
Home: 36,
ArrowLeft: 37,
ArrowUp: 38,
ArrowRight: 39,
ArrowDown: 40,
Select: 41,
Print: 42,
Execute: 43,
PrintScreen: 44,
Insert: 45,
Delete: 46,
0: 48,
')': 48,
1: 49,
'!': 49,
2: 50,
'@': 50,
3: 51,
'#': 51,
4: 52,
$: 52,
5: 53,
'%': 53,
6: 54,
'^': 54,
7: 55,
'&': 55,
8: 56,
'*': 56,
9: 57,
'(': 57,
a: 65,
A: 65,
b: 66,
B: 66,
c: 67,
C: 67,
d: 68,
D: 68,
e: 69,
E: 69,
f: 70,
F: 70,
g: 71,
G: 71,
h: 72,
H: 72,
i: 73,
I: 73,
j: 74,
J: 74,
k: 75,
K: 75,
l: 76,
L: 76,
m: 77,
M: 77,
n: 78,
N: 78,
o: 79,
O: 79,
p: 80,
P: 80,
q: 81,
Q: 81,
r: 82,
R: 82,
s: 83,
S: 83,
t: 84,
T: 84,
u: 85,
U: 85,
v: 86,
V: 86,
w: 87,
W: 87,
x: 88,
X: 88,
y: 89,
Y: 89,
z: 90,
Z: 90,
OS: 91,
ContextMenu: 93,
F1: 112,
F2: 113,
F3: 114,
F4: 115,
F5: 116,
F6: 117,
F7: 118,
F8: 119,
F9: 120,
F10: 121,
F11: 122,
F12: 123,
F13: 124,
F14: 125,
F15: 126,
F16: 127,
F17: 128,
F18: 129,
F19: 130,
F20: 131,
F21: 132,
F22: 133,
F23: 134,
F24: 135,
NumLock: 144,
ScrollLock: 145,
VolumeMute: 181,
VolumeDown: 182,
VolumeUp: 183,
';': 186,
':': 186,
'=': 187,
'+': 187,
',': 188,
'<': 188,
'-': 189,
_: 189,
'.': 190,
'>': 190,
'/': 191,
'?': 191,
'`': 192,
'~': 192,
'[': 219,
'{': 219,
'\\': 220,
'|': 220,
']': 221,
'}': 221,
"'": 222,
'"': 222,
Meta: 224,
AltGraph: 225,
Attn: 246,
CrSel: 247,
ExSel: 248,
EraseEof: 249,
Play: 250,
ZoomOut: 251,
26 English letters ignore case. At present, only one shortcut key can be registered for a command. If you don't need to register a shortcut key, fill in the blank string. Metakey is the same as controlkey. Just write control. At present, the third parameter can only obtain store, which needs to be modified later. In version 0.2.0, the second parameter can obtain config. At the same time, the commander does not export from index, but obtains it from config when it needs to be used. The last parameter is the display name.
Right click menu
The right-click menu can be customized:
//Custom right click
const contextMenuState = config.getContextMenuState();
const unmountContextMenu = contextMenuState.unmountContextMenu;
const commander = config.getCommanderRegister();
const ContextMenu = () => {
const handleclick = () => {
unmountContextMenu();
};
const forceUpdate = useState(0)[1];
contextMenuState.forceUpdate = () => {
forceUpdate((pre) => pre + 1);
};
return (
<div
style={{
left: contextMenuState.left,
top: contextMenuState.top,
position: 'fixed',
background: 'rgb(24, 23, 23)',
}}
>
<div
style={{ width: '100%' }}
onClick={() => {
commander.exec('redo');
handleclick();
}}
>
< button > Customize < / button >
</div>
</div>
);
};
contextMenuState.contextMenu = <ContextMenu></ContextMenu>;
Get the contextmenustate first. There is an unmountcontextmenu on the contextmenustate, which is the method to close the right-click menu. Therefore, you need to call close after clicking. At the same time, the left and top above are the right-click positions. In addition, you also need to add a strong brush assignment to forceupdate in the component to follow when the component moves.
Function development
Function import
The function import is made into an object and placed in initfunctionmap
initFunctionMap: functionMap,
Function development
The key name will be displayed, so the key name is unique. Its values are two objects, one is the function content FN, and the other is the configuration item config (the function name needs to be passed in for display above 0.10.0). Each configuration in the array in config will be displayed for the user to configure. Name is the display name. Data represents where to get the data. You can choose to get it from input, data source and context (CTX). In addition, there is a special pop-up window (modal). The receive in options indicates which key args will get the value from. Multi indicates whether multiple option configurations are allowed. Two functions changeuservalue and changeuservaluerecord are written in dooringx lib. The first function will make the obtained results into an array. If it is not multi, the first result will be taken. The second function will make the results into objects. For example, if the user selects Keya in the data source, the key value pair of the data source will be returned as an object. FN, the first CTX parameter represents the context. If there is a conversion function, it may need to be used (for example, the result of the first function should be exported to the following functions) The second parameter next needs to be executed after running, otherwise the event chain will not exit in the function all the time. The third parameter config can get the entire config object. The fourth parameter args is filled in by the user and will be returned according to the fields filled in options. The fifth is eventlist, which can obtain the parameters of the whole event chain. The sixth parameter Iname can get the user's choice.
General get request function:{
fn: (ctx, next, config, args, _eventList, iname) => {
Console.log (args, 'parameter x');
const userSelect = iname.data;
const urlVal = changeUserValue(
Userselect ['request URL'],
args,
'_url',
config,
ctx
); / / input datasource CTX / / datasource will get the value, and CTX will get the field on CTX
const paramSource = changeUserValueRecord(
//Settings can only be retrieved from datasource or CTX
Userselect ['request parameter'],
args,
'_origin',
config,
ctx
);
const ctxVal = changeUserValue(
Userselect ['return context field'],
args,
'_ctx',
config,
ctx
);
//Check whether the parameter exists
//All are arrays, and non multi takes the first one.
const url = urlVal[0];
if (!url) {
return next();
}
const ctxKey = ctxVal[0];
axios
.get(url, {
params: {
...paramSource,
},
})
.then((res) => {
const data = res.data;
ctx[ctxKey] = data;
next();
})
.catch((e) => {
console.log(e);
next();
});
},
config: [
{
Name: 'request URL',
data: ['dataSource', 'ctx', 'input'],
options: {
receive: '_url',
multi: false,
},
},
{
Name: 'request parameters',
data: ['dataSource', 'ctx'],
options: {
receive: '_origin',
multi: true,
},
},
{
Name: 'return context field',
data: ['input'],
options: {
receive: '_ctx',
multi: false,
},
},
],
Name: 'general get request function'
},
Timing and function loading
If necessary, generally use:
eventCenter.manualUpdateMap(cur, displayName, arr);
In the manual updatemap, the first is the time name, the second is the display name, and the third is the user's choice. After updating the event center, you also need to update the store, and the result is subject to the store.