import { withDirectives as _withDirectives, mergeProps as _mergeProps, vShow as _vShow, createVNode as _createVNode, Fragment as _Fragment } from "vue";
import "./styles/select.css";
import { computed, nextTick, onBeforeUnmount, ref, watch } from "vue";
import { convertToUnit, debounce, getUid, keyValues, pick, wrapInArray } from "@veno-ui/utils";
import { genericComponent } from "../../utils";
import { makeScrollbar, useScrollbar } from "../../composables/scrollbar";
import { useProxiedModel } from "../../composables/proxied-model";
import { Input } from "../input/input";
import { Menu } from "../menu";
import { List, ListItem, ListItemAvatar } from "../list";
import { Tag } from "../tag";
import { TagGroup } from "../tag-group";
import { Progress } from "../progress";
import { Icon } from "../icon";
import { Checkbox } from "../checkbox";
function defaultFilter(value, query, _item) {
  if (value == null || query == null)
    return -1;
  return value.toString().toLocaleLowerCase().indexOf(query.toString().toLocaleLowerCase());
}
const Select = genericComponent()({
  name: "VeSelect",
  props: {
    /**
     * @zh 追加的内联图标
     */
    appendInnerIcon: {
      type: String,
      default: "$dropdown"
    },
    /**
     * @zh 可选项卸载后是否从已选项中移除
     */
    deleteAfterUnmount: {
      type: Boolean,
      default: true
    },
    /**
     * @zh 可过滤的
     */
    filterable: Boolean,
    /**
     * @zh ID
     */
    id: String,
    /**
     * @zh 选择框可选项
     */
    items: Array,
    /**
     * @zh 可选项文本字段
     */
    itemText: {
      type: String,
      default: "text"
    },
    /**
     * @zh 可选项值字段
     */
    itemValue: {
      type: String,
      default: "value"
    },
    /**
     * @zh 加载中
     */
    loading: Boolean,
    /**
     * @zh 当前值
     */
    modelValue: {
      type: null,
      default: () => []
    },
    /**
     * @zh 是否多选
     */
    multiple: Boolean,
    /**
     * @zh 无数据时显示的文本
     */
    noDataText: {
      type: String,
      default: "\u6682\u65E0\u6570\u636E"
    },
    /**
     * @zh 是否在清除时打开可选菜单
     */
    openOnClear: Boolean,
    /**
     * @zh 输入框占位符
     */
    placeholder: String,
    /**
     * @zh 查询字符串
     */
    query: String,
    /**
     * @zh 标签
     */
    tags: Boolean,
    /**
     * @zh 只读
     */
    readonly: Boolean,
    /**
     * @zh 返回对象值
     */
    returnObject: Boolean,
    ...makeScrollbar()
  },
  emits: {
    "click:clear": (_event) => true,
    "update:modelValue": (_modelValue) => true,
    "update:query": (_query) => true
  },
  setup(props, {
    emit,
    slots
  }) {
    const activator = ref();
    const pendingIndex = ref();
    const id = computed(() => props.id || `ve-select-${getUid()}`);
    const inputRef = ref();
    const isActiveMenu = ref(false);
    const listRef = ref();
    const menuRef = ref();
    const mirrorRef = ref();
    const tagInputRef = ref();
    const tagInputWidth = ref();
    const items = computed(() => props.items?.map(normalizeItem) ?? []);
    const {
      scrollbarClasses
    } = useScrollbar(props);
    const model = useProxiedModel(props, "modelValue");
    const arrayModel = computed({
      get: () => wrapInArray(model.value),
      set: (v) => model.value = props.multiple ? v : v[0]
    });
    const query = ref(props.query);
    const active = computed({
      get: () => arrayModel.value.map((v) => v && typeof v === "object" ? v[props.itemValue] : v),
      set: (val) => {
        arrayModel.value = props.returnObject ? val.map(getItem).filter((v) => v !== void 0) : val;
        if (!props.multiple)
          isActiveMenu.value = false;
      }
    });
    const selections = computed(() => {
      return active.value.map((v) => {
        return items.value.find((i) => i.value === v);
      }).filter((v) => v !== void 0);
    });
    const onUpdateQuery = debounce((v) => {
      if (props.loading)
        return;
      emit("update:query", v);
    }, 300);
    const inputValue = computed({
      get: () => {
        if (props.filterable && isActiveMenu.value)
          return query.value || void 0;
        if (props.tags)
          return void 0;
        return selections.value.map((item) => item.text).join(", ") || void 0;
      },
      set: (val) => {
        query.value = val;
        onUpdateQuery(val);
        if (isActiveMenu.value) {
          nextTick(() => {
            menuRef.value?.overlayRef?.updatePosition?.();
          });
        }
      }
    });
    const placeholder = computed(() => {
      return props.tags && active.value.length > 0 ? void 0 : props.placeholder;
    });
    const filteredItems = computed(() => items.value.filter((v) => !v.filtered));
    function getItem(v) {
      return props.items?.find((i) => {
        const i1 = typeof i === "object" ? i[props.itemValue] : i;
        const v1 = typeof v === "object" ? v[props.itemValue] : v;
        return i1 === v1;
      });
    }
    function normalizeItem(item) {
      let v;
      if (typeof item === "object") {
        const value = item[props.itemValue];
        const text = String(item[props.itemText] ?? value);
        v = {
          text,
          value
        };
      } else {
        v = {
          text: String(item),
          value: item
        };
      }
      if (props.filterable && query.value) {
        v.filtered = defaultFilter(v.text, query.value) === -1;
      }
      return v;
    }
    watch(() => inputRef.value, (val) => {
      activator.value = val.$el.querySelector(".ve-input-control");
    });
    watch(() => props.query, (val) => {
      query.value = val;
    });
    function onBlur() {
      isActiveMenu.value = false;
    }
    function onClear(e) {
      arrayModel.value = [];
      if (props.openOnClear) {
        isActiveMenu.value = true;
      } else {
        inputRef.value.blur();
      }
      emit("click:clear", e);
    }
    function onControl() {
      if (props.readonly)
        return;
      if (isActiveMenu.value) {
        inputRef.value?.blur();
      } else {
        isActiveMenu.value = true;
      }
    }
    const onKeydownMove = debounce(({
      key
    }) => {
      const index = pendingIndex.value ?? filteredItems.value.findIndex(({
        value
      }) => value === active.value[0]);
      const lastIndex = filteredItems.value.length - 1;
      if (key === keyValues.up) {
        pendingIndex.value = [-1, 0].includes(index) ? lastIndex : Math.max(0, index - 1);
      }
      if (key === keyValues.down) {
        pendingIndex.value = [-1, lastIndex].includes(index) ? 0 : Math.min(lastIndex, index + 1);
      }
      nextTick(() => {
        const el = listRef.value.$el;
        if (!el)
          return;
        const itemEl = el.querySelector(".ve-select__item--pendding");
        if (!itemEl)
          return;
        const itemTop = itemEl.offsetTop;
        if (itemTop <= el.scrollTop) {
          el.scrollTop -= el.scrollTop - itemTop;
        } else {
          const itemBottom = itemEl.offsetTop + itemEl.offsetHeight;
          const scrollBottom = el.scrollTop + el.offsetHeight;
          if (itemBottom >= scrollBottom) {
            el.scrollTop += itemBottom - scrollBottom;
          }
        }
      });
    }, 30);
    const onKeydownEnter = debounce(() => {
      if (!isActiveMenu.value) {
        isActiveMenu.value = true;
      } else if (filteredItems.value.length > 0) {
        const index = pendingIndex.value in filteredItems.value ? pendingIndex.value : 0;
        const value = filteredItems.value[index].value;
        if (props.multiple) {
          active.value = active.value.includes(value) ? active.value.filter((v) => v !== value) : active.value.concat([value]);
        } else {
          active.value = [value];
        }
      }
    }, 60);
    const onKeydownDelete = debounce(() => {
      active.value = active.value.filter((_, i) => i !== active.value.length - 1);
    }, 60);
    function onKeydown(e) {
      const {
        key
      } = e;
      if ([keyValues.up, keyValues.down].includes(key)) {
        e.preventDefault();
        onKeydownMove(e);
      }
      if ([keyValues.enter, keyValues.space, " "].includes(key)) {
        e.preventDefault();
        onKeydownEnter();
      }
      if (key === keyValues.backspace && isActiveMenu.value && props.multiple && props.filterable && props.tags && !query.value && active.value.length > 0) {
        onKeydownDelete();
      }
      if (key === keyValues.esc && isActiveMenu.value) {
        isActiveMenu.value = false;
      }
    }
    function resizeTagInput() {
      tagInputWidth.value = Math.max(0, mirrorRef.value.scrollWidth) + 1;
    }
    let observer;
    watch(mirrorRef, (val) => {
      if (val) {
        observer = new ResizeObserver(resizeTagInput);
        observer.observe(mirrorRef.value);
      } else {
        observer?.disconnect();
      }
    });
    onBeforeUnmount(() => {
      observer?.disconnect();
    });
    watch(isActiveMenu, (val) => {
      if (!val)
        pendingIndex.value = void 0;
    });
    return () => {
      const inputProps = props.multiple && !props.tags ? {
        autoResize: true,
        rows: 1,
        type: "textarea"
      } : {};
      const [listSlots, inputSlots] = pick(slots, ["item", "header", "title", "subtitle"]);
      return _createVNode(Input, _mergeProps(inputProps, {
        "class": ["ve-select", {
          "ve-select--active-menu": isActiveMenu.value
        }],
        "id": id.value,
        "inputAttach": tagInputRef.value,
        "controlValue": model.value,
        "modelValue": inputValue.value,
        "onUpdate:modelValue": ($event) => inputValue.value = $event,
        "onBlur": onBlur,
        "onClick:clear": onClear,
        "onClick:control": onControl,
        "onInput": (e) => {
          if (mirrorRef.value) {
            mirrorRef.value.textContent = e.target.value;
            resizeTagInput();
          }
        },
        "onKeydown": onKeydown,
        "onMousedown": (e) => e.preventDefault(),
        "placeholder": placeholder.value,
        "readonly": !props.filterable,
        "ref": inputRef
      }), {
        ...inputSlots,
        "append-inner": () => _createVNode(_Fragment, null, [!props.loading && _createVNode(Icon, {
          "icon": props.appendInnerIcon
        }, null), props.loading && _createVNode(Progress, {
          "size": "14",
          "variant": "circular",
          "stroke-width": "2",
          "indeterminate": true
        }, null)]),
        "default": () => _createVNode(_Fragment, null, [activator.value && _createVNode(Menu, {
          "ref": menuRef,
          "contentClass": "ve-select__menu-wrapper",
          "id": `${id.value}-menu`,
          "modelValue": isActiveMenu.value,
          "onUpdate:modelValue": ($event) => isActiveMenu.value = $event,
          "activator": activator.value,
          "minWidth": void 0,
          "openOnClick": false,
          "eager": true
        }, {
          default: () => [_createVNode(List, {
            "ref": listRef,
            "class": scrollbarClasses.value,
            "maxHeight": 250,
            "items": items.value,
            "active": active.value,
            "onUpdate:active": ($event) => active.value = $event,
            "activeStrategy": props.multiple ? "multiple" : "single",
            "deleteAfterUnmount": props.deleteAfterUnmount
          }, {
            ...listSlots,
            item: (itemSlot) => {
              const {
                item,
                index
              } = itemSlot;
              const itemSlotProps = {
                onMousedown: (e) => e.preventDefault(),
                class: {
                  "ve-select__item--pendding": pendingIndex.value === index
                }
              };
              if (slots.item) {
                return slots.item({
                  ...itemSlot,
                  props: itemSlotProps
                });
              }
              return _withDirectives(_createVNode(ListItem, _mergeProps(itemSlotProps, item, {
                "link": true,
                "key": item.value
              }), {
                prepend: props.multiple ? () => _createVNode(ListItemAvatar, {
                  "start": true,
                  "color": "inherit",
                  "variant": "text"
                }, {
                  default: () => [_createVNode(Checkbox, {
                    "model-value": active.value.includes(item.value)
                  }, null)]
                }) : void 0
              }), [[_vShow, !item.filtered]]);
            },
            append: () => _createVNode(_Fragment, null, [filteredItems.value.length === 0 && (slots["no-data"]?.() ?? _createVNode(ListItem, {
              "text": props.noDataText
            }, null))])
          })]
        }), props.tags && _createVNode(TagGroup, {
          "class": "ve-select__selections",
          "size": "x-small"
        }, {
          default: () => [selections.value.map((item) => {
            function close() {
              active.value = active.value.filter((v) => v !== item.value);
            }
            return slots.tag?.({
              item,
              close
            }) ?? _createVNode(Tag, {
              "aria-hidden": "true",
              "tag": "div",
              "key": item.value,
              "closable": true,
              "text": item.text,
              "onClick:close": (e) => {
                e.stopPropagation();
                close();
              }
            }, null);
          }), _createVNode("div", {
            "class": "ve-select__tag-input",
            "style": {
              width: convertToUnit(selections.value.length > 0 ? tagInputWidth.value : "100%")
            },
            "ref": tagInputRef
          }, [_createVNode("span", {
            "class": "ve-select__tag-input-mirror",
            "ref": mirrorRef,
            "aria-hidden": "true"
          }, [query.value])])]
        })])
      });
    };
  }
});
export {
  Select
};
//# sourceMappingURL=select.mjs.map
