├── .babelrc ├── README.md ├── editor.env ├── lib └── EmailEditor.js ├── package-lock.json ├── package.json ├── public ├── email_logo.png └── email_template.png ├── src └── EmailEditor.js └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["react", "env", "stage-0"] 3 | } 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React-Email-Editor 2 | 3 | A [React.js](https://reactjs.org/) wrapper component around [Email-Editor](https://github.com/ravenappdev/email-editor). It is built on [craft.js](https://craft.js.org/) that provides a drag-n-drop system and handles the way user components should be rendered, updated, and moved. It loads in an iframe and can be embedded directly in your application. 4 | 5 | ![Optional Text](public/email_template.png) 6 | 7 | ## Live Demo 8 | 9 | Checkout the demo app here - https://email-editor-demo-blue.vercel.app/ 10 | 11 | ## Installation 12 | 13 | To use the React Email Editor, install it from NPM and include it in your own React build process. 14 | 15 | ``` 16 | npm i raven-react-email-editor 17 | ``` 18 | 19 | ## Example Usage 20 | 21 | ``` 22 | import React, { useState, useCallback, useRef } from "react"; 23 | import EmailEditor from "raven-react-email-editor"; 24 | 25 | const myStyle = { 26 | div: { 27 | height: "100vh", 28 | }, 29 | nav: { 30 | height: "8%", 31 | borderBottom: "1px solid #a39f9f", 32 | }, 33 | button: { 34 | float: "right", 35 | margin: "10px 20px 10px 10px", 36 | padding: "10px 40px", 37 | color: "white", 38 | border: "1px solid rgba(88, 80, 236, 0.5)", 39 | fontSize: "0.875rem", 40 | backgroundColor: "#5850EC", 41 | borderRadius: "4px", 42 | cursor: "pointer", 43 | }, 44 | editor: { 45 | height: "91.9%", 46 | }, 47 | }; 48 | 49 | function App() { 50 | const [savedState, setSavedState] = useState({ state: "", html: "" }); 51 | const [isLoaded, setIsLoaded] = useState(false); 52 | const editorRef = useRef(null); 53 | const onLoad = () => { 54 | setIsLoaded(true); 55 | }; 56 | 57 | const onFetched = useCallback((state, html) => { 58 | setSavedState((prevState) => ({ 59 | ...prevState, 60 | state: state, 61 | html: html, 62 | })); 63 | setIsLoaded(true); 64 | }, []); 65 | 66 | const onEditorSave = useCallback(() => { 67 | if (isLoaded) { 68 | setIsLoaded(false); 69 | editorRef.current.fetchState(); 70 | } 71 | }, [isLoaded]); 72 | 73 | return ( 74 |
75 | 80 |
81 | 87 |
88 |
89 | ); 90 | } 91 | 92 | export default App; 93 | 94 | 95 | ``` 96 | 97 | ### Props 98 | 99 | | **props** | **description** | 100 | | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------- | 101 | | **onEditorLoad** | callback when editor has loaded completely. Params - empty | 102 | | **onFetched** | callback that gives the state of editor and html of the email. Params - state, html | 103 | | **state** | describes the editor's state. pass a state you saved earlier to load an already designed email. pass empty to load an empty editor. | 104 | 105 | ## License 106 | 107 | MIT Licensed 108 | -------------------------------------------------------------------------------- /editor.env: -------------------------------------------------------------------------------- 1 | DEFAULT_HOST = https://editor.ravenapp.dev/ -------------------------------------------------------------------------------- /lib/EmailEditor.js: -------------------------------------------------------------------------------- 1 | module.exports=function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}return r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=0)}([function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n,o=r(1),a=(n=o)&&n.__esModule?n:{default:n};var i=null;var u=(0,o.forwardRef)((function(e,t){e.className;var r=e.state,n=e.onEditorLoad,u=e.onFetched,d=e.editorHostUrl,s=void 0===d?"https://editor.ravenapp.dev/":d;!function(e,t){var r={};for(var n in e)t.indexOf(n)>=0||Object.prototype.hasOwnProperty.call(e,n)&&(r[n]=e[n])}(e,["className","state","onEditorLoad","onFetched","editorHostUrl"]),console.log(s);var l=(0,o.useCallback)((function(e){if(s.includes(e.origin))switch(e.data.message){case"editorLoaded":n();break;case"savedState":var t=e.data.value;u(t.state,t.html)}}),[n,u,s]);return(0,o.useEffect)((function(){window.removeEventListener("message",i),i=l,window.addEventListener("message",l)}),[l]),(0,o.useImperativeHandle)(t,(function(){return{fetchState:function(){window.frames.emailEditor.postMessage({message:"fetchState",value:!0},s)}}})),a.default.createElement("iframe",{title:"my-editor",ref:t,name:"emailEditor",frameBorder:"0",marginWidth:"0",marginHeight:"0",width:"100%",height:"100%",onLoad:function(){window.frames.emailEditor.postMessage({message:"loadEditor",value:r},s)},src:s})}));t.default=u},function(e,t){e.exports=require("react")}]); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "raven-react-email-editor", 3 | "version": "1.0.6", 4 | "description": "Raven's email editor component for React.js", 5 | "main": "./lib/EmailEditor.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/ravenappdev/email-editor" 9 | }, 10 | "scripts": { 11 | "build": "webpack" 12 | }, 13 | "author": "", 14 | "license": "MIT", 15 | "devDependencies": { 16 | "babel-core": "^6.21.0", 17 | "babel-loader": "^7.1.5", 18 | "babel-preset-env": "^1.6.1", 19 | "babel-preset-react": "^6.16.0", 20 | "babel-preset-stage-0": "^6.24.1", 21 | "css-loader": "^3.5.1", 22 | "dotenv-webpack": "^7.0.3", 23 | "path": "^0.12.7", 24 | "prop-types": "^15.6.0", 25 | "react": "^16.14.0", 26 | "react-dom": "^16.0.0", 27 | "style-loader": "^1.1.3", 28 | "webpack": "^4.46.0", 29 | "webpack-cli": "^3.3.12" 30 | }, 31 | "dependencies": { 32 | "@babel/core": "^7.16.0", 33 | "@babel/preset-env": "^7.16.4", 34 | "@babel/preset-react": "^7.16.0", 35 | "@babel/preset-stage-0": "^7.8.3" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /public/email_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ravenappdev/react-email-editor/1e9cc74661ca48bc5373ce6cc04efbe909ffb117/public/email_logo.png -------------------------------------------------------------------------------- /public/email_template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ravenappdev/react-email-editor/1e9cc74661ca48bc5373ce6cc04efbe909ffb117/public/email_template.png -------------------------------------------------------------------------------- /src/EmailEditor.js: -------------------------------------------------------------------------------- 1 | import React, { 2 | forwardRef, 3 | useCallback, 4 | useEffect, 5 | useImperativeHandle, 6 | } from "react"; 7 | 8 | var prevCallback = null; 9 | const DEFAULT_HOST = process.env.DEFAULT_HOST; 10 | function EmailEditor( 11 | { 12 | className, 13 | state, 14 | onEditorLoad, 15 | onFetched, 16 | editorHostUrl = DEFAULT_HOST, 17 | ...rest 18 | }, 19 | ref 20 | ) { 21 | console.log(editorHostUrl); 22 | const receiveMessage = useCallback( 23 | (event) => { 24 | //TO FIX: repeat calls to receive messge 25 | if (!editorHostUrl.includes(event.origin)) return; 26 | const message = event.data.message; 27 | switch (message) { 28 | case "editorLoaded": 29 | onEditorLoad(); 30 | break; 31 | case "savedState": 32 | var obj = event.data.value; 33 | onFetched(obj["state"], obj["html"]); 34 | break; 35 | default: 36 | } 37 | }, 38 | [onEditorLoad, onFetched, editorHostUrl] 39 | ); 40 | useEffect(() => { 41 | window.removeEventListener("message", prevCallback); 42 | prevCallback = receiveMessage; 43 | 44 | window.addEventListener("message", receiveMessage); 45 | }, [receiveMessage]); 46 | 47 | useImperativeHandle(ref, () => ({ 48 | fetchState() { 49 | window.frames["emailEditor"].postMessage( 50 | { message: "fetchState", value: true }, 51 | editorHostUrl 52 | ); 53 | }, 54 | })); 55 | 56 | const onLoad = () => { 57 | window.frames["emailEditor"].postMessage( 58 | { message: "loadEditor", value: state }, 59 | editorHostUrl 60 | ); 61 | // window.removeEventListener("beforeunload", onPageUnload); 62 | // window.addEventListener("beforeunload", onPageUnload); 63 | }; 64 | 65 | return ( 66 |