diff --git a/events/templates/events/event_detail.html b/events/templates/events/event_detail.html index 1861fe2..7feb6db 100644 --- a/events/templates/events/event_detail.html +++ b/events/templates/events/event_detail.html @@ -38,11 +38,12 @@
-
+
{{ frame.filename }} in {{ frame.function }} line {{ frame.lineno }}.
-
{# todo this still shows the last frame of non-last exceptions, I just want the very last one #} +
{# todo the if-statements on forloop.last still show the last frame of non-last exceptions, I just want the very last one #}
    {% for line in frame.pre_context %}
  1. {{ line }} {# leave space to avoid collapse #}
  2. {% endfor %} diff --git a/static/js/issue.js b/static/js/issue.js index 61abd02..7c7788a 100644 --- a/static/js/issue.js +++ b/static/js/issue.js @@ -1,39 +1,94 @@ "use strict"; +// This is the important part! +function collapseSection(element) { + if (element.getAttribute("data-collapsed") === "true") { + return; + } + + element.classList.remove("xl:flex"); // this appears to be necessary, not sure why + + // get the height of the element's inner content, regardless of its actual size + var sectionHeight = element.scrollHeight; + + // temporarily disable all css transitions + var elementTransition = element.style.transition; + element.style.transition = ''; + + // on the next frame (as soon as the previous style change has taken effect), + // explicitly set the element's height to its current pixel height, so we + // aren't transitioning out of 'auto' + requestAnimationFrame(function() { + element.style.height = sectionHeight + 'px'; + element.style.transition = elementTransition; + + // on the next frame (as soon as the previous style change has taken effect), + // have the element transition to height: 0 + requestAnimationFrame(function() { + element.style.height = 0 + 'px'; + }); + }); + + // mark the section as "currently collapsed" + element.setAttribute('data-collapsed', 'true'); +} + +function expandSection(element) { + if (element.getAttribute("data-collapsed") !== "true") { + return; + } + + element.classList.add("xl:flex"); // add back + + // get the height of the element's inner content, regardless of its actual size + var sectionHeight = element.scrollHeight; + + // have the element transition to the height of its inner content + element.style.height = sectionHeight + 'px'; + + // when the next css transition finishes (which should be the one we just triggered) + const foo = function(e) { + // remove this event listener so it only gets triggered once + element.removeEventListener('transitionend', foo); + + // remove "height" from the element's inline styles, so it can return to its initial value + element.style.removeProperty("height"); + } + + element.addEventListener('transitionend', foo); + + // mark the section as "currently not collapsed" + element.setAttribute('data-collapsed', 'false'); +} + function toggleFrameVisibility(frameHeader) { const frameDetails = frameHeader.parentNode.querySelector(".js-frame-details"); - if (frameDetails.classList.contains("hidden")) { - frameDetails.classList.remove("hidden"); - frameDetails.classList.add("xl:flex"); // add back + if (frameDetails.getAttribute("data-collapsed") === "true") { + expandSection(frameDetails); } else { - frameDetails.classList.add("hidden"); - frameDetails.classList.remove("xl:flex"); // this appears to be necessary, not sure why + collapseSection(frameDetails); } } function showAllFrames(frameHeader) { document.querySelectorAll(".js-frame-details").forEach((frameDetails) => { - frameDetails.classList.remove("hidden"); - frameDetails.classList.add("xl:flex"); + expandSection(frameDetails); }); } function showInAppFrames(frameHeader) { document.querySelectorAll(".js-frame-details").forEach((frameDetails) => { if (frameDetails.classList.contains("js-in-app")) { - frameDetails.classList.remove("hidden"); - frameDetails.classList.add("xl:flex"); // add back + expandSection(frameDetails); } else { - frameDetails.classList.add("hidden"); - frameDetails.classList.remove("xl:flex"); // this appears to be necessary, not sure why + collapseSection(frameDetails); } }); } function hideAllFrames(frameHeader) { document.querySelectorAll(".js-frame-details").forEach((frameDetails) => { - frameDetails.classList.add("hidden"); - frameDetails.classList.remove("xl:flex"); // this appears to be necessary, not sure why + collapseSection(frameDetails); }); } diff --git a/theme/static/css/dist/styles.css b/theme/static/css/dist/styles.css index 15235a7..d3ebc4c 100644 --- a/theme/static/css/dist/styles.css +++ b/theme/static/css/dist/styles.css @@ -876,6 +876,14 @@ select { height: 100vh; } +.h-0 { + height: 0px; +} + +.max-h-0 { + max-height: 0px; +} + .w-1\/4 { width: 25%; } @@ -932,6 +940,10 @@ select { justify-items: end; } +.overflow-hidden { + overflow: hidden; +} + .whitespace-pre { white-space: pre; } @@ -1193,6 +1205,24 @@ select { filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); } +.transition-all { + transition-property: all; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + +.delay-150 { + transition-delay: 150ms; +} + +.delay-1000 { + transition-delay: 1000ms; +} + +.duration-1000 { + transition-duration: 1000ms; +} + .ease-in-out { transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); }