hotwired/stimulus-rails

Dialog Bypass on Rapid Increment/Decrement Clicks with Stimulus

AdamMusa opened this issue · 0 comments

When rapidly clicking the increment or decrement buttons, the dialog intended to appear based on specific countValue conditions (10 or -3) sometimes fails to show as expected.

bug

To Reproduce the Bug

  1. Copy and paste the provided code into your project.
  2. Run the application.
  3. Rapidly click the increment or decrement button to observe the dialog behavior.
import { Controller } from "@hotwired/stimulus"

// Connects to data-controller="counter"
export default class extends Controller {
  static targets = ['count','showDialog']
  static values = {count: Number,active: Boolean}
  connect() {
    this.countValue = 0
    this.activeValue = false;
    this.updateCount()
    this.showDialog()

  }

  increment(){
    this.countValue++
    this.updateCount()
    this.showDialog()
  }

  decrement(){
   this.countValue--
   this.updateCount()
   this.showDialog()
  }

  updateCount(){
    this.countTarget.textContent = this.countValue
  }
  showDialog(){
    this.activeValue =  (this.countValue == 10 || this.countValue == -3)
    if(this.activeValue) this.showDialogTarget.classList.remove('hidden')
  }

  showDialog() {
    const shouldShowDialog = (this.countValue === 10 || this.countValue === -3);
    if (shouldShowDialog) {
      this.showDialogTarget.classList.remove('hidden');
      setTimeout(() => {
        this.showDialogTarget.classList.remove('opacity-0', 'pointer-events-none');
        this.showDialogTarget.classList.add('opacity-100');
      }, 10); // Delay to ensure class removal is processed
    } else {
      this.showDialogTarget.classList.remove('opacity-100');
      this.showDialogTarget.classList.add('opacity-0');
      setTimeout(() => {
        this.showDialogTarget.classList.add('hidden');
        this.showDialogTarget.classList.remove('pointer-events-none');
      }, 300); // Duration of the fade-out animation
    }
  }
  closeDialog() {
    this.countValue = 0;
    this.countTarget.textContent = 0;
  
    // Add move-left class for the animation
    this.showDialogTarget.classList.add('move-left');
  
    setTimeout(() => {
      this.showDialogTarget.classList.add('hidden');
      this.showDialogTarget.classList.remove('pointer-events-none');
      // Remove the move-left class to reset for future openings
      this.showDialogTarget.classList.remove('move-left');
    }, 300); // Duration of the animation
  }
  
}

this is my view code

<div data-controller="counter" class="w-full h-full">
  <!-- Increment button with multiple actions -->
  <button data-action="click->counter#increment mouseover->counter#increment">Increment</button>
  
  <!-- Display count value -->
  <p data-counter-target="count"></p>
  
  <!-- Decrement button -->
  <button data-action="click->counter#decrement">Decrement</button>
  
  <!-- Dialog element with data-controller-target -->
 <!-- Dialog element with data-controller-target -->
  <div data-counter-target="showDialog" class="fixed inset-0 flex items-center justify-center z-50 opacity-0 pointer-events-none transition-opacity duration-300 ease-out hidden">
    <div class="bg-black bg-opacity-70 p-6 shadow-lg w-full h-full flex items-center justify-center">
      <div class="bg-white p-6 rounded-lg max-w-4xl w-full">
        <h2 class="text-black text-xl font-semibold mb-4">Dialog Title</h2>
        <p class="text-black mb-4">This is the content of the dialog card. You can add more text or HTML here.</p>
        <button data-action="click->counter#closeDialog" class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600">
          Close
        </button>
      </div>
    </div>
  </div>
</div>

<div data-controller="home">
  <div data-home-target="change">
  </div>
  <h1 class="font-bold text-4xl" data-action="click->home#open">Home#index</h1>
  <p  data-action="click->home#replace">Find me in app/views/home/index.html.erb</p>
</div>

Expected Behavior
The dialog should consistently appear when countValue is 10 or -3 in my case, and disappear otherwise, regardless of how quickly the increment or decrement buttons are clicked.