Skip to content

Commit

Permalink
Add MJML support
Browse files Browse the repository at this point in the history
This commit adds MJML support to Keila.

Huge thanks to @jdrouet for his Rust implementation of MJML and @paulgoetze for the Elixir wrapper
  • Loading branch information
wmnnd committed Aug 19, 2024
1 parent 1479070 commit 569a1e7
Show file tree
Hide file tree
Showing 23 changed files with 1,583 additions and 8 deletions.
18 changes: 18 additions & 0 deletions assets/js/campaign-editors/mjml/helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export const indentAndAutocompleteWithTab = {
key: "Tab",
preventDefault: true,
shift: indentLess,
run: (e) => {
if (!completionStatus(e.state)) return indentMore(e)
return acceptCompletion(e)
}
}

export const saveUpdates = (source) => {
return EditorView.updateListener.of((e) => {
if (e.docChanged) {
source.value = e.state.doc.toString()
source.dispatchEvent(new Event("change", { bubbles: true }))
}
})
}
51 changes: 51 additions & 0 deletions assets/js/campaign-editors/mjml/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { acceptCompletion, completionStatus } from "@codemirror/autocomplete"
import { defaultKeymap, indentLess, indentMore } from "@codemirror/commands"
import { html } from "@codemirror/lang-html"
import { EditorState } from "@codemirror/state"
import { EditorView, keymap } from "@codemirror/view"
import { basicSetup } from "codemirror"

import tags from "./autocomplete.js"
import { indentAndAutocompleteWithTab, saveUpdates } from "./helpers.js"
import theme from "./theme.js"

export default class MjmlEditor {
constructor(place, source) {
this.source = source
this.place = place

let state = EditorState.create({
doc: source.value,
extensions: [
basicSetup,
html({ extraTags: tags, selfClosingTags: true }),
keymap.of([...defaultKeymap, indentAndAutocompleteWithTab]),
theme,
saveUpdates(source)
]
})

this.view = new EditorView({
state: state,
parent: place
})

place.parentNode.parentNode.addEventListener("x-show-image-dialog", () => {
document
.querySelector("[data-dialog-for=image]")
.dispatchEvent(new CustomEvent("x-show", { detail: {} }))
window.addEventListener(
"update-image",
(e) => {
const { src } = e.detail
if (!src) {
this.view.focus()
return
}
this.view.dispatch(this.view.state.replaceSelection(src))
},
{ once: true }
)
})
}
}
Loading

0 comments on commit 569a1e7

Please sign in to comment.