override render()

in polygerrit-ui/app/embed/diff/gr-diff-image-viewer/gr-image-viewer.ts [409:646]


  override render() {
    const src = this.baseSelected ? this.baseUrl : this.revisionUrl;

    const sourceImage = html`
      <img
        id="source-image"
        src="${src}"
        class="${classMap({checkerboard: this.checkerboardSelected})}"
        style="${styleMap({
          backgroundColor: this.checkerboardSelected
            ? ''
            : this.backgroundColor,
        })}"
        @load="${this.updateSizes}"
      />
    `;

    const sourceImageWithHighlight = html`
      <div id="source-plus-highlight-container">
        ${sourceImage}
        <img
          id="highlight-image"
          style="${styleMap({
            opacity: this.showHighlight ? '1' : '0',
            // When the highlight layer is not being shown, saving the image or
            // opening it in a new tab from the context menu, e.g. for external
            // comparison, should give back the source image, not the highlight
            // layer.
            'pointer-events': this.showHighlight ? 'auto' : 'none',
          })}"
          src="${ifDefined(this.diffHighlightSrc)}"
        />
      </div>
    `;

    const versionExplanation = html`
      <div id="version-explanation">
        This file is being ${this.revisionUrl ? 'added' : 'deleted'}.
      </div>
    `;

    // This uses the unelevated and outlined attributes from mwc-button with
    // manual styling, for a more seamless transition later.
    const leftClasses = {
      left: true,
      unelevated: this.baseSelected,
      outlined: !this.baseSelected,
    };
    const rightClasses = {
      right: true,
      unelevated: !this.baseSelected,
      outlined: this.baseSelected,
    };
    const versionToggle = html`
      <div id="version-switcher">
        <paper-button
          class="${classMap(leftClasses)}"
          @click="${this.selectBase}"
        >
          Base
        </paper-button>
        <paper-fab mini icon="gr-icons:swapHoriz" @click="${this.manualBlink}">
        </paper-fab>
        <paper-button
          class="${classMap(rightClasses)}"
          @click="${this.selectRevision}"
        >
          Revision
        </paper-button>
      </div>
    `;

    const versionSwitcher = html`
      ${this.baseUrl && this.revisionUrl ? versionToggle : versionExplanation}
    `;

    const highlightSwitcher = this.diffHighlightSrc
      ? html`
          <paper-checkbox
            id="highlight-changes"
            ?checked="${this.showHighlight}"
            @change="${this.showHighlightChanged}"
          >
            Highlight differences
          </paper-checkbox>
        `
      : '';

    const overviewImage = html`
      <gr-overview-image
        .frameRect="${this.overviewFrame}"
        @center-updated="${this.onOverviewCenterUpdated}"
      >
        <img
          src="${src}"
          class="${classMap({checkerboard: this.checkerboardSelected})}"
          style="${styleMap({
            backgroundColor: this.checkerboardSelected
              ? ''
              : this.backgroundColor,
          })}"
        />
      </gr-overview-image>
    `;

    const zoomControl = html`
      <paper-dropdown-menu id="zoom-control" label="Zoom">
        <paper-listbox
          slot="dropdown-content"
          selected="fit"
          .attrForSelected="${'value'}"
          @selected-changed="${this.zoomControlChanged}"
        >
          ${this.zoomLevels.map(
            zoomLevel => html`
              <paper-item value="${zoomLevel}">
                ${zoomLevel === 'fit' ? 'Fit' : `${zoomLevel * 100}%`}
              </paper-item>
            `
          )}
        </paper-listbox>
      </paper-dropdown-menu>
    `;

    const followMouse = html`
      <paper-checkbox
        id="follow-mouse"
        ?checked="${this.followMouse}"
        @change="${this.followMouseChanged}"
      >
        Magnifier follows mouse
      </paper-checkbox>
    `;

    const backgroundPicker = html`
      <div class="color-picker">
        <div class="label">Background</div>
        <div class="options">
          ${this.renderCheckerboardButton()}
          ${this.colorPickerCallbacks.map(({color, callback}) =>
            this.renderColorPickerButton(color, callback)
          )}
        </div>
      </div>
    `;

    /*
     * We want the content to fill the available space until it can display
     * without being cropped, the maximum of which will be determined by
     * (max-)width and (max-)height constraints on the host element; but we
     * are also limiting the displayed content to the measured dimensions of
     * the host element without overflow, so we need something else to take up
     * the requested space unconditionally.
     */
    const spacerScale = Math.max(this.scale, 1);
    const spacerWidth = this.imageSize.width * spacerScale;
    const spacerHeight = this.imageSize.height * spacerScale;
    const spacer = html`
      <div
        id="spacer"
        style="${styleMap({
          width: `${spacerWidth}px`,
          height: `${spacerHeight}px`,
        })}"
      ></div>
    `;

    const automaticBlink = html`
      <paper-fab
        id="automatic-blink-button"
        class="${classMap({show: this.automaticBlinkShown})}"
        title="Automatic blink"
        icon="gr-icons:${this.automaticBlink ? 'pause' : 'playArrow'}"
        @click="${this.toggleAutomaticBlink}"
      >
      </paper-fab>
    `;

    // To pass CSS mixins for @apply to Polymer components, they need to appear
    // in <style> inside the template.
    /* eslint-disable lit/prefer-static-styles */
    const customStyle = html`
      <style>
        paper-item {
          --paper-item-min-height: 48;
          --paper-item: {
            min-height: 48px;
            padding: 0 var(--spacing-xl);
          }
          --paper-item-focused-before: {
            background-color: var(--selection-background-color);
          }
          --paper-item-focused: {
            background-color: var(--selection-background-color);
          }
        }
      </style>
    `;

    return html`
      ${customStyle}
      <div
        class="imageArea"
        @mousemove="${this.mousemoveImageArea}"
        @mouseleave="${this.mouseleaveImageArea}"
      >
        <gr-zoomed-image
          class="${classMap({
            base: this.baseSelected,
            revision: !this.baseSelected,
          })}"
          style="${styleMap({
            ...this.zoomedImageStyle,
            cursor: this.grabbing ? 'grabbing' : 'pointer',
          })}"
          .scale="${this.scale}"
          .frameRect="${this.magnifierFrame}"
          @mousedown="${this.mousedownMagnifier}"
          @mouseup="${this.mouseupMagnifier}"
          @mousemove="${this.mousemoveMagnifier}"
          @mouseleave="${this.mouseleaveMagnifier}"
          @dragstart="${this.dragstartMagnifier}"
        >
          ${sourceImageWithHighlight}
        </gr-zoomed-image>
        ${this.baseUrl && this.revisionUrl ? automaticBlink : ''} ${spacer}
      </div>

      <div class="dimensions">
        ${this.imageSize.width} x ${this.imageSize.height}
      </div>

      <paper-card class="controls">
        ${versionSwitcher} ${highlightSwitcher} ${overviewImage} ${zoomControl}
        ${!this.scaledSelected ? followMouse : ''} ${backgroundPicker}
      </paper-card>
    `;
  }