如何使用 AlpineJS 和 Flatpickr 停止传播?

How to stop propagation with AlpineJS and Flatpickr?

我的应用程序中有一个过滤器模态 window。过滤器模态具有 @click.outside="filters = false",因此如果用户在模态之外单击,它将隐藏。在该过滤器模式中,我可以选择使用 Flatpickr 的最短日期。

问题是当您单击箭头更改月份 - 或者顶部的月份或年份 - 过滤器模式将隐藏。

我认为我需要在元素上使用 e.stopPropagation@click.prevent,但它在我尝试过的每个地方都不起作用。

如何才能使 Flatpicker window 内的任何点击都不会向上传播并关闭过滤器模式?

这是我的完整代码 -

<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>

我不相信 AlpineJS 能够做到这一点,所以你可以加入一些自定义 JS。您可以在结束正文标记之前的脚本标记中添加此代码:

[...document.getElementsByClassName("flatpickr-calendar")].forEach($el => {
    $el.addEventListener("click", e => e.stopPropagation());
});

用户打开 Flatpickr 弹出窗口后,Flatpickr 会将日历附加到正文 的末端 ,而不是在 div 的内部,因此它是“外部”点击.

AlpineJS 通过向 window(或类似的东西)添加事件侦听器来确定“外部”点击,当事件冒泡(传播)时,它会测试点击目标是否是请求外部的元素点击。如果没有,则触发一个事件,表明有“外部”点击。

这段代码本质上做的是,一旦点击 Flatpickr 弹出日历,我们就会阻止事件冒泡到 AlpineJS,所以 Alpine 不知道 window根本没有,因此 @click.outside 不会在点击日历时触发。

此问题的一个简单解决方案是引入一个 pickerOpen 变量,通过 onOpen and onClose hooks 监视 Flatpickr 弹出窗口的状态。然后仅在 Flatkpickr 弹出窗口处于非活动状态时关闭模式 window。

<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>