import { createTextVNode as _createTextVNode, mergeProps as _mergeProps, createVNode as _createVNode, Fragment as _Fragment } from "vue";
// Styles
import "./VAutocomplete.css";

// Components
import { makeSelectProps } from "../VSelect/VSelect.mjs";
import { VCheckboxBtn } from "../VCheckbox/index.mjs";
import { VChip } from "../VChip/index.mjs";
import { VDefaultsProvider } from "../VDefaultsProvider/index.mjs";
import { VList, VListItem } from "../VList/index.mjs";
import { VMenu } from "../VMenu/index.mjs";
import { VTextField } from "../VTextField/index.mjs"; // Composables
import { forwardRefs } from "../../composables/forwardRefs.mjs";
import { makeFilterProps, useFilter } from "../../composables/filter.mjs";
import { makeTransitionProps } from "../../composables/transition.mjs";
import { useForm } from "../../composables/form.mjs";
import { useItems } from "../../composables/items.mjs";
import { useLocale } from "../../composables/locale.mjs";
import { useProxiedModel } from "../../composables/proxiedModel.mjs"; // Utility
import { computed, mergeProps, nextTick, ref, watch } from 'vue';
import { genericComponent, omit, useRender, wrapInArray } from "../../util/index.mjs";
import { filterVTextFieldProps, makeVTextFieldProps } from "../VTextField/VTextField.mjs"; // Types
function highlightResult(text, matches, length) {
  if (matches == null) return text;
  if (Array.isArray(matches)) throw new Error('Multiple matches is not implemented');
  return typeof matches === 'number' && ~matches ? _createVNode(_Fragment, null, [_createVNode("span", {
    "class": "v-autocomplete__unmask"
  }, [text.substr(0, matches)]), _createVNode("span", {
    "class": "v-autocomplete__mask"
  }, [text.substr(matches, length)]), _createVNode("span", {
    "class": "v-autocomplete__unmask"
  }, [text.substr(matches + length)])]) : text;
}
export const VAutocomplete = genericComponent()({
  name: 'VAutocomplete',
  props: {
    // TODO: implement post keyboard support
    // autoSelectFirst: Boolean,
    search: String,
    ...makeFilterProps({
      filterKeys: ['title']
    }),
    ...makeSelectProps(),
    ...omit(makeVTextFieldProps({
      modelValue: null
    }), ['validationValue', 'dirty', 'appendInnerIcon']),
    ...makeTransitionProps({
      transition: false
    })
  },
  emits: {
    'update:search': val => true,
    'update:modelValue': val => true,
    'update:menu': val => true
  },
  setup(props, _ref) {
    let {
      slots
    } = _ref;
    const {
      t
    } = useLocale();
    const vTextFieldRef = ref();
    const isFocused = ref(false);
    const isPristine = ref(true);
    const menu = useProxiedModel(props, 'menu');
    const {
      items,
      transformIn,
      transformOut
    } = useItems(props);
    const search = useProxiedModel(props, 'search', '');
    const model = useProxiedModel(props, 'modelValue', [], v => transformIn(wrapInArray(v)), v => {
      const transformed = transformOut(v);
      return props.multiple ? transformed : transformed[0] ?? null;
    });
    const form = useForm();
    const {
      filteredItems,
      getMatches
    } = useFilter(props, items, computed(() => isPristine.value ? undefined : search.value));
    const selections = computed(() => {
      return model.value.map(v => {
        return items.value.find(item => props.valueComparator(item.value, v.value)) || v;
      });
    });
    const displayItems = computed(() => {
      if (props.hideSelected) {
        return filteredItems.value.filter(filteredItem => !selections.value.some(s => s.value === filteredItem.value));
      }
      return filteredItems.value;
    });
    const selected = computed(() => selections.value.map(selection => selection.props.value));
    const listRef = ref();
    function onClear(e) {
      if (props.openOnClear) {
        menu.value = true;
      }
      search.value = '';
    }
    function onMousedownControl() {
      if (props.hideNoData && !items.value.length || props.readonly || form?.isReadonly.value) return;
      menu.value = true;
    }
    function onKeydown(e) {
      if (props.readonly || form?.isReadonly.value) return;
      if (['Enter', 'ArrowDown', 'ArrowUp'].includes(e.key)) {
        e.preventDefault();
      }
      if (['Enter', 'ArrowDown'].includes(e.key)) {
        menu.value = true;
      }
      if (['Escape'].includes(e.key)) {
        menu.value = false;
      }
      if (['Enter', 'Escape', 'Tab'].includes(e.key)) {
        isPristine.value = true;
      }
      if (e.key === 'ArrowDown') {
        listRef.value?.focus('next');
      } else if (e.key === 'ArrowUp') {
        listRef.value?.focus('prev');
      }
    }
    function onInput(e) {
      search.value = e.target.value;
    }
    function onAfterLeave() {
      if (isFocused.value) isPristine.value = true;
    }
    function onFocusin(e) {
      isFocused.value = true;
    }
    function onFocusout(e) {
      if (e.relatedTarget == null) {
        vTextFieldRef.value?.focus();
      }
    }
    const isSelecting = ref(false);
    function select(item) {
      if (props.multiple) {
        const index = selected.value.findIndex(selection => props.valueComparator(selection, item.value));
        if (index === -1) {
          model.value = [...model.value, item];
          search.value = '';
        } else {
          const value = [...model.value];
          value.splice(index, 1);
          model.value = value;
        }
      } else {
        model.value = [item];
        isSelecting.value = true;
        if (!slots.selection) {
          search.value = item.title;
        }
        menu.value = false;
        isPristine.value = true;
        nextTick(() => isSelecting.value = false);
      }
    }
    watch(isFocused, val => {
      if (val) {
        isSelecting.value = true;
        search.value = props.multiple || !!slots.selection ? '' : String(selections.value.at(-1)?.props.title ?? '');
        isPristine.value = true;
        nextTick(() => isSelecting.value = false);
      } else {
        menu.value = false;
        search.value = '';
      }
    });
    watch(search, val => {
      if (!isFocused.value || isSelecting.value) return;
      if (val) menu.value = true;
      isPristine.value = !val;
    });
    useRender(() => {
      const hasChips = !!(props.chips || slots.chip);
      const hasList = !!(!props.hideNoData || displayItems.value.length || slots.prepend || slots.append || slots['no-data']);
      const [textFieldProps] = filterVTextFieldProps(props);
      return _createVNode(VTextField, _mergeProps({
        "ref": vTextFieldRef
      }, textFieldProps, {
        "modelValue": search.value,
        "onUpdate:modelValue": v => {
          if (v == null) model.value = [];
        },
        "validationValue": model.externalValue,
        "dirty": model.value.length > 0,
        "onInput": onInput,
        "class": ['v-autocomplete', {
          'v-autocomplete--active-menu': menu.value,
          'v-autocomplete--chips': !!props.chips,
          [`v-autocomplete--${props.multiple ? 'multiple' : 'single'}`]: true,
          'v-autocomplete--selection-slot': !!slots.selection
        }],
        "appendInnerIcon": props.menuIcon,
        "readonly": props.readonly,
        "onClick:clear": onClear,
        "onMousedown:control": onMousedownControl,
        "onFocus": () => isFocused.value = true,
        "onBlur": () => isFocused.value = false,
        "onKeydown": onKeydown
      }), {
        ...slots,
        default: () => _createVNode(_Fragment, null, [_createVNode(VMenu, _mergeProps({
          "modelValue": menu.value,
          "onUpdate:modelValue": $event => menu.value = $event,
          "activator": "parent",
          "contentClass": "v-autocomplete__content",
          "eager": props.eager,
          "maxHeight": 310,
          "openOnClick": false,
          "closeOnContentClick": false,
          "transition": props.transition,
          "onAfterLeave": onAfterLeave
        }, props.menuProps), {
          default: () => [hasList && _createVNode(VList, {
            "ref": listRef,
            "selected": selected.value,
            "selectStrategy": props.multiple ? 'independent' : 'single-independent',
            "onMousedown": e => e.preventDefault(),
            "onFocusin": onFocusin,
            "onFocusout": onFocusout
          }, {
            default: () => [!displayItems.value.length && !props.hideNoData && (slots['no-data']?.() ?? _createVNode(VListItem, {
              "title": t(props.noDataText)
            }, null)), slots['prepend-item']?.(), displayItems.value.map((item, index) => slots.item?.({
              item,
              index,
              props: mergeProps(item.props, {
                onClick: () => select(item)
              })
            }) ?? _createVNode(VListItem, _mergeProps({
              "key": index
            }, item.props, {
              "onClick": () => select(item)
            }), {
              prepend: _ref2 => {
                let {
                  isSelected
                } = _ref2;
                return props.multiple && !props.hideSelected ? _createVNode(VCheckboxBtn, {
                  "modelValue": isSelected,
                  "ripple": false
                }, null) : undefined;
              },
              title: () => {
                return isPristine.value ? item.title : highlightResult(item.title, getMatches(item)?.title, search.value?.length ?? 0);
              }
            })), slots['append-item']?.()]
          })]
        }), selections.value.map((item, index) => {
          function onChipClose(e) {
            e.stopPropagation();
            e.preventDefault();
            select(item);
          }
          const slotProps = {
            'onClick:close': onChipClose,
            modelValue: true,
            'onUpdate:modelValue': undefined
          };
          return _createVNode("div", {
            "key": item.value,
            "class": "v-autocomplete__selection"
          }, [hasChips ? _createVNode(VDefaultsProvider, {
            "defaults": {
              VChip: {
                closable: props.closableChips,
                size: 'small',
                text: item.title
              }
            }
          }, {
            default: () => [slots.chip ? slots.chip({
              item,
              index,
              props: slotProps
            }) : _createVNode(VChip, slotProps, null)]
          }) : slots.selection ? slots.selection({
            item,
            index
          }) : _createVNode("span", {
            "class": "v-autocomplete__selection-text"
          }, [item.title, props.multiple && index < selections.value.length - 1 && _createVNode("span", {
            "class": "v-autocomplete__selection-comma"
          }, [_createTextVNode(",")])])]);
        })])
      });
    });
    return forwardRefs({
      isFocused,
      isPristine,
      menu,
      search,
      filteredItems,
      select
    }, vTextFieldRef);
  }
});
//# sourceMappingURL=VAutocomplete.mjs.map