feat(post-form): EasyMDE editor + /post-scoped ESM bundle (U3)
Add a page-scoped js/src/post-form.js bundle (built ESM + code-splitting so U4's heic-to becomes a deferred chunk) loaded only by post-form.html.twig via a new base head_assets block. EasyMDE replaces the bare textarea with a minimal toolbar (bold/italic/list/link/preview), FA-free via CSS glyphs, and syncs codemirror.save() on change + capture-phase submit so the inline required-field validator and payload see the live value. Refs R7, R15, KTD2, KTD3.
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* /post journal form bundle — page-scoped (loaded only by post-form.html.twig).
|
||||
* Kept out of the global main.js so EasyMDE (+ the U4 HEIC converter, lazy-loaded)
|
||||
* never ship on any other page. Built as ESM with code-splitting so the heic-to
|
||||
* dynamic import becomes a separately-fetched chunk (see package.json / KTD2).
|
||||
*
|
||||
* Every init function presence-guards its own DOM, mirroring main.js's
|
||||
* initTripStats pattern, so this is a no-op if an element is absent.
|
||||
*/
|
||||
import EasyMDE from 'easymde';
|
||||
import 'easymde/dist/easymde.min.css';
|
||||
import './post-form.css';
|
||||
|
||||
/* ── Markdown editor (EasyMDE) ───────────────────────────── */
|
||||
function initEditor() {
|
||||
var textarea = document.querySelector('textarea[name="data[content]"]');
|
||||
if (!textarea) return null;
|
||||
|
||||
var editor = new EasyMDE({
|
||||
element: textarea,
|
||||
spellChecker: false,
|
||||
autoDownloadFontAwesome: false, // toolbar glyphs come from post-form.css, no FA CDN
|
||||
status: false,
|
||||
placeholder: 'What happened today?',
|
||||
minHeight: '180px',
|
||||
// Minimal toolbar; custom classNames drive the CSS glyphs in post-form.css.
|
||||
toolbar: [
|
||||
{ name: 'bold', action: EasyMDE.toggleBold, className: 'mde-btn mde-bold', title: 'Bold' },
|
||||
{ name: 'italic', action: EasyMDE.toggleItalic, className: 'mde-btn mde-italic', title: 'Italic' },
|
||||
{ name: 'unordered-list', action: EasyMDE.toggleUnorderedList, className: 'mde-btn mde-ul', title: 'Bulleted list' },
|
||||
{ name: 'link', action: EasyMDE.drawLink, className: 'mde-btn mde-link', title: 'Insert link' },
|
||||
'|',
|
||||
{ name: 'preview', action: EasyMDE.togglePreview, className: 'mde-btn mde-preview', title: 'Toggle preview' },
|
||||
],
|
||||
});
|
||||
|
||||
// KTD3 / R15: keep the underlying <textarea> in sync with the editor so the
|
||||
// form's custom required-field validator (which reads [name="data[content]"])
|
||||
// and the submitted payload both see the live value.
|
||||
editor.codemirror.on('change', function () { editor.codemirror.save(); });
|
||||
|
||||
var form = textarea.closest('form');
|
||||
if (form) {
|
||||
// Capture phase fires before the inline bubble-phase validator, so the
|
||||
// textarea already holds the current value when validation reads it.
|
||||
form.addEventListener('submit', function () { editor.codemirror.save(); }, true);
|
||||
}
|
||||
|
||||
return editor;
|
||||
}
|
||||
|
||||
/* ── Boot ────────────────────────────────────────────────── */
|
||||
function boot() {
|
||||
// Exposed for later units (U6 draft restore) and the Playwright specs;
|
||||
// null when this bundle loads on a page without the content field.
|
||||
window.postFormEditor = initEditor();
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', boot);
|
||||
} else {
|
||||
boot();
|
||||
}
|
||||
Reference in New Issue
Block a user