Search code examples
javascriptalpine.jsflatpickr

How to stop propagation with AlpineJS and Flatpickr?


I have a modal window for filters on my application. The filters modal has @click.outside="filters = false" so if the user clicks outside of the modal it will hide. Inside of that filters modal I have an option for choosing the minimum date for which I'm using Flatpickr.

The problem is when you click on the arrows to change the month - or the month or year at the top - the filters modal will hide.

I believe that I need to use e.stopPropagation or @click.prevent on the element but it hasn't worked in each spot that I've tried it.

How do I make it so that any clicks inside of the Flatpicker window doesn't propagate up and close the filters modal?

Here is my full code -

<div x-show="filters" @click.outside="filters = false" x-on:keydown.escape.window="filters = false" class="absolute shadow-lg z-40 mt-4">
  <div x-trap="filters">
    <div>
      <label for="filter-date-min" class="block text-sm font-semibold leading-5 text-gray-700">Minimum Date</label>
      <div class="mt-1 relative rounded-md shadow-sm">
        <div x-data="{
          value: '',
          init() {
            let picker = flatpickr(this.$refs.picker, {
              dateFormat: 'Y-m-d',
              defaultDate: this.value,
              onChange: (date, dateString) => {
                this.value = dateString
              },
            })
          },
        }">
          <input id="filter-date-min" placeholder="MM/DD/YYYY" x-ref="picker" x-bind:value="value" class="flex-1 min-w-0 block w-full px-3 py-2 rounded-none rounded-r-md focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm border-gray-300 border active" autocomplete="off">
        </div>
      </div>
    </div>
  </div>
</div>

Solution

  • A simple solution for this issue is to introduce a pickerOpen variable that monitors the Flatpickr popup's state via the onOpen and onClose hooks. Then only close the modal window when Flatkpickr popup is inactive.

    <div x-data="{filters: false, pickerOpen: false}">
      <div x-show="filters"
           @click.outside="if (!pickerOpen) {filters = false}"
           x-on:keydown.escape.window="if (!pickerOpen) {filters = false}"
           class="absolute shadow-lg z-40 mt-4">
        <div x-trap="filters">
          <div>
            <label for="filter-date-min" class="block text-sm font-semibold leading-5 text-gray-700">Minimum Date</label>
            <div class="mt-1 relative rounded-md shadow-sm">
              <div x-data="{
                value: '',
                init() {
                  let picker = flatpickr(this.$refs.picker, {
                    dateFormat: 'Y-m-d',
                    defaultDate: this.value,
                    onOpen: () => {this.pickerOpen = true},
                    onClose: () => {this.pickerOpen = false},
                    onChange: (date, dateString) => {
                      this.value = dateString
                    },
                  })
                },
              }">
                <input id="filter-date-min" placeholder="MM/DD/YYYY" x-ref="picker" x-bind:value="value" autocomplete="off"
                       class="flex-1 min-w-0 block w-full px-3 py-2 rounded-none rounded-r-md focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm border-gray-300 border active">
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>