<template>
  <div class="dropdown">
    <a class="dropdown__selected" @click="show()">
      <template v-if="hasValue()">
        <i
          v-if="icon"
          class="dropdown__selected-icon fas"
          :class="'fa-' + icon"
        ></i>
        {{ name }}
      </template>
      <template v-if="!hasValue()">
        Make a choice
      </template>
      <i class="dropdown__toggle-icon fas fa-caret-down"></i>
    </a>
    <div class="dropdown__items" :class="{ hidden: !open }">
      <div class="select__search">
        <i class="select__search-icon fas fa-search"></i>
        <input
          ref="search"
          v-model="query"
          type="text"
          class="select__search-input"
          :placeholder="searchText"
          @keyup="search(query)"
        />
      </div>
      <ul class="select__items">
        <slot></slot>
      </ul>
    </div>
  </div>
</template>

<script>
import { isElement } from '@baserow/modules/core/utils/dom'

// @TODO focus on tab
export default {
  name: 'Dropdown',
  props: {
    value: {
      type: [String, Number, Boolean, Object],
      required: false,
      default: null,
    },
    searchText: {
      type: String,
      required: false,
      default: 'Search',
    },
  },
  data() {
    return {
      open: false,
      name: null,
      icon: null,
      query: '',
    }
  },
  watch: {
    /**
     * If the value changes we have to update the visible name and icon.
     */
    value(newValue) {
      this.setDisplayValue(newValue)
    },
  },
  /**
   * When the dropdown first loads we have to check if there already is a value, if
   * there is, we have to update the displayed name and icon.
   */
  mounted() {
    if (this.hasValue()) {
      this.setDisplayValue(this.value)
    }
  },
  methods: {
    /**
     * Returns true if there is a value.
     * @return {boolean}
     */
    hasValue() {
      return !!this.value
    },
    /**
     * Shows the lists of choices, so a user can change the value.
     */
    show() {
      this.open = true
      this.$emit('show')

      // We have to wait for the input to be visible before we can focus.
      this.$nextTick(() => {
        this.$refs.search.focus()
      })

      // If the user clicks outside the dropdown while the list of choices of open we
      // have to hide them.
      this.$el.clickOutsideEvent = (event) => {
        if (
          // Check if the context menu is still open
          this.open &&
          // If the click was outside the context element because we want to ignore
          // clicks inside it.
          !isElement(this.$el, event.target)
        ) {
          this.hide()
        }
      }
      document.body.addEventListener('click', this.$el.clickOutsideEvent)
    },
    /**
     * Hides the list of choices
     */
    hide() {
      this.open = false
      this.$emit('hide')

      // Make sure that all the items are visible the next time we open the dropdown.
      this.query = ''
      this.search(this.query)

      document.body.removeEventListener('click', this.$el.clickOutsideEvent)
    },
    /**
     * Selects a new value which will also be
     */
    select(value) {
      this.$emit('input', value)
      this.$emit('change', value)
      this.hide()
    },
    /**
     * If not empty it will only show children that contain the given query.
     */
    search(query) {
      this.$children.forEach((item) => {
        item.search(query)
      })
    },
    /**
     * Changes the selected name and icon of the dropdown based on the provided value.
     */
    setDisplayValue(value) {
      this.$children.forEach((item) => {
        if (item.value === value) {
          this.name = item.name
          this.icon = item.icon
        }
      })
    },
  },
}
</script>