blob: 81104a69c968e417f2083837ba37c284ceed0cbb [file] [log] [blame]
/**
* @module module/image-compare-sk
* @description <h2><code>image-compare-sk</code></h2>
*
* Shows a side by side comparison of two images. If there's nothing to compare against, it will
* only display one.
*
*/
import { define } from 'elements-sk/define';
import { html } from 'lit-html';
import dialogPolyfill from 'dialog-polyfill';
import { ElementSk } from '../../../infra-sk/modules/ElementSk';
import { MultiZoomSk } from '../multi-zoom-sk/multi-zoom-sk';
import 'elements-sk/icon/open-in-new-icon-sk';
import 'elements-sk/styles/buttons';
import { digestDiffImagePath, digestImagePath } from '../common';
import '../multi-zoom-sk';
export interface ImageComparisonData {
digest: string;
title: string;
detail: string;
}
export class ImageCompareSk extends ElementSk {
private static template = (ele: ImageCompareSk) => html`
<div class=comparison_bar>
<figure>
<img class="thumbnail ${ele._fullSizeLeftImage ? 'fullsize' : ''}"
alt="left image"
src=${digestImagePath(ele.left.digest)}
@click=${ele.toggleFullSizeLeftImage}>
<figcaption>
<span class=legend_dot></span>
<a target=_blank rel=noopener href=${ele.left.detail}>${ele.left.title}</a>
</figcaption>
</figure>
<a target=_blank rel=noopener href=${digestImagePath(ele.left.digest)}>
<open-in-new-icon-sk></open-in-new-icon-sk>
</a>
${ImageCompareSk.comparison(ele)}
</div>
<button class=zoom_btn ?hidden=${!ele.right} @click=${ele.openZoomWindow}>Zoom</button>
<dialog class=zoom_dialog @close=${ele.closeEvent}))}>
<button class=close_btn @click=${ele.closeDialog}>Close</button>
</dialog>
`;
private static comparison = (ele: ImageCompareSk) => {
if (!ele.right) {
if (ele.isComputingDiffs) {
return html`<div class=computing title="Check back later">
Computing closest positive and negative image. Check back later.</div>`;
}
return html`<div class=no_compare>No other images to compare against.</div>`;
}
const diffSrc = digestDiffImagePath(ele.left.digest, ele.right.digest);
return html`
<img class="thumbnail diff ${ele._fullSizeDiffImage ? 'fullsize' : ''}"
alt="diff between left and right image"
src=${diffSrc}
@click=${ele.toggleFullSizeDiffImage}>
<a target=_blank rel=noopener href=${diffSrc}>
<open-in-new-icon-sk></open-in-new-icon-sk>
</a>
<figure>
<img class="thumbnail ${ele._fullSizeRightImage ? 'fullsize' : ''}"
alt="right image"
src=${digestImagePath(ele.right.digest)}
@click=${ele.toggleFullSizeRightImage}>
<figcaption>
<a target=_blank rel=noopener href=${ele.right.detail}>${ele.right.title}</a>
</figcaption>
</figure>
<a target=_blank rel=noopener href=${digestImagePath(ele.right.digest)}>
<open-in-new-icon-sk></open-in-new-icon-sk>
</a>
`;
};
private _left: ImageComparisonData = {
digest: '',
title: '',
// We can't derive the detail url w/o also passing down changelistID, crs etc, so we have
// the caller compute those URLs and pass them into this element.
detail: '',
}
private _right: ImageComparisonData | null = null;
private computingDiffs = false;
private _fullSizeImages = false;
private _fullSizeLeftImage = false;
private _fullSizeDiffImage = false;
private _fullSizeRightImage = false;
constructor() {
super(ImageCompareSk.template);
}
connectedCallback(): void {
super.connectedCallback();
this._render();
dialogPolyfill.registerDialog(this.querySelector('dialog.zoom_dialog')!);
}
get isComputingDiffs(): boolean { return this.computingDiffs; }
set isComputingDiffs(b: boolean) {
this.computingDiffs = b;
this._render();
}
get left(): ImageComparisonData { return this._left; }
set left(img: ImageComparisonData) {
this._left = img;
this._render();
}
get right(): ImageComparisonData | null { return this._right; }
set right(img: ImageComparisonData | null) {
this._right = img;
this._render();
}
/** Whether to show thumbnails or full size images. */
get fullSizeImages(): boolean {
return this._fullSizeImages;
}
set fullSizeImages(val: boolean) {
this._fullSizeImages = val;
this._fullSizeLeftImage = val;
this._fullSizeDiffImage = val;
this._fullSizeRightImage = val;
this._render();
}
private closeDialog() {
// This will fire a close event.
this.querySelector<HTMLDialogElement>('dialog.zoom_dialog')?.close();
}
private closeEvent() {
// We clean up both when the user clicks the close button as well if they hit escape by waiting
// for the close event (instead of handling this in closeDialog().
const dialog = this.querySelector<HTMLDialogElement>('dialog.zoom_dialog');
const zoom = this.querySelector<MultiZoomSk>('dialog multi-zoom-sk');
if (dialog && zoom) {
// Removing the element from the dom removes the keybinding handlers and lets the browser
// free up the image resources.
dialog.removeChild(zoom);
}
}
openZoomWindow(): void {
const ele = new MultiZoomSk();
ele.details = {
leftImageSrc: digestImagePath(this.left.digest),
rightImageSrc: digestImagePath(this.right!.digest),
diffImageSrc: digestDiffImagePath(this.left.digest, this.right!.digest),
leftLabel: this.left.title,
rightLabel: this.right!.title,
};
const dialog = this.querySelector<HTMLDialogElement>('dialog.zoom_dialog')!;
// put the dialog before the button
dialog.insertBefore(ele, dialog.childNodes[0]);
dialog.showModal();
}
private toggleFullSizeLeftImage(): void {
this._fullSizeLeftImage = !this._fullSizeLeftImage;
this._render();
}
private toggleFullSizeDiffImage(): void {
this._fullSizeDiffImage = !this._fullSizeDiffImage;
this._render();
}
private toggleFullSizeRightImage(): void {
this._fullSizeRightImage = !this._fullSizeRightImage;
this._render();
}
}
define('image-compare-sk', ImageCompareSk);