Editor Side Panel Example

This example searches the Pixabay web site for images.

(function (imageSearch) {
  imageSearch.dragStart = function (event) {
    const imageData = JSON.parse(event.currentTarget.getAttribute('data-hit'));
    const dataUrl = this.getDataUrlFromImage(event.currentTarget, imageData.webformatWidth, imageData.webformatHeight);
    const jsonData = JSON.stringify({
      files: [
        {
          dataUrl: dataUrl,
          name: imageData.tags.replace(/, /g, '-') + '-' + imageData.id,
          mimeType: 'image/jpeg'
        }
      ]
    });
    event.dataTransfer.setData('application/x-web-component-data', jsonData);
  };

  imageSearch.getDataUrlFromImage = function (image, width, height) {
    let canvas = document.createElement('canvas');
    canvas.width = width;
    canvas.height = height;

    const context = canvas.getContext('2d');
    context.drawImage(image, 0, 0);

    return canvas.toDataURL('image/jpeg');
  };
})(window.imageSearch || (window.imageSearch = {}));

class ImageSearch extends HTMLElement {
  constructor() {
    super();

    this.attachShadow({ mode: 'open' });
    this.shadowRoot.innerHTML = `
      <style>
        :host {
          margin: 0;
          padding: 0;
          width: 100%;
          height: 100%;
          display: flex; }

        .content-wrapper {
          flex-grow: 1;
          flex-direction:
              column; margin: 0 10px 0 10px;
          height: calc(100vh - 60px);
        }

        #wrapper {
          flex-grow: 1;
          flex-direction: column;
          position: relative;
          overflow: auto;
          height: calc(100% - 60px);
          margin-top: 28px;
        }

        .search {
          position: relative;
        }

        #images {
          overflow-y: auto;
          overflow-x: hidden;
          position: absolute;
          top: 10px;
          bottom: 10px;
          left: 0;
          right: 0;
          scrollbar-face-color: #000000;
          scrollbar-track-color: transparent;
        }

        #images::-webkit-scrollbar {
          width: 9px;
          height: 9px;
        }

        #images::-webkit-scrollbar-thumb {
          background: #333333;
          border-left: 2px solid #444444;
        }

        #images::-webkit-scrollbar-track {
          background: transparent;
        }

        #images img {
          display: block;
          max-height: 250px;
          max-width: 80%;
          padding: 2px;
          margin: 0 auto 10px auto;
          cursor: pointer;
          border: 2px solid #9c9c9c;
          opacity: 0.8;
          transition: all 250ms ease-in-out;
        }

        #images img:hover {
          border: 2px solid #ffffff;
          opacity: 1;
        }

        #search-term {
          position: absolute;
          z-index: 1;
          width: 100%;
          height: 28px;
          border: 2px solid #000000;
          background: #333333;
          font-family: "Hind", Helvetica Neue, Helvetica, Arial, Sans-serif;
          color: #ffffff;
          font-weight: 300;
          font-size: 14px;
          padding-left: 10px;
          padding-right: 28px;
          box-sizing: border-box;
        }



        #search-term:focus,  .searchButton:focus {
          outline: none;
        }

        .searchButton {
          position: absolute;
          z-index: 2;
          top: 2px;
          right: 2px;
          width: 24px;
          height: 24px;
          border: 0;
          cursor: pointer;
          background: transparent;
          color: #9c9c9c;
        }

        .searchButton:before {
          font: 16px 'cf';
          font-style: normal;
          font-weight: normal;
          padding: 2px 0 0 4px;
          line-height: 24px;
          content: '\\e878';
          -webkit-font-smoothing: antialiased;
          -moz-osx-font-smoothing: grayscale;
        }

        .searchButton:hover{
          color: #ffffff;
        }

        h1 {
          display: block;
          width: 100%;
          height: 44px;
          margin: 0;
          padding: 0;
          background: url("webcomponents/image-search/pixabay_logo.png") no-repeat center 10px;
        }

      </style>
      <div class="content-wrapper">
        <h1></h1>
        <div class="search">
          <input id="search-term" type="textbox" value="" placeholder="Search in Pixabay free images">
          <div id="search" class="searchButton"></div>
        </div>

        <div id="wrapper">
          <div id="images"></div>
        </div>
      </div>
    `;
  }

  connectedCallback() {
    if (this.cueInterface.editor && this.cueInterface.editor.getTags) {
      const tags = this.cueInterface.editor.getTags();
      if (tags && tags.length > 0) {
        this.shadowRoot.querySelector('#search-term').value = tags[0].value;
      }
    }
    this.search();
    $(this.shadowRoot.querySelector('#search-term')).keypress(event => {
      if (event.which === 13 /* ENTER */) {
        this.search();
      }
    });
    $(this.shadowRoot.querySelector('#search')).click(this.search.bind(this));
  }

  ImageSearchProto.search = function() {
    var searchTerm = panelShadowRoot.querySelector('#search-term').value;
    searchTerm = searchTerm.replace(' ', '+');
    this.loadImages(searchTerm);
  };

  loadImages(searchTerm) {
    const xhr = new XMLHttpRequest();
    xhr.open('GET', 'https://pixabay.com/api/?key=<YOUR_API_KEY>&q=' + searchTerm + '&image_type=photo&safesearch=true', true);

    xhr.onload = function () {
      if (xhr.readyState === 4) {
        if (xhr.status === 200) {
          this.showImages(xhr.responseText);
        }
        else {
          console.error(xhr.statusText);
        }
      }
    }.bind(this);

    xhr.onerror = function () {
      console.error(xhr.statusText);
    };

    xhr.send(null);
  };

  ImageSearchProto.showImages = function(responseData) {
    var responseJson = JSON.parse(responseData);
    var images = '';
    responseJson.hits.forEach(function (hit) {
      images += "<img src='" + hit.webformatURL + "' data-hit='" + JSON.stringify(hit) + "' crossorigin='anonymous' ondragstart='imageSearch.dragStart(event);'>";
    });

    images += '<a href="https://pixabay.com/" target="_blank"><img src="https://pixabay.com/static/img/public/medium_rectangle_a.png" alt="Pixabay"></a>';

    panelShadowRoot.querySelector('#images').innerHTML = images;
  }
;

customElements.define('image-search', ImageSearch);

class ImageSearchIcon extends HTMLElement {
  constructor() {
    super();

    this.attachShadow({ mode: 'open' });
    this.shadowRoot.innerHTML = `
      <style>
        :host { margin: 0; padding: 6px; display: block; }
        img { max-width: 80%; position: relative; top: 3px; left: 3px; }
      </style>
      <img class="icon">
    `;

    ImageSearchIconProto.createdCallback = function () {
      var template = thisDoc.querySelector('template#icon').content;
    iconShadowRoot = this.attachShadow({ mode: 'open' });
    iconShadowRoot.appendChild(template.cloneNode(true));
  };

  ImageSearchIconProto.attachedCallback = function() {
    if (this.cueInterface.homeScreen) {
      this.activeStateChanged(this.cueInterface.isActive());
      this.cueInterface.addActiveWatcher(function (active) {
        this.activeStateChanged(active);
      }.bind(this));
    }
    else {
      var img = iconShadowRoot.querySelector('img.icon');
      img.src = this.getAbsolutePath(this.editorIconPath);
    }
  };

  ImageSearchIconProto.activeStateChanged = function(active) {
    var img = iconShadowRoot.querySelector('img.icon');
    if (active) {
      img.src = this.getAbsolutePath(this.activeIconPath);
    }
    else {
      img.src = this.getAbsolutePath(this.inactiveIconPath);
    }
  };

  getAbsolutePath(path) {
    const baseURI = import.meta.url;
    return baseURI.substring(0, baseURI.lastIndexOf('/') + 1) + path;
  }
}
customElements.define('image-search-icon', ImageSearchIcon);