<script setup>
import { computed, ref, nextTick, provide, watch, onMounted, onBeforeUnmount } from "vue";
import { useNamespace, useFormItem, useId, useLocale, useConfig } from "@yh-ui/hooks";
import { useComponentTheme } from "@yh-ui/theme";
import YhTooltip from "../../tooltip";
import { SelectContextKey } from "./select";
defineOptions({
  name: "YhSelect"
});
const props = defineProps({
  modelValue: { type: [String, Number, Boolean, Array], required: false },
  options: { type: Array, required: false },
  placeholder: { type: String, required: false },
  disabled: { type: Boolean, required: false, default: false },
  clearable: { type: Boolean, required: false, default: false },
  size: { type: String, required: false, default: void 0 },
  multiple: { type: Boolean, required: false, default: false },
  multipleLimit: { type: Number, required: false, default: 0 },
  filterable: { type: Boolean, required: false, default: false },
  filterMethod: { type: Function, required: false },
  remote: { type: Boolean, required: false, default: false },
  remoteMethod: { type: Function, required: false },
  loading: { type: Boolean, required: false, default: false },
  loadingText: { type: String, required: false, default: "" },
  noMatchText: { type: String, required: false, default: "" },
  noDataText: { type: String, required: false, default: "" },
  allowCreate: { type: Boolean, required: false, default: false },
  collapseTags: { type: Boolean, required: false, default: false },
  collapseTagsTooltip: { type: Boolean, required: false, default: false },
  maxCollapseTags: { type: Number, required: false, default: 1 },
  popperClass: { type: String, required: false },
  teleported: { type: Boolean, required: false, default: true },
  fitInputWidth: { type: Boolean, required: false, default: true },
  tagType: { type: String, required: false, default: "" },
  virtualScroll: { type: Boolean, required: false, default: false },
  itemHeight: { type: Number, required: false, default: 34 },
  height: { type: Number, required: false, default: 274 },
  validateEvent: { type: Boolean, required: false, default: true },
  valueKey: { type: String, required: false, default: "value" },
  labelKey: { type: String, required: false, default: "label" },
  themeOverrides: { type: null, required: false }
});
const emit = defineEmits(["update:modelValue", "change", "focus", "blur", "clear", "visible-change", "remove-tag"]);
const ns = useNamespace("select");
const { t } = useLocale();
const inputId = useId();
const { themeStyle } = useComponentTheme(
  "select",
  computed(() => props.themeOverrides)
);
const { form, formItem, validate: triggerValidate } = useFormItem();
const { globalSize } = useConfig();
const selectSize = computed(
  () => props.size || formItem?.size || form?.size || globalSize.value || "default"
);
const translatedLoadingText = computed(() => props.loadingText || t("select.loading"));
const translatedNoMatchText = computed(() => props.noMatchText || t("select.noMatch"));
const translatedNoDataText = computed(() => props.noDataText || t("select.noData"));
const wrapperRef = ref();
const inputRef = ref();
const visible = ref(false);
const focused = ref(false);
const hovering = ref(false);
const query = ref("");
const hoveredIndex = ref(-1);
const createdOptions = ref([]);
const slotOptions = ref([]);
const onOptionCreate = (option) => {
  const index = slotOptions.value.findIndex((o) => o.value === option.value);
  if (index > -1) {
    slotOptions.value.splice(index, 1, option);
  } else {
    slotOptions.value.push(option);
  }
};
const onOptionDestroy = (value) => {
  const index = slotOptions.value.findIndex((o) => o.value === value);
  if (index > -1) {
    slotOptions.value.splice(index, 1);
  }
};
const dropdownStyle = ref({});
const updateDropdownPosition = () => {
  if (typeof window === "undefined" || !wrapperRef.value || !props.teleported) return;
  const rect = wrapperRef.value.getBoundingClientRect();
  const styles = window.getComputedStyle(wrapperRef.value);
  const primary = styles.getPropertyValue("--yh-color-primary").trim();
  const primaryRgb = styles.getPropertyValue("--yh-color-primary-rgb").trim();
  const primaryLight9 = styles.getPropertyValue("--yh-color-primary-light-9").trim();
  dropdownStyle.value = {
    ...themeStyle.value,
    position: "fixed",
    top: `${rect.bottom + 4}px`,
    left: `${rect.left}px`,
    minWidth: props.fitInputWidth ? `${rect.width}px` : void 0,
    zIndex: "2000",
    "--yh-color-primary": primary,
    "--yh-color-primary-rgb": primaryRgb,
    "--yh-color-primary-light-9": primaryLight9
  };
};
watch(visible, (val) => {
  if (val && props.teleported) {
    nextTick(updateDropdownPosition);
  }
});
const dropdownRef = ref();
const handleOutsideClick = (e) => {
  const target = e.target;
  if (visible.value && wrapperRef.value && !wrapperRef.value.contains(target) && (!dropdownRef.value || !dropdownRef.value.contains(target))) {
    visible.value = false;
    emit("visible-change", false);
  }
};
onMounted(() => {
  if (typeof window !== "undefined") {
    if (props.teleported) {
      window.addEventListener("scroll", updateDropdownPosition, true);
      window.addEventListener("resize", updateDropdownPosition);
    }
    window.addEventListener("click", handleOutsideClick);
  }
});
onBeforeUnmount(() => {
  if (typeof window !== "undefined") {
    if (props.teleported) {
      window.removeEventListener("scroll", updateDropdownPosition, true);
      window.removeEventListener("resize", updateDropdownPosition);
    }
    window.removeEventListener("click", handleOutsideClick);
  }
});
const allOptions = computed(() => {
  return [...createdOptions.value, ...slotOptions.value, ...props.options || []];
});
const filteredOptions = computed(() => {
  if (!props.filterable || !query.value) {
    return allOptions.value;
  }
  const q = query.value.toLowerCase();
  return allOptions.value.filter((opt) => {
    const label = String(opt[props.labelKey || "label"] || opt.label || "");
    return label.toLowerCase().includes(q);
  });
});
const scrollTop = ref(0);
const virtualConfig = computed(() => {
  const itemHeight = props.itemHeight || 34;
  const containerHeight = props.height || 274;
  const overscan = 3;
  const items = filteredOptions.value;
  if (!props.virtualScroll || items.length === 0) {
    return {
      visibleItems: items,
      totalHeight: items.length * itemHeight,
      offsetY: 0,
      startIndex: 0,
      endIndex: items.length
    };
  }
  const visibleCount = Math.ceil(containerHeight / itemHeight);
  const start = Math.floor(scrollTop.value / itemHeight);
  const startIndex2 = Math.max(0, start - overscan);
  const endIndex = Math.min(items.length, start + visibleCount + overscan);
  return {
    visibleItems: items.slice(startIndex2, endIndex),
    totalHeight: items.length * itemHeight,
    offsetY: startIndex2 * itemHeight,
    startIndex: startIndex2,
    endIndex
  };
});
const handleVirtualScroll = (event) => {
  const target = event.target;
  scrollTop.value = target.scrollTop;
};
const displayOptions = computed(() => {
  return props.virtualScroll ? virtualConfig.value.visibleItems : filteredOptions.value;
});
const totalHeight = computed(() => virtualConfig.value.totalHeight);
const offsetY = computed(() => virtualConfig.value.offsetY);
const startIndex = computed(() => virtualConfig.value.startIndex);
const normalizedModelValue = computed(() => {
  if (props.multiple) {
    return Array.isArray(props.modelValue) ? props.modelValue : [];
  }
  return props.modelValue;
});
const selectedLabels = computed(() => {
  if (props.multiple) {
    const values = Array.isArray(normalizedModelValue.value) ? normalizedModelValue.value : [];
    return values.map((v) => {
      const opt2 = allOptions.value.find((o) => o[props.valueKey || "value"] === v);
      return opt2 ? opt2[props.labelKey || "label"] || opt2.label : String(v);
    });
  }
  const opt = allOptions.value.find(
    (o) => o[props.valueKey || "value"] === normalizedModelValue.value
  );
  return opt ? opt[props.labelKey || "label"] || opt.label : String(normalizedModelValue.value ?? "");
});
const displayTags = computed(() => {
  if (!props.multiple || !Array.isArray(selectedLabels.value)) return [];
  if (props.collapseTags) {
    return selectedLabels.value.slice(0, props.maxCollapseTags);
  }
  return selectedLabels.value;
});
const collapsedCount = computed(() => {
  if (!props.multiple || !props.collapseTags || !Array.isArray(selectedLabels.value)) return 0;
  return Math.max(0, selectedLabels.value.length - (props.maxCollapseTags || 1));
});
const collapsedTags = computed(() => {
  if (!props.multiple || !props.collapseTags || !Array.isArray(selectedLabels.value)) return [];
  return selectedLabels.value.slice(props.maxCollapseTags || 1);
});
const showClear = computed(
  () => props.clearable && !props.disabled && (props.multiple ? Array.isArray(normalizedModelValue.value) && normalizedModelValue.value.length > 0 : normalizedModelValue.value !== void 0 && normalizedModelValue.value !== "") && (focused.value || hovering.value)
);
const hasValue = computed(() => {
  if (props.multiple) {
    return Array.isArray(normalizedModelValue.value) && normalizedModelValue.value.length > 0;
  }
  return normalizedModelValue.value !== void 0 && normalizedModelValue.value !== "";
});
const wrapperClasses = computed(() => [
  ns.b(),
  ns.m(selectSize.value),
  ns.is("disabled", props.disabled),
  ns.is("focused", focused.value || visible.value),
  ns.is("multiple", props.multiple)
]);
const isSelected = (value) => {
  if (props.multiple) {
    return Array.isArray(normalizedModelValue.value) && normalizedModelValue.value.includes(value);
  }
  return normalizedModelValue.value === value;
};
const handleOptionSelect = (option, event) => {
  if (option.disabled) return;
  if (event) {
    event.stopPropagation();
  }
  const value = option[props.valueKey || "value"];
  if (props.multiple) {
    const values = Array.isArray(normalizedModelValue.value) ? [...normalizedModelValue.value] : [];
    const index = values.indexOf(value);
    if (index > -1) {
      values.splice(index, 1);
    } else {
      if (props.multipleLimit && props.multipleLimit > 0 && values.length >= props.multipleLimit) {
        return;
      }
      values.push(value);
    }
    emit("update:modelValue", values);
    emit("change", values);
  } else {
    emit("update:modelValue", value);
    emit("change", value);
    visible.value = false;
  }
  if (props.validateEvent) {
    triggerValidate("change");
  }
  query.value = "";
};
const handleRemoveTag = (value, event) => {
  event.stopPropagation();
  if (props.disabled) return;
  const values = Array.isArray(normalizedModelValue.value) ? [...normalizedModelValue.value] : [];
  const index = values.indexOf(value);
  if (index > -1) {
    values.splice(index, 1);
    emit("update:modelValue", values);
    emit("change", values);
    emit("remove-tag", value);
    if (props.validateEvent) {
      triggerValidate("change");
    }
  }
};
const handleClear = (event) => {
  event.stopPropagation();
  const value = props.multiple ? [] : void 0;
  emit("update:modelValue", value);
  emit("change", value);
  emit("clear");
  query.value = "";
  if (props.validateEvent) {
    triggerValidate("change");
  }
};
const toggleDropdown = () => {
  if (props.disabled) return;
  visible.value = !visible.value;
  emit("visible-change", visible.value);
  if (visible.value) {
    nextTick(() => {
      if (props.filterable) {
        inputRef.value?.focus();
      }
    });
  }
};
const handleInput = (event) => {
  const target = event.target;
  query.value = target.value;
  if (!(props.remote && props.remoteMethod) && props.filterMethod) {
    props.filterMethod(query.value);
  }
};
watch(query, (val) => {
  if (props.remote && props.remoteMethod) {
    props.remoteMethod(val);
  }
});
watch(
  () => props.modelValue,
  () => {
    if (props.remote && props.remoteMethod) {
      props.remoteMethod(query.value);
    }
  }
);
const handleKeydown = (event) => {
  switch (event.key) {
    case "ArrowDown":
      event.preventDefault();
      if (!visible.value) {
        visible.value = true;
        emit("visible-change", true);
      } else {
        hoveredIndex.value = Math.min(hoveredIndex.value + 1, filteredOptions.value.length - 1);
      }
      break;
    case "ArrowUp":
      event.preventDefault();
      hoveredIndex.value = Math.max(hoveredIndex.value - 1, 0);
      break;
    case "Enter":
      event.preventDefault();
      if (visible.value && hoveredIndex.value >= 0 && filteredOptions.value[hoveredIndex.value]) {
        handleOptionSelect(filteredOptions.value[hoveredIndex.value]);
      } else if (props.allowCreate && query.value) {
        const newOption = {
          value: query.value,
          label: query.value,
          [props.valueKey || "value"]: query.value,
          [props.labelKey || "label"]: query.value
        };
        createdOptions.value.push(newOption);
        handleOptionSelect(newOption);
      }
      break;
    case "Escape":
      visible.value = false;
      emit("visible-change", false);
      break;
    case "Backspace":
      if (props.multiple && !query.value && Array.isArray(normalizedModelValue.value) && normalizedModelValue.value.length > 0) {
        const values = [...normalizedModelValue.value];
        const removed = values.pop();
        if (removed !== void 0) {
          emit("update:modelValue", values);
          emit("change", values);
          emit("remove-tag", removed);
        }
      }
      break;
  }
};
const isClickingDropdown = ref(false);
const handleFocus = (event) => {
  focused.value = true;
  emit("focus", event);
};
const handleBlur = (event) => {
  if (isClickingDropdown.value) {
    return;
  }
  focused.value = false;
  visible.value = false;
  emit("visible-change", false);
  emit("blur", event);
  if (props.validateEvent) {
    triggerValidate("blur");
  }
};
const handleDropdownMousedown = (event) => {
  event.preventDefault();
  isClickingDropdown.value = true;
};
const handleDropdownMouseup = () => {
  setTimeout(() => {
    isClickingDropdown.value = false;
  }, 0);
};
const handleOptionClick = (option, event) => {
  event.stopPropagation();
  handleOptionSelect(option, event);
  if (!props.multiple) {
    nextTick(() => {
      inputRef.value?.focus();
    });
  }
};
const handleMouseEnter = () => {
  hovering.value = true;
};
const handleMouseLeave = () => {
  hovering.value = false;
};
watch(hoveredIndex, (val) => {
  if (val < 0) return;
  if (props.virtualScroll) {
    const itemHeight = props.itemHeight || 34;
    const containerHeight = props.height || 274;
    const currentScrollTop = scrollTop.value;
    const itemTop = val * itemHeight;
    const itemBottom = itemTop + itemHeight;
    if (itemTop < currentScrollTop) {
      scrollTop.value = itemTop;
      nextTick(() => {
        const listEl = dropdownRef.value?.querySelector(`.${ns.e("options")}`);
        if (listEl) listEl.scrollTop = itemTop;
      });
    } else if (itemBottom > currentScrollTop + containerHeight) {
      const targetScrollTop = itemBottom - containerHeight;
      scrollTop.value = targetScrollTop;
      nextTick(() => {
        const listEl = dropdownRef.value?.querySelector(`.${ns.e("options")}`);
        if (listEl) listEl.scrollTop = targetScrollTop;
      });
    }
  } else {
    nextTick(() => {
      const listEl = dropdownRef.value?.querySelector(`.${ns.e("options")}`);
      if (!listEl) return;
      const items = listEl.querySelectorAll(`.${ns.e("option")}`);
      const targetEl = items[val];
      if (targetEl) {
        const containerTop = listEl.scrollTop;
        const containerBottom = containerTop + listEl.clientHeight;
        const elemTop = targetEl.offsetTop;
        const elemBottom = elemTop + targetEl.offsetHeight;
        if (elemTop < containerTop) {
          listEl.scrollTop = elemTop;
        } else if (elemBottom > containerBottom) {
          listEl.scrollTop = elemBottom - listEl.clientHeight;
        }
      }
    });
  }
});
const focus = () => {
  inputRef.value?.focus();
};
const blur = () => {
  inputRef.value?.blur();
};
provide(SelectContextKey, {
  props,
  selectValue: computed(() => normalizedModelValue.value),
  hoveredIndex,
  handleOptionSelect: (option, event) => handleOptionSelect(option, event),
  isSelected,
  onOptionCreate,
  onOptionDestroy
});
defineExpose({
  focus,
  blur,
  inputRef,
  triggerRemoteMethod: (val) => {
    if (props.remote && props.remoteMethod) {
      props.remoteMethod(val);
    }
  }
});
</script>

<template>
  <div
    ref="wrapperRef"
    :class="wrapperClasses"
    :style="themeStyle"
    @mouseenter="handleMouseEnter"
    @mouseleave="handleMouseLeave"
    @click="toggleDropdown"
  >
    <!-- 输入区域 -->
    <div :class="ns.e('wrapper')">
      <!-- 多选标签 -->
      <template v-if="multiple">
        <span
          v-for="(label, index) in displayTags"
          :key="index"
          :class="[ns.e('tag'), tagType ? `yh-tag--${tagType}` : '']"
        >
          <span :class="ns.e('tag-text')">{{ label }}</span>
          <span
            :class="ns.e('tag-close')"
            @click="handleRemoveTag(Array.isArray(normalizedModelValue) ? normalizedModelValue[index] : normalizedModelValue, $event)"
          >
            <svg viewBox="0 0 1024 1024" width="1em" height="1em">
              <path
                fill="currentColor"
                d="M764.288 214.592L512 466.88 259.712 214.592a31.936 31.936 0 0 0-45.12 45.12L466.752 512 214.528 764.224a31.936 31.936 0 1 0 45.12 45.184L512 557.184l252.288 252.288a31.936 31.936 0 0 0 45.12-45.12L557.12 512.064l252.288-252.352a31.936 31.936 0 1 0-45.12-45.184z"
              />
            </svg>
          </span>
        </span>
        <YhTooltip
          v-if="collapsedCount > 0 && collapseTagsTooltip"
          placement="top"
          :class="ns.e('tag')"
        >
          <span>+{{ collapsedCount }}</span>
          <template #content>
            <div
              :class="ns.e('tooltip-content')"
              style="display: flex; flex-wrap: wrap; gap: 4px; padding: 4px"
            >
              <span
                v-for="(tag, idx) in collapsedTags"
                :key="idx"
                :class="ns.e('tooltip-tag')"
                style="white-space: nowrap"
              >
                {{ tag }}
              </span>
            </div>
          </template>
        </YhTooltip>
        <span v-else-if="collapsedCount > 0" :class="ns.e('tag')"> +{{ collapsedCount }} </span>
      </template>

      <!-- 输入框 -->
      <input
        ref="inputRef"
        :id="inputId"
        :class="ns.e('inner')"
        :value="filterable ? query : ''"
        :placeholder="hasValue ? '' : placeholder ?? t('select.placeholder')"
        :disabled="disabled"
        :readonly="!filterable"
        autocomplete="off"
        role="combobox"
        :aria-label="placeholder ?? t('select.placeholder')"
        :aria-expanded="visible"
        :aria-controls="`${inputId}-listbox`"
        @input="handleInput"
        @focus="handleFocus"
        @blur="handleBlur"
        @keydown="handleKeydown"
      />

      <!-- 单选显示值 -->
      <span v-if="!multiple && hasValue && !query" :class="ns.e('selected-value')">
        {{ selectedLabels }}
      </span>

      <!-- 后缀图标 -->
      <span :class="ns.e('suffix')">
        <!-- 清空按钮 -->
        <span v-if="showClear" :class="[ns.e('icon'), ns.e('clear')]" @click.stop="handleClear">
          <svg viewBox="0 0 1024 1024" width="1em" height="1em">
            <path
              fill="currentColor"
              d="M512 64a448 448 0 1 1 0 896 448 448 0 0 1 0-896zm0 393.664L407.936 353.6a38.4 38.4 0 1 0-54.336 54.336L457.664 512 353.6 616.064a38.4 38.4 0 1 0 54.336 54.336L512 566.336 616.064 670.4a38.4 38.4 0 1 0 54.336-54.336L566.336 512 670.4 407.936a38.4 38.4 0 1 0-54.336-54.336L512 457.664z"
            />
          </svg>
        </span>

        <!-- 箭头图标 -->
        <span :class="[ns.e('icon'), ns.e('arrow'), {
  'is-reverse': visible
}]">
          <svg viewBox="0 0 1024 1024" width="1em" height="1em">
            <path
              fill="currentColor"
              d="M831.872 340.864L512 652.672 192.128 340.864a30.592 30.592 0 0 0-42.752 0 29.12 29.12 0 0 0 0 41.6L489.664 714.24a32 32 0 0 0 44.672 0l340.288-331.712a29.12 29.12 0 0 0 0-41.728 30.592 30.592 0 0 0-42.752 0z"
            />
          </svg>
        </span>
      </span>
    </div>

    <!-- 下拉框 -->
    <Teleport to="body" :disabled="!teleported">
      <Transition :name="ns.b('dropdown')">
        <div
          ref="dropdownRef"
          v-show="visible"
          :class="[ns.e('dropdown'), popperClass]"
          :style="teleported ? dropdownStyle : {
  minWidth: fitInputWidth && wrapperRef ? `${wrapperRef.offsetWidth}px` : void 0
}"
          @mousedown="handleDropdownMousedown"
          @mouseup="handleDropdownMouseup"
        >
          <!-- 加载状态 -->
          <div v-if="loading" :class="ns.e('loading')">
            <svg :class="ns.e('loading-icon')" viewBox="0 0 1024 1024">
              <path
                fill="currentColor"
                d="M512 64a32 32 0 0 1 32 32v192a32 32 0 0 1-64 0V96a32 32 0 0 1 32-32zm0 640a32 32 0 0 1 32 32v192a32 32 0 1 1-64 0V736a32 32 0 0 1 32-32zm448-192a32 32 0 0 1-32 32H736a32 32 0 1 1 0-64h192a32 32 0 0 1 32 32zm-640 0a32 32 0 0 1-32 32H96a32 32 0 0 1 0-64h192a32 32 0 0 1 32 32z"
              />
            </svg>
            <span>{{ translatedLoadingText }}</span>
          </div>

          <!-- 无数据 -->
          <div v-else-if="filteredOptions.length === 0" :class="ns.e('empty')">
            <slot name="empty">
              {{ query ? translatedNoMatchText : translatedNoDataText }}
            </slot>
          </div>

          <!-- 选项列表 -->
          <div
            v-else
            :id="`${inputId}-listbox`"
            :class="ns.e('options')"
            role="listbox"
            :style="virtualScroll ? {
  height: `${height}px`,
  overflow: 'auto'
} : {}"
            @scroll="virtualScroll ? handleVirtualScroll($event) : void 0"
          >
            <!-- 虚拟滚动占位 -->
            <div v-if="virtualScroll" :style="{
  height: `${totalHeight}px`,
  position: 'relative'
}">
              <div :style="{
  transform: `translateY(${offsetY}px)`
}">
                <div
                  v-for="(option, index) in displayOptions"
                  :key="String(option[valueKey || 'value'])"
                  :class="[ns.e('option'), ns.is('selected', isSelected(option[valueKey || 'value'])), ns.is('disabled', option.disabled), ns.is('hovering', hoveredIndex === startIndex + index)]"
                  role="option"
                  :aria-selected="isSelected(option[valueKey || 'value'])"
                  @mousedown.prevent
                  @click="handleOptionClick(option, $event)"
                  @mouseenter="hoveredIndex = startIndex + index"
                >
                  <slot name="option" :option="option">
                    {{ option[labelKey || "label"] || option.label }}
                  </slot>
                  <span
                    v-if="multiple && isSelected(option[valueKey || 'value'])"
                    :class="ns.e('option-check')"
                  >
                    <svg viewBox="0 0 1024 1024" width="1em" height="1em">
                      <path
                        fill="currentColor"
                        d="M406.656 706.944L195.84 496.256a32 32 0 1 0-45.248 45.248l256 256 512-512a32 32 0 0 0-45.248-45.248L406.592 706.944z"
                      />
                    </svg>
                  </span>
                </div>
              </div>
            </div>

            <!-- 普通列表 -->
            <template v-else>
              <div
                v-for="(option, index) in displayOptions"
                :key="String(option[valueKey || 'value'])"
                :class="[ns.e('option'), ns.is('selected', isSelected(option[valueKey || 'value'])), ns.is('disabled', option.disabled), ns.is('hovering', hoveredIndex === index)]"
                role="option"
                :aria-selected="isSelected(option[valueKey || 'value'])"
                @mousedown.prevent
                @click="handleOptionClick(option, $event)"
                @mouseenter="hoveredIndex = index"
              >
                <slot name="option" :option="option">
                  {{ option[labelKey || "label"] || option.label }}
                </slot>
                <span
                  v-if="multiple && isSelected(option[valueKey || 'value'])"
                  :class="ns.e('option-check')"
                >
                  <svg viewBox="0 0 1024 1024" width="1em" height="1em">
                    <path
                      fill="currentColor"
                      d="M406.656 706.944L195.84 496.256a32 32 0 1 0-45.248 45.248l256 256 512-512a32 32 0 0 0-45.248-45.248L406.592 706.944z"
                    />
                  </svg>
                </span>
              </div>
            </template>
          </div>
        </div>
      </Transition>
    </Teleport>

    <!-- 隐藏插槽，用于注册 Option -->
    <div v-show="false" v-if="$slots.default">
      <slot />
    </div>
  </div>
</template>

<style>
@charset "UTF-8";
/**
 * YH-UI Sass Variables
 * 原具体的 CSS 变量输出规则 (:root, html.dark 等) 已移至 root.scss，以防止各组件样式编译时产生重复的全局变量 CSS 冗余。
 */
.yh-select {
  position: relative;
  display: inline-flex;
  width: 100%;
  font-size: 14px;
}
.yh-select__wrapper {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px;
  position: relative;
  width: 100%;
  min-height: 32px;
  padding: 2px 30px 2px 11px;
  background-color: var(--yh-fill-color-blank, #fff);
  border: 1px solid var(--yh-border-color, #dcdfe6);
  border-radius: var(--yh-border-radius-base, 4px);
  box-sizing: border-box;
  transition: border-color 0.2s;
  cursor: pointer;
}
.yh-select__wrapper:hover {
  border-color: var(--yh-border-color-hover, #c0c4cc);
}

.yh-select__inner {
  flex: 1;
  min-width: 50px;
  height: 26px;
  padding: 0;
  color: var(--yh-text-color-regular, #606266);
  font-size: inherit;
  line-height: 26px;
  background: transparent;
  border: none;
  outline: none;
  cursor: pointer;
}
.yh-select__inner::placeholder {
  color: var(--yh-text-color-placeholder, #a8abb2);
}
.yh-select__inner:disabled {
  cursor: not-allowed;
}

.yh-select__selected-value {
  position: absolute;
  left: 11px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--yh-text-color-regular, #606266);
  pointer-events: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: calc(100% - 50px);
}

.yh-select__suffix {
  position: absolute;
  right: 8px;
  top: 50%;
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: var(--yh-text-color-placeholder, #a8abb2);
}

.yh-select__icon {
  width: 14px;
  height: 14px;
  line-height: 1;
}

.yh-select__clear {
  cursor: pointer;
  transition: color 0.2s;
}
.yh-select__clear:hover {
  color: var(--yh-text-color-secondary, #909399);
}

.yh-select__arrow {
  transition: transform 0.3s;
}
.yh-select__arrow.is-reverse {
  transform: rotate(180deg);
}

.yh-select__tag {
  display: inline-flex;
  align-items: center;
  max-width: 100%;
  height: 22px;
  padding: 0 8px;
  font-size: 12px;
  color: var(--yh-text-color-regular, #606266);
  background-color: var(--yh-fill-color, #f0f2f5);
  border-radius: var(--yh-border-radius-small, 2px);
}

.yh-select__tag-text {
  max-width: 100px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.yh-select__tag-close {
  display: inline-flex;
  align-items: center;
  margin-left: 4px;
  cursor: pointer;
  transition: color 0.2s;
}
.yh-select__tag-close:hover {
  color: var(--yh-color-danger, #f56c6c);
}
.yh-select__tag-close svg {
  width: 12px;
  height: 12px;
}

.yh-select__dropdown {
  position: absolute;
  top: 100%;
  left: 0;
  z-index: 2000;
  margin-top: 4px;
  padding: 6px 0;
  background-color: var(--yh-bg-color-overlay, #fff);
  border: 1px solid var(--yh-border-color-light, #e4e7ed);
  border-radius: var(--yh-border-radius-base, 4px);
  box-shadow: var(--yh-box-shadow-light, 0 2px 12px 0 rgba(0, 0, 0, 0.1));
  cursor: default;
  pointer-events: auto;
}

.yh-select__options {
  max-height: 274px;
  overflow-y: auto;
  pointer-events: auto;
}

.yh-select__option {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 20px;
  height: 34px;
  line-height: 34px;
  font-size: 14px;
  color: var(--yh-text-color-regular, #606266);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer;
  pointer-events: auto;
  user-select: none;
}
.yh-select__option:hover:not(.is-disabled) {
  background-color: var(--yh-fill-color-light, #f5f7fa);
}
.yh-select__option.is-disabled {
  color: var(--yh-text-color-placeholder, #a8abb2);
  cursor: not-allowed;
}

.yh-select__option.is-hovering {
  background-color: var(--yh-fill-color-light, #f5f7fa);
}
.yh-select__option.is-selected {
  color: var(--yh-color-white, #ffffff) !important;
  background-color: var(--yh-color-primary, #409eff) !important;
  font-weight: 700;
  z-index: 1;
}
.yh-select__option.is-selected.is-hovering {
  background-color: var(--yh-color-primary-light-3, #79bbff);
}
.yh-select__option.is-selected.is-disabled {
  background-color: var(--yh-fill-color-light, #f5f7fa) !important;
  color: var(--yh-text-color-placeholder, #a8abb2) !important;
}

.yh-select__option-check {
  color: var(--yh-color-primary, #409eff);
}
.yh-select__option-check svg {
  width: 14px;
  height: 14px;
}

.yh-select__loading {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 10px 0;
  color: var(--yh-text-color-secondary, #909399);
  font-size: 14px;
}

.yh-select__loading-icon {
  width: 16px;
  height: 16px;
  margin-right: 8px;
  animation: yh-select-loading 1s linear infinite;
}

.yh-select__empty {
  padding: 10px 0;
  text-align: center;
  color: var(--yh-text-color-secondary, #909399);
  font-size: 14px;
}

.yh-select.is-focused .yh-select__wrapper {
  border-color: var(--yh-color-primary, #409eff);
}
.yh-select.is-disabled {
  cursor: not-allowed;
}
.yh-select.is-disabled .yh-select__wrapper {
  background-color: var(--yh-fill-color-light, #f5f7fa);
  border-color: var(--yh-border-color-light, #e4e7ed);
  cursor: not-allowed;
}
.yh-select.is-disabled .yh-select__inner {
  color: var(--yh-text-color-placeholder, #a8abb2);
}
.yh-select.is-multiple .yh-select__inner {
  height: 22px;
  line-height: 22px;
}
.yh-select--large {
  font-size: 14px;
}
.yh-select--large .yh-select__wrapper {
  min-height: 40px;
}
.yh-select--large .yh-select__inner {
  height: 34px;
  line-height: 34px;
}
.yh-select--large .yh-select__tag {
  height: 26px;
}
.yh-select--large.is-multiple .yh-select__inner {
  height: 26px;
  line-height: 26px;
}

.yh-select--small {
  font-size: 12px;
}
.yh-select--small .yh-select__wrapper {
  min-height: 24px;
}
.yh-select--small .yh-select__inner {
  height: 18px;
  line-height: 18px;
}
.yh-select--small .yh-select__tag {
  height: 18px;
  font-size: 10px;
}
.yh-select--small.is-multiple .yh-select__inner {
  height: 18px;
  line-height: 18px;
}

.yh-select-dropdown-enter-active,
.yh-select-dropdown-leave-active {
  transition: opacity 0.2s, transform 0.2s;
}

.yh-select-dropdown-enter-from,
.yh-select-dropdown-leave-to {
  opacity: 0;
  transform: translateY(-10px);
}

@keyframes yh-select-loading {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}
</style>
