Skip to content

Commit 626b6e3

Browse files
Add table support to Post editor (#388)
Co-authored-by: Sam Hamilton <sam@sh81.com>
1 parent 80265b0 commit 626b6e3

File tree

9 files changed

+97
-42
lines changed

9 files changed

+97
-42
lines changed

assets/js/app.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@ Hooks.EditorJS = {
2424
},
2525
};
2626

27-
Hooks.HiddenInputChange = {};
28-
2927
Hooks.HandleDragNDrop = {
3028
mounted() {
3129
const dragNDropList = document.querySelector(

assets/js/editor.js

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import EditorJS from "@editorjs/editorjs";
22
import Header from "@editorjs/header";
33
import Image from "@editorjs/image";
44
import List from "@editorjs/list";
5+
import Table from "@editorjs/table";
56
import editorParser from "editorjs-html";
67

78
const csrfToken = document
@@ -16,18 +17,39 @@ const customImageParser = ({ data }) => {
1617
alt="${alt}" ${data.caption ? `caption="${data.caption}"` : ""} />`;
1718
};
1819

19-
const parser = editorParser({ image: customImageParser });
20+
const customTableParser = ({ data }) => {
21+
let tableBody = "";
22+
23+
data.content.forEach((element, index) => {
24+
const cellTag = data.withHeadings && index == 0 ? "th" : "td";
25+
let row = "";
26+
27+
row = "<tr>";
28+
29+
element.forEach((elem) => {
30+
row += `<${cellTag}> ${elem} </${cellTag}>`;
31+
});
32+
33+
tableBody += row + "</tr>";
34+
});
35+
36+
return "<table>" + tableBody + "</table>";
37+
};
38+
39+
const parser = editorParser({
40+
image: customImageParser,
41+
table: customTableParser,
42+
});
2043

2144
const HTMLEditorJS = (element) => {
2245
const inputEditorJSON = document.querySelector("#form-editor-json");
2346
const inputHTML = document.querySelector("#form-html");
2447

25-
inputEditorJSON.value = element.dataset.postData
26-
? JSON.stringify(JSON.parse(element.dataset.postData))
27-
: "";
28-
inputHTML.value = element.dataset.postData
29-
? JSON.stringify(parser.parse(JSON.parse(element.dataset.postData)))
30-
: "";
48+
if (element.dataset.postData) {
49+
const jsonData = JSON.parse(element.dataset.postData);
50+
inputEditorJSON.value = JSON.stringify(jsonData);
51+
inputHTML.value = parser.parse(jsonData);
52+
}
3153

3254
const editor = new EditorJS({
3355
tools: {
@@ -48,12 +70,19 @@ const HTMLEditorJS = (element) => {
4870
class: List,
4971
inlineToolbar: true,
5072
},
73+
table: {
74+
class: Table,
75+
inlineToolbar: true,
76+
config: {
77+
withHeadings: true,
78+
},
79+
},
5180
},
5281
data: JSON.parse(element.dataset.postData || "{}"),
5382
onChange: () => {
5483
editor.save().then((outputData) => {
5584
inputEditorJSON.value = JSON.stringify(outputData);
56-
inputHTML.value = JSON.stringify(parser.parse(outputData));
85+
inputHTML.value = parser.parse(outputData);
5786
});
5887
},
5988
});

assets/package-lock.json

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

assets/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"@editorjs/header": "^2.8.8",
1515
"@editorjs/image": "github:martide/editor-js-image#WEB-8452-image-alt",
1616
"@editorjs/list": "^2.0.3",
17+
"@editorjs/table": "^2.4.3",
1718
"@eslint/js": "^9.18.0",
1819
"@tailwindcss/typography": "^0.5.16",
1920
"editorjs-html": "^4.0.5",

lib/literature/components/forms/post_form_component.ex

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -255,10 +255,8 @@ defmodule Literature.PostFormComponent do
255255

256256
@impl Phoenix.LiveComponent
257257
def handle_event("save", %{"post" => post_params}, socket) do
258-
html = post_params["html"]
259-
html = if is_binary(html) and html != "", do: Jason.decode!(post_params["html"]), else: []
260258
action = socket.assigns.action
261-
post_params = post_params |> Map.put("html", html) |> merge_post_params(socket, action)
259+
post_params = merge_post_params(post_params, socket, action)
262260
post = socket.assigns.post
263261

264262
socket
@@ -343,10 +341,22 @@ defmodule Literature.PostFormComponent do
343341
defp put_validation(changeset, :edit_post), do: Map.put(changeset, :action, :validate)
344342

345343
defp build_html(%{"html" => html} = params) do
346-
html = Enum.map(html, &parse_image_tag/1)
344+
html =
345+
(html || "")
346+
|> Floki.parse_fragment!()
347+
|> Enum.map(&parse_tag/1)
348+
347349
%{params | "html" => html}
348350
end
349351

352+
defp parse_tag({"img", _, _} = image_tag) do
353+
image_tag
354+
|> Floki.raw_html()
355+
|> parse_image_tag()
356+
end
357+
358+
defp parse_tag(tag), do: Floki.raw_html(tag)
359+
350360
defp delete_icon(assigns) do
351361
~H"""
352362
<svg

mix.exs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ defmodule Literature.MixProject do
6969
{:timex, "~> 3.7"},
7070
{:poolboy, "~> 1.5"},
7171
{:ex_cldr_languages, "~> 0.3"},
72+
{:floki, "~> 0.33"},
7273

7374
# Test
7475
{:credo, "~> 1.6", only: [:test, :dev], runtime: false},

priv/static/css/app.css

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

priv/static/js/app.js

Lines changed: 22 additions & 24 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/literature/live/post_live_test.exs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ defmodule Literature.PostLiveTest do
149149

150150
assert html =~ "New Post"
151151

152-
html = Jason.encode!(["<p>some html</p>"])
152+
html = "<p>some html</p>"
153153

154154
new_live
155155
|> form("#post-form",
@@ -226,7 +226,7 @@ defmodule Literature.PostLiveTest do
226226
Routes.literature_dashboard_path(conn, :edit_post, publication.slug, post.slug)
227227
)
228228

229-
html = Jason.encode!(["<p>some html</p>"])
229+
html = "<p>some html</p>"
230230

231231
view
232232
|> form("#post-form", post: @update_attrs)
@@ -237,7 +237,7 @@ defmodule Literature.PostLiveTest do
237237
assert path == Routes.literature_dashboard_path(conn, :list_posts, publication.slug)
238238
assert flash["success"] == "Post updated successfully"
239239

240-
assert Literature.get_post!(post.id).html == Jason.decode!(html)
240+
assert Literature.get_post!(post.id).html == [html]
241241

242242
# Will not be able to follow redirect since redirect happens after async result
243243
end

0 commit comments

Comments
 (0)