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 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>] [<EntryPoint>]
let main args = let main args =
@ -54,6 +63,7 @@ let main args =
let _ = app.UseCookiePolicy (CookiePolicyOptions (MinimumSameSitePolicy = SameSiteMode.Strict)) let _ = app.UseCookiePolicy (CookiePolicyOptions (MinimumSameSitePolicy = SameSiteMode.Strict))
let _ = app.UseStaticFiles () let _ = app.UseStaticFiles ()
let _ = app.UseRouting () let _ = app.UseRouting ()
let _ = app.UseMiddleware<BufferedBodyMiddleware> ()
let _ = app.UseAuthentication () let _ = app.UseAuthentication ()
let _ = app.UseAuthorization () let _ = app.UseAuthorization ()
let _ = app.UseSession () let _ = app.UseSession ()

View File

@ -226,9 +226,10 @@ module Api =
// POST: /api/markdown-preview // POST: /api/markdown-preview
let markdownPreview : HttpHandler = requireUser >=> fun next ctx -> task { let markdownPreview : HttpHandler = requireUser >=> fun next ctx -> task {
let _ = ctx.Request.Body.Seek(0L, SeekOrigin.Begin)
use reader = new StreamReader (ctx.Request.Body) use reader = new StreamReader (ctx.Request.Body)
let! preview = reader.ReadToEndAsync () 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 FullTime = false
Biography = "" Biography = ""
Experience = None Experience = None
Skills = [| Skills = [||]
{ Id = "1"; Description = "test 1"; Notes = None }
{ Id = "3"; Description = "test 2"; Notes = Some "noted" }
{ Id = "4"; Description = "asfasdfa"; Notes = None }
|]
} }
/// Create an instance of this form from the given profile /// 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 "Markdown"
] ]
rawText " &nbsp; " rawText " &nbsp; "
button [ _type "button"; _id $"{name}PreviewButton"; _class "btn btn-outline-secondary btn-sm rounded-pill" button [ _type "button"; _id $"{name}PreviewButton"
_onclick $"jjj.showPreview('{name}')" ] [ _class "btn btn-outline-secondary btn-sm rounded-pill" ] [
rawText "Preview" 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" ] [ div [ _id $"{name}Edit"; _class "form-floating jjj-shown" ] [
textarea (List.append attrs textarea (List.append attrs
[ _id name; _name name; _class "form-control jjj-markdown-editor"; _rows "10" ]) [ [ _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 ] 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 (_) { } } 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 * Show a preview of the Markdown in the given editor
* @param {string} editorId The ID of the Markdown editor whose preview should be shown * @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`) const editBtn = document.getElementById(`${editorId}EditButton`)
/** @type {HTMLDivElement} */ /** @type {HTMLDivElement} */
const editDiv = document.getElementById(`${editorId}Edit`) const editDiv = document.getElementById(`${editorId}Edit`)
/** @type {HTMLTextAreaElement} */
const editor = document.getElementById(editorId)
/** @type {HTMLButtonElement} */ /** @type {HTMLButtonElement} */
const previewBtn = document.getElementById(`${editorId}PreviewButton`) const previewBtn = document.getElementById(`${editorId}PreviewButton`)
/** @type {HTMLDivElement} */ /** @type {HTMLDivElement} */
@ -69,20 +79,12 @@ this.jjj = {
editBtn.classList.remove("btn-primary") editBtn.classList.remove("btn-primary")
editBtn.classList.add("btn-outline-secondary") 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.remove("btn-outline-secondary")
previewBtn.classList.add("btn-primary") previewBtn.classList.add("btn-primary")
previewBtn.removeAttribute("onclick") previewBtn.removeEventListener("click", () => { this.showPreview(editorId) })
editDiv.classList.remove("jjj-shown") const preview = await fetch("/api/markdown-preview", { method: "POST", body: editor.value })
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 })
let text let text
if (preview.ok) { if (preview.ok) {
text = await preview.text() text = await preview.text()
@ -90,6 +92,12 @@ this.jjj = {
text = `<p class="text-danger"><strong> ERROR ${preview.status}</strong> &ndash; ${preview.statusText}` text = `<p class="text-danger"><strong> ERROR ${preview.status}</strong> &ndash; ${preview.statusText}`
} }
previewDiv.innerHTML = text 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`) const editBtn = document.getElementById(`${editorId}EditButton`)
/** @type {HTMLDivElement} */ /** @type {HTMLDivElement} */
const editDiv = document.getElementById(`${editorId}Edit`) const editDiv = document.getElementById(`${editorId}Edit`)
/** @type {HTMLTextAreaElement} */
const editor = document.getElementById(editorId)
/** @type {HTMLButtonElement} */ /** @type {HTMLButtonElement} */
const previewBtn = document.getElementById(`${editorId}PreviewButton`) const previewBtn = document.getElementById(`${editorId}PreviewButton`)
/** @type {HTMLDivElement} */ /** @type {HTMLDivElement} */
const previewDiv = document.getElementById(`${editorId}Preview`) const previewDiv = document.getElementById(`${editorId}Preview`)
previewtBtn.classList.remove("btn-primary") previewBtn.classList.remove("btn-primary")
previewBtn.classList.add("btn-outline-secondary") previewBtn.classList.add("btn-outline-secondary")
previewtBtn.addAttribute("onclick", `jjj.showPreview('{editorId}')`) this.markdownOnLoad(editorId)
editBtn.classList.remove("btn-outline-secondary") editBtn.classList.remove("btn-outline-secondary")
editBtn.classList.add("btn-primary") editBtn.classList.add("btn-primary")
editBtn.removeAttribute("onclick") editBtn.removeEventListener("click", () => { this.showEditor(editorId) })
previewDiv.classList.remove("jjj-shown") previewDiv.classList.remove("jjj-shown")
previewDiv.classList.add("jjj-not-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 */ would be; this overrides that for this textarea specifically */
height: inherit !important; height: inherit !important;
} }
.jjj-markdown-preview {
border: solid 1px lightgray;
border-radius: .5rem;
}
/* Footer styling */ /* Footer styling */
footer { footer {
display: flex; display: flex;