Enhance JSON display by integrating pretty JSON formatter in various templates

This commit is contained in:
Ross
2026-03-16 12:08:34 +00:00
parent 3ea51f976d
commit 1cadbab742
5 changed files with 80 additions and 78 deletions
@@ -169,7 +169,13 @@
<div class="mt-3"> <div class="mt-3">
<details> <details>
<summary class="small">Annotation JSON</summary> <summary class="small">Annotation JSON</summary>
<div class="mt-2 small">{% if question.image_annotations %}<pre class="mb-0">{{ question.image_annotations }}</pre>{% else %}No saved annotations.{% endif %}</div> <div class="mt-2 small">
{% if question.image_annotations %}
{% include 'atlas/partials/json_pretty.html' with json_value=question.image_annotations title='Image annotations' %}
{% else %}
No saved annotations.
{% endif %}
</div>
</details> </details>
</div> </div>
@@ -743,12 +743,10 @@
<details> <details>
<summary class="small text-muted" style="cursor:pointer;">Show advanced (viewer state & annotations)</summary> <summary class="small text-muted" style="cursor:pointer;">Show advanced (viewer state & annotations)</summary>
<div> <div>
<strong>Viewer State:</strong> {% include 'atlas/partials/json_pretty.html' with json_value=ds.viewerstate title='Viewer State' %}
<pre class="small">{{ ds.viewerstate|default:"{}" }}</pre>
</div> </div>
<div> <div>
<strong>Annotations:</strong> {% include 'atlas/partials/json_pretty.html' with json_value=ds.annotations title='Annotations' %}
<pre class="small">{{ ds.annotations|default:"{}" }}</pre>
</div> </div>
</details> </details>
</div> </div>
@@ -67,12 +67,10 @@
<details> <details>
<summary class="small text-muted" style="cursor:pointer;">Show advanced (viewer state & annotations)</summary> <summary class="small text-muted" style="cursor:pointer;">Show advanced (viewer state & annotations)</summary>
<div> <div>
<strong>Viewer State:</strong> {% include 'atlas/partials/json_pretty.html' with json_value=ds.viewerstate title='Viewer State' %}
<pre class="small">{{ ds.viewerstate|default:"{}" }}</pre>
</div> </div>
<div> <div>
<strong>Annotations:</strong> {% include 'atlas/partials/json_pretty.html' with json_value=ds.annotations title='Annotations' %}
<pre class="small">{{ ds.annotations|default:"{}" }}</pre>
</div> </div>
</details> </details>
</div> </div>
@@ -71,26 +71,26 @@
hx-after hx-after
class="btn btn-warning btn-sm" class="btn btn-warning btn-sm"
onclick=" onclick="
this.style.display='none'; this.style.display='none';
var li = this.closest('li'); var li = this.closest('li');
var ul = li.parentNode; var ul = li.parentNode;
ul.insertBefore(li, ul.firstChild); ul.insertBefore(li, ul.firstChild);
li.scrollIntoView({behavior: 'smooth', block: 'center'}); li.scrollIntoView({behavior: 'smooth', block: 'center'});
setTimeout(function() { setTimeout(function() {
var form = document.querySelector('#displayset-edit-form-{{ ds.pk }} form'); var form = document.querySelector('#displayset-edit-form-{{ ds.pk }} form');
if (form) { if (form) {
var cancelBtn = document.createElement('button'); var cancelBtn = document.createElement('button');
cancelBtn.type = 'button'; cancelBtn.type = 'button';
cancelBtn.className = 'btn btn-secondary btn-sm ms-2'; cancelBtn.className = 'btn btn-secondary btn-sm ms-2';
cancelBtn.textContent = 'Cancel'; cancelBtn.textContent = 'Cancel';
cancelBtn.onclick = function() { cancelBtn.onclick = function() {
document.querySelector('#displayset-edit-form-{{ ds.pk }}').innerHTML = ''; document.querySelector('#displayset-edit-form-{{ ds.pk }}').innerHTML = '';
document.querySelector('#displayset-{{ ds.pk }}-edit-btn').style.display = ''; document.querySelector('#displayset-{{ ds.pk }}-edit-btn').style.display = '';
}; };
form.appendChild(cancelBtn); form.appendChild(cancelBtn);
} }
}, 100); }, 100);
" "
>Edit</button> >Edit</button>
<button <button
hx-delete="{% url 'atlas:case_displaysets_delete' ds.pk %}" hx-delete="{% url 'atlas:case_displaysets_delete' ds.pk %}"
+49 -49
View File
@@ -10,69 +10,69 @@
<div class="json-container small" style="max-height:360px; overflow:auto; font-family: monospace; background: #f8f9fa; padding: .5rem; border-radius: .25rem;"></div> <div class="json-container small" style="max-height:360px; overflow:auto; font-family: monospace; background: #f8f9fa; padding: .5rem; border-radius: .25rem;"></div>
<script> <script>
(function(){ (function(){
// Parse template-provided JSON value into a JS object // Parse template-provided JSON value into a JS object
var obj; var obj;
try{ try{
obj = {{ json_value|default:"{}"|safe }}; obj = {{ json_value|default:"{}"|safe }};
}catch(e){ }catch(e){
try{ obj = JSON.parse(String({{ json_value|default:"{}"|safe }})); }catch(e){ obj = {}; } try{ obj = JSON.parse(String({{ json_value|default:"{}"|safe }})); }catch(e){ obj = {}; }
} }
var container = document.currentScript.previousElementSibling; var container = document.currentScript.previousElementSibling;
var btnCopy = document.currentScript.parentNode.querySelector('.btn-copy-json'); var btnCopy = document.currentScript.parentNode.querySelector('.btn-copy-json');
var btnToggle = document.currentScript.parentNode.querySelector('.btn-toggle-json'); var btnToggle = document.currentScript.parentNode.querySelector('.btn-toggle-json');
function renderWithFormatter(depth){ function renderWithFormatter(depth){
container.innerHTML = ''; container.innerHTML = '';
if (window.JSONFormatter){ if (window.JSONFormatter){
try{ try{
var fmt = new JSONFormatter(obj, depth || 1, {hoverPreviewEnabled: false, animateOpen: false}); var fmt = new JSONFormatter(obj, depth || 1, {hoverPreviewEnabled: false, animateOpen: false});
container.appendChild(fmt.render()); container.appendChild(fmt.render());
return fmt; return fmt;
}catch(e){ }catch(e){
container.textContent = JSON.stringify(obj, null, 2);
return null;
}
} else {
container.textContent = JSON.stringify(obj, null, 2); container.textContent = JSON.stringify(obj, null, 2);
return null; return null;
} }
} else {
container.textContent = JSON.stringify(obj, null, 2);
return null;
} }
}
// Dynamic load of json-formatter-js if not already present // Dynamic load of json-formatter-js if not already present
function ensureFormatter(cb){ function ensureFormatter(cb){
if (window.JSONFormatter) return cb(); if (window.JSONFormatter) return cb();
var s = document.createElement('script'); var s = document.createElement('script');
s.src = 'https://unpkg.com/json-formatter-js@2.3.3/dist/json-formatter.umd.js'; s.src = 'https://unpkg.com/json-formatter-js@2.3.3/dist/json-formatter.umd.js';
s.onload = function(){ setTimeout(cb, 0); }; s.onload = function(){ setTimeout(cb, 0); };
s.onerror = function(){ cb(); }; s.onerror = function(){ cb(); };
document.head.appendChild(s); document.head.appendChild(s);
} }
// Keep current formatter instance so we can re-render collapsed/expanded // Keep current formatter instance so we can re-render collapsed/expanded
var currentFormatter = null; var currentFormatter = null;
ensureFormatter(function(){ currentFormatter = renderWithFormatter(1); }); ensureFormatter(function(){ currentFormatter = renderWithFormatter(1); });
btnToggle.addEventListener('click', function(){ btnToggle.addEventListener('click', function(){
// Toggle between collapsed (depth=1) and expanded (depth=0 => fully expanded) // Toggle between collapsed (depth=1) and expanded (depth=0 => fully expanded)
if (btnToggle.dataset.state === 'expanded'){ if (btnToggle.dataset.state === 'expanded'){
currentFormatter = renderWithFormatter(1); currentFormatter = renderWithFormatter(1);
btnToggle.dataset.state = 'collapsed'; btnToggle.dataset.state = 'collapsed';
btnToggle.textContent = 'Show'; btnToggle.textContent = 'Show';
} else { } else {
currentFormatter = renderWithFormatter(0); currentFormatter = renderWithFormatter(0);
btnToggle.dataset.state = 'expanded'; btnToggle.dataset.state = 'expanded';
btnToggle.textContent = 'Hide'; btnToggle.textContent = 'Hide';
} }
}); });
btnCopy.addEventListener('click', function(){ btnCopy.addEventListener('click', function(){
var text = JSON.stringify(obj, null, 2); var text = JSON.stringify(obj, null, 2);
if (navigator.clipboard){ navigator.clipboard.writeText(text).catch(function(){}); } if (navigator.clipboard){ navigator.clipboard.writeText(text).catch(function(){}); }
else { var ta = document.createElement('textarea'); ta.value = text; document.body.appendChild(ta); ta.select(); try{document.execCommand('copy');}catch(e){} document.body.removeChild(ta); } else { var ta = document.createElement('textarea'); ta.value = text; document.body.appendChild(ta); ta.select(); try{document.execCommand('copy');}catch(e){} document.body.removeChild(ta); }
}); });
})(); })();
</script> </script>
</div> </div>