diff --git a/common/static/common/scripts/markdown-preview.js b/common/static/common/scripts/markdown-preview.js new file mode 100644 index 00000000..84b60d88 --- /dev/null +++ b/common/static/common/scripts/markdown-preview.js @@ -0,0 +1,78 @@ +const activeTabCls = "is-active" + +function initMarkdownPreview(fieldName, texts) { + const field = document.querySelector(`textarea.form-control[name="${fieldName}"]`); + const markdown = document.querySelector(`#markdown-preview-${fieldName}`); + const tokenInput = document.querySelector(`input[name=csrfmiddlewaretoken]`) + const writeTab = document.querySelector(`#tab-write-${fieldName}`); + const previewTab = document.querySelector(`#tab-preview-${fieldName}`); + const write = document.querySelector(`#write-${fieldName}`); + const preview = document.querySelector(`#preview-${fieldName}`); + let lastText; // To avoid calling multiple times unchanged text + + function toggleActiveTab() { + writeTab.classList.toggle(activeTabCls); + previewTab.classList.toggle(activeTabCls); + } + + function opacity_75(text) { + return `${text}`; + } + + function nothingToPreview() { + markdown.innerHTML = opacity_75(texts.nothing_to_preview); + } + + function switchTabs(on, off) { + on.style.display = "block"; + off.style.display = "none"; + toggleActiveTab(); + } + + if (previewTab && field) { + previewTab.addEventListener("click", function (e) { + e.preventDefault(); + if (!previewTab.classList.contains(activeTabCls)) { + switchTabs(preview, write) + if (field.value !== lastText) { + markdown.innerHTML = opacity_75(texts.loading); + lastText = field.value; + if (field.value.trim()) { + fetch("/markdown/", { + method: "POST", + headers: { + "X-CSRFToken": tokenInput.value, + }, + body: new URLSearchParams({ + text: field.value, + }), + }) + .then((response) => response.json()) + .then((data) => { + const preview_markdown = data.markdown; + if (preview_markdown.trim()) { + markdown.innerHTML = preview_markdown; + } else { + nothingToPreview(); + } + }) + .catch((err) => { + lastText = undefined; + markdown.innerHTML = opacity_75(texts.error); + }); + } else { + nothingToPreview(); + } + } + } + }); + } + if (writeTab) { + writeTab.addEventListener("click", function (e) { + e.preventDefault(); + if (!writeTab.classList.contains(activeTabCls)) { + switchTabs(write, preview) + } + }); + } +} diff --git a/common/templates/common/components/field.html b/common/templates/common/components/field.html index 826cc098..4730591d 100644 --- a/common/templates/common/components/field.html +++ b/common/templates/common/components/field.html @@ -1,7 +1,8 @@ -{% load common %} +{% load i18n common %} {% spaceless %} {% with type=field.field.widget.input_type classes=classes|default:"" placeholder=placeholder|default:"" %} {% with field=field|remove_cols_rows|add_classes:classes|set_placeholder:placeholder %} + {% with is_markdown=field.name|is_markdown_field %} {% autoescape off %} {% firstof label field.label as label %} {% firstof help_text field.help_text as help_text %} @@ -27,6 +28,16 @@ {% if field.field.required or required %}*{% endif %} {% endif %} + + {% if is_markdown and not field.is_hidden %} +
+ + {% endif %} {% if icon %}