feat: add syntax highlighting to post editor
This commit is contained in:
parent
6efbd749be
commit
593036a712
|
@ -12,12 +12,26 @@
|
|||
|
||||
<input type="hidden" data-blog-pid="@post.Id">
|
||||
|
||||
<div style="margin-bottom: 20px;">
|
||||
<button id="save-button" class="btn btn-primary"><i class="fa-solid fa-floppy-disk fa-fw"></i> Save <span class="text-muted">(Ctrl+S)</span></button>
|
||||
<a href="/blog/@post.Published.ToString(@"yyyy\/MM\/dd")/@post.Slug" target="_blank" class="btn btn-info"><i class="fa-solid fa-magnifying-glass"></i> Preview</a>
|
||||
</div>
|
||||
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th>Post ID</th>
|
||||
<td><input class="form-control" type="text" value="@post.Id" disabled="disabled"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Title</th>
|
||||
<td><input class="form-control" id="post-title" type="text" value="@post.Title"></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="row" style="margin-top: 20px;">
|
||||
<div class="col-md-6 col-sm-12">
|
||||
<textarea id="content" style="width: 100%; font-family: monospace; min-height: calc(100vh - 80px); max-height: 100%">@post.Body</textarea>
|
||||
<div id="editing-area" class="col-md-6 col-sm-12">
|
||||
<textarea id="content" spellcheck="false">@post.Body</textarea>
|
||||
<pre id="highlighting" aria-hidden="true"><code id="highlighting-content" class="language-markdown">@post.Body</code></pre>
|
||||
</div>
|
||||
<div class="col-md-6 col-sm-12" style="overflow-y: scroll; background: #1E1E1E">
|
||||
<article id="article-preview" style="background: #333; max-width: 700px; margin: 20px auto; padding: 20px;">
|
||||
|
|
|
@ -41,3 +41,66 @@ pre {
|
|||
code[class*="language-"] {
|
||||
background: none !important;
|
||||
}
|
||||
|
||||
article {
|
||||
*:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
div.alert {
|
||||
*:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
#save-button {
|
||||
transition: 0.4s;
|
||||
width: 10em;
|
||||
}
|
||||
|
||||
textarea {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
#editing-area {
|
||||
.toolbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
#highlighting, #content {
|
||||
margin: 10px;
|
||||
padding: 10px;
|
||||
border: 0;
|
||||
width: calc(100% - 32px);
|
||||
height: 500px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#highlighting, #content, #highlighting * {
|
||||
font-size: 12pt;
|
||||
font-family: monospace;
|
||||
line-height: 15pt;
|
||||
}
|
||||
|
||||
#highlighting {
|
||||
margin-top: -511px;
|
||||
z-index: 0;
|
||||
background: #1E1E1E;
|
||||
}
|
||||
|
||||
#content {
|
||||
z-index: 10;
|
||||
color: transparent;
|
||||
background: transparent;
|
||||
caret-color: #FFFFFF;
|
||||
resize: none;
|
||||
overflow-wrap: normal;
|
||||
overflow-x: scroll;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
#highlighting-content, #highlighting-content code {
|
||||
white-space: pre !important;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
import BlogPost from "../app/BlogPost";
|
||||
import API from "../app/API";
|
||||
import UI from "../app/UI";
|
||||
|
||||
declare const Prism: any;
|
||||
|
||||
(() => {
|
||||
getCurrentBlogPost().then(post => {
|
||||
|
@ -7,9 +10,12 @@ import API from "../app/API";
|
|||
return;
|
||||
}
|
||||
|
||||
const saveButton = document.getElementById("save-button");
|
||||
const preview = document.getElementById("article-preview");
|
||||
const saveButton = document.getElementById("save-button") as HTMLButtonElement;
|
||||
const preview = document.getElementById("article-preview") as HTMLAnchorElement;
|
||||
const content = document.getElementById("content") as HTMLTextAreaElement;
|
||||
const title = document.getElementById("post-title") as HTMLInputElement;
|
||||
const highlighting = document.getElementById("highlighting");
|
||||
const highlightingContent = document.getElementById("highlighting-content");
|
||||
|
||||
saveButton.addEventListener("click", async (e: MouseEvent) => {
|
||||
await savePost();
|
||||
|
@ -20,6 +26,22 @@ import API from "../app/API";
|
|||
e.preventDefault();
|
||||
await savePost();
|
||||
preview.innerHTML = post.content;
|
||||
UI.updateUI(preview);
|
||||
// Prism.highlightAllUnder(preview);
|
||||
}
|
||||
});
|
||||
|
||||
content.addEventListener("keydown", async (e: KeyboardEvent) => {
|
||||
if (e.key === "Tab") {
|
||||
e.preventDefault();
|
||||
|
||||
const start = content.selectionStart;
|
||||
const end = content.selectionEnd;
|
||||
const text = content.value;
|
||||
content.value = `${text.slice(0, start)} ${text.slice(start, end)}`;
|
||||
updateEditView();
|
||||
content.selectionStart = start + 4;
|
||||
content.selectionEnd = end ? end + 4 : start + 4;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -43,6 +65,34 @@ import API from "../app/API";
|
|||
saveButton.innerHTML = '<i class="fa-solid fa-floppy-disk fa-fw"></i> Save <span class="text-muted">(Ctrl+S)</span>';
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
updateEditView();
|
||||
content.addEventListener("input", () => updateEditView());
|
||||
content.addEventListener("scroll", () => syncEditorScroll());
|
||||
function updateEditView() {
|
||||
highlightingContent.innerHTML = Prism.highlight(content.value, Prism.languages.markdown);
|
||||
document.querySelectorAll("#highlighting-content span.token.code").forEach(el => {
|
||||
const languageSpan = el.querySelector(".code-language") as HTMLSpanElement;
|
||||
if (!languageSpan) {
|
||||
return;
|
||||
}
|
||||
|
||||
const language = languageSpan.innerText;
|
||||
const span = el.querySelector(".code-block");
|
||||
if (!span) {
|
||||
return;
|
||||
}
|
||||
|
||||
span.outerHTML = `<code class="${span.className} language-${language}" style="padding:0;">${span.innerHTML}</code>`;
|
||||
Prism.highlightAllUnder(highlightingContent);
|
||||
});
|
||||
syncEditorScroll();
|
||||
}
|
||||
|
||||
function syncEditorScroll() {
|
||||
highlighting.scrollTop = content.scrollTop;
|
||||
highlighting.scrollLeft = content.scrollLeft;
|
||||
}
|
||||
});
|
||||
|
||||
async function getCurrentBlogPost(): Promise<BlogPost> {
|
||||
|
|
Loading…
Reference in New Issue