Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | 125x 129x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 123x 123x 123x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 125x 19x 12x 4x 4x 4x 4x 4x 4x 4x 19x 19x 14x 19x 2x 2x 2x 2x 2x 2x 1x 2x 2x 2x 2x 5x 5x 1x 4x 2x 2x 125x 125x 125x 125x 125x 125x | import { css, html, PropertyValueMap } from 'lit';
import { property, state } from 'lit/decorators.js';
import { RapidElement } from '../RapidElement';
import { getClasses } from '../utils';
/**
* A full-screen viewer for an image attachment. `showElement` is handed the
* element that was clicked (a raw <img> or a temba-thumbnail exposing a `url`),
* and we present that image centered and contained within the viewport with a
* short fade / scale-in. Clicking anywhere dismisses it.
*/
export class Lightbox extends RapidElement {
static get styles() {
return css`
:host {
position: fixed;
top: 0;
left: 0;
z-index: 10000;
}
/* a full-viewport layer that centers the image, catches the dismiss
click, and lays a light translucent mask over the page behind */
.backdrop {
position: fixed;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.5);
opacity: 0;
pointer-events: none;
transition: opacity var(--anim) ease;
}
.backdrop.zoom {
opacity: 1;
pointer-events: auto;
}
img {
/* contain the image within the viewport at any aspect ratio, on
its own solid backing (so transparent images aren't see-through) */
box-sizing: border-box;
max-width: 90vw;
max-height: 90vh;
object-fit: contain;
background: #fff;
padding: 6px;
border-radius: calc(var(--curvature) * 1.5);
box-shadow: 0 0 24px 6px rgba(0, 0, 0, 0.4);
transform: scale(0.85);
opacity: 0;
transition:
transform var(--anim) ease,
opacity var(--anim) ease;
}
.zoom img {
transform: scale(1);
opacity: 1;
}
`;
}
@property({ type: Number })
animationTime = 300;
@property({ type: Boolean })
show = false;
@property({ type: Boolean })
zoom = false;
@state()
private url = '';
protected updated(
changed: PropertyValueMap<any> | Map<PropertyKey, unknown>
): void {
// mount first (opacity 0 / scaled down), then flip to zoom on the next
// tick so the transition to the visible state actually animates
if (changed.has('show') && this.show) {
window.setTimeout(() => {
this.zoom = true;
}, 0);
}
// once zoomed out, wait for the fade to finish before unmounting the image
if (changed.has('zoom') && !this.zoom && this.show) {
window.setTimeout(() => {
// unless a re-open during the fade-out already zoomed us back in
if (!this.zoom) {
this.show = false;
}
}, this.animationTime);
}
}
public showElement(ele: HTMLElement) {
// the clicked element is either a raw <img> or a temba-thumbnail that
// exposes the attachment url; take whichever gives us an image source
this.url = (ele as HTMLImageElement).src || (ele as any).url || '';
if (this.show) {
// already mounted (e.g. re-opened mid-dismissal) — re-zoom in place
this.zoom = true;
} else {
this.show = true; // updated() flips zoom on the next tick
}
}
public handleClick() {
this.zoom = false;
}
public render() {
return html`
<div
class=${getClasses({ backdrop: true, zoom: this.zoom })}
style="--anim: ${this.animationTime}ms"
@click=${this.handleClick}
>
${this.show && this.url
? html`<img src=${this.url} alt="attachment" />`
: null}
</div>
`;
}
}
|