Stimulus ile Panoya Kopyalama

Bu yazıda Stimulus’un temel bazı özelliklerinden bahsederken, Stimulus ile Rails uygulamasına ‘Panoya Kopyalama’ özelliğinin nasıl eklenebileceğinden bahsedilecek.

Örnek olarak view’ımızda şarkı sözleri olsun.

Bu HTML kodunun çıktısı şu şekilde olacaktır.

Html kodu ciktiis

Amacımız kullanıcı kopyalama ikonuna tıklandığında şarkı sözlerinin kullanıcının panosuna (clipboard’a) kopyalanmasını sağlamak.

İkon’a tıklandığında şarkı sözlerinin panoya kopyalanmasını sağlamak için gerekli adımlar şu şekilde sıralanabilir:

  1. Stimulus’ta clipboard_controller.js oluşturulmalı.
  2. Kopyalanacak içeriğin, oluşturulan controllera referans (target) edilmesi sağlanmalı.
  3. İkon’un data attribute‘una ikona tıklandığında çalışması üzere, data-action attribute’unda copy adında bir JS methodu tanımlanmalı.
  4. Kopyalanılacak içeriğin (şarkı sözleri) clipboard controller‘da referans (target) edilmesi sağlanmalı.
  5. Kopyalama işleminin başarılı olduğunu kullanıcıya bildirebilmek (feedback) amacıyla; ikonun konumunda bir süreliğine ‘Kopyalandı.’ yazmalı ve ikon eski haline dönmeli.

HTML tarafında gerekli değişiklikleri uyguladığımızda, kod şu şekilde görünecektir.

  1. Tüm içeriği saran div (wrapper), data-controller özelliği ile clipboard_controller.js‘e bağlanması sağlandı. Böylece Stimulus controller’ın scope’u tanımlanmış oldu1.
  2. İkon buton ile wrap edildi, Butona tıklandığında JS controller’da oluşturulacak copy methodunun çalışmasını sağlandı.
  3. Butonun varsayılan davranışı tıklama (click) olduğu için, yalnızca action’i yazmamız yetecektir. jQuery’deki gibi click->clipboard#copy şeklinde bir syntax’a gerek duyulmamaktadır2.
  4. 2 numaralı hedefi (şarkı sözlerini controller’a target olarak tanımlamak) gerçekleştirmek için şarkı sozlerinin oldugu div’e text-container target’i verildi.
  5. İkonu wrap eden butona clipboard-button target’i tanimlanarak 5 numaralı hedefi gerçeklestirmek için target hazırlandı.
  6. 5 numaralı hedefini gerçekleştirmek için, clipboard controller’a notify-message-value gönderildi, bu sayede lokalize edilmiş bir mesaj ile ‘Kopyalamanın başarılı olduğunu’ kullanıcıya bildirilebilecek.

Şimdi, Stimulus tarafında oluşacak targetlarımız ve methodlarımız belli, bunları oluşturabiliriz.

Target’lar temelde querySelector’ler ile çalışan, ilgili tanımlı elementleri bulup erişmemizi, üzerinde işlem yapmamızı kolaylaştıran bir yöntemdir. Stimulus, controller’ın scope’undaki elemanları queryElement methodu aracılığıyla data attributelar üzerinden bulur ve kullanabilmemize olanak sağlamaktadır3.

// targets

  static targets = ['textContainer', 'clipboardButton']

Value tanımlaması esnasında value’nun tipini ve varsayılan değerini vererek daha tutarlı bir çalışma şekli sağlayabiliriz. Örneğin, lokalizasyon tanımlanmadıysa varsayılan olarak İngilizce’yi kullanarak kullanıcıya bildirim yapılması sağlanabilir.

// values

  static values = {
    notifyMessage: {
            type:    String,
            default: 'Copied!' // value if not defined as data attr
        }
  }

Yukarıda dikkat edilmesi gereken bir diğer husus, HTML’de kebab-case olarak tanımlanan notify-message değişkeninin Stimulus’ta camelCase’e dönüşmesidir. Naming convention’a dikkat edilmesi gerekmektedir.4

Methodlar:

Öncelikle copy methodumuzla başlayalım ve bu method’un clipboard ikonuna tıklandığında çalışacağını hatırlayalım. Targetlara this.[targetName]Target şeklinde erişilmektedir ve bu methodda textContainerTarget‘ın innerText‘i clipboard’a yazılmıştır (kopyalanmıştır).

  copy(event) {
    event.preventDefault()
    event.stopPropagation()

    const text = this.textContainerTarget.innerText
    navigator.clipboard.writeText(text)
  }

Elimizdekileri birleştirirsek:

import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
   static values = {
      succesMessage: {
              type:    String,
              default: 'Copied!'
          }
   }

  static targets = ['textContainer', 'clipboardButton']

  copy(event) {
    event.preventDefault()
    event.stopPropagation()

    const text = this.textContainerTarget.innerText
    navigator.clipboard.writeText(text)
  }
}

Şu an aslında controllerda pnaoya kopyalama işlemi yapılabilmektedir. Fakat durumun başarılı olduğunu kullanıcıya ifade etmek için bazı eklemeler yapmamız gerekiyor.. succesMessage‘ı henüz kullanmadık.

Yapacağımız işlem aslında basitçe notifyUI methodu aracılığıyla kopyalama ikonunun içeriğini değiştirip, bir süre sonra eski haline döndürülmesidir.

Panoya kopyalama işlemi async bir işlem oldugu için navigator.clipboard.writeText(text).then(notifyUI) şeklinde ekrana feedback veren bir method tanımlamamız gerekmektedir. Bunu sağlamak için setTimeout methodunu kullanmak gereklidir.

Bunun için öncelikle state’i (yani eski haline dönüşünü) sağlayabilmek için Stimulus’un callback actionlarindan olan connect() methodunu kullanalım ve innerHTML’ın eski halini hafızaya alalım. connect methodu controller DOM’a her bağlandığında çalışmaktadır. 5

  connect() {
    this.clipboardButtonInitialInnerHTML = this.clipboardButtonTarget.innerHTML
  }

notifyUI methodunu ekleyelim.

  notifyUI() {
    this.clipboardButtonTarget.innerHTML = this.notifyMessageValue
    setTimeout(() => {
      this.clipboardButtonTarget.innerHTML = this.clipboardButtonInitialInnerHTML
    }, 2000)
  }

Özetle: Yukarıdaki kod parçası, kopyalama işlemi başarılı olduğunda kullanıcıya bildirim vermek ve ikonu eski haline döndürmek için kullanılmaktadır. notifyUI methodu, navigator.clipboard.writeText(text) işleminin ardından çalışır.

İlk olarak, ikonun içeriğini successMessageValue ile değiştirmektedir, böylece kullanıcıya başarılı bir kopyalama işlemi olduğunu bildirir. Ardından, setTimeout kullanarak 2 saniye sonra ikonun orijinal halini geri almasını sağlamaktadır.

NotifyUI methodunun çalışma şekli

Elimizdeki tüm bu methodları birleştirdiğimizde clipboard_controller.js dosyasının son hali aşağıdaki gibi olacaktır.

Bu yazıda ‘copy to clipboard’ fonksiyonalitesi sağlamak için ilgili controller’ın oluşturulması üzerinden temel Stimulus yöntemleri üzerine konuştuk.



Faydalı kaynaklar: