Closed rodenp closed 5 years ago
Hi @rodenp unfortunately at the moment my answer would be the same as before. To create what you're asking you have to write a custom render logic and the only available example is from the MJML preset: https://github.com/artf/grapesjs-mjml/blob/master/src/components/index.js
Hi Artur,
Thanks for your response.
There is little documentation on rendering and this is a very important and potentially powerful means of creating cross platform applications.
I will look into the code as suggested but before that it would be helpful if you could provide some guidance on rendering and how grapesjs uses it to render to the canvas.
How would i ensure that a component renders once using the most appropriate rendering engine? I need to ensure that for example react nodes are also not rendered outside of react.
regards Peter
From: Artur Arseniev notifications@github.com Sent: Tuesday, 23 April 2019 6:29 AM To: artf/grapesjs Cc: rodenp; Mention Subject: Re: [artf/grapesjs] Integrating React components (#1970)
Hi @rodenphttps://github.com/rodenp unfortunately at the moment my answer would be the same as beforehttps://github.com/artf/grapesjs/issues/170#issuecomment-316530325. To create what you're asking you have to write a custom render logic and the only available example is from the MJML preset: https://github.com/artf/grapesjs-mjml/blob/master/src/components/index.js
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHubhttps://github.com/artf/grapesjs/issues/1970#issuecomment-485574573, or mute the threadhttps://github.com/notifications/unsubscribe-auth/ABHGD7MHYD46P2COF22GXITPRY36LANCNFSM4HHMKGRQ.
It is really useful tool. It will add more value If we can use Angular or React component
Hi,
I created a poof of concept implementation/example of the integration of a React component as a GrapesJS block/component here:
https://github.com/beepsoft/grapesjs-react-component-example
Although "integrating with React" could mean many things here's the use case I tried to solve:
For all this to work I had to implement a custom HTML parser and replace the built-in parser somehow, but the way I did is kind of hackish. @artf Could you please add a public API to be able to provide a custom HTML parser the same way as it is possible to provide a custom CSS parser?
Really cool @beepsoft especially all the explanation of the process
Could you please add a public API to be able to provide a custom HTML parser the same way as it is possible to provide a custom CSS parser?
I'll see if I'll be able to add something quickly without too much refactoring but from what I see your way is quite safe even if you're using not public methods.
Anyway, the project you've made is able to render one only component, the <Timer/>
, I hardly believe anyone will ever jump in such a journey just to add one React component. I think that we have to create a kind of generic React component renderer that will be able to display automatically any React component.
An example of what I'd expect as an API
import grapesjs from 'grapesjs';
import ReactRenderer from 'grapesjs-react-renderer';
grapesjs.init({
...
plugins: [
// this could contain the custom HTML parser, code generator, commands, etc.
ReactRenderer,
],
});
// ...
// somewhere in the code or another plugin
import ReactComponent from 'some/react/component';
import { addComponent } from 'grapesjs-react-renderer';
// ...
addComponent(editor, ReactComponent, {
name: 'Component name',
block: { // eg. add also a block
label: 'Block name',
media: '<svg ...',
},
traits: [ ... ], // Add custom traits or build them by reading the component propTypes
// ... other options
});
@artf Thanks for your feedback!
... I hardly believe anyone will ever jump in such a journey just to add one React component. I think that we have to create a kind of generic React component renderer that will be able to display automatically any React component.
I'm not sure what you mean by that. There can many blocks with a React component displayed by each, or one block could be built from many React components. Or am I wrong about that?
ReactRenderer
sounds really interesting, I just cannot imagine with my limited GrapseJS knowledge how this would work in the end.
And I know this is far fetched, but I can imagine grapedrop.com to be become something like this: https://builderx.io/
or builder without the "x" https://builder.io/
I've seen them already, the all-in-one flow provided be BuilderX is quite outstanding
ReactRenderer sounds really interesting, I just cannot imagine with my limited GrapseJS knowledge how this would work in the end.
@beepsoft
Well more or less you're there, just stick with the idea to have one-to-one relation between React and GrapesJS components (in your project you're mixing some of them, like <Timer.Days />
, <Timer.Hours />
, etc.).
This is a tiny example of what I'd expect from addComponent
import React from 'react';
import ReactDOM from 'react-dom';
import ReactComponent from 'some/react/component';
const addComponent = (editor, component, opts = {}) => {
const { id, name, tag, block } = opts;
const type = `react-${id}`;
editor.DomComponents.addType(type, {
model: {
defaults: {
tagName: tag,
// Traits should reflect React component props
traits: [ ... ],
}
},
view: {
render() {
const { model, el } = this;
const reactEl = React.createElement(
ReactComponent,
model.getAttributes(),
// TODO return children components as react elements
);
ReactDOM.render(reactEl, el);
return this;
}
}
});
block && editor.BlockManager.add(type, {
label: name || tag,
content: { type },
...block,
});
};
@artf OK, I get it. I may try to do something in that direction.
Hi @beepsoft I'm very glad to see this thread still active. How are you going with your implementation? I also see the potential of adding react components to grapesjs. I would really like to see this implemented and am wondering what it would take to get this done.
@rodenp Nothing yet, trying to find the time to do it.
@artf/grapesjsmailto:reply@reply.github.com that would be awesome. 🙏
Peter Roden
From: beep notifications@github.com Sent: Monday, November 4, 2019 9:35:10 AM To: artf/grapesjs grapesjs@noreply.github.com Cc: rodenp rodenp@hotmail.com; Mention mention@noreply.github.com Subject: Re: [artf/grapesjs] Integrating React components (#1970)
@rodenphttps://github.com/rodenp Nothing yet, trying to find the time to do it.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHubhttps://github.com/artf/grapesjs/issues/1970?email_source=notifications&email_token=ABHGD7PR2KQGZMLFKYMM3PDQR7UE5A5CNFSM4HHMKGR2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEC6UZ4I#issuecomment-549276913, or unsubscribehttps://github.com/notifications/unsubscribe-auth/ABHGD7II57I6YPTWQJ7N4ODQR7UE5ANCNFSM4HHMKGRQ.
I proceeded some with the idea to have 1:1 mapping of react components and grapesjs components.
This is still the Timer example, where now Timer.Days, Timer.Hours etc. are handled by their matching type/component added using addReactComponent
. Also added support for builtin HTML tags that appear inside react components (via ReactDefaultType
) and some hack to also have textnodes inside a react component tree.
@artf m I on the right track with these?
What I am missing and cannot figure out:
ReactDefaultType
in the Layout Manager) and have the blue line and controls around them?on()
event) where I could rerender the react component tree (unmount the current one and rerender again). What event should I subscribe to? Or is there any other lifecycle method I should override in the components?Thanks!
@artf m I on the right track with these?
yeah, definitely is what I was talking about
How can I have the individual react components really act in the editor as individual grapesjs components, ie. to be able to select them individually (the Timer.Hours or the span above it, which appear as of type ReactDefaultType in the Layout Manager) and have the blue line and controls around them?
When you hover/click any HTMLElement in the canvas, GrapesJS just tries to see if the DOM element has the component Model (this is when it happens on click) and the value is actually written in ComponentView I think the problem with React is ReactDOM.render(reactEl, el)
actually replaces el
with another element so the model value is lost and you have to reset it again.
And be careful in isComponent
because of this one
if (el.tagName == config.tagName) {
eg. with <Timer.Hours/>
you will get
if ('TIMER.HOURSE' == 'Timer.Hours') { // and obviously is false
this is how tagName works in HTML
In the Layout Manager I can move around the Timer elements, however I need a mechanism (an on() event) where I could rerender the react component tree (unmount the current one and rerender again). What event should I subscribe to? Or is there any other lifecycle method I should override in the components?
You should be able listen to children changes (add and remove) in this way
componentModel.components().on('add remove', () => console.log('changed'))
Hi @artf, I am also trying to render some React components through GrapesJS blocks. In the documentation (https://grapesjs.com/docs/modules/Components.html#tips) it is mentioned that
By default, GrapesJS understands objects generated from React JSX preset, so, if you're working in the React app probably you're already using JSX and you don't need to do anything else, your environment is already configured to parse JSX in javascript files.
..does that mean "in a React app"?
and then a method that shows a possible lowercase component (probably a JSX/React component)
editor.addComponents(<div>
<custom-component data-gjs-prop="someValue" title="foo">
Hello!
</custom-component>
</div>);
I am confused regarding the conversation in this thread as in the docs it says it is already 'possible'?
How would we go about importing that
Best,
I just saw @beepsoft implementation and the Timer renders fine. Maybe we could work out simple boilerplate steps to implement another simpler component (such as react-card-preview)?
Ok, I added react-card-preview in the dependencies of @beepsoft project and duplicated the 'timer' folder with a 'card' folder, replaced all the plugin references and I can render the component! Here is the react-card-preview (as you can see I was quite happy)
Thanks for this @beepsoft !!
Fantastic work @beepsoft Integrating React properly into the core would be a great step and could make way for the support of other frameworks. This will raise the bar for grapesjs to another level.
@rodenp Thanks! These are just toy examples for now but at least they show that it is doable.
Yet another JSX editor/generator in town: https://blocks-ui.com/
I just had a quick look. It's very very basic. I'm also discovering new things like grommet -> https://v2.grommet.io/ which is backed by HP. Grommet has a visual designer, which looks very promising. Again it is under development with bugs and stuff. I'd really like to find a smart intuitive drag and drop (react) editor where i can just drag components without having to constantly adjust the properties. Something that can handle pseudo absolute positioning nicely.
There is a guy from Discord who showed how he reached a good point of React component integration in GrapesJS https://codesandbox.io/s/immutable-river-f43bt He said he'll try to polish it and bring this to a more stable state. I've invited him to post here, I think he got the point, hope to hear more from him (it's also a really good starting point for others)
Wow, it looks cool and it's neat he "hacked" toHtml
with the React tag name instead of overriding the HTML parser. It is much cleaner this way :-)
2 things are missing, I think:
toHTML()
time?And I didn't know about the Discord channel, going to join.
@beepsoft you can extend getAttrToHTML
for that case and replace class
with className
Hey guys. I'm the author of that code sandbox. Regarding CSS, there are several css-in-js approaches. I managed to get material-ui and styled-jsx working (Although I had to patch styled-jsx, but it was upstramed)
Can you get me examples of what is css solution is not working right now?
Hi @emilsedgh,
Can you get me examples of what is css solution is not working right now?
I just tried to use GrapesJS's CSS panel to set some css values on your react component and that didn't work. I could only set a css class, see screenshot below. I don't really know, though, whether general css editing should work or not out of the box.
Do you maybe have a newer example of your react integration with GrapesJS's CSS panel working on your components?
Oh. The CSS panel is not supposed to work on all react components as most react components simply don't accept a css style or anything.
Of course, if the react component you're writing does accept CSS input as one of it's props, then I suppose we can enable it for that.
My approach does support passing Grapes Attributes as React props. For example in the example you can see that you can change outline of an MUI component.
The base component automatically translates Grapes attributes to React props. Therefore all you have to do to get more editing options is to define attributes and then define the traits:
editor.DomComponents.addType("MuiButton", {
model: {
...model,
attributes: {
color: "primary", // These are the attributes that will be sent over to
variant: "contained" // the React component with their default values.
},
defaults: {
component: Button, // This is the React Component
stylable: false, // If your React component does support styling, you can just set this to true.
editable: true,
traits: [ // Defining traits will cause UI elements to allow manipulating it
{
type: "select",
label: "Variant",
name: "variant",
options: [
{ value: "contained", name: "Contained" },
{ value: "outlined", name: "Outlined" }
]
},
{
type: "checkbox",
label: "Disabled",
name: "disabled"
},
{
type: "select",
label: "Color",
name: "color",
options: [
{ value: "primary", name: "Primary" },
{ value: "secondary", name: "Secondary" }
]
}
]
}
},
view,
isComponent: el => el.tagName === "MUIBUTTON"
});
}
There is a guy from Discord who showed how he reached a good point of React component integration in GrapesJS https://codesandbox.io/s/immutable-river-f43bt
@emilsedgh, @artf this sandbox doesn't work anymore. Do you have any idea what went wrong?
+1 for the sandbox
I am trying to integrate "reactstrap" with Grapejs but without luck. I tried following the example of @beepsoft but i am stuck at one place. I can render a React component (in view using "Reactdom.render") and created jsx/html parser as @beepsoft explained. All this is working for simple React components such as "Button" etc.
Challenge is when you need nested components such as "Layout".
Am I thinking it correctly. Has someone figured this out? I am struggling with this for many days now and this will be of great help
@emilsedgh codepen is not working so not able to figure that part out...
I don't know exactly why the sandbox provided by @emilsedgh stopped working but I've tried to refactor it slightly and now it seems to work properly https://codesandbox.io/s/grapesjs-react-components-n6sff For sure there is still a bunch of stuff to improve, but for anyone who wants to explore the integration of react components, that is the proper way.
Thanks @artf, it works great! 🎉
Hi @artf, thanks for sharing this approach to render react components. However we are figuring out on one thing.
Based on some actions by user within the react component, need to change the attribute passed to that react component. Any pointers there?
Nice work @artf of integration with React!!! 🎉 🎉 However I am wondering how it can be passed to the compilers such as next.js to generate the final static sites?? (as a static site generator)
Once again, in you Listing component, it's like a black box. How to expose its 3 child div to the Layer manager, canvas, and style manager so that we can edit their styles?? I wonder it requires a lot of workload......
Hey,
I know this issue is closed, but I'm trying to use GrapesJS with Mui v5, and it seems there was some refactor and deprecation.. so the sandbox does not work anymore.. StylesProvider & JSS have been deprecated and I don't know how to fix the createReactEl function. If you have any idea or already did it somewhere, I'd love to hear from it! Thanks for your help!
Hi! @artf this is best demo for react components https://codesandbox.io/s/grapesjs-react-components-n6sff, but i have a small question
I trying to create a react text component. When i click on text, default rich rext editor not showing. What to do?
Hello @artf! hope you doing well. Is this https://codesandbox.io/s/grapesjs-react-components-n6sff also possible using vuejs?
I am trying to integrate "reactstrap" with Grapejs but without luck. I tried following the example of @beepsoft but i am stuck at one place. I can render a React component (in view using "Reactdom.render") and created jsx/html parser as @beepsoft explained. All this is working for simple React components such as "Button" etc.
Challenge is when you need nested components such as "Layout".
- I want to drag "Layout component) - working fine as explained below
- Then, I want to drag another component inside "Layout" component. There does not seem to any way to drop a component inside another React component (as react components are only in view and not in model).
Am I thinking it correctly. Has someone figured this out? I am struggling with this for many days now and this will be of great help
@emilsedgh codepen is not working so not able to figure that part out...
@megarg Can I see the code you wrote for jsx to html? Did you rewrite ToHTML()?
Thank you very much!
Hey,
I know this issue is closed, but I'm trying to use GrapesJS with Mui v5, and it seems there was some refactor and deprecation.. so the sandbox does not work anymore.. StylesProvider & JSS have been deprecated and I don't know how to fix the createReactEl function. If you have any idea or already did it somewhere, I'd love to hear from it! Thanks for your help!
@dmitrysurkin I am facing the same problem, were you able to find some solution for it so far?
Hi @artf , I am Trying to add custom tabs and accordian in grapejs but when I want to add multiple tabs or accordian they have same id which is statically defined. is there any way to update statically define id with dynamic id when I drag and drop my custom block to canvas ? I unable to update content in table cells. is There any possible way for changing content of table ?
hi, @dmitrysurkin i am using same example like https://codesandbox.io/s/grapesjs-react-components-n6sff but i am not getting the proper html instead i got: <body><DigitalSignature mlsid="Default MLSID" editable="" id="ieyp"></DigitalSignature></body>
Here is my code:
import DigitalSignature from "./DigitalSignature";
export default (editor) => {
editor.Components.addType("DigitalSignature", {
extend: "react-component",
model: {
defaults: {
component: DigitalSignature,
stylable: true,
resizable: true,
editable: true,
draggable: true,
droppable: true,
attributes: {
mlsid: "Default MLSID",
editable: true,
},
traits: [
{
type: "number",
label: "MLS ID",
name: "mlsid",
},
],
},
// Define the HTML exporter for the component
export: function () {
const imageUrl = this.getImageUrl();
return `<img src="${imageUrl}"/>`;
},
// Define a helper method to get the image URL
getImageUrl: function () {
// Get the trimmed canvas image as data URL
const dataUrl = sigCanvas.current
.getTrimmedCanvas()
.toDataURL("image/png");
return dataUrl;
},
},
isComponent: (el) => el.tagName === "DIGITALSIGNATURE",
});
editor.BlockManager.add("DigitalSignature", {
label: "<div class='gjs-fonts gjs-f-b1'>Signature</div>",
category: "Form",
content: `<DigitalSignature />`,
});
};
Editor Code:
import React, { useEffect, useState } from "react";
import * as ReactDOM from "react-dom";
import { useLocation } from "react-router-dom";
import { useDispatch, useSelector } from "react-redux";
import grapesjs from "grapesjs";
import gjsBlockBasic from "grapesjs-blocks-basic";
import "grapesjs/dist/css/grapes.min.css";
import { postThunk } from "../redux/GPPDSlice";
import { setResponseV2 } from "../redux/layoutSlice";
import SocialModal from "../models/SocialModal";
import BaseReactComponent from "./base-react-component";
import ReactComponents from "./react-components";
function ProposalEditor() {
// Hooks
const dispatch = useDispatch();
const { state } = useLocation();
const { user } = useSelector((State) => State.user);
const {
fetchedData: {
data: { proposal },
},
} = useSelector((State) => State.GPPD);
const [OTPModal, setOTPModal] = useState(false);
const [editor, setEditor] = useState(null);
const [runOnce, setRunOnce] = useState(true);
useEffect(() => {
if (runOnce) {
const editor = grapesjs.init({
container: "#gjs",
width: "auto",
storageManager: false,
fromElement: true,
plugins: [gjsBlockBasic, BaseReactComponent, ReactComponents],
pluginsOpts: {
gjsBlockBasic: {},
},
});
setEditor(editor);
}
setRunOnce(false);
}, []);
useEffect(() => {
console.log("QQQQQQQQQQQQQQQQQQQQQQQQQAA", editor?.getHtml());
// Add some more blocks
editor?.BlockManager.add("my-button", {
id: "my-button",
label: "Button",
category: "Form",
media: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M22 9c0-.6-.5-1-1.3-1H3.4C2.5 8 2 8.4 2 9v6c0 .6.5 1 1.3 1h17.4c.8 0 1.3-.4 1.3-1V9zm-1 6H3V9h18v6z"></path><path d="M4 11.5h16v1H4z"></path></svg>`,
content: {
type: "button",
content: '<button class="btn">Button</button>',
style: {
padding: "10px",
border: "1px solid #ccc",
},
},
});
editor?.BlockManager.add("my-page-break", {
id: "my-page-break",
label: "Page Break",
category: "Form",
media: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M5 5h14v2h-2v10h2v2H5v-2h2V7H5z"/>
<path d="M7 7h10v10H7z"/>
</svg>
`,
content: {
// type: "text",
content:
'<div class="page-break">----------------Page Break----------------</div>',
style: {
"text-align": "center",
},
},
});
editor?.BlockManager.add("my-checkbox", {
id: "my-checkbox",
label: "Checkbox",
category: "Form",
media: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<rect x="3" y="3" width="18" height="18" rx="2" ry="2" fill="none" stroke="currentColor" stroke-width="2"/>
<path d="M9 13l2 2 4-4"/>
</svg>
`,
content: {
type: "checkbox",
content: '<input type="checkbox" class="my-checkbox" />',
},
});
// Load the page
editor?.setComponents(proposal?.proposalPage.pageHtmlData);
editor?.setStyle(proposal?.proposalPage.pageCssData);
// Remove multiple blocks
const blocksToRemove = ["link", "map", "column3-7"];
blocksToRemove.forEach((blockId) => {
editor?.BlockManager.remove(blockId);
});
// Styling the convas
const canvas = editor?.Canvas.getElement();
if (canvas) {
canvas.style.height = "90vh";
canvas.style.width = "65%";
canvas.style.backgroundColor = "red !important";
canvas.style.border = "1px solid black";
canvas.style.margin = "30px 90px";
}
// Add a save button to the editor
const saveButton = document.createElement("button");
saveButton.textContent = "Save & Publish";
saveButton.addEventListener("click", () => {
const pageHtmlData = editor?.getHtml();
const pageCssData = editor?.getCss();
savePage(pageHtmlData, pageCssData, `/addProposal/${user?._id}`);
});
editor?.Panels.addButton("options", {
id: "save",
className: "saveAndPublish",
el: saveButton,
});
}, [editor]);
// Function to save the page data to the server
const savePage = (pageHtmlData, pageCssData, api_url) => {
console.log("WWWWWWWWWWWW", pageHtmlData);
dispatch(
postThunk({
proposalName: state?.proposalName,
projectName: state?.projectName,
proposalPage: { pageHtmlData, pageCssData },
api_url,
})
)
.then((res) => {
if (res?.payload?.data?.status == 200) {
setOTPModal(true);
} else if (res?.payload?.response.data != 200) {
dispatch(setResponseV2(res.payload.response.data));
}
})
.catch((err) => {
console.log("Fornt end server crash", err);
});
};
return (
<React.Fragment>
<div id="gjs">
<h1>Hello World Component!!!!!!!!!!!!</h1>
</div>
<SocialModal displayModal={OTPModal} setDisplayModal={setOTPModal} />
</React.Fragment>
);
}
export default ProposalEditor;
Anybody made this working ?
https://codesandbox.io/s/grapesjs-react-components-n6sff is a properly working example that integrates React (as well as Material UI) inside Grapes.
hi, thank you for this awesome library. I am trying to integrate Prime React component and I tried all the ways that are mentioned in this thread however I didn't got any success yet. Please can anyone help me?
Thanks in advance.
Hey, I know this issue is closed, but I'm trying to use GrapesJS with Mui v5, and it seems there was some refactor and deprecation.. so the sandbox does not work anymore.. StylesProvider & JSS have been deprecated and I don't know how to fix the createReactEl function. If you have any idea or already did it somewhere, I'd love to hear from it! Thanks for your help!
@dmitrysurkin I am facing the same problem, were you able to find some solution for it so far?
hey @AylanBoscarino facing the same issue right now, have u found a workaround yet?
Firstly what a great tool you have here.
I'm sorry if i ask questions that have been asked before. I have tried to read through the documentation and googled posts but am still a bit stuck.
I am like a few people trying to integrate React components into grapesjs.
https://github.com/artf/grapesjs/issues/170 mentions custom render logic. How would i implement custom render logic?
and i found this -> https://www.npmjs.com/package/grapesjs-react which does not contain any actual draggable components that i could see.
Are there any examples that actually have a sample react component that can be dragged onto the canvas?
Can you outline the areas that will need to be addressed when integrating react components.
Thanks again for an awesome project.