Enhance markup inserter with logging and auto-attach functionality for textareas
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
// Simple markup inserter module
|
// Simple markup inserter module
|
||||||
(function(global){
|
(function(global){
|
||||||
|
console.log('markup_inserter.js loaded');
|
||||||
function insertAtCursor(el, text){
|
function insertAtCursor(el, text){
|
||||||
if(!el) return;
|
if(!el) return;
|
||||||
const start = el.selectionStart || 0;
|
const start = el.selectionStart || 0;
|
||||||
@@ -32,6 +33,7 @@
|
|||||||
if(!textarea) return null;
|
if(!textarea) return null;
|
||||||
// avoid double attaching
|
// avoid double attaching
|
||||||
if(textarea._markupToolbarAttached) return null;
|
if(textarea._markupToolbarAttached) return null;
|
||||||
|
console.log('Attaching markup toolbar to textarea', textarea);
|
||||||
const toolbar = createToolbar();
|
const toolbar = createToolbar();
|
||||||
textarea.parentNode.insertBefore(toolbar, textarea);
|
textarea.parentNode.insertBefore(toolbar, textarea);
|
||||||
toolbar.querySelectorAll('.markup-insert-button').forEach(function(btn){
|
toolbar.querySelectorAll('.markup-insert-button').forEach(function(btn){
|
||||||
@@ -53,6 +55,7 @@
|
|||||||
function initForNames(names){
|
function initForNames(names){
|
||||||
if(!Array.isArray(names)) return;
|
if(!Array.isArray(names)) return;
|
||||||
document.addEventListener('DOMContentLoaded', function(){
|
document.addEventListener('DOMContentLoaded', function(){
|
||||||
|
console.log('markupInserter.initForNames DOMContentLoaded handler running for', names);
|
||||||
names.forEach(function(name){
|
names.forEach(function(name){
|
||||||
const el = document.querySelector('textarea[name="'+name+'"]');
|
const el = document.querySelector('textarea[name="'+name+'"]');
|
||||||
if(el) attachToolbarToTextarea(el);
|
if(el) attachToolbarToTextarea(el);
|
||||||
@@ -70,23 +73,48 @@
|
|||||||
|
|
||||||
// Auto-attach to placeholders rendered in HTMX responses or server-side includes.
|
// Auto-attach to placeholders rendered in HTMX responses or server-side includes.
|
||||||
document.addEventListener('DOMContentLoaded', function(){
|
document.addEventListener('DOMContentLoaded', function(){
|
||||||
|
console.log('markupInserter DOMContentLoaded auto-attach running');
|
||||||
|
// Attach to explicit placeholders
|
||||||
document.querySelectorAll('.markup-toolbar-placeholder').forEach(function(ph){
|
document.querySelectorAll('.markup-toolbar-placeholder').forEach(function(ph){
|
||||||
// find next textarea sibling
|
|
||||||
let ta = ph.nextElementSibling;
|
let ta = ph.nextElementSibling;
|
||||||
if(ta && ta.tagName === 'TEXTAREA') attachToolbarToTextarea(ta);
|
if(ta && ta.tagName === 'TEXTAREA') attachToolbarToTextarea(ta);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Also auto-init for common case textarea names to cover full-page forms
|
||||||
|
try{ initForNames(['description','history','discussion','report','notes']); }catch(e){}
|
||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener('htmx:afterSwap', function(evt){
|
document.addEventListener('htmx:afterSwap', function(evt){
|
||||||
|
console.log('markupInserter htmx:afterSwap event', evt && evt.detail && evt.detail.target);
|
||||||
try{
|
try{
|
||||||
const target = evt.detail && evt.detail.target ? evt.detail.target : document;
|
const target = evt.detail && evt.detail.target ? evt.detail.target : document;
|
||||||
(target.querySelectorAll ? target : document).querySelectorAll('.markup-toolbar-placeholder').forEach(function(ph){
|
(target.querySelectorAll ? target : document).querySelectorAll('.markup-toolbar-placeholder').forEach(function(ph){
|
||||||
let ta = ph.nextElementSibling;
|
let ta = ph.nextElementSibling;
|
||||||
if(ta && ta.tagName === 'TEXTAREA') attachToolbarToTextarea(ta);
|
if(ta && ta.tagName === 'TEXTAREA') attachToolbarToTextarea(ta);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Also attach to any textareas within the swapped target that match our common names
|
||||||
|
var ctx = evt.detail && evt.detail.target ? evt.detail.target : document;
|
||||||
|
['description','history','discussion','report','notes'].forEach(function(name){
|
||||||
|
ctx.querySelectorAll && ctx.querySelectorAll('textarea[name="'+name+'"]') && ctx.querySelectorAll('textarea[name="'+name+'"]').forEach(function(ta){ attachToolbarToTextarea(ta); });
|
||||||
|
});
|
||||||
}catch(e){/* ignore */}
|
}catch(e){/* ignore */}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Fallback: attach toolbar when a textarea receives focus (covers dynamically created editors)
|
||||||
|
document.addEventListener('focusin', function(e){
|
||||||
|
console.log('markupInserter focusin event for', e && e.target);
|
||||||
|
try{
|
||||||
|
var el = e.target;
|
||||||
|
if(!el || el.tagName !== 'TEXTAREA') return;
|
||||||
|
// Attach for common names or if a placeholder is present
|
||||||
|
var name = el.getAttribute('name') || '';
|
||||||
|
if(['description','history','discussion','report','notes'].includes(name) || (el.previousElementSibling && el.previousElementSibling.classList && el.previousElementSibling.classList.contains('markup-toolbar-placeholder'))){
|
||||||
|
attachToolbarToTextarea(el);
|
||||||
|
}
|
||||||
|
}catch(err){/* ignore */}
|
||||||
|
}, true);
|
||||||
|
|
||||||
// Expose API
|
// Expose API
|
||||||
global.markupInserter = {
|
global.markupInserter = {
|
||||||
attachToolbarToTextarea: attachToolbarToTextarea,
|
attachToolbarToTextarea: attachToolbarToTextarea,
|
||||||
|
|||||||
@@ -146,58 +146,58 @@
|
|||||||
|
|
||||||
|
|
||||||
<!-- Differential Collapsible Panel -->
|
<!-- Differential Collapsible Panel -->
|
||||||
<div class="card my-3 bg-dark text-white">
|
<div class="card my-3 bg-dark text-white">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<a class="collapsed text-white" data-bs-toggle="collapse" href="#differentialPanel" role="button" aria-expanded="false" aria-controls="differentialPanel">
|
<a class="collapsed text-white" data-bs-toggle="collapse" href="#differentialPanel" role="button" aria-expanded="false" aria-controls="differentialPanel">
|
||||||
<h3 class="d-inline">Differential</h3>
|
<h3 class="d-inline">Differential</h3>
|
||||||
<span class="ms-2">(click to expand/collapse)</span>
|
<span class="ms-2">(click to expand/collapse)</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="collapse" id="differentialPanel">
|
<div class="collapse" id="differentialPanel">
|
||||||
<input type="button" value="Add Another Differential" id="add_more_differential" class="btn btn-sm btn-primary float-end">
|
<input type="button" value="Add Another Differential" id="add_more_differential" class="btn btn-sm btn-primary float-end">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div id="casedifferential_formset" class="list_formset">
|
<div id="casedifferential_formset" class="list_formset">
|
||||||
<ol>
|
<ol>
|
||||||
{% for form in casedifferential_formset %}
|
{% for form in casedifferential_formset %}
|
||||||
<li class="no-error casedifferential_formset">
|
<li class="no-error casedifferential_formset">
|
||||||
{{form.non_field_errors}}
|
{{form.non_field_errors}}
|
||||||
{{form.errors}}
|
{{form.errors}}
|
||||||
{{ form | crispy}}
|
{{ form | crispy}}
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ol>
|
</ol>
|
||||||
|
</div>
|
||||||
|
{{ casedifferential_formset.management_form | crispy }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{ casedifferential_formset.management_form | crispy }}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Resource Collapsible Panel -->
|
<!-- Resource Collapsible Panel -->
|
||||||
<div class="card my-3 bg-dark text-white">
|
<div class="card my-3 bg-dark text-white">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<a class="collapsed text-white" data-bs-toggle="collapse" href="#resourcePanel" role="button" aria-expanded="false" aria-controls="resourcePanel">
|
<a class="collapsed text-white" data-bs-toggle="collapse" href="#resourcePanel" role="button" aria-expanded="false" aria-controls="resourcePanel">
|
||||||
<h3 class="d-inline">Resource</h3>
|
<h3 class="d-inline">Resource</h3>
|
||||||
<span class="ms-2">(click to expand/collapse)</span>
|
<span class="ms-2">(click to expand/collapse)</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="collapse" id="resourcePanel">
|
<div class="collapse" id="resourcePanel">
|
||||||
<input type="button" value="Add Another Resource" id="add_more_resource" class="btn btn-sm btn-primary float-end">
|
<input type="button" value="Add Another Resource" id="add_more_resource" class="btn btn-sm btn-primary float-end">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div id="caseresource_formset" class="list_formset">
|
<div id="caseresource_formset" class="list_formset">
|
||||||
<ol>
|
<ol>
|
||||||
{% for form in caseresource_formset %}
|
{% for form in caseresource_formset %}
|
||||||
<li class="no-error caseresource_formset">
|
<li class="no-error caseresource_formset">
|
||||||
{{form.non_field_errors}}
|
{{form.non_field_errors}}
|
||||||
{{form.errors}}
|
{{form.errors}}
|
||||||
{{ form | crispy}}
|
{{ form | crispy}}
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ol>
|
</ol>
|
||||||
|
</div>
|
||||||
|
{{ caseresource_formset.management_form | crispy}}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{ caseresource_formset.management_form | crispy}}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<br/>
|
<br/>
|
||||||
<input type="submit" class="submit-button" value="Submit" name="submit">
|
<input type="submit" class="submit-button" value="Submit" name="submit">
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
Reference in New Issue
Block a user