<template>
  <div class="RSelect">
    <span class="RSelect-Label">{{ label }}</span>
    <template>
      <q-select
        dense
        options-dense
        borderless
        menu-anchor="bottom start"
        virtual-scroll-slice-size="10"
        dropdown-icon="pgs-shevron-down-off"
        class="RSelect-Content"
        v-model="selected"
        :options="options"
        :display-value="displayValue"
        use-input
        fill-input
        :hide-selected="hideSelected"
        :disable="disable"
        @filter="filterFn"
      >
        <template v-if="$slots.prepend" v-slot:prepend>
          <slot name="prepend" />
        </template>
        <template v-if="$slots.append" v-slot:append>
          <slot name="append" />
        </template>

        <template v-slot:option="scope">
          <template v-if="scope.opt.children.length">
            <q-item
              class="text-weight-bold fit-this q-py-xs"
              :label="scope.opt.optgroup"
            >
              <q-item-section>{{ scope.opt.optgroup }}</q-item-section>
            </q-item>
            <template v-for="child in scope.opt.children">
              <q-item
                :key="child.value"
                clickable
                v-close-popup="true"
                @click="itemClickAction(child)"
                :class="{
                  'fit-this q-py-xs': true,
                  'bg-light-blue-1': isActive(child),
                }"
              >
                <q-item-section>
                  <q-item-label v-html="child.label" class="q-ml-md" />
                </q-item-section>
              </q-item>
            </template>
          </template>
        </template>
      </q-select>
    </template>
  </div>
</template>
<script lang="ts">
import { QItem, QItemLabel, QItemSection, QSelect } from 'quasar';
import { Component, Model, Prop, Vue, Watch } from 'vue-property-decorator';
import get from 'lodash/get';

interface Option {
  label: string;
  value: string;
}

interface OptionGroup {
  optgroup: string;
  children: Option[];
}

@Component({ components: { QSelect, QItem, QItemLabel, QItemSection } })
export default class RSelectGroup extends Vue {
  @Prop({ default: [] }) stringOptions: [];
  @Prop() label: string;
  @Prop() emit: string;
  @Prop({ default: null }) model: Option[] | Option | string;
  @Prop() disable?: boolean | '';
  @Prop({ default: '' }) placeholder: string;
  @Prop({ default: true }) hideSelected: boolean;

  @Model('update')
  value: Option;

  options: OptionGroup | Option[] = [];
  selected: Option | null = null;

  get displayValue(): string {
    return this.value?.label || this.placeholder;
  }

  @Watch('selected')
  checkSelectedLength(val: Option): void {
    if (this.emit) {
      this.$emit('update', get(val, this.emit));
      return;
    }
    this.$emit('update', val);
  }

  itemClickAction(item: Option): Option | '' {
    return !this.disable && !this.isActive(item) ? (this.selected = item) : '';
  }

  isActive(child: Option): boolean {
    if (!this.selected) {
      return false;
    }

    return (this.selected as Option).value === child.value;
  }

  filterFn(val: string, update: (arg0: { (): void; (): void }) => void): void {
    if (val === '') {
      update(() => {
        this.options = this.stringOptions;
      });
      return;
    }

    /**
     * Обновление списка под введенный в инпут фильтр
     */
    update(() => {
      const needle = val.toLowerCase();
      const findChild = (item: Option) =>
        item.label.toLowerCase().includes(needle);
      const find = (item: OptionGroup) => {
        const filteredChildren = item.children.filter(findChild);
        const filteredTitle = item.optgroup.toLowerCase().includes(needle);
        if (filteredTitle && filteredChildren.length === 0) {
          return item;
        }
        if (filteredTitle || filteredChildren.length > 0) {
          return { optgroup: item.optgroup, children: filteredChildren };
        }
      };
      this.options =
        (this.stringOptions.length &&
          this.stringOptions.map(find).filter((item) => !!item)) ||
        [];
    });
  }
}
</script>
<style lang="scss" scoped>
.fit-this {
  max-width: 458px;
}
</style>
