BasicControlsCustomizationPluginReact.jsContext menuMinimapReroute
This plugin uses a classic preset that includes visual components for nodes, connections, sockets, and input controls. It leverages styled-components
to design these components.
This plugin can be used in any application, regardless of your stack (React.js, Vue.js, Angular, etc.).
npm i rete-react-plugin rete-render-utils styled-components
If you're using this plugin in an application that doesn't utilize React.js, make sure to install the required React.js dependencies as well.
npm i react@18 react-dom@18
import { createRoot } from "react-dom/client";
import { AreaPlugin } from "rete-area-plugin";
import { ReactPlugin, Presets, ReactArea2D } from "rete-react-plugin";
type AreaExtra = ReactArea2D<Schemes>;
// ....
const render = new ReactPlugin<Schemes, AreaExtra>({ createRoot });
render.addPreset(Presets.classic.setup());
area.use(render);
Check out the Basic page for an example of how to use this rendering plugin.
In case you're using React.js version 16 (or 17), just remove the createRoot
method
const render = new ReactPlugin<Schemes, AreaExtra>();
When working with React app, useRete
hook eliminates the need for boilerplate code that binds an editor to HTML element. This becomes particularly crucial for dynamic app updates where the old instance of the editor must be removed and replaced with a new one.
import { useRete } from 'rete-react-plugin';
function App() {
const [ref, editor] = useRete(createEditor)
return <div ref={ref} className="rete"></div>
}
where createEditor
should return object with destroy
method (usually it has area.destroy()
call)
This plugin provides built-in controls that are displayed based on the following objects:
ClassicPreset.InputControl
as <input type="number" />
or <input type="text" />
Simply add the control to the node
node.addControl('my-control', new ClassicPreset.InputControl("number", {
initial: 0,
readonly: false,
change(value) { }
}))
If you want to add different types of controls, you can return the necessary functional component in the control
handler of customize
property.
render.addPreset(Presets.classic.setup({
customize: {
control(context) {
if (context.payload.isButton) {
return (props: { data: { isButton: true, label: string, onClick: () => void }}) => (
<button
onPointerDown={(e) => e.stopPropagation()}
onClick={props.data.onClick}
>
{props.data.label}
</button>
)
}
if (context.payload instanceof ClassicPreset.InputControl) { // don't forget to explicitly specify the built-in Presets.classic.Control
return Presets.classic.Control;
}
}
}
}));
node.addControl('my-button', { isButton: true, label: 'Click', onClick() {} })
This is a simplified version suitable for introductory purposes. For projects, it is recommended to follow the approach demonstrated in the example
Make sure to call stopPropagation
in onPointerDown
to prevent the area from intercepting events such as click
. If you are encountering this issue in React 16 or your interactive elements are added to a custom node instead of a control, try the following solution:
import { Drag } from "rete-react-plugin";
<Drag.NoDrag>
<button>
{props.data.label}
</button>
</Drag.NoDrag>
Or use a hook to avoid extra nesting
const ref = React.useRef(null)
Drag.useNoDrag(ref)
<button ref={ref}>
{props.data.label}
</button>
In a similar manner to the approach outlined above, you can replace node, connection, or socket components.
The easiest approach is to extend the current component and use styled-components
to add extra styles.
import { Presets } from "rete-react-plugin";
import { css } from "styled-components";
const myStyles = css<{ selected?: boolean }>`
background: white;
${(props) => props.selected && css`
border-color: red;
`}
`;
function StyledNode(props: { data: Schemes['Node'] }) {
return <Presets.classic.Node styles={() => myStyles} {...props} />;
}
render.addPreset(Presets.classic.setup({
customize: {
node() {
return StyledNode
}
}
}))
Implementing this will result in all your nodes using myStyles
.
You can add an extra condition to apply these styles only to specific nodes.
render.addPreset(Presets.classic.setup({
customize: {
node(context) {
if (context.payload.label === "White") {
return StyledNode;
}
return Presets.classic.Node;
}
}
}))
await editor.addNode(new ClassicPreset.Node('White'))
If you want to completely change the node structure, you can implement your own component similar to Node.tsx from the classic preset.
import { CustomNode } from './CustomNode'
render.addPreset(Presets.classic.setup({
customize: {
node() {
return CustomNode
}
}
}))
The implementation of CustomNode
is available in the CustomNode.tsx file of the Customization for React.js example.
Use Connection.tsx as a starting point from the presets/classic/components directory of the plugin's source code.
import { CustomConnection } from './CustomConnection'
render.addPreset(Presets.classic.setup({
customize: {
connection() {
return CustomConnection
}
}
}))
Use Socket.tsx as a starting point from the presets/classic/components directory of the plugin's source code.
import { CustomSocket } from './CustomSocket'
render.addPreset(Presets.classic.setup({
customize: {
socket() {
return CustomSocket
}
}
}))
As the context menu components utilize styled-components
, you can customize their styles by:
import styled from "styled-components";
const { Menu, Common, Search, Item, Subitems } = Presets.contextMenu
const CustomMenu = styled(Menu)`
width: 320px;
`
const CustomItem = styled(Item)`
background: grey;
`
render.addPreset(Presets.contextMenu.setup({
customize: {
main: () => CustomMenu,
item: () => CustomItem,
common: () => Common,
search: () => Search,
subitems: () => Subitems
}
}))
Check out the complete result on the Customization for React.js example page.