Source

components/shared/SearchResults/FilterResults.vue

<template lang="pug">
div.filter_results(
  v-b-tooltip.hover.viewport,
  :title="$t('common.results_toolbar.filter_button_tooltip')"
)
  v-select(
    ref="select"
    :searcheable="true" 
    :clearable="true"
    :value="value"
    @input="v=>$emit('input',v)"
    class="filter_results"
    :append-to-body="true" :calculate-position="withPopper"
    :options="options"
  )
    template( v-slot:search="{ attributes, events }")
      b-button.mini-btn.d-flex.justify.align-items-center.p-0(variant="outline-primary"
        data-name="filter_button"
      ) 
        em.fas.fa-filter.m-0(
          data-name="filter_button"
          maxlength="1"
          class=""
          v-bind="attributes"
          v-on="events" )
</template>
<script>
import { createPopper } from '@popperjs/core'
import $ from 'jquery'
import i18n from '@/i18n'

export const filterResultsSelectOptions = {
  none: {
    label: i18n.t('location_module.search_filters.none'),
    value: 'NONE',
  },
}

/**
 * Used by Location module
 * @todo Refactor migrate vue-select to vue-multiselect and remove vue-select dependency
 */
export default {
  props: {
    size: {
      type: Number,
      default: 16,
    },
    color: {
      type: String,
      default: '--color-black',
    },
    value: {
      type: Object,
      default: () => ({}),
    },
    circuitFilters: {
      type: Boolean,
      default: true,
    },
    missionFilters: {
      type: Boolean,
      default: true,
    },
    chronoFilters: {
      type: Boolean,
      default: false,
    },
  },
  computed: {
    style() {
      return `font-size:${this.size}px; color: var(${this.color})`
    },
    options() {
      return [
        filterResultsSelectOptions.none,
        {
          label: this.$t('location_module.search_filters.contact_on__move'),
          value: 'CONTACT_ON__MOVE',
        },
        {
          label: this.$t('location_module.search_filters.contact_on__stop'),
          value: 'CONTACT_ON__STOP',
        },
        {
          label: this.$t('location_module.search_filters.contact_on__slow'),
          value: 'CONTACT_ON__SLOW',
        },
        {
          label: this.$t('location_module.search_filters.contact_off'),
          value: 'CONTACT_OFF',
        },
        ...(this.circuitFilters
          ? [
              {
                label: this.$t('location_module.search_filters.active_circuit'),
                value: 'ACTIVE_CIRCUIT',
              },
            ]
          : []),
        ...(this.missionFilters
          ? [
              {
                label: this.$t('location_module.search_filters.active_mission'),
                value: 'ACTIVE_MISSION',
              },
            ]
          : []),
        ...(this.chronoFilters
          ? [
              {
                label: this.$t('location_module.search_filters.chrono_driving'),
                value: 'CHRONO_DRIVING',
              },
              {
                label: this.$t(
                  'location_module.search_filters.chrono_available'
                ),
                value: 'CHRONO_AVAILABLE',
              },
              {
                label: this.$t('location_module.search_filters.chrono_resting'),
                value: 'CHRONO_RESTING',
              },
              {
                label: this.$t('location_module.search_filters.chrono_working'),
                value: 'CHRONO_WORKING',
              },
            ]
          : []),
      ]
    },
  },
  watch: {
    value() {
      let index = this.options.findIndex((o) => o.value == this.value.value)
      this.$refs.select.typeAheadPointer = index
    },
  },
  mounted() {
    this.bindCloseOptionsOnBlur()
  },
  destroyed() {
    $(window).off('click', this.onClick)
    $(window).on('keyup', this.onKeyUp)
  },
  methods: {
    /**
     * Will close the options list if the user clicks outside the options or press Escape
     */
    bindCloseOptionsOnBlur() {
      const shouldClose = (e) => this.$refs.select && this.$refs.select.open
      this.onClick = (e) => {
        if (shouldClose(e) && e.target.dataset.name !== 'filter_button') {
          this.$refs.select.closeSearchOptions(e)
        }
      }
      this.onKeyUp = (e) => {
        if (shouldClose(e) && e.key === 'Escape') {
          this.$refs.select.closeSearchOptions(e)
        }
      }
      $(window).on('click', this.onClick)
      $(window).on('keyup', this.onKeyUp)
    },
    withPopper(dropdownList, component, { width }) {
      /**
       * We need to explicitly define the dropdown width since
       * it is usually inherited from the parent with CSS.
       */
      dropdownList.style.width = 'fit-content'

      /**
       * Here we position the dropdownList relative to the $refs.toggle Element.
       *
       * The 'offset' modifier aligns the dropdown so that the $refs.toggle and
       * the dropdownList overlap by 1 pixel.
       *
       * The 'toggleClass' modifier adds a 'drop-up' class to the Vue Select
       * wrapper so that we can set some styles for when the dropdown is placed
       * above.
       */
      const popper = createPopper(component.$refs.toggle, dropdownList, {
        placement: 'right-end',
        modifiers: [
          {
            name: 'offset',
            options: {
              offset: [0, -1],
            },
          },
          {
            name: 'toggleClass',
            enabled: true,
            phase: 'write',
            fn({ state }) {
              component.$el.classList.toggle('drop-down', true)
            },
          },
        ],
      })
    },
  },
}
</script>
<style lang="scss">
.filter_results {
  padding: 3px;
}
.filter_results em {
  color: var(--color-black);
  margin-top: 10px;
}
.filter_results .vs__selected {
  font-size: 10px;
  display: none;
}
.filter_results .vs__dropdown-toggle {
  border: 0px;
  width: fit-content;
  cursor: pointer;
}
.filter_results .vs__actions {
  display: none;
}
.filter_results .vs--single.vs--open .vs__selected {
  display: none;
}
.filter_results button {
  position: relative;
  bottom: -4px;
}
</style>