feat: add inline title editing JavaScript
This commit is contained in:
@@ -549,6 +549,82 @@ recipeImageUploadJs =
|
|||||||
, "})();"
|
, "})();"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
-- | Inline JavaScript for in-place recipe title editing.
|
||||||
|
titleEditJs :: Text
|
||||||
|
titleEditJs =
|
||||||
|
T.unlines
|
||||||
|
[ "(function(){"
|
||||||
|
, "'use strict';"
|
||||||
|
, "var h1 = document.querySelector('.roux-recipe-title');"
|
||||||
|
, "if (!h1) return;"
|
||||||
|
, "var originalText = '';"
|
||||||
|
, "var filenameEl = document.getElementById('roux-current-recipe');"
|
||||||
|
, "if (!filenameEl) return;"
|
||||||
|
, "var recipeFilename = filenameEl.textContent.trim();"
|
||||||
|
, ""
|
||||||
|
, "h1.addEventListener('click', function(e) {"
|
||||||
|
, " if (h1.getAttribute('contenteditable') === 'true') return;"
|
||||||
|
, " originalText = h1.textContent.trim();"
|
||||||
|
, " h1.setAttribute('contenteditable', 'true');"
|
||||||
|
, " h1.classList.add('roux-editing');"
|
||||||
|
, " var range = document.createRange();"
|
||||||
|
, " var sel = window.getSelection();"
|
||||||
|
, " range.selectNodeContents(h1);"
|
||||||
|
, " range.collapse(false);"
|
||||||
|
, " sel.removeAllRanges();"
|
||||||
|
, " sel.addRange(range);"
|
||||||
|
, "});"
|
||||||
|
, ""
|
||||||
|
, "h1.addEventListener('keydown', function(e) {"
|
||||||
|
, " if (e.key === 'Enter' && !e.shiftKey) {"
|
||||||
|
, " e.preventDefault();"
|
||||||
|
, " h1.blur();"
|
||||||
|
, " } else if (e.key === 'Escape') {"
|
||||||
|
, " e.preventDefault();"
|
||||||
|
, " h1.textContent = originalText;"
|
||||||
|
, " h1.removeAttribute('contenteditable');"
|
||||||
|
, " h1.classList.remove('roux-editing');"
|
||||||
|
, " }"
|
||||||
|
, "});"
|
||||||
|
, ""
|
||||||
|
, "h1.addEventListener('blur', function() {"
|
||||||
|
, " if (h1.getAttribute('contenteditable') !== 'true') return;"
|
||||||
|
, " var newText = h1.textContent.trim();"
|
||||||
|
, " if (newText === originalText || !newText) {"
|
||||||
|
, " h1.textContent = originalText;"
|
||||||
|
, " h1.removeAttribute('contenteditable');"
|
||||||
|
, " h1.classList.remove('roux-editing');"
|
||||||
|
, " return;"
|
||||||
|
, " }"
|
||||||
|
, " h1.removeAttribute('contenteditable');"
|
||||||
|
, " h1.classList.remove('roux-editing');"
|
||||||
|
, " var xhr = new XMLHttpRequest();"
|
||||||
|
, " xhr.open('PATCH', '/recipes/' + encodeURIComponent(recipeFilename) + '/title', true);"
|
||||||
|
, " xhr.setRequestHeader('Content-Type', 'application/json');"
|
||||||
|
, " xhr.onload = function() {"
|
||||||
|
, " if (xhr.status === 200) {"
|
||||||
|
, " try {"
|
||||||
|
, " var resp = JSON.parse(xhr.responseText);"
|
||||||
|
, " h1.textContent = resp.title || newText;"
|
||||||
|
, " } catch(e) {"
|
||||||
|
, " h1.textContent = newText;"
|
||||||
|
, " }"
|
||||||
|
, " } else {"
|
||||||
|
, " h1.textContent = originalText;"
|
||||||
|
, " h1.classList.add('roux-title-error');"
|
||||||
|
, " setTimeout(function() { h1.classList.remove('roux-title-error'); }, 2000);"
|
||||||
|
, " }"
|
||||||
|
, " };"
|
||||||
|
, " xhr.onerror = function() {"
|
||||||
|
, " h1.textContent = originalText;"
|
||||||
|
, " h1.classList.add('roux-title-error');"
|
||||||
|
, " setTimeout(function() { h1.classList.remove('roux-title-error'); }, 2000);"
|
||||||
|
, " };"
|
||||||
|
, " xhr.send(JSON.stringify({ title: newText }));"
|
||||||
|
, "});"
|
||||||
|
, "})();"
|
||||||
|
]
|
||||||
|
|
||||||
sseIndexJs :: Text
|
sseIndexJs :: Text
|
||||||
sseIndexJs =
|
sseIndexJs =
|
||||||
T.unlines
|
T.unlines
|
||||||
@@ -713,6 +789,7 @@ recipePage info =
|
|||||||
H.script ! A.type_ "text/javascript" $ H.preEscapedText sseRecipeJs
|
H.script ! A.type_ "text/javascript" $ H.preEscapedText sseRecipeJs
|
||||||
H.script ! A.type_ "text/javascript" $ H.preEscapedText timerJs
|
H.script ! A.type_ "text/javascript" $ H.preEscapedText timerJs
|
||||||
H.script ! A.type_ "text/javascript" $ H.preEscapedText recipeImageUploadJs
|
H.script ! A.type_ "text/javascript" $ H.preEscapedText recipeImageUploadJs
|
||||||
|
H.script ! A.type_ "text/javascript" $ H.preEscapedText titleEditJs
|
||||||
|
|
||||||
-- | Render the body of a recipe page (no page shell).
|
-- | Render the body of a recipe page (no page shell).
|
||||||
renderRecipe :: FilePath -> Recipe -> Html
|
renderRecipe :: FilePath -> Recipe -> Html
|
||||||
|
|||||||
Reference in New Issue
Block a user