핫라인과 스위치로 램프 박스를 만들다.js

본고에서 우리는 Hotwire의 두 부분(Turbo와 Stimulus)이 어떻게 아름다운 협업에서 공동으로 일하고 유쾌한 개발 체험을 가진lightbox를 구축하는지를 탐색할 것이다.
이 특별한 예는 새로운 Rails 프런트엔드 스택을 사용할 때의 주요 부분과 기술을 보여줍니다.Turbo를 사용하여 라이트박스 모드에 이미지를 지연 불러오고 자극할 것입니다
  • 타사 JavaScript 라이브러리(Swiper.js),
  • 포장
  • Turbo 프레임 컨텐트를 타겟으로 스왑,
  • 에서 새로운 action parameters
  • 을 사용하여 태그와 전달에 동적 속성을 정의합니다
  • events을 사용하여 컨트롤러 간에 원데이터를 교환하기 위해 느슨하게 통신한다.
  • 나는 숀 P. 도일의 우수한 hotwire examples template을 토대로 이 모델을 구축하여 비교 방법을 쉽게 만들었다🙇.
    우리 시작합시다!먼저 ActiveStorage를 통해 여러 이미지가 연결된 에셋을 추가합니다.
    $ bin/rails g scaffold Article title:string images:attachments
    
    
    우리는 thumb 성명에서 has_many_attached이라는 변수를 직접 정의했다.
    # app/models/article.rb
    
    class Article < ApplicationRecord
      has_many_attached :images do |attachable|
        attachable.variant :thumb, resize_to_limit: [640, 480]
      end
    end
    
    
    네 편의 문장을 추가한 후, 우리는 호출했다
    $ bin/setup
    
    
    데이터베이스를 만들고 피드를 만듭니다.그 다음에, 우리는 test/fixtures/files 디렉터리를 만들고, 거기에 예시적인 그림을 배치한다.Rails 콘솔에서는 기사마다 2개의 이미지만 추가할 수 있습니다.
    Article.find_each.with_index do |article, article_index|
      (1..2).each do |file_index|
        index = article_index * 2 + file_index
        article.images.attach(io: File.open(Rails.root.join("test/fixtures/files/#{index}.jpeg")), filename: "#{index}.jpeg")
      end
    end
    
    
    글과 이미지 축소판을 격자에 표시합니다.
    <!-- app/views/articles/index.html.erb -->
    <h1>Articles</h1>
    
    <div id="articles" class="grid grid-cols-2 gap-4">
      <% @articles.each do |article| %>
        <%= render article %>
      <% end %>
    </div>
    
    <!-- app/views/articles/_article.html.erb -->
    <div class="bg-white overflow-hidden shadow rounded-lg" id="<%= dom_id article %>">
      <div class="bg-white px-4 py-5 border-b border-gray-200 sm:px-6">
        <h3 class="text-lg leading-6 font-medium text-gray-900 m-0"><%= article.title %></h3>
      </div>
    
      <ul class="list-none grid grid-cols-2 gap-x-4 gap-y-8 px-4">
        <% article.images.each do |image| %>
          <li class="relative">
            <div class="group block w-full h-32 rounded-lg bg-gray-100 focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-offset-gray-100 focus-within:ring-indigo-500 overflow-hidden">
              <%= image_tag image.variant(:thumb), class: "object-cover h-full w-full group-hover:opacity-75" %>
            </div>
          </li>
        <% end %>
      </ul>
    </div>
    
    
    이것이 바로 그것이 보이는 모습이다.


    모형을 세우다
    다음 단계에서 우리는 모드 표기와 논리를 실현할 것이다.HTML 템플릿은 기본적으로 모드 배경, 닫기 버튼 및 컨텐츠 영역으로 구성되어 있습니다.
    <!-- app/views/attachments/_modal.html.erb -->
    <div class="hidden fixed z-30 inset-0 overflow-y-auto" data-lightbox-target="modal">
     <div class="flex items-center justify-center h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
    <%# Background %>
     <div class="opacity-0 fixed inset-0 transition-opacity" data-lightbox-target="background">
     <div class="absolute inset-0 bg-gray-700 bg-opacity-75">
    <%# top left slide labels %>
     <div class="sm:block absolute top-0 left-0 pt-4 pl-4">
     <span class="text-white font-medium"><!-- will hold the slide name --></span>
     </div>
    
    <%# X-close icon in top-right corner %>
     <div class="sm:block absolute top-0 right-0 pt-4 pr-4">
    <%= link_to "#", class: "text-gray-100" do %>
     <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path></svg>
    <% end %>
     </div>
     </div>
     </div>
    
    <%# Trick the browser into centering modal contents %>
     <span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">&#8203;</span>
    
     <div class="relative inline-block align-bottom rounded-lg text-left overflow-hidden transition-all sm:my-8 sm:align-middle sm:w-3/4 inset-0">
     <!-- content will go here -->
     </div>
     </div>
    </div>
    
    
    기사 색인 보기에서는 다음을 보여 줍니다.
      <!-- app/views/artices/index.html.erb -->
      <h1>Articles</h1>
    
    + <%= render "attachments/modal" %>
    
      <div id="articles" class="grid grid-cols-2 gap-4">
        <% @articles.each do |article| %>
          <%= render article %>
        <% end %>
      </div>
    
    
    이제 더 많은 토론을 하기 전에, 가장 기본적인 모드 기능인 열기와 닫기, 정상적으로 작동할 수 있도록 확보합시다.이를 위해, 우리는 자극 컨트롤러의 메인 디렉터리에 lightbox_controller을 만들었다.위 태그에서 찾을 수 있는 modalbackground 목표에 추가하고 handleOpenhandleClose 작업을 정의합니다. 이 두 작업은 본질적으로 CSS 클래스를 전환하여 제어 모드의 가시성을 제어합니다.
    // app/javascript/controllers/lightbox_controller.js
    import { Controller } from "stimulus";
    
    export default class extends Controller {
      static targets = ["modal", "background"];
    
      handleOpen(event) {
        event.preventDefault();
        this.modalTarget.classList.remove("hidden");
        this.backgroundTarget.classList.remove("opacity-0");
        this.backgroundTarget.classList.add("opacity-100");
      }
    
      handleClose(event) {
        event.preventDefault();
        this.modalTarget.classList.add("hidden");
        this.backgroundTarget.classList.remove("opacity-100");
        this.backgroundTarget.classList.add("opacity-0");
      }
    }
    
    
    이것은 응용 프로그램 레이아웃의 <body> 요소에 추가되므로 모든 뷰에서 램프를 정의할 수 있습니다.
    <!-- app/views/layouts/application.html.erb -->
    <!DOCTYPE html>
    <html>
      <head>
        <title>HotwireExampleTemplate</title>
        <meta name="viewport" content="width=device-width,initial-scale=1">
        <%= csrf_meta_tags %>
        <%= csp_meta_tag %>
    
        <script src="https://cdn.tailwindcss.com"></script>
        <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
        <%= javascript_importmap_tags %>
      </head>
    
    - <body class="font-sans">
    + <body class="font-sans" data-controller="lightbox">
        <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 font">
          <div class="max-w-3xl mx-auto">
            <%= yield %>
          </div>
        </div>
      </body>
    </html>
    
    
    모드를 열려면 이미지를 클릭할 때 handleOpen 작업을 호출합니다.
      <!-- app/views/articles/_article.html.erb -->
      <div class="bg-white overflow-hidden shadow rounded-lg" id="<%= dom_id article %>">
        <div class="bg-white px-4 py-5 border-b border-gray-200 sm:px-6">
          <h3 class="text-lg leading-6 font-medium text-gray-900 m-0"><%= article.title %></h3>
        </div>
    
        <ul class="list-none grid grid-cols-2 gap-x-4 gap-y-8 px-4">
          <% article.images.each do |image| %>
            <li class="relative">
              <div class="group block w-full h-32 rounded-lg bg-gray-100 focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-offset-gray-100 focus-within:ring-indigo-500 overflow-hidden">
    - <%= image_tag image, class: "object-cover h-full w-full group-hover:opacity-75" %>
    + <%= image_tag image, class: "object-cover h-full w-full group-hover:opacity-75", data: {action: "click->lightbox#handleOpen"} %>
              </div>
            </li>
            <% end %>
        </ul>
      </div>
    
    
    그런 다음 오른쪽 위에 있는 닫기 버튼을 클릭하여 닫습니다.
      <!-- app/views/attachments/_modal.html.erb -->
      <div class="hidden fixed z-30 inset-0 overflow-y-auto" data-lightbox-target="modal">
        <div class="flex items-center justify-center h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
          <%# Background %>
          <div class="opacity-0 fixed inset-0 transition-opacity" data-lightbox-target="background">
            <div class="absolute inset-0 bg-gray-700 bg-opacity-75">
              <%# top left slide labels %>
              <div class="sm:block absolute top-0 left-0 pt-4 pl-4">
                <span class="text-white font-medium"><!-- will hold the slide name --></span>
              </div>
    
              <%# X-close icon in top-right corner %>
              <div class="sm:block absolute top-0 right-0 pt-4 pr-4">
    - <%= link_to "#", class: "text-gray-100" do %>
    + <%= link_to "#", class: "text-gray-100", data: {action: "click->lightbox#handleClose"} do %>
                    <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path></svg>
                  <% end %>
                </div>
            </div>
          </div>
    
          <%# Trick the browser into centering modal contents %>
          <span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">&#8203;</span>
    
          <div class="inline-block align-bottom rounded-lg text-left overflow-hidden transition-all sm:my-8 sm:align-middle sm:w-3/4 inset-0">
            <!-- content will go here -->
          </div>
        </div>
      </div>
    
    


    터보 프레임을 통해 램프 컨텐트 로드
    다음은 램프의 이미지를 표시하기 위해 지연된 터보 프레임을 사용하는 방법을 탐색할 것입니다.먼저 AttachmentsController을 생성하고 라우트를 정의합니다.
    $ bin/rails g controller Attachments index
    
      # config/routes.rb
    
      Rails.application.routes.draw do
        resources :articles do
    + resources :attachments, only: :index
        end
      end
    
    
    그림이 문장에 첨부되어 있기 때문에, 우리는 컨트롤러 params에서 조회를 진행하였다.성능상의 이유로 layout false을 설정하였습니다. 프런트엔드에서 스왑할 단일 터보 프레임을 렌더링할 때 전체 <head> 등을 렌더링할 필요가 없습니다.
    # app/controllers/attachments_controller.rb
    
    class AttachmentsController < ApplicationController
      layout false
    
      before_action :set_article, only: :index
    
      def index
      end
    
      private
    
      def set_article
        @article = Article.find(params[:article_id])
      end
    end
    
    
    우리는 <turbo_frame>을 보여 주었는데, 초보적인 테스트를 위해 <img> 라벨을 표시하기만 하면 된다.
    <!-- app/views/attachments/index.html.erb -->
    
    <%= turbo_frame_tag :attachment_gallery do %>
      <% record.images.each do |image| %>
        <%= image_tag image, class: "object-contain rounded-xl px-6 h-full mx-auto max-h-screen" %>
      <% end %>
    <% end %>
    
    
    현재 모드에서 우리는 내용 영역에 빈 터보 프레임 표시를 추가하고 우리의 자극 frame에 새로운 목표를 정의한다(lightbox_controller).
      <!-- app/views/attachments/_modal.html.erb -->
      <div class="hidden fixed z-30 inset-0 overflow-y-auto" data-lightbox-target="modal">
        <div class="flex items-center justify-center h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
          <%# Background %>
          <div class="opacity-0 fixed inset-0 transition-opacity" data-lightbox-target="background">
            <div class="absolute inset-0 bg-gray-700 bg-opacity-75">
              <%# top left slide labels %>
              <div class="sm:block absolute top-0 left-0 pt-4 pl-4">
                <span class="text-white font-medium"><!-- will hold the slide name --></span>
              </div>
    
              <%# X-close icon in top-right corner %>
              <div class="sm:block absolute top-0 right-0 pt-4 pr-4">
                 <%= link_to "#", class: "text-gray-100", data: {action: "click->lightbox#handleClose"} do %>
                    <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path></svg>
                  <% end %>
                </div>
            </div>
          </div>
    
          <%# Trick the browser into centering modal contents %>
          <span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">&#8203;</span>
    
          <div class="inline-block align-bottom rounded-lg text-left overflow-hidden transition-all sm:my-8 sm:align-middle sm:w-3/4 inset-0">
    - <!-- content will go here -->
    + <%= turbo_frame_tag :attachment_gallery, src: "", data: { "lightbox-target": "frame"} %>
          </div>
        </div>
      </div>
    
    
    이제 터보 프레임에 불러올 URL을 정의하는 동작 매개 변수를 이미지 탭에 추가할 수 있습니다.기억해라. data-lightbox-url-param이 호출될 때 당신의 자극적인 동작에 의해 해석될 수 있다. 우리는 아래에서 볼 것이다.
      <!-- app/views/articles/_article.html.erb -->
      <div class="bg-white overflow-hidden shadow rounded-lg" id="<%= dom_id article %>">
        <div class="bg-white px-4 py-5 border-b border-gray-200 sm:px-6">
          <h3 class="text-lg leading-6 font-medium text-gray-900 m-0"><%= article.title %></h3>
        </div>
    
        <ul class="list-none grid grid-cols-2 gap-x-4 gap-y-8 px-4">
          <% article.images.each do |image| %>
            <li class="relative">
              <div class="group block w-full h-32 rounded-lg bg-gray-100 focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-offset-gray-100 focus-within:ring-indigo-500 overflow-hidden">
    - <%= image_tag image, class: "object-cover h-full w-full group-hover:opacity-75", data: {action: "click->lightbox#handleOpen"} %>
    + <%= image_tag image.variant(:thumb),
    + class: "object-cover h-full w-full group-hover:opacity-75",
    + data: {
    + action: "click->lightbox#handleOpen",
    + lightbox_url_param: article_attachments_path(article)
    + } %>
              </div>
            </li>
          <% end %>
        </ul>
      </div>
    
    
    이를 붙이려면 framelightbox_controller에 추가하고 event.params 작업을 호출할 때 handleOpen에서 URL을 검색해야 합니다.남은 일은 터보 프레임의 src을 교체하는 것입니다. 이 지연 로드 메커니즘은 남은 작업을 완성할 것입니다.
      // app/javascript/controllers/lightbox_controller.js
      import { Controller } from "stimulus";
    
      export default class extends Controller {
    - static targets = ["modal", "background"];
    + static targets = ["modal", "background", "frame"];
    
        handleOpen(event) {
          event.preventDefault();
          this.modalTarget.classList.remove("hidden");
          this.backgroundTarget.classList.remove("opacity-0");
          this.backgroundTarget.classList.add("opacity-100");
    
    + const { url } = event.params;
    + this.frameTarget.src = url;
        }
    
        handleClose(event) {
          event.preventDefault();
          this.modalTarget.classList.add("hidden");
          this.backgroundTarget.classList.remove("opacity-100");
          this.backgroundTarget.classList.add("opacity-0");
        }
      }
    
    
    다음은 Turbo Frame 컨텐트와 선택한 기사의 차이점을 보여주는 중간 결과의 GIF입니다.

    주의: 조금만 노력하면 더욱 다태적일 수 있다(즉, Article과 귀속되지 않고 각종 부가항목과 귀속된다) - 이것은 독자에게 남겨진 연습이다:-)

    통합 Swiper.js
    현재, 이 이미지 목록을 램프 박스로 바꾸기 위해서, 우리는 swiper.js을 사용할 것이다.그러면 importmap에 고정시킵니다.
    $ bin/importmap pin swiper 
    
    
    또한 프레젠테이션을 위해 CDN에 swiper CSS를 추가했습니다.
    <!-- app/views/layouts/application.html.erb -->
    <!DOCTYPE html>
    <html>
      <head>
        <title>HotwireExampleTemplate</title>
        <meta name="viewport" content="width=device-width,initial-scale=1">
        <%= csrf_meta_tags %>
        <%= csp_meta_tag %>
    
    + <link
    + rel="stylesheet"
    + href="https://unpkg.com/swiper@8/swiper-bundle.min.css"
    + />
        <script src="https://cdn.tailwindcss.com"></script>
        <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
        <%= javascript_importmap_tags %>
      </head>
    
      <body class="font-sans" data-controller="lightbox">
        <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 font">
          <div class="max-w-3xl mx-auto">
            <%= yield %>
          </div>
        </div>
      </body>
    </html>
    
    
    Swiper는 Navigation과 같은 일부 기능을 모듈에서 제거하고 어플리케이션 엔트리 포인트에서 활성화합니다.
      // app/javascript/application.js
    
      // Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
      import "tailwind.config";
      import "@hotwired/turbo-rails";
      import "controllers";
      import "trix";
      import "@rails/actiontext";
    + import Swiper, { Navigation } from "swiper";
    
    + Swiper.use([Navigation]);
    
    
    자극 컨트롤러 포장 브러시는 상당히 간단하다.이것은 container 목표에서 swiper를 초기화하는 것일 뿐입니다. 우리는 이를 nextprev 목표를 내비게이션 링크로 설정합니다.
    // app/javascript/controllers/swiper_controller.js
    import Swiper from "swiper";
    import { Controller } from "@hotwired/stimulus";
    
    export default class extends Controller {
      static targets = ["container", "next", "prev"];
    
      connect() {
        this.swiper = new Swiper(this.containerTarget, {
          navigation: {
            nextEl: this.nextTarget,
            prevEl: this.prevTarget,
          },
        });
      }
    }
    
    
    지금 우리가 해야 할 일은 우리의 갤러리 표시를 swiper HTML layout에 부합하도록 재구성하는 것이다.이것은 많든 적든 우리가 swiper-wrapper류로 모든 내용을 한div에 포장하고 swiper-slide류로 각 슬라이드를 한div에 포장해야 한다는 것을 의미한다.또한 탐색 버튼에 태그를 추가했습니다.
    <!-- app/views/attachments/index.html.erb -->
    
      <%= turbo_frame_tag :attachment_gallery do %>
    - <% @article.images.each do |image| %>
    - <%= image_tag image, class: "object-contain rounded-xl px-6 h-full mx-auto max-h-screen" %>
    - <% end %>
    + <div data-controller="swiper">
    + <div class="swiper" data-swiper-target="container">
    + <div class="swiper-wrapper">
    + <% @article.images.each do |image| %>
    + <div class="swiper-slide flex justify-center"
    + <%= image_tag image, class: "object-contain rounded-xl h-96 mx-auto" %>
    + </div>
    + <% end %>
    + </div>
    + 
    + <div class="swiper-button-prev" data-swiper-target="prev">
    + </div>
    + <div class="swiper-button-next" data-swiper-target="next">
    + </div>
    + </div>
    + </div>
      <% end %>
    
    
    이것이 바로 그것의 진정한 의미이다.보기:


    올바른 슬라이드로 시작
    작은 디테일이 완벽한 램프 체험에 해롭다.현재 어떤 그림을 클릭하든지 첫 번째 슬라이드가 표시됩니다.

    우리는 무엇을 할 수 있습니까?이것이 바로 똑똑한 크로스 컨트롤러 통신이 시간을 절약할 수 있는 부분이다.Swiper는 슬라이드에서 slideTo을 찾을 수 있는 방법이 있기 때문에 우리가 해야 할 일은
  • 모드를 열 때 (키 하나)
  • 을 인용하는 방법을 찾습니다
  • 열쇠를 swiper_controller에게 전달
  • 호출 swiper.slideTo
  • 각 ActiveStorage blob에는 key이 있으며 축소판에 다른 자극 동작 매개 변수로 추가할 수 있습니다.
      <!-- app/views/articles/_article.html.erb -->
      <div class="bg-white overflow-hidden shadow rounded-lg" id="<%= dom_id article %>">
        <div class="bg-white px-4 py-5 border-b border-gray-200 sm:px-6">
          <h3 class="text-lg leading-6 font-medium text-gray-900 m-0"><%= article.title %></h3>
        </div>
    
        <ul class="list-none grid grid-cols-2 gap-x-4 gap-y-8 px-4">
          <% article.images.each do |image| %>
            <li class="relative">
              <div class="group block w-full h-32 rounded-lg bg-gray-100 focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-offset-gray-100 focus-within:ring-indigo-500 overflow-hidden">
                <%= image_tag image.variant(:thumb),
                 class: "object-cover h-full w-full group-hover:opacity-75",
                 data: {
                   action: "click->lightbox#handleOpen",
                   lightbox_url_param: article_attachments_path(article),
    + lightbox_key_param: image.key
                 } %>
              </div>
            </li>
          <% end %>
        </ul>
      </div>
    
    
    event.params에서 이전에 URL에 대해 설명한 것처럼 키를 캡처할 수 있습니다.현재 우리가 해야 할 일은 loaded promise on a Turbo Frame element을 기다리는 것이다. 프레임의 현재 내비게이션 (이미지 슬라이드 로드) 이 완성되면 그것이 해결될 것이다.그런 다음 이벤트의 세부 객체에 dispatch an “open” event을 추가합니다.
      // app/javascript/controllers/lightbox_controller.js
      import { Controller } from "stimulus";
    
      export default class extends Controller {
        static targets = ["modal", "background", "frame"];
    
    - handleOpen(event) {
    + async handleOpen(event) {
          event.preventDefault();
          this.modalTarget.classList.remove("hidden");
          this.backgroundTarget.classList.remove("opacity-0");
          this.backgroundTarget.classList.add("opacity-100");
    
    - const { url } = event.params;
    + const { url, key } = event.params;
          this.frameTarget.src = url;
    
    + await this.frameTarget.loaded;
    +
    + this.dispatch("open", { detail: { key } });
        }
    
        handleClose(event) {
          event.preventDefault();
          this.modalTarget.classList.add("hidden");
          this.backgroundTarget.classList.remove("opacity-100");
          this.backgroundTarget.classList.add("opacity-0");
        }
      }
    
    
    갤러리 보기에서, 우리는 이 사건을 감청하고 (새) swiper#naviate 조작을 호출합니다.또한 Blob key을 슬라이드마다 추가하여 다음을 선택할 수 있습니다.
    <!-- app/views/attachments/index.html.erb -->
    
      <%= turbo_frame_tag :attachment_gallery do %>
    - <div data-controller="swiper">
    + <div data-controller="swiper"
    + data-action="lightbox:open@document->swiper#navigate">
          <div class="swiper" data-swiper-target="container">
            <div class="swiper-wrapper">
              <% @article.images.each do |image| %>
                <div class="swiper-slide flex justify-center"
    + data-key="<%= image.key %>"
                  <%= image_tag image, class: "object-contain rounded-xl h-96 mx-auto" %>
                </div>
              <% end %>
            </div>
    
            <div class="swiper-button-prev" data-swiper-target="prev">
            </div>
            <div class="swiper-button-next" data-swiper-target="next">
            </div>
          </div>
        </div>
      <% end %>
    
    
    swiper_controller은 상기 동작 navigate을 갖추고 있다.더 잘 평가하기 위해서, 우리는 이벤트 전달의 키로 설정하는 activeKey 목표를 추가했습니다.변경된 콜백(activeKeyValueChanged)에서 위에서 정의한 데이터 속성을 사용하여 슬라이드에서 활성 키에 해당하는 정확한 인덱스를 찾습니다.그리고 slideTo을 호출하고 애니메이션 시간은 0으로 즉시 설정합니다.만약 우리가 외부에서 활동의 관건을 설정하고 싶다면 자극값의 사용은 깔끔한 확장점을 제공할 것이다.
      // app/javascript/controllers/swiper_controller.js
      import Swiper from "swiper";
      import { Controller } from "@hotwired/stimulus";
    
      export default class extends Controller {
        static targets = ["container", "next", "prev"];
    + static values = { activeKey: String };
    
        connect() {
          this.swiper = new Swiper(this.containerTarget, {
            navigation: {
              nextEl: this.nextTarget,
              prevEl: this.prevTarget,
            },
          });
        }
    + 
    + navigate(event) {
    + event.preventDefault();
    + 
    + const { key } = event.detail;
    + 
    + this.activeKeyValue = key;
    + }
    + 
    + activeKeyValueChanged() {
    + const activeSlide = this.swiper?.slides.findIndex(
    + (slide) => slide.dataset.key == this.activeKeyValue
    + );
    + 
    + this.swiper?.slideTo(activeSlide, 0);
    + }
      }
    
    
    현재 모든 것이 광고와 같다.


    이점: 슬라이드 이름 설정
    나는 너희들과 마지막 좋은 소식을 나누고 싶다.상기와 같은 메커니즘을 사용하면 우리는 상반된 방식으로 이벤트를 보내서 swiper_controller에서 lightbox_controller으로 보내서 모드 제목에 이벤트 슬라이드의 파일 이름을 표시할 것이다.이를 위해 Blob 키를 사용했던 것처럼 슬라이드의 데이터 세트에 파일 이름을 추가합니다.
      <!-- app/views/attachments/index.html.erb -->
    
      <%= turbo_frame_tag :attachment_gallery do %>
        <div data-controller="swiper"
          data-action="lightbox:open@document->swiper#navigate">
          <div class="swiper" data-swiper-target="container">
            <div class="swiper-wrapper">
              <% @article.images.each do |image| %>
                <div class="swiper-slide flex justify-center"
                     data-key="<%= image.key %>"
    + data-filename="<%= image.blob.filename %>">
                  <%= image_tag image, class: "object-contain rounded-xl h-96 mx-auto" %>
                </div>
              <% end %>
            </div>
    
            <div class="swiper-button-prev" data-swiper-target="prev">
            </div>
            <div class="swiper-button-next" data-swiper-target="next">
            </div>
          </div>
        </div>
      <% end %>
    
    
    Swiper는 slideChange과 같은 몇 가지 리셋을 공개했는데, 우리는 이 리셋을 사용하여 데이터 집합에서 slide-change 이벤트를 보냈는데, 이는 이벤트 슬라이드의 이름을 포함한다.
      // app/javascript/controllers/swiper_controller.js
      import Swiper from "swiper";
      import { Controller } from "@hotwired/stimulus";
    
      export default class extends Controller {
        static targets = ["container", "next", "prev"];
        static values = { activeKey: String };
    
        connect() {
          this.swiper = new Swiper(this.containerTarget, {
            navigation: {
              nextEl: this.nextTarget,
              prevEl: this.prevTarget,
            },
    + on: {
    + slideChange: this.handleSlideChange.bind(this),
    + },
          });
        }
    
        navigate(event) {
          event.preventDefault();
    
          const { key } = event.detail;
    
          this.activeKeyValue = key;
        }
    
        activeKeyValueChanged() {
          const activeSlide = this.swiper?.slides.findIndex(
            (slide) => slide.dataset.key == this.activeKeyValue
          );
    
          this.swiper?.slideTo(activeSlide, 0);
        }
    
    + handleSlideChange(swiper) {
    + this.dispatch("slide-change", {
    + detail: { slideName: swiper.slides[swiper.activeIndex].dataset.filename },
    + });
    + }
      }
    
    
    lightbox_controller에서 우리는 새로운 slideName 목표를 추가했고 우리는 새로운 조작 setSlideName에서 이 사건에서 이 목표를 채웠다.
      // app/javascript/controllers/lightbox_controller.js
      import { Controller } from "stimulus";
    
      export default class extends Controller {
    - static targets = ["modal", "background", "frame"];
    + static targets = ["modal", "background", "frame", "slideName"];
    
        async handleOpen(event) {
          event.preventDefault();
          this.modalTarget.classList.remove("hidden");
          this.backgroundTarget.classList.remove("opacity-0");
          this.backgroundTarget.classList.add("opacity-100");
    
          const { url, key } = event.params;
          this.frameTarget.src = url;
    
          await this.frameTarget.loaded;
    
          this.dispatch("open", { detail: { key } });
        }
    
        handleClose(event) {
          event.preventDefault();
          this.modalTarget.classList.add("hidden");
          this.backgroundTarget.classList.remove("opacity-100");
          this.backgroundTarget.classList.add("opacity-0");
        }
    
    + setSlideName({ detail }) {
    + this.slideNameTarget.textContent = detail.slideName;
    + }
      }
    
    
    마지막으로 누락된 부분은 태그에 이벤트 탐지기를 선언하고 slideName 대상을 정의하는 것입니다.
      <!-- app/views/attachments/_modal.html.erb -->
    - <div class="hidden fixed z-30 inset-0 overflow-y-auto" data-lightbox-target="modal">
    + <div class="hidden fixed z-30 inset-0 overflow-y-auto" data-lightbox-target="modal" data-action="swiper:slide-change->lightbox#setSlideName">
        <div class="flex items-center justify-center h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
          <%# Background %>
          <div class="opacity-0 fixed inset-0 transition-opacity" data-lightbox-target="background">
            <div class="absolute inset-0 bg-gray-700 bg-opacity-75">
              <%# top left slide labels %>
              <div class="sm:block absolute top-0 left-0 pt-4 pl-4">
    - <span class="text-white font-medium"><!-- will hold the slide name --></span>
    + <span class="text-white font-medium" data-lightbox-target="slideName"></span>
              </div>
    
              <%# X-close icon in top-right corner %>
              <div class="sm:block absolute top-0 right-0 pt-4 pr-4">
                 <%= link_to "#", class: "text-gray-100", data: {action: "click->lightbox#handleClose"} do %>
                    <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path></svg>
                  <% end %>
                </div>
            </div>
          </div>
    
          <%# Trick the browser into centering modal contents %>
          <span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">&#8203;</span>
    
          <div class="inline-block align-bottom rounded-lg text-left overflow-hidden transition-all sm:my-8 sm:align-middle sm:w-3/4 inset-0">
            <%= turbo_frame_tag :attachment_gallery, src: "", data: { "lightbox-target": "frame"} %>
          </div>
        </div>
      </div>
    
    
    Tada, 이것은 우리의 모드입니다. 제목에 파일 이름을 표시합니다!🎉

    좋은 웹페이지 즐겨찾기