<script setup>
import { ref, computed, watch, nextTick, onMounted, onBeforeUnmount, useSlots } from "vue";
import { useNamespace, useFormItem, useId, useZIndex, useLocale, useConfig } from "@yh-ui/hooks";
import { useComponentTheme } from "@yh-ui/theme";
defineOptions({ name: "YhMention" });
const props = defineProps({
  modelValue: { type: String, required: false, default: "" },
  options: { type: Array, required: false, default: () => [] },
  triggers: { type: Array, required: false, default: () => ["@"] },
  type: { type: String, required: false, default: "input" },
  placement: { type: null, required: false, default: "bottom" },
  placeholder: { type: String, required: false },
  disabled: { type: Boolean, required: false, default: false },
  readonly: { type: Boolean, required: false, default: false },
  size: { type: null, required: false, default: void 0 },
  maxlength: { type: Number, required: false },
  clearable: { type: Boolean, required: false, default: false },
  showWordLimit: { type: Boolean, required: false, default: false },
  prefixIcon: { type: null, required: false },
  suffixIcon: { type: null, required: false },
  maxCount: { type: Number, required: false, default: 8 },
  filterOption: { type: [Function, Boolean], required: false, default: void 0 },
  loading: { type: Boolean, required: false, default: false },
  loadingText: { type: String, required: false, default: void 0 },
  noDataText: { type: String, required: false, default: void 0 },
  teleported: { type: Boolean, required: false, default: true },
  popperClass: { type: String, required: false, default: "" },
  split: { type: String, required: false, default: " " },
  wholeWord: { type: Boolean, required: false, default: false },
  autofocus: { type: Boolean, required: false, default: false },
  rows: { type: Number, required: false, default: 3 },
  autosize: { type: [Boolean, Object], required: false },
  debounce: { type: Number, required: false, default: 100 },
  validateEvent: { type: Boolean, required: false, default: true },
  themeOverrides: { type: null, required: false }
});
const emit = defineEmits(["update:modelValue", "input", "change", "focus", "blur", "clear", "search", "select", "open", "close", "keydown"]);
const slots = useSlots();
const ns = useNamespace("mention");
const inputId = useId();
const { t } = useLocale();
const { nextZIndex } = useZIndex();
const { themeStyle } = useComponentTheme(
  "mention",
  computed(() => props.themeOverrides)
);
const { form, formItem, validate: triggerValidate } = useFormItem();
const { globalSize } = useConfig();
const mentionSize = computed(
  () => props.size || formItem?.size || form?.size || globalSize.value || "default"
);
const mentionDisabled = computed(() => props.disabled || form?.disabled || false);
const inputRef = ref();
const wrapperRef = ref();
const dropdownRef = ref();
const focused = ref(false);
const hovering = ref(false);
const dropdownVisible = ref(false);
const isClickingDropdown = ref(false);
const highlightedIndex = ref(-1);
const dropdownZIndex = ref(nextZIndex());
const triggerPos = ref(null);
const filteredOptions = computed(() => {
  const keyword = triggerPos.value?.keyword ?? "";
  return props.options.filter((opt) => {
    if (props.filterOption === false) return true;
    if (typeof props.filterOption === "function") {
      return props.filterOption(keyword, opt);
    }
    const text = (opt.label ?? opt.value).toLowerCase();
    return text.includes(keyword.toLowerCase());
  });
});
watch(
  () => filteredOptions.value,
  (newVal) => {
    if (!newVal || newVal.length === 0) {
      highlightedIndex.value = -1;
    } else if (highlightedIndex.value >= newVal.length) {
      highlightedIndex.value = 0;
    }
  },
  { immediate: true }
);
const groupedOptions = computed(() => {
  const map = /* @__PURE__ */ new Map();
  for (const opt of filteredOptions.value) {
    const g = opt.group ?? "";
    if (!map.has(g)) map.set(g, []);
    map.get(g).push(opt);
  }
  return map;
});
const wrapperClasses = computed(() => [
  ns.b(),
  ns.m(mentionSize.value),
  ns.is("disabled", mentionDisabled.value),
  ns.is("focused", focused.value),
  ns.is("textarea", props.type === "textarea"),
  !!slots.prefix || !!props.prefixIcon ? ns.is("prefix", true) : "",
  !!slots.suffix || !!props.suffixIcon || props.clearable ? ns.is("suffix", true) : ""
]);
const dropdownStyle = ref({});
const updateDropdownPosition = () => {
  if (!wrapperRef.value || !props.teleported) return;
  const rect = wrapperRef.value.getBoundingClientRect();
  const style = {
    position: "fixed",
    zIndex: String(dropdownZIndex.value),
    minWidth: `${rect.width}px`
  };
  if (props.placement === "top") {
    style.bottom = `${window.innerHeight - rect.top + 4}px`;
    style.left = `${rect.left}px`;
  } else {
    style.top = `${rect.bottom + 4}px`;
    style.left = `${rect.left}px`;
  }
  dropdownStyle.value = style;
};
watch(dropdownVisible, (val) => {
  if (val) {
    dropdownZIndex.value = nextZIndex();
    nextTick(updateDropdownPosition);
    emit("open");
  } else {
    dropdownStyle.value = {};
    emit("close");
  }
});
const parseTrigger = (value, cursorIndex) => {
  for (const trigger of props.triggers) {
    const segment = value.substring(0, cursorIndex);
    const lastIdx = segment.lastIndexOf(trigger);
    if (lastIdx === -1) continue;
    const after = segment.substring(lastIdx + trigger.length);
    if (/\s/.test(after)) continue;
    const before = segment[lastIdx - 1];
    if (before !== void 0 && !/[\s\n]/.test(before)) continue;
    return {
      index: lastIdx,
      trigger,
      keyword: after
    };
  }
  return null;
};
let debounceTimer = null;
const debouncedSearch = (keyword, trigger) => {
  if (debounceTimer) clearTimeout(debounceTimer);
  debounceTimer = setTimeout(() => {
    emit("search", keyword, trigger);
  }, props.debounce);
};
const handleInput = (event) => {
  if (event.isComposing) return;
  const target = event.target;
  const value = target.value;
  const rawCursor = target.selectionStart;
  const cursor = rawCursor === null || rawCursor === 0 && value.length > 0 ? value.length : rawCursor;
  emit("update:modelValue", value);
  emit("input", value);
  const hit = parseTrigger(value, cursor);
  if (hit) {
    triggerPos.value = hit;
    dropdownVisible.value = true;
    highlightedIndex.value = 0;
    debouncedSearch(hit.keyword, hit.trigger);
  } else {
    dropdownVisible.value = false;
    triggerPos.value = null;
  }
};
const handleChange = (event) => {
  const target = event.target;
  emit("change", target.value);
  if (props.validateEvent) triggerValidate("change");
};
const handleFocus = (event) => {
  focused.value = true;
  emit("focus", event);
};
const handleBlur = (event) => {
  setTimeout(() => {
    if (!isClickingDropdown.value) {
      focused.value = false;
      dropdownVisible.value = false;
      triggerPos.value = null;
      emit("blur", event);
      if (props.validateEvent) triggerValidate("blur");
    }
  }, 150);
};
const handleKeydown = (event) => {
  emit("keydown", event);
  if (event.key === "Backspace" && props.wholeWord) {
    const el = inputRef.value;
    if (el) {
      const cursor = el.selectionStart ?? 0;
      const value = props.modelValue ?? "";
      const segment = value.substring(0, cursor);
      for (const trigger of props.triggers) {
        const lastIdx = segment.lastIndexOf(trigger);
        if (lastIdx !== -1) {
          const mentionText = segment.substring(lastIdx + trigger.length);
          const hasSplit = props.split && mentionText.endsWith(props.split);
          const labelText = hasSplit && props.split ? mentionText.slice(0, -props.split.length) : mentionText;
          const matched = props.options.some((opt) => {
            const optText = opt.label ?? opt.value;
            return optText === labelText || opt.value === labelText;
          });
          if (matched) {
            event.preventDefault();
            const newValue = value.substring(0, lastIdx) + value.substring(cursor);
            emit("update:modelValue", newValue);
            nextTick(() => {
              el.setSelectionRange(lastIdx, lastIdx);
            });
            return;
          }
        }
      }
    }
  }
  if (!dropdownVisible.value) return;
  const total = filteredOptions.value.length;
  if (total <= 0) {
    highlightedIndex.value = -1;
    return;
  }
  switch (event.key) {
    case "ArrowDown":
      event.preventDefault();
      highlightedIndex.value = (highlightedIndex.value + 1) % total;
      break;
    case "ArrowUp":
      event.preventDefault();
      highlightedIndex.value = (highlightedIndex.value - 1 + total) % total;
      break;
    case "Enter":
      event.preventDefault();
      if (highlightedIndex.value >= 0 && filteredOptions.value[highlightedIndex.value]) {
        selectOption(filteredOptions.value[highlightedIndex.value]);
      }
      break;
    case "Escape":
      event.preventDefault();
      dropdownVisible.value = false;
      triggerPos.value = null;
      break;
    case "Tab":
      if (highlightedIndex.value >= 0 && filteredOptions.value[highlightedIndex.value]) {
        event.preventDefault();
        selectOption(filteredOptions.value[highlightedIndex.value]);
      } else {
        dropdownVisible.value = false;
      }
      break;
  }
};
const selectOption = (option) => {
  if (option.disabled || !triggerPos.value) return;
  const { index, trigger } = triggerPos.value;
  const before = (props.modelValue ?? "").substring(0, index);
  const after = (() => {
    const cursor = inputRef.value?.selectionStart ?? (props.modelValue ?? "").length;
    return (props.modelValue ?? "").substring(cursor);
  })();
  const label = option.label ?? option.value;
  const insertText = props.wholeWord ? `${trigger}${label}${props.split}` : `${trigger}${option.value}${props.split}`;
  const newValue = before + insertText + after;
  emit("update:modelValue", newValue);
  emit("select", option, trigger);
  dropdownVisible.value = false;
  triggerPos.value = null;
  nextTick(() => {
    const el = inputRef.value;
    if (!el) return;
    const pos = before.length + insertText.length;
    el.setSelectionRange(pos, pos);
    el.focus();
  });
  if (props.validateEvent) triggerValidate("change");
};
const showClear = computed(
  () => props.clearable && !mentionDisabled.value && !props.readonly && !!props.modelValue && (focused.value || hovering.value)
);
const handleClear = () => {
  emit("update:modelValue", "");
  emit("change", "");
  emit("clear");
  dropdownVisible.value = false;
  triggerPos.value = null;
  nextTick(() => focus());
};
const handleOutsideClick = (e) => {
  const target = e.target;
  if (wrapperRef.value?.contains(target) || dropdownRef.value?.contains(target)) return;
  dropdownVisible.value = false;
  triggerPos.value = null;
};
const handleResize = () => {
  if (dropdownVisible.value) updateDropdownPosition();
};
onMounted(() => {
  if (typeof window !== "undefined") {
    window.addEventListener("click", handleOutsideClick);
    window.addEventListener("resize", handleResize);
    if (props.teleported) {
      window.addEventListener("scroll", handleResize, true);
    }
  }
});
onBeforeUnmount(() => {
  if (typeof window !== "undefined") {
    window.removeEventListener("click", handleOutsideClick);
    window.removeEventListener("resize", handleResize);
    if (props.teleported) {
      window.removeEventListener("scroll", handleResize, true);
    }
  }
  if (debounceTimer) clearTimeout(debounceTimer);
});
const insertMention = (option, trigger) => {
  const t2 = trigger ?? props.triggers[0] ?? "@";
  const label = option.label ?? option.value;
  const insertText = props.wholeWord ? `${t2}${label}${props.split}` : `${t2}${option.value}${props.split}`;
  const current = props.modelValue ?? "";
  const cursor = inputRef.value?.selectionStart ?? current.length;
  const newValue = current.substring(0, cursor) + insertText + current.substring(cursor);
  emit("update:modelValue", newValue);
};
const focus = () => inputRef.value?.focus();
const blur = () => inputRef.value?.blur();
const clear = () => handleClear();
defineExpose({
  get ref() {
    return inputRef.value;
  },
  get wrapperRef() {
    return wrapperRef.value;
  },
  focus,
  blur,
  clear,
  insertMention
});
const textLength = computed(() => (props.modelValue ?? "").length);
</script>

<template>
  <div
    ref="wrapperRef"
    :class="wrapperClasses"
    :style="themeStyle"
    @mouseenter="hovering = true"
    @mouseleave="hovering = false"
  >
    <!-- 前缀 -->
    <span v-if="slots.prefix || prefixIcon" :class="ns.e('prefix')">
      <slot name="prefix">
        <component
          v-if="prefixIcon && typeof prefixIcon !== 'string'"
          :is="prefixIcon"
          :class="ns.e('icon')"
        />
        <span v-else-if="prefixIcon" :class="ns.e('icon')">{{ prefixIcon }}</span>
      </slot>
    </span>

    <!-- 输入框 -->
    <textarea
      v-if="type === 'textarea'"
      :id="inputId"
      ref="inputRef"
      :class="ns.e('inner')"
      :value="modelValue"
      :placeholder="placeholder ?? t('mention.placeholder')"
      :disabled="mentionDisabled"
      :readonly="readonly"
      :maxlength="maxlength"
      :rows="rows"
      :autofocus="autofocus"
      role="combobox"
      :aria-expanded="dropdownVisible"
      :aria-autocomplete="'list'"
      :aria-controls="`${inputId}-listbox`"
      :aria-activedescendant="highlightedIndex >= 0 ? `${inputId}-option-${highlightedIndex}` : void 0"
      @input="handleInput"
      @change="handleChange"
      @focus="handleFocus"
      @blur="handleBlur"
      @keydown="handleKeydown"
    />
    <input
      v-else
      :id="inputId"
      ref="inputRef"
      :class="ns.e('inner')"
      type="text"
      :value="modelValue"
      :placeholder="placeholder ?? t('mention.placeholder')"
      :disabled="mentionDisabled"
      :readonly="readonly"
      :maxlength="maxlength"
      :autofocus="autofocus"
      role="combobox"
      :aria-expanded="dropdownVisible"
      :aria-autocomplete="'list'"
      :aria-controls="`${inputId}-listbox`"
      :aria-activedescendant="highlightedIndex >= 0 ? `${inputId}-option-${highlightedIndex}` : void 0"
      @input="handleInput"
      @change="handleChange"
      @focus="handleFocus"
      @blur="handleBlur"
      @keydown="handleKeydown"
    />

    <!-- 后缀 / 清空 -->
    <span v-if="slots.suffix || suffixIcon || showClear || showWordLimit" :class="ns.e('suffix')">
      <!-- 清空按钮 -->
      <span
        v-if="showClear"
        :class="[ns.e('icon'), ns.e('clear')]"
        @mousedown.prevent
        @click.stop="handleClear"
        aria-label="Clear"
      >
        <svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" 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 v-if="showWordLimit && maxlength" :class="ns.e('count')">
        {{ textLength }} / {{ maxlength }}
      </span>
      <!-- 自定义后缀 -->
      <slot name="suffix">
        <component
          v-if="suffixIcon && typeof suffixIcon !== 'string'"
          :is="suffixIcon"
          :class="ns.e('icon')"
        />
        <span v-else-if="suffixIcon" :class="ns.e('icon')">{{ suffixIcon }}</span>
      </slot>
    </span>

    <!-- 下拉面板 -->
    <Teleport to="body" :disabled="!teleported">
      <Transition :name="ns.b('dropdown')">
        <div
          v-show="dropdownVisible"
          ref="dropdownRef"
          :id="`${inputId}-listbox`"
          :class="[ns.e('dropdown'), popperClass]"
          :style="teleported ? {
  ...themeStyle,
  ...dropdownStyle
} : themeStyle"
          role="listbox"
          @mousedown="isClickingDropdown = true"
          @mouseup="isClickingDropdown = false"
        >
          <!-- 加载中 -->
          <div v-if="loading" :class="ns.e('loading')">
            <slot name="loading">
              <svg
                :class="ns.e('loading-icon')"
                viewBox="0 0 1024 1024"
                xmlns="http://www.w3.org/2000/svg"
              >
                <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>{{ loadingText || t("mention.loading") }}</span>
            </slot>
          </div>

          <!-- 无数据 -->
          <div v-else-if="!loading && filteredOptions.length === 0" :class="ns.e('empty')">
            <slot name="empty">
              <span>{{ noDataText || t("mention.noData") }}</span>
            </slot>
          </div>

          <!-- 选项列表（分组支持） -->
          <template v-else>
            <template v-for="[group, groupOpts] in groupedOptions" :key="group">
              <!-- 分组标题 -->
              <div v-if="group" :class="ns.e('group-label')">{{ group }}</div>
              <!-- 选项条目 -->
              <div
                v-for="option in groupOpts"
                :key="option.value"
                :id="`${inputId}-option-${filteredOptions.indexOf(option)}`"
                :class="[ns.e('option'), ns.is('highlighted', filteredOptions.indexOf(option) === highlightedIndex), ns.is('disabled', !!option.disabled)]"
                role="option"
                :aria-selected="filteredOptions.indexOf(option) === highlightedIndex"
                :aria-disabled="option.disabled"
                @mousedown.prevent
                @click="selectOption(option)"
                @mouseenter="highlightedIndex = filteredOptions.indexOf(option)"
              >
                <slot name="option" :option="option" :keyword="triggerPos?.keyword ?? ''">
                  <!-- 头像 -->
                  <img
                    v-if="option.avatar"
                    :src="option.avatar"
                    :class="ns.e('option-avatar')"
                    :alt="option.label ?? option.value"
                  />
                  <div :class="ns.e('option-content')">
                    <span :class="ns.e('option-label')">{{ option.label ?? option.value }}</span>
                    <span v-if="option.description" :class="ns.e('option-desc')">{{
                      option.description
                    }}</span>
                  </div>
                </slot>
              </div>
            </template>
          </template>
        </div>
      </Transition>
    </Teleport>
  </div>
</template>

<style>
@charset "UTF-8";
/**
 * YH-UI Sass Variables
 * 原具体的 CSS 变量输出规则 (:root, html.dark 等) 已移至 root.scss，以防止各组件样式编译时产生重复的全局变量 CSS 冗余。
 */
.yh-mention {
  position: relative;
  display: inline-flex;
  align-items: center;
  width: 100%;
  font-size: var(--yh-mention-font-size, var(--yh-font-size-base));
  border-radius: var(--yh-mention-border-radius, var(--yh-border-radius-base));
  border: 1px solid var(--yh-mention-border-color, var(--yh-border-color));
  background-color: var(--yh-mention-bg-color, var(--yh-fill-color-blank));
  transition: border-color var(--yh-transition-duration), box-shadow var(--yh-transition-duration);
  box-sizing: border-box;
}
.yh-mention--large {
  height: var(--yh-mention-height-large, var(--yh-component-size-large));
  font-size: var(--yh-font-size-large);
}
.yh-mention--large .yh-mention__inner {
  padding: 0 14px;
}

.yh-mention--default {
  height: var(--yh-mention-height-default, var(--yh-component-size-default));
}

.yh-mention--small {
  height: var(--yh-mention-height-small, var(--yh-component-size-small));
  font-size: var(--yh-font-size-small);
}
.yh-mention--small .yh-mention__inner {
  padding: 0 8px;
}

.yh-mention.is-textarea {
  height: auto;
  align-items: flex-start;
}
.yh-mention.is-textarea .yh-mention__inner {
  resize: vertical;
  min-height: 80px;
  padding: 8px 12px;
  line-height: 1.6;
}

.yh-mention.is-textarea .yh-mention__prefix {
  align-self: flex-start;
  margin-top: 8px;
}

.yh-mention.is-textarea .yh-mention__suffix {
  align-self: flex-start;
  margin-top: 8px;
}

.yh-mention.is-focused {
  border-color: var(--yh-color-primary);
  box-shadow: 0 0 0 2px var(--yh-color-primary-light-8);
}

.yh-mention.is-disabled {
  cursor: not-allowed;
  background-color: var(--yh-disabled-bg-color);
  border-color: var(--yh-border-color-light);
}
.yh-mention.is-disabled .yh-mention__inner {
  color: var(--yh-disabled-text-color);
  cursor: not-allowed;
}

.yh-mention.is-prefix .yh-mention__inner {
  padding-left: 30px;
}

.yh-mention.is-suffix .yh-mention__inner {
  padding-right: 30px;
}

.yh-mention__inner {
  flex: 1;
  width: 100%;
  height: 100%;
  border: none;
  outline: none;
  background: transparent;
  color: var(--yh-text-color-regular);
  font-size: inherit;
  font-family: inherit;
  line-height: 1;
  padding: 0 12px;
  box-sizing: border-box;
}
.yh-mention__inner::placeholder {
  color: var(--yh-text-color-placeholder);
}
.yh-mention__inner[rows] {
  height: auto;
  line-height: 1.6;
}

.yh-mention__prefix {
  display: inline-flex;
  align-items: center;
  padding: 0 8px;
  color: var(--yh-text-color-placeholder);
  transition: color var(--yh-transition-duration);
  flex-shrink: 0;
  order: -1;
}

.yh-mention__suffix {
  display: inline-flex;
  align-items: center;
  padding: 0 8px;
  color: var(--yh-text-color-placeholder);
  transition: color var(--yh-transition-duration);
  flex-shrink: 0;
}

.yh-mention__icon {
  display: inline-flex;
  align-items: center;
  font-size: 16px;
  cursor: pointer;
  transition: color var(--yh-transition-duration);
}
.yh-mention__icon:hover {
  color: var(--yh-color-primary);
}

.yh-mention__clear {
  cursor: pointer;
  color: var(--yh-text-color-placeholder);
  transition: color var(--yh-transition-duration);
}
.yh-mention__clear:hover {
  color: var(--yh-text-color-secondary);
}

.yh-mention__count {
  font-size: 12px;
  color: var(--yh-text-color-secondary);
  padding: 0 4px;
  white-space: nowrap;
  cursor: default;
}

.yh-mention__dropdown {
  background-color: var(--yh-bg-color-overlay);
  border: 1px solid var(--yh-border-color-light);
  border-radius: var(--yh-border-radius-base);
  box-shadow: var(--yh-mention-dropdown-shadow, var(--yh-box-shadow-light));
  overflow: hidden;
  z-index: var(--yh-index-popper);
  max-height: 300px;
  overflow-y: auto;
  min-width: 160px;
}

.yh-mention__option {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  cursor: pointer;
  font-size: var(--yh-font-size-base);
  color: var(--yh-text-color-regular);
  transition: background-color 0.15s, color 0.15s;
  user-select: none;
}
.yh-mention__option:hover, .yh-mention__option.is-highlighted {
  background-color: var(--yh-fill-color-light);
  color: var(--yh-color-primary);
}
.yh-mention__option.is-disabled {
  color: var(--yh-disabled-text-color);
  cursor: not-allowed;
  background-color: transparent !important;
  pointer-events: none;
}

.yh-mention__option-avatar {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  object-fit: cover;
  flex-shrink: 0;
}

.yh-mention__option-content {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.yh-mention__option-label {
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.yh-mention__option-desc {
  font-size: 12px;
  color: var(--yh-text-color-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.yh-mention__group-label {
  padding: 6px 12px;
  font-size: 12px;
  color: var(--yh-text-color-secondary);
  background-color: var(--yh-fill-color-lighter);
  font-weight: 500;
}

.yh-mention__loading {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 16px;
  color: var(--yh-text-color-secondary);
  font-size: var(--yh-font-size-base);
}

.yh-mention__empty {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 16px;
  color: var(--yh-text-color-secondary);
  font-size: var(--yh-font-size-base);
}

.yh-mention__loading-icon {
  width: 16px;
  height: 16px;
  animation: yh-mention-spin 1s linear infinite;
}

.yh-mention-dropdown-enter-active,
.yh-mention-dropdown-leave-active {
  transition: opacity 0.2s cubic-bezier(0.18, 0.89, 0.32, 1.28), transform 0.2s cubic-bezier(0.18, 0.89, 0.32, 1.28);
  transform-origin: top center;
}

.yh-mention-dropdown-enter-from,
.yh-mention-dropdown-leave-to {
  opacity: 0;
  transform: scaleY(0.9);
}

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