noesya/osuny

View on GitHub
app/views/admin/communication/photo_imports/_selector.html.erb

Summary

Maintainability
Test Coverage
<%
# L'Escale du livre 2022
search = about.to_s
# communication_website_post_localization
l10n_identifier =  about.class
                        .base_class
                        .to_s
                        .parameterize
                        .underscore
# communication_website_post
about_identifier = l10n_identifier.remove('_localization')
# input[name="communication_website_post[localizations_attributes][0][featured_image]"]
about_featured_image_file = "input[name=\"#{about_identifier}[localizations_attributes][0][featured_image]\"]".html_safe
# input[name="communication_website_post[localizations_attributes][0][featured_image_delete]"]
about_featured_image_file_delete = "input[name=\"#{about_identifier}[localizations_attributes][0][featured_image_delete]\"]".html_safe
# input[name="communication_website_post[localizations_attributes][0][featured_image_infos]"]
about_featured_image_file_infos = "input[name=\"#{about_identifier}[localizations_attributes][0][featured_image_infos]\"]".html_safe
# .communication_website_post_localizations_featured_image
about_featured_image_image = ".#{about_identifier}_localizations_featured_image"
# #communication_website_post_localizations_attributes_0_featured_image_alt
about_featured_image_alt = "##{about_identifier}_localizations_attributes_0_featured_image_alt"
# #communication_website_post_localizations_featured_image_credit
about_featured_image_credit = "##{about_identifier}_localizations_attributes_0_featured_image_credit"
# fr, en...
lang = about&.language&.iso_code if about.respond_to? :language
# /admin/communication/photo_import.json?query=Page%20de%20test&per_page=12&page=1&lang=fr
unsplash_path = admin_communication_unsplash_path(website_id: nil, extranet_id: nil, journal_id: nil, format: :json)
pexels_path = admin_communication_pexels_path(website_id: nil, extranet_id: nil, journal_id: nil, format: :json)
%>

<div id="photo-import-app" v-cloak>
  <div class="spinner-border text-primary" role="status">
    <span class="sr-only"><%= t 'loading' %></span>
  </div>
  <div class="app-content">
    <button type="button"
            class="btn btn-secondary btn-sm"
            data-bs-toggle="modal"
            data-bs-target="#photoImportModal"
            >
      <%= t 'photo_import.open' %>
    </button>
    <input  type="hidden"
            name="photo_import[unsplash]"
            v-model="unsplash.selected">
    <input  class="form-control string optional"
            type="hidden"
            name="photo_import[pexels]"
            v-model="pexels.selected">
    <div  class="modal fade"
          id="photoImportModal"
          tabindex="-1"
          aria-labelledby="Unsplash"
          aria-hidden="true">
      <div class="modal-dialog modal-fullscreen modal-dialog-scrollable">
        <div class="modal-content">
          <div class="modal-header">
            <div class="col-auto d-none d-lg-block">
              <h5 class="modal-title"><%= t 'photo_import.title' %></h5>
            </div>
            <div class="col-auto d-flex flex-fill mx-lg-5">
              <div class="input-group">
                <input  type="text"
                        name="search"
                        placeholder="<%= t 'photo_import.placeholder' %>"
                        v-model="query"
                        class="form-control ms-auto">
                <button type="button"
                        class="btn btn-primary me-auto"
                        v-on:click="research"
                        aria-label="<%= t 'photo_import.search' %>">
                  <%= t 'photo_import.search' %>
                </button>
              </div>
            </div>
            <div class="col-auto">
              <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
            </div>
          </div>
          <div class="modal-body overflow-x-hidden" id="results">
            <div class="row">
              <div class="col-lg-6">
                <p v-if="unsplash.data.results.length === 0 || !unsplash.data" >
                  <%= t 'photo_import.nothing' %>
                </p>
                <div class="photo_import__results photo_import__unsplash photo_import__unsplash__results" ref="results">
                  <img  :src="image.thumb"
                        :alt="image.alt"
                        v-for="image in unsplash.data.results" class="photo_import__results__result"
                        v-on:click="selectUnsplash(image)">
                </div>
                <p class="small text-muted" v-if="unsplash.data.total_pages">
                  {{unsplash.page}} / {{unsplash.data.total_pages }}
                </p>
              </div>
              <div class="col-lg-6">
                <p v-if="pexels.data.results.length === 0 || !pexels.data" >
                  <%= t 'photo_import.nothing' %>
                </p>
                <div class="photo_import__results photo_import__pexels photo_import__pexels__results" ref="results">
                  <img  :src="image.thumb"
                        :alt="image.alt"
                        v-for="image in pexels.data.results"
                        v-on:click="selectPexels(image)"
                        class="photo_import__results__result">
                </div>
                <p class="small text-muted" v-if="pexels.data.total_pages">
                  {{pexels.page}} / {{pexels.data.total_pages }}
                </p>
              </div>
            </div>
          </div>
          <div class="modal-footer d-block">
            <div class="row">
              <div class="col-lg-6">
                <div class="row photo_import__unsplash photo_import__unsplash__nav">
                  <div class="col-lg-5">
                    <a  href="#"
                        v-if="unsplash.page > 1"
                        v-on:click="unsplash.page = unsplash.page - 1"
                        class="btn btn-light btn-sm">
                      <%= t 'photo_import.previous' %>
                    </a>
                  </div>
                  <div class="col-lg-2 text-center">
                    <%= image_tag 'communication/photo_imports/unsplash.svg', width: 100, alt: 'Unsplash' %>
                  </div>
                  <div class="col-lg-5 text-end">
                    <a  href="#" v-if="unsplash.page < unsplash.data.total_pages"
                        v-on:click="unsplash.page = unsplash.page + 1"
                        class="btn btn-light btn-sm">
                      <%= t 'photo_import.next' %>
                    </a>
                  </div>
                </div>
              </div>
              <div class="col-lg-6">
                <div class="row photo_import__pexels photo_import__pexels__nav">
                  <div class="col-lg-5">
                    <a  href="#"
                        v-if="pexels.page > 1"
                        v-on:click="pexels.page = pexels.page - 1"
                        class="btn btn-light btn-sm">
                      <%= t 'photo_import.previous' %>
                    </a>
                  </div>
                  <div class="col-lg-2 text-center">
                    <%= image_tag 'communication/photo_imports/pexels.png', width: 100, alt: 'Unsplash' %>
                  </div>
                  <div class="col-lg-5 text-end">
                    <a  href="#"
                        v-if="pexels.page < pexels.data.total_pages"
                        v-on:click="pexels.page = pexels.page + 1"
                        class="btn btn-light btn-sm">
                      <%= t 'photo_import.next' %>
                    </a>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

<%# Include vue.js before call Vue.createApp %>
<%= javascript_include_tag 'vue' %>

<script nonce="<%= request.content_security_policy_nonce %>">
  var app = Vue.createApp({
    data() {
      return {
        parameters: {
          per_page: 12,
          lang: '<%= lang %>',
        },
        query: <%= search.to_json.html_safe %>,
        unsplash: {
          url: '<%= unsplash_path %>',
          page: 1,
          data: {
            results: [],
            total: 0
          },
          selected: null
        },
        pexels: {
          url: '<%= pexels_path %>',
          page: 1,
          data: {
            results: [],
            total: 0
          },
          selected: null
        },
        targets: {
          fileInput: document.querySelector('<% about_featured_image_image %> <%= about_featured_image_file %>'),
          fileLabel: document.querySelector('<% about_featured_image_image %> .js-sdfi-deletable-file__label'),
          fileDeleteInput: document.querySelector('<%= about_featured_image_image %> <%= about_featured_image_file_delete %>'),
          fileInfosInput: document.querySelector('<%= about_featured_image_image %> <%= about_featured_image_file_infos %>'),
          image: document.querySelector('<%= about_featured_image_image %> img'),
          imageContainer: document.querySelector('<%= about_featured_image_image %> .sdfi-deletable-file__preview'),
          alt: document.querySelector('<%= about_featured_image_alt %>'),
          credit: document.querySelector('<%= about_featured_image_credit %>')
        },
        isOpened: false,
        isReady: false
      }
    },
    mounted() {
      var modalElement = document.querySelector('#photoImportModal')
      this.modal = bootstrap.Modal.getOrCreateInstance(modalElement);

      modalElement.addEventListener('show.bs.modal', function (){
        this.isOpened = true;
        this.research();
      }.bind(this));

      modalElement.addEventListener('hide.bs.modal', function() {
          this.isOpened = false;
      }.bind(this));

      document.addEventListener("keydown", function(event) {
        if (event.key === "Enter" && this.isOpened) {
          event.preventDefault();
          event.stopImmediatePropagation();
          this.research();
        }
      }.bind(this));

      this.isReady = true;
    },
    watch: {
      'unsplash.page': function(newVal, oldVal) {
        this.searchUnsplash();
      },
      'pexels.page': function(newVal, oldVal) {
        this.searchPexels();
      }
    },
    methods: {
      research() {
        this.searchUnsplash();
        this.searchPexels();
      },
      searchUnsplash() {
        var url = this.unsplash.url
                      + '?query=' + encodeURIComponent(this.query)
                      + '&page=' + this.unsplash.page
                      + '&per_page=' + this.parameters.per_page
                      + '&lang=' + this.parameters.lang
        this.search(url, this.unsplash);
      },
      searchPexels() {
        var url = this.pexels.url
                    + '?query=' + encodeURIComponent(this.query)
                    + '&page=' + this.pexels.page
                    + '&per_page=' + this.parameters.per_page
                    + '&lang=' + this.parameters.lang
        this.search(url, this.pexels);
      },
      search(url, source) {
        if (!this.query) {
          return null;
        }
        var xmlHttp = new XMLHttpRequest();
        xmlHttp.onreadystatechange = function() {
          if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
            source.data = JSON.parse(xmlHttp.responseText);
            document.getElementById('results').scrollTop = 0;
          }
        }.bind(this);
        xmlHttp.open("GET", url, false);
        xmlHttp.send(null);
      },
      selectUnsplash(image) {
        this.unsplash.selected = image.id;
        this.pexels.selected = null;
        this.select(image);
      },
      selectPexels(image) {
        this.unsplash.selected = null;
        this.pexels.selected = image.id;
        this.select(image);
      },
      select(image) {
        this.targets.fileInput.value = null;
        this.targets.fileLabel.textContent = image.filename;
        this.targets.fileDeleteInput.value = null;
        this.targets.fileInfosInput.value = null;
        // Refresh in case cropper replaces img
        this.targets.image = document.querySelector('<%= about_featured_image_image %> img');
        if (!this.targets.image) {
          this.targets.image = document.createElement('img');
          this.targets.image.classList.add('img-fluid', 'img-thumbnail');
          this.targets.image.width = 1000;
          this.targets.imageContainer.appendChild(this.targets.image);
        }

        this.targets.image.setAttribute('src', image.preview);
        this.targets.imageContainer.parentElement.classList.add('sdfi-deletable-file--with-file');

        $(this.targets.credit).summernote('code', image.credit);

        this.modal.hide();
      }
    }
  });

  window.addEventListener('load', function(){
    setTimeout(function() {
      app.mount('#photo-import-app');
    }, 1000);
  });
</script>