Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 21 additions & 11 deletions website/static/website/css/publications.css
Original file line number Diff line number Diff line change
Expand Up @@ -694,9 +694,14 @@
and style its contents. */

.artifact-preview-popover {
/* Compact card. Talk slides are landscape, so width drives the size; keep it
modest. Posters (often portrait) are bounded by the image max-height. */
max-width: 272px;
/* The card hugs its image: thumbnailPreview.js sizes the image to a fixed box
and the card shrink-wraps it. base.css sets a global `.popover { width: 50vw;
min-width: 300px }`, so we must reset both here or the card can't shrink to
the image (that was the whitespace beside portrait posters). max-width is a
generous ceiling so a full-width landscape image (+ padding) is never clipped. */
width: auto;
min-width: 0;
max-width: 300px;
/* Bootstrap sets .popover { display: none }; the JS reveals it. Keep it
above page chrome but below modals. */
z-index: 1060;
Expand All @@ -713,12 +718,16 @@

.artifact-preview-image {
display: block;
width: 100%;
height: auto;
/* Never let a tall poster run past the viewport. */
max-height: 45vh;
object-fit: contain;
border-radius: var(--border-radius-sm, 3px);
/* Height-capped (not width-driven), so portrait posters stay compact instead
of running tall; landscape ones still fill the card width. width/height are
left to intrinsic aspect, bounded by the maxes. Narrower (portrait) images
are centered. */
margin: 0 auto;
max-width: 100%;
max-height: 240px;
/* Rounded rect + subtle outline so the thumbnail reads as a framed preview. */
border-radius: var(--border-radius-md, 8px);
border: 1px solid var(--color-border, #d0d0d0);
}

.artifact-preview-actions {
Expand Down Expand Up @@ -750,9 +759,10 @@
outline-offset: var(--focus-ring-offset, 2px);
}

/* File size, pushed to the trailing edge of each action row. */
/* File size sits right after its label (not pushed to the trailing edge), so a
lone action doesn't leave an awkward gap when the card is as wide as the
poster image. */
.artifact-preview-size {
margin-left: auto;
color: var(--color-text-secondary, #666);
font-size: 0.85em;
}
35 changes: 35 additions & 0 deletions website/static/website/js/thumbnailPreview.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@
const ARROW_HALF = 11;
/** Delay (ms) before a hover-opened card hides, so the pointer can reach it. */
const HIDE_DELAY = 140;
/** Box the preview image is fit into (px). The image is sized explicitly in
* JS to these bounds so the card hugs it — CSS shrink-to-fit uses the image's
* intrinsic (600px-wide thumbnail) size and can't account for the height cap,
* which is what left whitespace beside portrait thumbnails. */
const PREVIEW_MAX_W = 260;
const PREVIEW_MAX_H = 240;

/* ===========================================================================
STATE
Expand Down Expand Up @@ -126,6 +132,35 @@
content.appendChild(template.content.cloneNode(true));
popover.appendChild(content);

// Size the preview image explicitly (fit within PREVIEW_MAX_W x
// PREVIEW_MAX_H, preserving aspect) so the card hugs it. Without a definite
// width the card sizes to the thumbnail's intrinsic 600px (capped), then the
// height cap shrinks the image — leaving whitespace beside portrait posters.
const img = popover.querySelector('.artifact-preview-image');
if (img) {
const sizeToImage = function () {
const nw = img.naturalWidth;
const nh = img.naturalHeight;
if (!nw || !nh) {
return;
}
const scale = Math.min(PREVIEW_MAX_W / nw, PREVIEW_MAX_H / nh, 1);
img.style.width = Math.round(nw * scale) + 'px';
img.style.height = Math.round(nh * scale) + 'px';
// The card's size just changed; re-anchor it to the trigger.
if (activePopover === popover && activeTrigger === trigger) {
positionPopover(trigger, popover);
}
};
// Cached images are already complete (no 'load' will fire); size now so the
// popover is measured correctly when open() positions it.
if (img.complete && img.naturalWidth) {
sizeToImage();
} else {
img.addEventListener('load', sizeToImage);
}
}

return popover;
}

Expand Down
Loading