├── .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 | 
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 |
78 | );
79 | }
80 |
81 | const forwardedEditor = forwardRef(EmailEditor);
82 | export default forwardedEditor;
83 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | const Dotenv = require("dotenv-webpack");
3 | module.exports = {
4 | mode: "production",
5 | entry: "./src/EmailEditor.js",
6 | output: {
7 | path: path.resolve("lib"),
8 | filename: "EmailEditor.js",
9 | libraryTarget: "commonjs2",
10 | },
11 | plugins: [
12 | new Dotenv({
13 | path: "./editor.env",
14 | safe: true,
15 | allowEmptyValues: true,
16 | systemvars: true,
17 | silent: true,
18 | defaults: false,
19 | }),
20 | ],
21 | module: {
22 | rules: [
23 | {
24 | test: /\.js?$/,
25 | exclude: /(node_modules)/,
26 | use: "babel-loader",
27 | },
28 | {
29 | test: /\.css$/,
30 | use: ["style-loader", "css-loader"],
31 | },
32 | ],
33 | },
34 | resolve: {
35 | alias: {
36 | react: path.resolve(__dirname, "./node_modules/react"),
37 | "react-dom": path.resolve(__dirname, "./node_modules/react-dom"),
38 | },
39 | },
40 | externals: {
41 | // Don't bundle react or react-dom
42 | react: {
43 | commonjs: "react",
44 | commonjs2: "react",
45 | amd: "React",
46 | root: "React",
47 | },
48 | "react-dom": {
49 | commonjs: "react-dom",
50 | commonjs2: "react-dom",
51 | amd: "ReactDOM",
52 | root: "ReactDOM",
53 | },
54 | },
55 | };
56 |
--------------------------------------------------------------------------------