diff --git a/README.md b/README.md
index 9adf57e..23011c2 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@ This plugin enables the usage of [MJML](https://mjml.io/) components inside the
-Supported MJML components:
+Supported MJML components (using default mjml-browser parser):
`mj-mjml`
`mj-head`
`mj-body`
@@ -41,10 +41,12 @@ Supported MJML components:
|`blocks`|Which blocks to add|(all)|
|`block`|Add custom block options, based on block id.|`(blockId) => ({})`|
|`codeViewerTheme`|Code viewer theme.|`hopscotch`|
+|`customComponents`|List of components which will be added to default one |`[]` |
|`fonts`|Custom fonts on exported HTML header [more info](https://github.com/mjmlio/mjml#inside-nodejs)|`{}`|
|`importPlaceholder`|Placeholder MJML template for the import modal|`''`|
|`imagePlaceholderSrc`|Image placeholder source|`'https://via.placeholder.com/350x250/78c5d6/fff'`|
|`i18n`|I18n object containing language [more info](https://grapesjs.com/docs/modules/I18n.html#configuration)|`{}`|
+|`mjmlParser`|Custom [mjml-browser](https://www.npmjs.com/package/mjml-browser) instance. Allows to extend MJML functionality or add custom MJML components |`(input: string \| MJMLJsonObject, opt: MJMLParsingOptions) => MJMLParseResults`|
|`overwriteExport`|Overwrite default export command|`true`|
|`preMjml`|String before the MJML in export code|`''`|
|`postMjml`|String after the MJML in export code|`''`|
@@ -181,6 +183,39 @@ editor.on('load', () => {
});
```
+### Using Independent mjml-browser Build
+
+In case, you have your own version of MJML with custom or extended components, it is possible
+to override default [mjml parser](https://github.com/mjmlio/mjml/tree/master/packages/mjml-browser)
+with custom one and create custom grapesJS components.
+
+For further info how to create MJML Component, you can
+[visit components folder](https://github.com/GrapesJS/mjml/tree/master/src/components)
+or you can go to [docs](https://grapesjs.com/docs/modules/Components.html#define-custom-component-type).
+
+```ts
+import 'grapesjs/dist/css/grapes.min.css'
+import grapesJS from 'grapesjs'
+import grapesJSMJML from 'grapesjs-mjml'
+import customMjmlParser from 'custom-mjml-parser';
+
+import customImage from 'custom/components/path'
+
+grapesJS.init({
+ fromElement: true,
+ container: '#gjs',
+ plugins: [grapesJSMJML],
+ pluginsOpts: {
+ [grapesJSMJML]: {
+ mjmlParser: customMjmlParser,
+ customComponents: [
+ customImage,
+ ]
+ }
+ },
+});
+```
+
## Development
Clone the repository
diff --git a/package-lock.json b/package-lock.json
index 4f2032a..69aa401 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2427,6 +2427,55 @@
"node": ">=0.10.0"
}
},
+ "@types/json-schema": {
+ "version": "7.0.12",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz",
+ "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==",
+ "dev": true
+ },
+ "@types/mime": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
+ "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==",
+ "dev": true
+ },
+ "@types/mjml": {
+ "version": "4.7.4",
+ "resolved": "https://registry.npmjs.org/@types/mjml/-/mjml-4.7.4.tgz",
+ "integrity": "sha512-vyi1vzWgMzFMwZY7GSZYX0GU0dmtC8vLHwpgk+NWmwbwRSrlieVyJ9sn5elodwUfklJM7yGl0zQeet1brKTWaQ==",
+ "requires": {
+ "@types/mjml-core": "*"
+ }
+ },
+ "@types/mjml-core": {
+ "version": "4.7.4",
+ "resolved": "https://registry.npmjs.org/@types/mjml-core/-/mjml-core-4.7.4.tgz",
+ "integrity": "sha512-hajbYITLm/wJU99Of50Dmn/k4ok+mrhJs4qDdnveJsINdiNJhQd+03C6Kt09vF9biB23cEI4pPeLrJNYfIZf7g=="
+ },
+ "@types/node": {
+ "version": "20.3.1",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.1.tgz",
+ "integrity": "sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg==",
+ "dev": true
+ },
+ "@types/qs": {
+ "version": "6.9.7",
+ "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
+ "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==",
+ "dev": true
+ },
+ "@types/range-parser": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz",
+ "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==",
+ "dev": true
+ },
+ "@types/retry": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz",
+ "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==",
+ "dev": true
+ },
"node_modules/@jest/core/node_modules/fill-range/node_modules/extend-shallow": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
diff --git a/package.json b/package.json
index 2b68145..24fdd19 100644
--- a/package.json
+++ b/package.json
@@ -28,6 +28,7 @@
"author": "Artur Arseniev",
"license": "BSD-3-Clause",
"dependencies": {
+ "@types/mjml": "^4.7.4",
"mjml-browser": "^4.13.0"
},
"babel": {
diff --git a/src/commands/index.ts b/src/commands/index.ts
index aa8a80f..0b4dca0 100644
--- a/src/commands/index.ts
+++ b/src/commands/index.ts
@@ -22,7 +22,7 @@ export default (editor: Editor, opts: RequiredPluginOptions) => {
Commands.add(cmdGetMjmlToHtml, (ed, _, opt) => {
const mjml = Commands.run(cmdGetMjml);
- return mjmlConvert(mjml, opts.fonts, opt);
+ return mjmlConvert(opts.mjmlParser, mjml, opts.fonts, opt);
});
openExportMjml(editor, opts, cmdOpenExport);
diff --git a/src/components/Body.ts b/src/components/Body.ts
index 550be38..5d4b089 100644
--- a/src/components/Body.ts
+++ b/src/components/Body.ts
@@ -1,5 +1,6 @@
// Specs: https://documentation.mjml.io/#mj-body
import type { Editor } from 'grapesjs';
+import { ComponentPluginOptions } from '.';
import { type as typeHero } from './Hero';
import { type as typeRaw } from './Raw';
import { type as typeSection } from './Section';
@@ -8,7 +9,7 @@ import { componentsToQuery, getName, isComponentType } from './utils';
export const type = 'mj-body';
-export default (editor: Editor, { coreMjmlModel, coreMjmlView }: any) => {
+export default (editor: Editor, { coreMjmlModel, coreMjmlView }: ComponentPluginOptions) => {
editor.Components.addType(type, {
isComponent: isComponentType(type),
model: {
diff --git a/src/components/Button.ts b/src/components/Button.ts
index cc2b440..09d0910 100644
--- a/src/components/Button.ts
+++ b/src/components/Button.ts
@@ -1,12 +1,13 @@
// Specs: https://documentation.mjml.io/#mj-button
import type { Editor } from 'grapesjs';
+import { ComponentPluginOptions } from '.';
import { componentsToQuery, getName, isComponentType } from './utils';
import { type as typeColumn } from './Column';
import { type as typeHero } from './Hero';
export const type = 'mj-button';
-export default (editor: Editor, { coreMjmlModel, coreMjmlView }: any) => {
+export default (editor: Editor, { coreMjmlModel, coreMjmlView }: ComponentPluginOptions) => {
editor.Components.addType(type, {
isComponent: isComponentType(type),
extend: 'link',
diff --git a/src/components/Column.ts b/src/components/Column.ts
index f99725a..c0ff086 100644
--- a/src/components/Column.ts
+++ b/src/components/Column.ts
@@ -1,11 +1,13 @@
// Specs: https://documentation.mjml.io/#mj-column
import type { Editor } from 'grapesjs';
+import { ComponentPluginOptions } from '.';
import { componentsToQuery, getName, isComponentType, mjmlConvert } from './utils';
import { type as typeSection } from './Section';
+
export const type = 'mj-column';
-export default (editor: Editor, { opt, coreMjmlModel, coreMjmlView, sandboxEl }: any) => {
+export default (editor: Editor, { opt, coreMjmlModel, coreMjmlView, sandboxEl }: ComponentPluginOptions) => {
const clmPadd = opt.columnsPadding;
editor.Components.addType(type, {
@@ -37,7 +39,7 @@ export default (editor: Editor, { opt, coreMjmlModel, coreMjmlView, sandboxEl }:
getTemplateFromMjml() {
const mjmlTmpl = this.getMjmlTemplate();
const innerMjml = this.getInnerMjmlTemplate();
- const htmlOutput = mjmlConvert(`${mjmlTmpl.start}
+ const htmlOutput = mjmlConvert(opt.mjmlParser, `${mjmlTmpl.start}
${innerMjml.start}${innerMjml.end}${mjmlTmpl.end}`, opt.fonts);
const html = htmlOutput.html;
diff --git a/src/components/Divider.ts b/src/components/Divider.ts
index 126082c..d983b01 100644
--- a/src/components/Divider.ts
+++ b/src/components/Divider.ts
@@ -1,12 +1,13 @@
// Specs: https://documentation.mjml.io/#mj-divider
import type { Editor } from 'grapesjs';
+import { ComponentPluginOptions } from '.';
import { componentsToQuery, getName, isComponentType } from './utils';
import { type as typeColumn } from './Column';
import { type as typeHero } from './Hero';
export const type = 'mj-divider';
-export default (editor: Editor, { coreMjmlModel, coreMjmlView }: any) => {
+export default (editor: Editor, { coreMjmlModel, coreMjmlView }: ComponentPluginOptions) => {
editor.Components.addType(type, {
isComponent: isComponentType(type),
model: {
diff --git a/src/components/Font.ts b/src/components/Font.ts
index 9686d3b..f551b53 100644
--- a/src/components/Font.ts
+++ b/src/components/Font.ts
@@ -1,11 +1,12 @@
// Specs: https://documentation.mjml.io/#mj-font
import type { Editor } from 'grapesjs';
+import { ComponentPluginOptions } from '.';
import { componentsToQuery, isComponentType, mjmlConvert } from './utils';
import { type as typeHead } from './Head';
export const type = 'mj-font';
-export default (editor: Editor, { opt, coreMjmlModel, coreMjmlView, sandboxEl }: any) => {
+export default (editor: Editor, { opt, coreMjmlModel, coreMjmlView, sandboxEl }: ComponentPluginOptions) => {
editor.Components.addType(type, {
isComponent: isComponentType(type),
model: {
@@ -40,7 +41,7 @@ export default (editor: Editor, { opt, coreMjmlModel, coreMjmlView, sandboxEl }:
getTemplateFromMjml() {
let mjmlTmpl = this.getMjmlTemplate();
let innerMjml = this.getInnerMjmlTemplate();
- const htmlOutput = mjmlConvert(`${mjmlTmpl.start}
+ const htmlOutput = mjmlConvert(opt.mjmlParser, `${mjmlTmpl.start}
${innerMjml.start}${innerMjml.end}${mjmlTmpl.end}`, opt.fonts);
let html = htmlOutput.html;
let start = html.indexOf('') + 6;
diff --git a/src/components/Group.ts b/src/components/Group.ts
index 7463632..e79cfab 100644
--- a/src/components/Group.ts
+++ b/src/components/Group.ts
@@ -1,12 +1,13 @@
// Specs: https://documentation.mjml.io/#mj-group
import type { Editor } from 'grapesjs';
+import { ComponentPluginOptions } from '.';
import { componentsToQuery, getName, isComponentType } from './utils';
import { type as typeSection } from './Section';
import { type as typeColumn } from './Column';
export const type = 'mj-group';
-export default (editor: Editor, { coreMjmlModel, coreMjmlView }: any) => {
+export default (editor: Editor, { coreMjmlModel, coreMjmlView }: ComponentPluginOptions) => {
editor.Components.addType(type, {
isComponent: isComponentType(type),
model: {
diff --git a/src/components/Hero.ts b/src/components/Hero.ts
index 0439ff0..11bcc1b 100644
--- a/src/components/Hero.ts
+++ b/src/components/Hero.ts
@@ -1,5 +1,6 @@
// Specs: https://documentation.mjml.io/#mj-hero
import type { Editor } from 'grapesjs';
+import { ComponentPluginOptions } from '.';
import { componentsToQuery, getName, isComponentType } from './utils';
import { type as typeBody } from './Body';
import { type as typeText } from './Text';
@@ -12,7 +13,7 @@ import { type as typeSpacer } from './Spacer';
export const type = 'mj-hero';
-export default (editor: Editor, { coreMjmlModel, coreMjmlView }: any) => {
+export default (editor: Editor, { coreMjmlModel, coreMjmlView }: ComponentPluginOptions) => {
editor.Components.addType(type, {
isComponent: isComponentType(type),
model: {
diff --git a/src/components/Image.ts b/src/components/Image.ts
index a3180af..57ee274 100644
--- a/src/components/Image.ts
+++ b/src/components/Image.ts
@@ -1,5 +1,6 @@
// Specs: https://documentation.mjml.io/#mj-image
import type { Editor } from 'grapesjs';
+import { ComponentPluginOptions } from '.';
import { componentsToQuery, getName, isComponentType } from './utils';
import { type as typeSection } from './Section';
import { type as typeColumn } from './Column';
@@ -7,7 +8,7 @@ import { type as typeHero } from './Hero';
export const type = 'mj-image';
-export default (editor: Editor, { coreMjmlModel, coreMjmlView }: any) => {
+export default (editor: Editor, { coreMjmlModel, coreMjmlView }: ComponentPluginOptions) => {
editor.Components.addType(type, {
isComponent: isComponentType(type),
extend: 'image',
diff --git a/src/components/NavBar.ts b/src/components/NavBar.ts
index 67b8224..c559901 100644
--- a/src/components/NavBar.ts
+++ b/src/components/NavBar.ts
@@ -1,5 +1,6 @@
// Specs https://documentation.mjml.io/#mj-navbar
import type { Editor } from 'grapesjs';
+import { ComponentPluginOptions } from '.';
import { componentsToQuery, getName, isComponentType, mjmlConvert } from './utils';
import { type as typeColumn } from './Column';
import { type as typeHero } from './Hero';
@@ -7,7 +8,7 @@ import { type as typeNavBarLink } from './NavBarLink';
export const type = 'mj-navbar';
-export default (editor: Editor, { opt, coreMjmlModel, coreMjmlView, sandboxEl }: any) => {
+export default (editor: Editor, { opt, coreMjmlModel, coreMjmlView, sandboxEl }: ComponentPluginOptions) => {
editor.Components.addType(type, {
isComponent: isComponentType(type),
model: {
@@ -51,7 +52,7 @@ export default (editor: Editor, { opt, coreMjmlModel, coreMjmlView, sandboxEl }:
getTemplateFromMjml() {
const mjmlTmpl = this.getMjmlTemplate();
const innerMjml = this.getInnerMjmlTemplate();
- const htmlOutput = mjmlConvert(`${mjmlTmpl.start}
+ const htmlOutput = mjmlConvert(opt.mjmlParser, `${mjmlTmpl.start}
${innerMjml.start}${innerMjml.end}${mjmlTmpl.end}`, opt.fonts);
const html = htmlOutput.html;
diff --git a/src/components/NavBarLink.ts b/src/components/NavBarLink.ts
index 96e6602..3114f53 100644
--- a/src/components/NavBarLink.ts
+++ b/src/components/NavBarLink.ts
@@ -1,11 +1,12 @@
// Specs: https://documentation.mjml.io/#mj-navbar-link
import type { Editor } from 'grapesjs';
+import { ComponentPluginOptions } from '.';
import { componentsToQuery, getName, isComponentType } from './utils';
import { type as typeNavBar } from './NavBar';
export const type = 'mj-navbar-link';
-export default (editor: Editor, { coreMjmlModel, coreMjmlView }: any) => {
+export default (editor: Editor, { coreMjmlModel, coreMjmlView }: ComponentPluginOptions) => {
editor.Components.addType(type, {
isComponent: isComponentType(type),
extend: 'link',
diff --git a/src/components/Raw.ts b/src/components/Raw.ts
index 4666539..4b393c5 100644
--- a/src/components/Raw.ts
+++ b/src/components/Raw.ts
@@ -1,12 +1,13 @@
// Specs: https://documentation.mjml.io/#mj-raw
import type { Editor } from 'grapesjs';
+import { ComponentPluginOptions } from '.';
import { componentsToQuery, getName, isComponentType } from './utils';
import { type as typeBody } from './Body';
import { type as typeHead } from './Head';
export const type = 'mj-raw';
-export default (editor: Editor, { coreMjmlModel, coreMjmlView }: any) => {
+export default (editor: Editor, { coreMjmlModel, coreMjmlView }: ComponentPluginOptions) => {
editor.Components.addType(type, {
isComponent: isComponentType(type),
model: {
diff --git a/src/components/Section.ts b/src/components/Section.ts
index b1ff50b..72c173e 100644
--- a/src/components/Section.ts
+++ b/src/components/Section.ts
@@ -1,5 +1,6 @@
// Specs: https://documentation.mjml.io/#mj-section
import type { Editor } from 'grapesjs';
+import { ComponentPluginOptions } from '.';
import { componentsToQuery, getName, isComponentType } from './utils';
import { type as typeBody } from './Body';
import { type as typeWrapper } from './Wrapper';
@@ -8,7 +9,7 @@ import { type as typeGroup } from './Group';
export const type = 'mj-section';
-export default (editor: Editor, { coreMjmlModel, coreMjmlView }: any) => {
+export default (editor: Editor, { coreMjmlModel, coreMjmlView }: ComponentPluginOptions) => {
editor.Components.addType(type, {
isComponent: isComponentType(type),
diff --git a/src/components/Social.ts b/src/components/Social.ts
index 17f6baf..90f3368 100644
--- a/src/components/Social.ts
+++ b/src/components/Social.ts
@@ -1,5 +1,6 @@
// Specs: https://documentation.mjml.io/#mjml-social
import type { Editor } from 'grapesjs';
+import { ComponentPluginOptions } from '.';
import { componentsToQuery, getName, isComponentType } from './utils';
import { type as typeColumn } from './Column';
import { type as typeHero } from './Hero';
@@ -7,7 +8,7 @@ import { type as typeSocialElement } from './SocialElement';
export const type = 'mj-social';
-export default (editor: Editor, { coreMjmlModel, coreMjmlView }: any) => {
+export default (editor: Editor, { coreMjmlModel, coreMjmlView }: ComponentPluginOptions) => {
editor.Components.addType(type, {
isComponent: isComponentType(type),
diff --git a/src/components/SocialElement.ts b/src/components/SocialElement.ts
index 391c8c4..c590e3c 100644
--- a/src/components/SocialElement.ts
+++ b/src/components/SocialElement.ts
@@ -1,11 +1,12 @@
// Specs: https://documentation.mjml.io/#mjml-social
import type { Editor } from 'grapesjs';
+import { ComponentPluginOptions } from '.';
import { componentsToQuery, getName, isComponentType } from './utils';
import { type as typeSocial } from './Social';
export const type = 'mj-social-element';
-export default (editor: Editor, { coreMjmlModel, coreMjmlView }: any) => {
+export default (editor: Editor, { coreMjmlModel, coreMjmlView }: ComponentPluginOptions) => {
editor.Components.addType(type, {
isComponent: isComponentType(type),
diff --git a/src/components/Spacer.ts b/src/components/Spacer.ts
index 94454f4..ab8326e 100644
--- a/src/components/Spacer.ts
+++ b/src/components/Spacer.ts
@@ -1,12 +1,13 @@
// Specs: https://documentation.mjml.io/#mjml-spacer
import type { Editor } from 'grapesjs';
+import { ComponentPluginOptions } from '.';
import { componentsToQuery, getName, isComponentType } from './utils';
import { type as typeColumn } from './Column';
import { type as typeHero } from './Hero';
export const type = 'mj-spacer';
-export default (editor: Editor, { coreMjmlModel, coreMjmlView }: any) => {
+export default (editor: Editor, { coreMjmlModel, coreMjmlView }: ComponentPluginOptions) => {
editor.Components.addType(type, {
isComponent: isComponentType(type),
diff --git a/src/components/Style.ts b/src/components/Style.ts
index 6242b7f..95f026a 100644
--- a/src/components/Style.ts
+++ b/src/components/Style.ts
@@ -1,11 +1,12 @@
// Specs: https://documentation.mjml.io/#mj-style
import type { Editor } from 'grapesjs';
+import { ComponentPluginOptions } from '.';
import { componentsToQuery, isComponentType, mjmlConvert } from './utils';
import { type as typeHead } from './Head';
export const type = 'mj-style';
-export default (editor: Editor, { opt, coreMjmlModel, coreMjmlView, sandboxEl }: any) => {
+export default (editor: Editor, { opt, coreMjmlModel, coreMjmlView, sandboxEl }: ComponentPluginOptions) => {
editor.Components.addType(type, {
isComponent: isComponentType(type),
@@ -35,7 +36,7 @@ export default (editor: Editor, { opt, coreMjmlModel, coreMjmlView, sandboxEl }:
getTemplateFromMjml() {
let mjmlTmpl = this.getMjmlTemplate();
let innerMjml = this.getInnerMjmlTemplate();
- const htmlOutput = mjmlConvert(`${mjmlTmpl.start}
+ const htmlOutput = mjmlConvert(opt.mjmlParser, `${mjmlTmpl.start}
${innerMjml.start}${innerMjml.end}${mjmlTmpl.end}`, opt.fonts);
let html = htmlOutput.html;
let start = html.indexOf('') + 6;
diff --git a/src/components/Text.ts b/src/components/Text.ts
index 40ee8b9..2cf11bc 100644
--- a/src/components/Text.ts
+++ b/src/components/Text.ts
@@ -1,12 +1,13 @@
// Specs: https://documentation.mjml.io/#mjml-text
import type { Editor } from 'grapesjs';
+import { ComponentPluginOptions } from '.';
import { componentsToQuery, getName, isComponentType } from './utils';
import { type as typeColumn } from './Column';
import { type as typeHero } from './Hero';
export const type = 'mj-text';
-export default (editor: Editor, { coreMjmlModel, coreMjmlView }: any) => {
+export default (editor: Editor, { coreMjmlModel, coreMjmlView }: ComponentPluginOptions) => {
editor.Components.addType(type, {
extend: 'text',
extendFnView: ['onActive'],
diff --git a/src/components/Wrapper.ts b/src/components/Wrapper.ts
index 4fe42a1..82c9257 100644
--- a/src/components/Wrapper.ts
+++ b/src/components/Wrapper.ts
@@ -1,12 +1,13 @@
// Specs: https://documentation.mjml.io/#mjml-wrapper
import type { Editor } from 'grapesjs';
+import { ComponentPluginOptions } from '.';
import { componentsToQuery, getName, isComponentType } from './utils';
import { type as typeBody } from './Body';
import { type as typeSection } from './Section';
export const type = 'mj-wrapper';
-export default (editor: Editor, { coreMjmlModel, coreMjmlView }: any) => {
+export default (editor: Editor, { coreMjmlModel, coreMjmlView }: ComponentPluginOptions) => {
editor.Components.addType(type, {
isComponent: isComponentType(type),
diff --git a/src/components/index.ts b/src/components/index.ts
index 0b6c09c..74c1102 100644
--- a/src/components/index.ts
+++ b/src/components/index.ts
@@ -1,5 +1,5 @@
-import type { Editor } from 'grapesjs';
-import { mjmlConvert, debounce } from './utils';
+import type { Editor, PluginOptions } from 'grapesjs';
+import { mjmlConvert, debounce, componentsToQuery } from './utils';
import loadMjml from './mjml';
import loadHead from './Head';
import loadStyle from './Style';
@@ -22,6 +22,20 @@ import loadHero from './Hero';
import loadRaw from './Raw';
import { RequiredPluginOptions } from '..';
+export type ComponentPluginOptions = {
+ /**
+ * Core model, which can be extended
+ */
+ coreMjmlModel: any;
+ /**
+ * Core view, which can be extended
+ */
+ coreMjmlView: any;
+ opt: Required;
+ sandboxEl: HTMLDivElement;
+ componentsToQuery: typeof componentsToQuery,
+}
+
export default (editor: Editor, opt: RequiredPluginOptions) => {
const { Components } = editor;
// @ts-ignore
@@ -204,7 +218,7 @@ export default (editor: Editor, opt: RequiredPluginOptions) => {
const mjmlTmpl = this.getMjmlTemplate();
const innerMjml = this.getInnerMjmlTemplate();
const mjml = `${mjmlTmpl.start}${innerMjml.start}${innerMjml.end}${mjmlTmpl.end}`;
- const htmlOutput = mjmlConvert(mjml, opt.fonts);
+ const htmlOutput = mjmlConvert(opt.mjmlParser, mjml, opt.fonts);
let html = htmlOutput.html;
html = html.replace(//, '');
let start = html.indexOf('') + 6;
@@ -266,9 +280,8 @@ export default (editor: Editor, opt: RequiredPluginOptions) => {
}
} as any;
-
// MJML Internal view (for elements inside mj-columns)
- const compOpts = { coreMjmlModel, coreMjmlView, opt, sandboxEl };
+ const compOpts = { coreMjmlModel, coreMjmlView, opt, sandboxEl, componentsToQuery};
// Avoid the tag from the default wrapper
editor.Components.addType('wrapper', {
@@ -303,6 +316,7 @@ export default (editor: Editor, opt: RequiredPluginOptions) => {
loadNavBarLink,
loadHero,
loadRaw,
+ ...opt.customComponents,
]
.forEach(module => module(editor, compOpts));
};
diff --git a/src/components/mjml.ts b/src/components/mjml.ts
index a0af7ac..aa26f0f 100644
--- a/src/components/mjml.ts
+++ b/src/components/mjml.ts
@@ -1,12 +1,13 @@
// Specs: https://documentation.mjml.io/#mjml
import type { Editor } from 'grapesjs';
+import { ComponentPluginOptions } from '.';
import { isComponentType, componentsToQuery } from './utils';
import { type as typeHead } from './Head';
import { type as typeBody } from './Body';
export const type = 'mjml';
-export default (editor: Editor, { coreMjmlModel, coreMjmlView }: any) => {
+export default (editor: Editor, { coreMjmlModel, coreMjmlView }: ComponentPluginOptions) => {
editor.Components.addType(type, {
isComponent: isComponentType(type),
model: {
diff --git a/src/components/parser.ts b/src/components/parser.ts
new file mode 100644
index 0000000..e18be4a
--- /dev/null
+++ b/src/components/parser.ts
@@ -0,0 +1,15 @@
+// @ts-ignore
+import mjml2html from 'mjml-browser';
+import mjml from "mjml-core";
+
+/**
+ * MJML Parser.
+ *
+ * @see {@link https://github.com/mjmlio/mjml/tree/master/packages/mjml-core}
+ */
+export type MjmlParser = typeof mjml;
+
+/**
+ * MJML Parser instance.
+ */
+export default mjml2html as MjmlParser;
diff --git a/src/components/utils.ts b/src/components/utils.ts
index 1a20e00..35972d6 100644
--- a/src/components/utils.ts
+++ b/src/components/utils.ts
@@ -1,14 +1,14 @@
-// @ts-ignore
-import mjml2html from 'mjml-browser';
import type { Editor } from 'grapesjs';
+import { MJMLParsingOptions } from "mjml-core";
+import { MjmlParser } from "./parser";
export const isComponentType = (type: string) => (el: Element) => (el.tagName || '').toLowerCase() === type;
-export function mjmlConvert (mjml: string, fonts: Record, opts: Record = {}) {
- const options = {
+export function mjmlConvert (parser: MjmlParser, mjml: string, fonts: Record, opts: Partial = {}) {
+ const options: MJMLParsingOptions = {
useMjmlConfigOptions: false,
- mjmlConfigPath: null,
- filePath: null,
+ mjmlConfigPath: undefined,
+ filePath: undefined,
...opts,
};
@@ -18,7 +18,7 @@ export function mjmlConvert (mjml: string, fonts: Record, opts:
options.fonts = fonts;
}
- return mjml2html(mjml, options);
+ return parser(mjml, options);
}
export const componentsToQuery = (cmps: string | string[]): string => {
diff --git a/src/index.ts b/src/index.ts
index a3092e2..8c17574 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,6 +1,7 @@
-import type { Plugin } from 'grapesjs';
+import type { Editor, Plugin } from 'grapesjs';
import loadBlocks from './blocks';
-import loadComponents from './components';
+import loadComponents, { ComponentPluginOptions } from './components';
+import mjml2html, { MjmlParser } from './components/parser';
import loadCommands from './commands';
import loadPanels from './panels';
import loadStyle from './style';
@@ -26,6 +27,13 @@ export type PluginOptions = {
*/
codeViewerTheme?: string;
+ /**
+ * Add custom MJML components
+ *
+ * @default []
+ */
+ customComponents?: ((editor: Editor, componentOptions: ComponentPluginOptions) => void)[],
+
/**
* Placeholder MJML template for the import modal
* @default ''
@@ -38,6 +46,12 @@ export type PluginOptions = {
*/
imagePlaceholderSrc?: string;
+ /**
+ * Custom MJML parser.
+ * @default mjml-browser instance
+ */
+ mjmlParser?: MjmlParser;
+
/**
* Overwrite default export command
* @default true
@@ -128,8 +142,10 @@ const plugin: Plugin = (editor, opt = {}) => {
],
block: () => ({}),
codeViewerTheme: 'hopscotch',
+ customComponents: [],
importPlaceholder: '',
imagePlaceholderSrc: '',
+ mjmlParser: mjml2html,
overwriteExport: true,
preMjml: '',
postMjml: '',