add reusable babycode editor

This commit is contained in:
Lera Elvoé 2025-05-24 02:45:54 +03:00
parent ec3f144b4e
commit 66318698e5
Signed by: yagich
SSH Key Fingerprint: SHA256:6xjGb6uA7lAVcULa7byPEN//rQ0wPoG+UzYVMfZnbvc
10 changed files with 116 additions and 33 deletions

View File

@ -394,6 +394,12 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus, select:focus
align-items: baseline;
height: 100%;
}
.post-edit-form > textarea {
height: 100%;
.babycode-editor {
height: 150px;
}
ul {
margin: 10px 0 10px 30px;
padding: 0;
}

54
js/post-editor.js Normal file
View File

@ -0,0 +1,54 @@
{
let ta = document.getElementById("post_content");
const buttonBold = document.getElementById("post-editor-bold");
const buttonItalics = document.getElementById("post-editor-italics");
const buttonStrike = document.getElementById("post-editor-strike");
const buttonCode = document.getElementById("post-editor-code");
function insertTag(tagStart, newline = false) {
const tagEnd = tagStart;
const tagInsertStart = `[${tagStart}]${newline ? "\n" : ""}`;
const tagInsertEnd = `${newline ? "\n" : ""}[/${tagEnd}]`;
const hasSelection = ta.selectionStart !== ta.selectionEnd;
const text = ta.value;
if (hasSelection) {
const realStart = Math.min(ta.selectionStart, ta.selectionEnd);
const realEnd = Math.max(ta.selectionStart, ta.selectionEnd);
const selectionLength = realEnd - realStart;
const strStart = text.slice(0, realStart);
const strEnd = text.substring(realEnd);
const frag = `${tagInsertStart}${text.slice(realStart, realEnd)}${tagInsertEnd}`;
const reconst = `${strStart}${frag}${strEnd}`;
ta.value = reconst;
ta.setSelectionRange(realStart + tagInsertStart.length, realStart + tagInsertStart.length + selectionLength);
ta.focus()
} else {
const cursor = ta.selectionStart;
const strStart = text.slice(0, cursor);
const strEnd = text.substr(cursor);
const newCursor = strStart.length + tagInsertStart.length;
const reconst = `${strStart}${tagInsertStart}${tagInsertEnd}${strEnd}`;
ta.value = reconst;
ta.setSelectionRange(newCursor, newCursor);
ta.focus()
}
}
buttonBold.addEventListener("click", (e) => {
e.preventDefault();
insertTag("b")
})
buttonItalics.addEventListener("click", (e) => {
e.preventDefault();
insertTag("i")
})
buttonStrike.addEventListener("click", (e) => {
e.preventDefault();
insertTag("s")
})
buttonCode.addEventListener("click", (e) => {
e.preventDefault();
insertTag("code", true)
})
}

View File

@ -1,8 +1,10 @@
const ta = document.getElementById("post_content");
{
const ta = document.getElementById("post_content");
for (let button of document.querySelectorAll(".reply-button")) {
button.addEventListener("click", (e) => {
ta.value += button.value;
ta.scrollIntoView()
})
for (let button of document.querySelectorAll(".reply-button")) {
button.addEventListener("click", (e) => {
ta.value += button.value;
ta.scrollIntoView()
})
}
}

View File

@ -397,8 +397,14 @@ input[type="text"], input[type="password"], textarea, select {
flex-direction: column;
align-items: baseline;
height: 100%;
&>textarea{
height: 100%;
}
}
.babycode-editor {
height: 150px;
}
ul {
margin: 10px 0 10px 30px;
padding: 0;
}

View File

@ -0,0 +1,8 @@
<span>
<button type=button id="post-editor-bold" title="Insert Bold">B</button>
<button type=button id="post-editor-italics" title="Insert Italics">I</button>
<button type=button id="post-editor-strike" title="Insert Strikethrough">S</button>
<button type=button id="post-editor-code" title="Insert Code block">Code</button>
</span>
<textarea class="babycode-editor" name="<%= ta_name %>" id="post_content" placeholder="Post body" required><%- prefill or "" %></textarea>
<script src="/static/js/post-editor.js"></script>

View File

@ -0,0 +1,16 @@
<%
local save_button_text = "Post reply"
if cancel_url then
save_button_text = "Save"
end
%>
<form class="post-edit-form" method="post" action="<%= url or "" %>">
<% render ("views.common.babycode-editor-component", {ta_name = ta_name, prefill = prefill}) %>
<span>
<input type=submit value="<%= save_button_text %>">
<% if cancel_url then %>
<a class="linkbutton warn" href="<%= cancel_url %>">Cancel</a>
<% end %>
</span>
<% render("views.common.bbcode_help") %>
</form>

View File

@ -1,5 +1,5 @@
<details>
<summary>Supported babycode tags</summary>
<summary>babycode guide</summary>
<ul>
<li>[b]<b>bold</b>[/b]</li>
<li>[i]<i>italic</i>[/i]</li>
@ -8,4 +8,4 @@
<li>[url]<a href="https://unlabeled-url.example.com">https://unlabeled-url.example.com</a>[/url]</li>
<li>[code]<code>code block</code>[/code]</li>
</ul>
</details>
</details>

View File

@ -9,8 +9,8 @@
</select><br>
<label for="title">Thread title</label>
<input type="text" id="title" name="title" placeholder="Required" required>
<label for="initial_post">Post body</label>
<textarea id="initial_post" name="initial_post" placeholder="Required" rows=5 required></textarea>
<label for="initial_post">Post body</label><br>
<% render("views.common.babycode-editor-component", {ta_name = "initial_post"}) %>
<% render "views.common.bbcode_help" %>
<input type="submit" value="Create thread">
</form>

View File

@ -54,13 +54,11 @@
<% if not edit then %>
<%- post.content %>
<% else %>
<form class="post-edit-form" method="post">
<textarea name="new_content" required><%- post.original_markup %></textarea>
<span>
<input type=submit value="Save">
<a class="linkbutton warn" href="<%= url_for("thread", {slug = thread.slug}, {page = page}) .. "#post-" .. post.id %>">Cancel</a>
</span>
</form>
<% render("views.common.babycode-editor", {
cancel_url = url_for("thread", {slug = thread.slug}, {page = page}) .. "#post-" .. post.id,
prefill = post.original_markup,
ta_name = "new_content"
}) %>
<% end %>
</div>
</div>

View File

@ -1,10 +1,6 @@
<%
local is_locked = ntob(thread.is_locked)
local can_post = (not is_locked and not me:is_guest()) or me:is_mod()
-- not locked, not guest, not mod = not false and not false or false = true and true or false = true
-- locked, not guest, not mod = not true and not false or false = false and true or false = false
-- not locked, guest, not mod = not false and not true or false = true and false or false = false
-- not locked, not guest, mod = not false and not false or true = true and true and true
%>
<main>
<nav class="darkbg">
@ -24,10 +20,7 @@
<% render("views.common.infobox", {kind = constants.InfoboxKind.LOCK, msg = "This thread is locked."}) %>
<% end -%>
<% if can_post then %>
<h1>Respond to "<%= thread.title %>"</h1>
<form method="post">
<textarea id="post_content" name="post_content" placeholder="Response body" rows=7 required></textarea><br>
<input type="submit" value="Post reply">
</form>
<script src="/static/js/thread.js"></script>
<h1>Respond to "<%= thread.title %>"</h1>
<% render("views.common.babycode-editor", {ta_name="post_content"}) %>
<script src="/static/js/thread.js"></script>
<% end %>