Skip to content

Commit 7a73090

Browse files
committed
Add MJML support
This commit adds MJML support to Keila. Huge thanks to @jdrouet for his Rust implementation of MJML and @paulgoetze for the Elixir wrapper
1 parent 1479070 commit 7a73090

File tree

23 files changed

+1585
-8
lines changed

23 files changed

+1585
-8
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { acceptCompletion, completionStatus } from "@codemirror/autocomplete"
2+
import { indentLess, indentMore } from "@codemirror/commands"
3+
4+
export const indentAndAutocompleteWithTab = {
5+
key: "Tab",
6+
preventDefault: true,
7+
shift: indentLess,
8+
run: (e) => {
9+
if (!completionStatus(e.state)) return indentMore(e)
10+
return acceptCompletion(e)
11+
}
12+
}
13+
14+
export const saveUpdates = (source) => {
15+
return EditorView.updateListener.of((e) => {
16+
if (e.docChanged) {
17+
source.value = e.state.doc.toString()
18+
source.dispatchEvent(new Event("change", { bubbles: true }))
19+
}
20+
})
21+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { defaultKeymap } from "@codemirror/commands"
2+
import { html } from "@codemirror/lang-html"
3+
import { EditorState } from "@codemirror/state"
4+
import { EditorView, keymap } from "@codemirror/view"
5+
import { basicSetup } from "codemirror"
6+
7+
import { indentAndAutocompleteWithTab, saveUpdates } from "./helpers.js"
8+
import tags from "./tags.js"
9+
import theme from "./theme.js"
10+
11+
export default class MjmlEditor {
12+
constructor(place, source) {
13+
this.source = source
14+
this.place = place
15+
16+
let state = EditorState.create({
17+
doc: source.value,
18+
extensions: [
19+
basicSetup,
20+
html({ extraTags: tags, selfClosingTags: true }),
21+
keymap.of([...defaultKeymap, indentAndAutocompleteWithTab]),
22+
theme,
23+
saveUpdates(source)
24+
]
25+
})
26+
27+
this.view = new EditorView({
28+
state: state,
29+
parent: place
30+
})
31+
32+
place.parentNode.parentNode.addEventListener("x-show-image-dialog", () => {
33+
document
34+
.querySelector("[data-dialog-for=image]")
35+
.dispatchEvent(new CustomEvent("x-show", { detail: {} }))
36+
window.addEventListener(
37+
"update-image",
38+
(e) => {
39+
const { src } = e.detail
40+
if (!src) {
41+
this.view.focus()
42+
return
43+
}
44+
this.view.dispatch(this.view.state.replaceSelection(src))
45+
},
46+
{ once: true }
47+
)
48+
})
49+
}
50+
}

0 commit comments

Comments
 (0)