Giter Site home page Giter Site logo

react-plugin's Introduction

Rete.js React plugin

Made in Ukraine Discord

Rete.js plugin

Key features

  • Render elements: visualize an elements such as nodes and connections using React.js components
  • Customization: modify appearance and behavior for a personalized workflow
  • Presets: predefined React.js components for different types of features
    • Classic: provides a classic visualization of nodes, connections, and controls
    • Context menu: provides a classic appearance for rete-context-menu-plugin
    • Minimap: provides a classic appearance for rete-minimap-plugin
    • Reroute: provides a classic appearance for rete-connection-reroute-plugin

Getting Started

Please refer to the guide and example using this plugin

Contribution

Please refer to the Contribution guide

License

MIT

react-plugin's People

Contributors

alexnix avatar dependabot[bot] avatar ni55an avatar rete-js[bot] avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

react-plugin's Issues

How to update an controller?

I want to create a node that previews previous values using react, rete.js, and Chart JS.

But change of props or call to update method is not refreshing the component.

How can I fix that?

Relevant code below or codesandbox link with boilerplate

class ChartController extends Rete.Control {
    static component = ({ data }) => {
        console.log("Rerender")
        return <Line data={data} ></Line>
    }

    addValue(value) {
        this.props.data.datasets[0].data.push({ y: value, x: new Date().getTime() })
        this.update()
    }

    constructor(key) {
        super(key)
        this.render = 'react';
        this.component = ChartController.component
        this.props = {
            data: {
                "datasets": [
                    {
                        "fill": true,
                        "data": [{ "y": 0, "x": 1646927922557 }, { "y": 0, "x": 1646927926815 }, { "y": 1, "x": 1646927927804 }, { "y": 2, "x": 1646927927983 }, { "y": 3, "x": 1646927928133 }, { "y": 4, "x": 1646927928287 }, { "y": 5, "x": 1646927928429 }, { "y": 6, "x": 1646927928580 }, { "y": 7, "x": 1646927928726 }, { "y": 8, "x": 1646927928855 }, { "y": 9, "x": 1646927929022 }, { "y": 10, "x": 1646927929142 }],
                        "backgroundColor": "rgba(255, 99, 132, 0.2)",
                        "borderColor": "rgba(255, 99, 132, 1)",
                        "borderWidth": 1
                    }],
            }
        }
    }
}

export class Preview extends Rete.Component {
    constructor() {
        super("Preview")
    }

    builder(node) {
        this.node = node
        this.max_size = 1_000
        this.preview = new ChartController('preview')

        return node
            .addInput(new Rete.Input('num', "Input", numSocket, false))
            .addOutput(new Rete.Output('num', "Output", numSocket, false))
            .addControl(this.preview)
    }

    worker(_node, inputs, outputs) {
        const base = inputs['num']
        const val = (base.length == 0 || typeof base[0] != 'number') ? 0 : base[0]
        this.preview.addValue(val)
        outputs['num'] = val
        return outputs
    }
}

Option to not insert styles with default css classes into global app css scope

Couple issues on how current implementation inserts CSS.

  1. If there is anything else on the app which has classname "node" or any other default classes, it will conflict. CSS scope in plugin's declaration are all global and forced to be inserted into the app. If want to have couple of rete instances on same app with different visuals, again both will conflict.

  2. If want to have custom theme, this css is forced to stay in app code now

I believe there should be an option if user would like or not to have the default CSS, the default option could be to insert these styles into app, but plugin should allow option to override this behaviour. To not insert CSS at all automatically and/or overwrite the css string.

image

image

Clicking on Node rerenders all the Nodes in app.

There is one problem with following code. When you select the node, it will rerender all the nodes created with the help of React. It causes lots of issues including unnecessary recalculations and updates inside the node. E.g. I use dropdown as my control. Each time the dropdown gets clicked all nodes get rerendered and my dropdown closes cause its state resets. Can we have some other logic for applying className, cause if I correctly understand, this is the main reason this method was implemented.
image

Changing the shape of node

I want to change the shape of node to any logic gate like AND gate OR gate etc. Is there any way to do that?

Passing a `styles` prop to Presets.classic.Node results in "Invalid value for prop" error

As the title says, passing a styles prop to Presets.classic.Node results in the following warning:

styled-components: it looks like an unknown prop "styles" is being sent through to the DOM, which will likely trigger a React console error. If you would like automatic filtering of unknown props, you can opt-into that behavior via `<StyleSheetManager shouldForwardProp={...}>` (connect an API like `@emotion/is-prop-valid`) or consider using transient props (`$` prefix for automatic filtering.)

and the following error (which for some amusing reason chooses to call itself a "Warning":

Warning: Invalid value for prop `styles` on <div> tag. Either remove it from the element, or pass a string or number value to keep it in the DOM. For details, see https://reactjs.org/link/attribute-behavior 

Example code which causes this:

const myStyle = css`
    background: red;
`;

export function StyledNode(props: {
    data: Schemes['Node'],
    emit: (props: ReactArea2D<Schemes>) => void;
}) {
    return <Presets.classic.Node styles={() => myStyle} {...props} />;
}

From what I've seen, the solution would be to use styled-components' transient props, by simply prefixing styles with $ in Node. I assume all the components under src/presets/classic/components that use the styles prop also suffer from this.

import * as React from 'react' seems unnecessary

the import * as React from 'react' in Node.tsx, Control.tsx RefControl.tsx ... seems unnecessary.

when I put react-plugin src directory to node_modules\rete-react-plugin\ to make it easy to navigator to typescript source code, pnpm tauri build give err.

Clicking React-based controls results in dragging the node

You can see this in the Example linked from the README. In Chrome, clicking anywhere on a React-based input and dragging (as though attempting to drag-select the text, for instance) results in dragging the node rather than selecting the text. Likewise, clicking on the up/down arrows that appear at the right of the inputs and dragging also results in dragging the node. (Interestingly, in Firefox, clicking in the input itself does not drag the node, but clicking in the arrows does.)

I had hoped that there would be a simple fix along the lines of Call preventDefault() in pointerDown() in the connection-plugin but it turns out that the React component receives the event after it has already been handled by the rete node. As explained in Mixing React and DOM events, React event handlers are attached to the document object and so events only bubble up to React's handlers after passing through all other objects, including Rete nodes. From the same document:

What if you only want to let React handlers get called before your native handler? Since event handlers are called in order of registration, the answer is to listen on the document object (or window, depending on the event).

The workaround that I've come up with is to change the Drag class in the rete repository as shown below. The essence of it is to attach the pointerdown handler to the window rather than to the rete node. This is consistent with the way the pointermove and pointerup events are already handled in the same class. The handler then must determine from the target whether the event occurred on a rete node or on the rete container. To facilitate this, the rete-node class is added to rete node elements.

drag.ts

initEvents() {
    this.el.style.touchAction = 'none';

    const destroyDown = listenWindow('pointerdown', this.down.bind(this));
    const destroyMove = listenWindow('pointermove', this.move.bind(this));
    const destroyUp = listenWindow('pointerup', this.up.bind(this));

    return () => { destroyDown(); destroyMove(); destroyUp(); }
}

down(e: PointerEvent) {
    let el: Element | null = e.target as Element;
    while (el) {
        if (el === this.el) {
            e.stopPropagation();
            this.pointerStart = [e.pageX, e.pageY]

            this.onStart(e);
            break;
        }
        else if (el.classList.contains('rete-node')) {
            // don't propagate past node to container
            e.stopPropagation();
            break;
        }
        el = el.parentElement;
    }
}

node.ts

constructor(node: Node, component: Component, emitter: Emitter<EventsTypes>) {
    super(emitter);

    this.node = node;
    this.component = component;
    this.el = document.createElement('div');
    this.el.classList.add('rete-node');
    this.el.style.position = 'absolute';

    ...

With these changes in place, the React-based controls can then call stopPropagation() for pointerdown events to prevent them from propagating to the node, which prevents the node from initiating the drag.

I am submitting this as an issue on this repository rather than as a PR on the rete repository because the issue is specific to the use of the react-render-plugin -- I don't know enough about Vue's event-handling model to know if it has a similar issue -- and because you may have an alternative solution in mind. If so, or if you have other thoughts or suggestions, let me know and I can give it a try. That said, if you think this approach has merit I can certainly submit it as a rete PR upon request.

Conventional React Pattern

Hi,

Struggling to integrate this with a project- I'm not really sure how to debug because there is so much customization even for simple examples (e.g. importing the connection plugin & react render plugin). I recommend creating a repository with a conventional react component pattern. It would probably look like the following...

import ReteComponent from "react-rete"

export const MyApp = () => {
  return (
    <ReteComponent
      plugins={[/* ... */]}
      components={[/* ... */]}
      graph={{
        "1": { /* ... */ },
        "2": { /* ... */ },
      }}
      onChange={(newGraph, output) => console.log(output)}
    />
  )
} 

I think something like that would increase adoption of this framework. It probably shouldn't be in this repository but rather one named react-rete.

Connection link UI miss align

Hi,
I'm using the react render plugin. While connecting two components, At the time of linking the connection line is draw somewhere its miss aligning li in this example
https://codesandbox.io/s/retejs-react-render-t899c. Here you can add new component by cloning and try to connect the node. Then you can see the lines are drawing between some where.
. After finishing the connection only its correctly aligned. How to fix the issue? Thanks in advance :)

Loading With Initial Data And Updating On Change

Hi there,

Thanks for writing this plugin, it's really appreciated!

I have hit a bit of a snag and I've tried a number of ways to get around it with no luck.

Basically, I want to load the editor with some initial data and then as the editor changes, I want to track the state. This is so I can save the data to a DB and load it up again later.

The issue is, if I initialise the editor with the state and then update the state as the editor changes, I end up in an infinite loop.

I have put an example together (based on your code).
If you take a look in the example.js file, if you were to uncomment the setState in the onChange function you can see what I mean.

Code is here:
https://codesandbox.io/s/retejs-react-render-gpg9j?fontsize=14

I would be really grateful for some hints, this is a the final blocker I have in a project I am working on.
Thanks!

Custom control data binding

I am struggling to get this one work. I am trying to implement a slider in the node using MaterialUI. The node looks fine and the slider works fine but I can't see the data updated in the node state.
This is my control

import React from 'react';
import Rete from 'rete';

import {Slider, Grid} from '@material-ui/core';

function ContinuosSlider(v){
    const ref = React.useRef();
    React.useEffect(() => {
      ref.current = value;
    });     

    const [value, setValue] = React.useState(ref.current);

    const handleChange = (event, newValue) => {
        setValue(newValue);
      };     

    return <>
        <Grid container>
            <Grid item xs>
                <Slider step={0.001} value={value} onChange={handleChange} onPointerMove={e => e.stopPropagation()}></Slider>
            </Grid>
            <Grid item>
                {value}
            </Grid>
        </Grid>
    </>

}

export class DummyControl extends Rete.Control {
    constructor(emitter, key, name, node) {
      super(key);
      this.render = 'react';
      this.component = ContinuosSlider;
      this.props = { emitter, name };

      const initial = node.data[key] || 0;

      this.props = {
        value: initial,
        onChange: v => {
          this.setValue(v);
          this.emitter.trigger("process");
        }
      };
    }

    setValue(val) {
        this.props.value = val;
        this.putData(this.key, val);
        this.update();
      }
}

And this is the components

export class DummyComponent extends Rete.Component {
    constructor(props) {
      super("Dummy");
      this.data.component = DummyNode; // optional
      this.state = {
            socket: props.socket,
        }
    }
  
    builder(node) {
      var out = new Rete.Output("num", "Number", this.state.socket);
      var ctrl = new DummyControl(this.editor, "num", 'Number', node);
  
      return node
        .addControl(ctrl)
        .addOutput(out);
    }
  
    worker(node, inputs, outputs) {
      outputs["num"] = node.data.num;
    }
  }

The node itselft is the standard code so it is not useful to post it.
What am I missing?

flushSync error in ConnectionWrapper

This is for V2, on the next branch
After spinning up a rete-kit react editor, adding or removing connections results in the following error in the console:

Warning: flushSync was called from inside a lifecycle method. React cannot flush when React is already rendering. Consider moving this call to a scheduler task or micro task.

It doesn't appear to affect the functionality, it's just annoying in the console.

This is coming from ConnectionWrapper. Should be a simple fix. One way to solve this is to wrap the flushAsyc function in a timeout. That gives react a chance to finish the render.

I don't have permission to create a PR on this repo, but I've made the following change in a local repo and the error does not appear in my built plugin.

image

TypeScript Support

Hi, I evaluated rete for one of our projects to use it eventually in a future epic.
For now, we can't to use it since rete react dose not support TypeScript.
I could use @ts-ignore every were, but that dosen't feel good.

Uses ReactDOM.Render() which is unsupported by ReactJS v18

If you force past the installation issues mentioned in #27 and #20 and use the latest ReactJS (v18.1.0 right now) then you get the warning:

Warning: ReactDOM.render is no longer supported in React 18. 
Use createRoot instead. 
Until you switch to the new API, your app will behave as if it's running React 17. 
Learn more: https://reactjs.org/link/switch-to-createroot

This is because ReactDOM.render is being used to render nodes:
https://github.com/retejs/react-render-plugin/blob/84b6db1f341ee363f735f84f47ad538366f044e2/src/index.jsx#L11)

and controls:
https://github.com/retejs/react-render-plugin/blob/84b6db1f341ee363f735f84f47ad538366f044e2/src/index.jsx#L22

We are supposed to use ReactDOM.createRoot() (https://reactjs.org/docs/react-dom-client.html#createroot) instead now.

I had a play around with swapping to using this, but React 18 uses concurrent rendering which causes some issues. I found that if you programmatically create some nodes and connect their IO (as per the example https://codesandbox.io/s/retejs-react-render-t899c) then it fails on connecting the IO because the sockets haven't been bound yet and therefore can't be found. The binding appears to happen after the render, but editor.connect() is reached before the render has completed.

I'm not too sure how to fix this without changing the core rete editor to support returning a promise from the render event handlers or something.

report warning when use react18+ with this plugin

Describe the bug

because the origin code in "renderer.ts" as : ReactDOM.render(element, getWrapper(container))
when my project with a react 18+, it will come with warning info like this uploaded picture.
maybe you can change this with the react 18+ createRoot?

Example to reproduce

No response

How to reproduce

  • create a project of react 18+ and use this plugin
  • see the info in website terminal

Expected behavior

report no warning info

Dependencies

"dependencies": {
"@ant-design/icons": "^5.3.6",
"@babel/generator": "^7.24.4",
"@babel/parser": "^7.24.4",
"@babel/traverse": "^7.24.1",
"@babel/types": "^7.24.0",
"antd": "^5.16.4",
"elkjs": "^0.8.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.23.0",
"rete": "^2.0.3",
"rete-area-plugin": "^2.0.4",
"rete-auto-arrange-plugin": "^2.0.1",
"rete-connection-plugin": "^2.0.1",
"rete-context-menu-plugin": "^2.0.2",
"rete-engine": "^2.0.1",
"rete-react-plugin": "^2.0.5",
"rete-render-utils": "^2.0.2",
"rete-scopes-plugin": "^2.0.1",
"rete-structures": "^2.0.1",
"styled-components": "^6.1.8"
},
"devDependencies": {
"@tsconfig/node20": "^20.1.4",
"@types/babel__generator": "^7.6.8",
"@types/babel__traverse": "^7.20.5",
"@types/node": "^20.12.5",
"@types/react": "^18.2.66",
"@types/react-dom": "^18.2.22",
"@typescript-eslint/eslint-plugin": "^7.2.0",
"@typescript-eslint/parser": "^7.2.0",
"@vitejs/plugin-react-swc": "^3.5.0",
"eslint": "^8.57.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.6",
"sass": "^1.75.0",
"typescript": "^5.2.2",
"vite": "^5.2.0",
"vite-plugin-node-polyfills": "^0.21.0",
"web-worker": "^1.3.0"
}

Platform

chrome 124

Relevant log output

Warning: ReactDOM.render is no longer supported in React 18. Use createRoot instead. Until you switch to the new API, your app will behave as if it's running React 17. Learn more: https://reactjs.org/link/switch-to-createroot

Code of Conduct

  • I agree to follow this project's Code of Conduct

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.