Finish Markdown editor/preview

This commit is contained in:
Daniel J. Summers 2023-01-14 14:13:45 -05:00
parent f813824648
commit e4a86ab701
6 changed files with 50 additions and 23 deletions

View File

@ -17,6 +17,15 @@ open Microsoft.Extensions.Hosting
open NodaTime
/// Enable buffering on the request body
type BufferedBodyMiddleware (next : RequestDelegate) =
member _.InvokeAsync (ctx : HttpContext) = task {
ctx.Request.EnableBuffering ()
return! next.Invoke ctx
}
[<EntryPoint>]
let main args =
@ -54,6 +63,7 @@ let main args =
let _ = app.UseCookiePolicy (CookiePolicyOptions (MinimumSameSitePolicy = SameSiteMode.Strict))
let _ = app.UseStaticFiles ()
let _ = app.UseRouting ()
let _ = app.UseMiddleware<BufferedBodyMiddleware> ()
let _ = app.UseAuthentication ()
let _ = app.UseAuthorization ()
let _ = app.UseSession ()

View File

@ -226,9 +226,10 @@ module Api =
// POST: /api/markdown-preview
let markdownPreview : HttpHandler = requireUser >=> fun next ctx -> task {
let _ = ctx.Request.Body.Seek(0L, SeekOrigin.Begin)
use reader = new StreamReader (ctx.Request.Body)
let! preview = reader.ReadToEndAsync ()
return! htmlString (MarkdownString.toHtml (Text (defaultArg (Option.ofObj preview) "--"))) next ctx
return! htmlString (MarkdownString.toHtml (Text preview)) next ctx
}

View File

@ -60,11 +60,7 @@ module EditProfileViewModel =
FullTime = false
Biography = ""
Experience = None
Skills = [|
{ Id = "1"; Description = "test 1"; Notes = None }
{ Id = "3"; Description = "test 2"; Notes = Some "noted" }
{ Id = "4"; Description = "asfasdfa"; Notes = None }
|]
Skills = [||]
}
/// Create an instance of this form from the given profile

View File

@ -37,12 +37,13 @@ let markdownEditor attrs name value editorLabel =
rawText "Markdown"
]
rawText " &nbsp; "
button [ _type "button"; _id $"{name}PreviewButton"; _class "btn btn-outline-secondary btn-sm rounded-pill"
_onclick $"jjj.showPreview('{name}')" ] [
button [ _type "button"; _id $"{name}PreviewButton"
_class "btn btn-outline-secondary btn-sm rounded-pill" ] [
rawText "Preview"
]
]
section [ _id $"{name}Preview"; _class "jjj-not-shown"; _ariaLabel "Rendered Markdown preview" ] []
section [ _id $"{name}Preview"; _class "jjj-not-shown jjj-markdown-preview px-2 pt-2"
_ariaLabel "Rendered Markdown preview" ] []
div [ _id $"{name}Edit"; _class "form-floating jjj-shown" ] [
textarea (List.append attrs
[ _id name; _name name; _class "form-control jjj-markdown-editor"; _rows "10" ]) [
@ -50,4 +51,9 @@ let markdownEditor attrs name value editorLabel =
]
label [ _for name ] [ rawText editorLabel ]
]
script [] [
rawText """document.addEventListener("DOMContentLoaded", function () {"""
rawText $" jjj.markdownOnLoad('{name}') "
rawText "})"
]
]

View File

@ -53,6 +53,14 @@ this.jjj = {
} catch (_) { }
},
/**
* Set up the onClick event for the preview button
* @param {string} editorId The ID of the editor to wire up
*/
markdownOnLoad(editorId) {
document.getElementById(`${editorId}PreviewButton`).addEventListener("click", () => { this.showPreview(editorId) })
},
/**
* Show a preview of the Markdown in the given editor
* @param {string} editorId The ID of the Markdown editor whose preview should be shown
@ -62,6 +70,8 @@ this.jjj = {
const editBtn = document.getElementById(`${editorId}EditButton`)
/** @type {HTMLDivElement} */
const editDiv = document.getElementById(`${editorId}Edit`)
/** @type {HTMLTextAreaElement} */
const editor = document.getElementById(editorId)
/** @type {HTMLButtonElement} */
const previewBtn = document.getElementById(`${editorId}PreviewButton`)
/** @type {HTMLDivElement} */
@ -69,20 +79,12 @@ this.jjj = {
editBtn.classList.remove("btn-primary")
editBtn.classList.add("btn-outline-secondary")
editBtn.addAttribute("onclick", `jjj.showEditor('{editorId}')`)
editBtn.addEventListener("click", () => { this.showEditor(editorId) })
previewBtn.classList.remove("btn-outline-secondary")
previewBtn.classList.add("btn-primary")
previewBtn.removeAttribute("onclick")
editDiv.classList.remove("jjj-shown")
editDiv.classList.add("jjj-not-shown")
previewDiv.innerHTML = "<p><strong><em>Loading preview...</em></strong></p>"
previewtDiv.classList.remove("jjj-not-shown")
previewDiv.classList.add("jjj-shown")
const preview = await fetch("/api/markdown-preview",
{ method: "POST", body: document.getElementById(editorId).textContent })
previewBtn.removeEventListener("click", () => { this.showPreview(editorId) })
const preview = await fetch("/api/markdown-preview", { method: "POST", body: editor.value })
let text
if (preview.ok) {
text = await preview.text()
@ -90,6 +92,12 @@ this.jjj = {
text = `<p class="text-danger"><strong> ERROR ${preview.status}</strong> &ndash; ${preview.statusText}`
}
previewDiv.innerHTML = text
editDiv.classList.remove("jjj-shown")
editDiv.classList.add("jjj-not-shown")
previewDiv.classList.remove("jjj-not-shown")
previewDiv.classList.add("jjj-shown")
},
/**
@ -101,17 +109,19 @@ this.jjj = {
const editBtn = document.getElementById(`${editorId}EditButton`)
/** @type {HTMLDivElement} */
const editDiv = document.getElementById(`${editorId}Edit`)
/** @type {HTMLTextAreaElement} */
const editor = document.getElementById(editorId)
/** @type {HTMLButtonElement} */
const previewBtn = document.getElementById(`${editorId}PreviewButton`)
/** @type {HTMLDivElement} */
const previewDiv = document.getElementById(`${editorId}Preview`)
previewtBtn.classList.remove("btn-primary")
previewBtn.classList.remove("btn-primary")
previewBtn.classList.add("btn-outline-secondary")
previewtBtn.addAttribute("onclick", `jjj.showPreview('{editorId}')`)
this.markdownOnLoad(editorId)
editBtn.classList.remove("btn-outline-secondary")
editBtn.classList.add("btn-primary")
editBtn.removeAttribute("onclick")
editBtn.removeEventListener("click", () => { this.showEditor(editorId) })
previewDiv.classList.remove("jjj-shown")
previewDiv.classList.add("jjj-not-shown")

View File

@ -212,6 +212,10 @@ span.jjj-audio-clip:hover {
would be; this overrides that for this textarea specifically */
height: inherit !important;
}
.jjj-markdown-preview {
border: solid 1px lightgray;
border-radius: .5rem;
}
/* Footer styling */
footer {
display: flex;