Add markup inserter functionality and render markup filter for enhanced text editing
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
// Simple markup inserter module
|
||||
(function(global){
|
||||
function insertAtCursor(el, text){
|
||||
if(!el) return;
|
||||
const start = el.selectionStart || 0;
|
||||
const end = el.selectionEnd || 0;
|
||||
const val = el.value || '';
|
||||
el.value = val.slice(0, start) + text + val.slice(end);
|
||||
el.selectionStart = el.selectionEnd = start + text.length;
|
||||
el.focus();
|
||||
}
|
||||
|
||||
function createToolbar(){
|
||||
const wrap = document.createElement('div');
|
||||
wrap.className = 'd-flex gap-2 mb-2 markup-inserter-toolbar';
|
||||
const group = document.createElement('div');
|
||||
group.className = 'btn-group';
|
||||
const makeBtn = function(type, text){
|
||||
const b = document.createElement('button');
|
||||
b.type = 'button'; b.className = 'btn btn-sm btn-outline-secondary markup-insert-button';
|
||||
b.dataset.type = type; b.textContent = text; return b;
|
||||
};
|
||||
group.appendChild(makeBtn('case','Insert case'));
|
||||
group.appendChild(makeBtn('finding','Insert finding'));
|
||||
group.appendChild(makeBtn('displayset','Insert displayset'));
|
||||
const hint = document.createElement('div'); hint.className='flex-fill text-muted small align-self-center'; hint.textContent='Use the buttons to insert markup like [[case:123]]';
|
||||
wrap.appendChild(group); wrap.appendChild(hint);
|
||||
return wrap;
|
||||
}
|
||||
|
||||
function attachToolbarToTextarea(textarea){
|
||||
if(!textarea) return null;
|
||||
// avoid double attaching
|
||||
if(textarea._markupToolbarAttached) return null;
|
||||
const toolbar = createToolbar();
|
||||
textarea.parentNode.insertBefore(toolbar, textarea);
|
||||
toolbar.querySelectorAll('.markup-insert-button').forEach(function(btn){
|
||||
btn.addEventListener('click', function(){
|
||||
const t = btn.dataset.type;
|
||||
if(t === 'case'){
|
||||
const id = prompt('Case ID to link to:'); if(!id) return; insertAtCursor(textarea, '[['+t+':'+id+']]' );
|
||||
} else if(t === 'finding'){
|
||||
const id = prompt('Finding ID to link to:'); if(!id) return; insertAtCursor(textarea, '[['+t+':'+id+']]' );
|
||||
} else if(t === 'displayset'){
|
||||
const caseId = prompt('Case ID containing the display set:'); if(!caseId) return; const setName = prompt('Display set name:'); if(!setName) return; insertAtCursor(textarea, '[['+t+':'+caseId+':'+setName+']]' );
|
||||
}
|
||||
});
|
||||
});
|
||||
textarea._markupToolbarAttached = true;
|
||||
return toolbar;
|
||||
}
|
||||
|
||||
function initForNames(names){
|
||||
if(!Array.isArray(names)) return;
|
||||
document.addEventListener('DOMContentLoaded', function(){
|
||||
names.forEach(function(name){
|
||||
const el = document.querySelector('textarea[name="'+name+'"]');
|
||||
if(el) attachToolbarToTextarea(el);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function attachToSelector(selector){
|
||||
document.addEventListener('DOMContentLoaded', function(){
|
||||
document.querySelectorAll(selector).forEach(function(el){
|
||||
if(el.tagName === 'TEXTAREA') attachToolbarToTextarea(el);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Auto-attach to placeholders rendered in HTMX responses or server-side includes.
|
||||
document.addEventListener('DOMContentLoaded', function(){
|
||||
document.querySelectorAll('.markup-toolbar-placeholder').forEach(function(ph){
|
||||
// find next textarea sibling
|
||||
let ta = ph.nextElementSibling;
|
||||
if(ta && ta.tagName === 'TEXTAREA') attachToolbarToTextarea(ta);
|
||||
});
|
||||
});
|
||||
|
||||
document.addEventListener('htmx:afterSwap', function(evt){
|
||||
try{
|
||||
const target = evt.detail && evt.detail.target ? evt.detail.target : document;
|
||||
(target.querySelectorAll ? target : document).querySelectorAll('.markup-toolbar-placeholder').forEach(function(ph){
|
||||
let ta = ph.nextElementSibling;
|
||||
if(ta && ta.tagName === 'TEXTAREA') attachToolbarToTextarea(ta);
|
||||
});
|
||||
}catch(e){/* ignore */}
|
||||
});
|
||||
|
||||
// Expose API
|
||||
global.markupInserter = {
|
||||
attachToolbarToTextarea: attachToolbarToTextarea,
|
||||
initForNames: initForNames,
|
||||
attachToSelector: attachToSelector
|
||||
};
|
||||
|
||||
})(window);
|
||||
Reference in New Issue
Block a user