{"version":3,"file":"mention.mjs","sources":["../../../../../../packages/components/mention/src/mention.vue"],"sourcesContent":["<template>\n  <div ref=\"wrapperRef\" :class=\"namespace\">\n    <hl-input\n      v-bind=\"mergeProps(passInputProps, $attrs)\"\n      ref=\"hlInputRef\"\n      :model-value=\"modelValue\"\n      @input=\"handleInputChange\"\n      @keydown=\"handleInputKeyDown\"\n      @focus=\"handleFocus\"\n      @blur=\"handleBlur\"\n      @mousedown=\"handleInputMouseDown\"\n    >\n      <template v-for=\"(_, name) in $slots\" #[name]=\"slotProps\">\n        <slot :name=\"name\" v-bind=\"slotProps\"></slot>\n      </template>\n    </hl-input>\n    <hl-tooltip\n      ref=\"tooltipRef\"\n      :visible=\"visible && (!!filteredOptions.length || loading)\"\n      :popper-class=\"['mention-popper', popperClass]\"\n      :popper-options=\"popperOptions\"\n      :placement=\"computedPlacement\"\n      :fallback-placements=\"computedFallbackPlacements\"\n      effect=\"light\"\n      pure\n      :offset=\"offset\"\n      :show-arrow=\"showArrow\"\n    >\n      <template #default>\n        <div :style=\"cursorStyle\"></div>\n      </template>\n      <template #content>\n        <hl-mention-dropdown\n          ref=\"dropdownRef\"\n          :options=\"filteredOptions\"\n          :disabled=\"disabled\"\n          :loading=\"loading\"\n          @select=\"handleSelect\"\n          @click.stop=\"hlInputRef?.focus\"\n        >\n          <template v-for=\"(_, name) in $slots\" #[name]=\"slotProps\">\n            <slot :name=\"name\" v-bind=\"slotProps\"></slot>\n          </template>\n        </hl-mention-dropdown>\n      </template>\n    </hl-tooltip>\n  </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { computed, mergeProps, nextTick, ref } from 'vue'\nimport { pick } from 'lodash-unified'\nimport { useFocusController, useNamespace } from '@hongluan-ui/hooks'\nimport HlInput, { inputProps } from '@hongluan-ui/components/input'\nimport HlTooltip from '@hongluan-ui/components/tooltip'\nimport { UPDATE_MODEL_EVENT } from '@hongluan-ui/constants'\nimport { isFunction } from '@hongluan-ui/utils'\nimport { mentionEmits, mentionProps } from './mention'\nimport { getCursorPosition, getMentionCtx } from './helper'\nimport HlMentionDropdown from './mention-dropdown.vue'\n\nimport type { Placement } from '@popperjs/core'\nimport type { CSSProperties } from 'vue'\nimport type { InputInstance } from '@hongluan-ui/components/input'\nimport type { TooltipInstance } from '@hongluan-ui/components/tooltip'\nimport type { MentionCtx, MentionOption } from './types'\n\ndefineOptions({\n  name: 'Mention',\n})\n\nconst props = defineProps(mentionProps)\nconst emit = defineEmits(mentionEmits)\n\nconst passInputProps = computed(() => pick(props, Object.keys(inputProps)))\n\nconst { namespace } = useNamespace('mention')\n\nconst hlInputRef = ref<InputInstance>()\nconst tooltipRef = ref<TooltipInstance>()\nconst dropdownRef = ref<InstanceType<typeof HlMentionDropdown>>()\n\nconst visible = ref(false)\nconst cursorStyle = ref<CSSProperties>()\nconst mentionCtx = ref<MentionCtx>()\n\nconst computedPlacement = computed<Placement>(() =>\n  props.showArrow ? props.placement : `${props.placement}-start`,\n)\n\nconst computedFallbackPlacements = computed<Placement[]>(() =>\n  props.showArrow ? ['bottom', 'top'] : ['bottom-start', 'top-start'],\n)\n\nconst filteredOptions = computed(() => {\n  const { filterOption, options } = props\n  if (!mentionCtx.value || !filterOption) return options\n  return options.filter(option =>\n    filterOption(mentionCtx.value!.pattern, option),\n  )\n})\n\nconst handleInputChange = (value: string) => {\n  emit('update:modelValue', value)\n  syncAfterCursorMove()\n}\n\nconst handleInputKeyDown = (e: KeyboardEvent | Event) => {\n  if (!('key' in e)) return\n  if (hlInputRef.value?.isComposing) return\n  if (['ArrowLeft', 'ArrowRight'].includes(e.key)) {\n    syncAfterCursorMove()\n  } else if (['ArrowUp', 'ArrowDown'].includes(e.key)) {\n    if (!visible.value) return\n    e.preventDefault()\n    const direction = e.key === 'ArrowUp' ? 'prev' : 'next'\n    dropdownRef.value?.navigateOptions(direction)\n  } else if (['Enter'].includes(e.key)) {\n    if (!visible.value) return\n    e.preventDefault()\n    if (dropdownRef.value?.hoverOption) {\n      dropdownRef.value?.selectHoverOption()\n    } else {\n      visible.value = false\n    }\n  } else if (['Backspace'].includes(e.key)) {\n    if (props.whole && mentionCtx.value) {\n      const { splitIndex, selectionEnd, pattern, prefixIndex, prefix } =\n        mentionCtx.value\n      const inputEl = getInputEl()\n      if (!inputEl) return\n      const inputValue = inputEl.value\n      const matchOption = props.options.find(item => item.value === pattern)\n      const isWhole = isFunction(props.checkIsWhole)\n        ? props.checkIsWhole(pattern, prefix)\n        : matchOption\n      if (isWhole && splitIndex !== -1 && splitIndex + 1 === selectionEnd) {\n        e.preventDefault()\n        const newValue =\n          inputValue.slice(0, prefixIndex) + inputValue.slice(splitIndex + 1)\n        emit(UPDATE_MODEL_EVENT, newValue)\n\n        const newSelectionEnd = prefixIndex\n        nextTick(() => {\n          // input value is updated\n          inputEl.selectionStart = newSelectionEnd\n          inputEl.selectionEnd = newSelectionEnd\n          syncDropdownVisible()\n        })\n      }\n    }\n  }\n}\n\nconst { wrapperRef, handleFocus, handleBlur } = useFocusController(hlInputRef, {\n  afterFocus() {\n    syncAfterCursorMove()\n  },\n  beforeBlur(event) {\n    return tooltipRef.value?.isFocusInsideContent(event)\n  },\n  afterBlur() {\n    visible.value = false\n  },\n})\n\nconst handleInputMouseDown = () => {\n  syncAfterCursorMove()\n}\n\nconst handleSelect = (item: MentionOption) => {\n  if (!mentionCtx.value) return\n  const inputEl = getInputEl()\n  if (!inputEl) return\n  const inputValue = inputEl.value\n  const { split } = props\n\n  const newEndPart = inputValue.slice(mentionCtx.value.end)\n  const alreadySeparated = newEndPart.startsWith(split)\n  const newMiddlePart = `${item.value}${alreadySeparated ? '' : split}`\n\n  const newValue =\n    inputValue.slice(0, mentionCtx.value.start) + newMiddlePart + newEndPart\n\n  emit(UPDATE_MODEL_EVENT, newValue)\n  emit('select', item, mentionCtx.value.prefix)\n\n  const newSelectionEnd =\n    mentionCtx.value.start + newMiddlePart.length + (alreadySeparated ? 1 : 0)\n\n  nextTick(() => {\n    // input value is updated\n    inputEl.selectionStart = newSelectionEnd\n    inputEl.selectionEnd = newSelectionEnd\n    inputEl.focus()\n    syncDropdownVisible()\n  })\n}\n\nconst getInputEl = () =>\n  props.nativeType === 'textarea'\n    ? hlInputRef.value?.textarea\n    : hlInputRef.value?.input\n\nconst syncAfterCursorMove = () => {\n  // can't use nextTick(), get cursor position will be wrong\n  setTimeout(() => {\n    syncCursor()\n    syncDropdownVisible()\n    nextTick(() => tooltipRef.value?.updatePopper())\n  }, 0)\n}\n\nconst syncCursor = () => {\n  const inputEl = getInputEl()\n  if (!inputEl) return\n\n  const caretPosition = getCursorPosition(inputEl)\n  const inputRect = inputEl.getBoundingClientRect()\n  const elInputRect = hlInputRef.value!.$el.getBoundingClientRect()\n\n  cursorStyle.value = {\n    position: 'absolute',\n    width: 0,\n    height: `${caretPosition.height}px`,\n    left: `${caretPosition.left + inputRect.left - elInputRect.left}px`,\n    top: `${caretPosition.top + inputRect.top - elInputRect.top}px`,\n  }\n}\n\nconst syncDropdownVisible = () => {\n  const inputEl = getInputEl()\n  if (document.activeElement !== inputEl) {\n    visible.value = false\n    return\n  }\n  const { prefix, split } = props\n  mentionCtx.value = getMentionCtx(inputEl, prefix, split)\n  if (mentionCtx.value && mentionCtx.value.splitIndex === -1) {\n    visible.value = true\n    emit('search', mentionCtx.value.pattern, mentionCtx.value.prefix)\n    return\n  }\n  visible.value = false\n}\n\ndefineExpose({\n  input: hlInputRef,\n  tooltip: tooltipRef,\n})\n</script>\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;oCAmEc;AAAA,EACZ,MAAM;AACR;;;;;;;AAKA,UAAM,iBAAiB,SAAS,MAAM,KAAK,OAAO,OAAO,KAAK,UAAU,CAAC,CAAC;AAE1E,UAAM,EAAE,cAAc,aAAa,SAAS;AAE5C,UAAM,aAAa;AACnB,UAAM,aAAa;AACnB,UAAM,cAAc;AAEpB,UAAM,UAAU,IAAI,KAAK;AACzB,UAAM,cAAc;AACpB,UAAM,aAAa;AAEnB,UAAM,oBAAoB,SAAoB,MAC5C,MAAM,YAAY,MAAM,YAAY,GAAG,MAAM,iBAC/C;AAEA,UAAM,6BAA6B,SAAsB,MACvD,MAAM,YAAY,CAAC,UAAU,KAAK,IAAI,CAAC,gBAAgB,WAAW,CACpE;AAEA,UAAM,kBAAkB,SAAS,MAAM;AACrC,YAAM,EAAE,cAAc,YAAY;AAClC,UAAI,CAAC,WAAW,SAAS,CAAC;AAAc,eAAO;AAC/C,aAAO,QAAQ,OAAO,YACpB,aAAa,WAAW,MAAO,SAAS,MAAM,CAChD;AAAA,KACD;AAED,UAAM,oBAAoB,CAAC,UAAkB;AAC3C,WAAK,qBAAqB,KAAK;AAC/B;AAAoB;AAGtB,UAAM,qBAAqB,CAAC,MAA6B;AACvD,UAAI;AAAe;AACnB;AAAmC;AACnC;AACE;AAAoB,6BACC;AACrB,aAAK;AAAe;AACpB,UAAE;AACF,0BAAkB;AAClB,2BAAmB;AAAyB,yBAC3B,WAAW;AAC5B,aAAK;AAAe;AACpB,UAAE;AACF,wBAAgB;AACd,6BAAmB,kBAAkB;AAAA;AAErC;AAAgB;AAClB;AAEA,kBAAU,2BAA2B;AACnC,kBAAQ;AAER,gBAAM;AACN;AAAc;AACd;AACA,gBAAM,oBAAoB;AAC1B,gBAAM,2BAA2B,gBAC7B;AAEJ,2DAAiD,MAAM;AACrD,cAAE;AACF;AAEA,6CAAiC;AAEjC;AACA;AAEE;AACA,sBAAQ;AACR,kCAAoB;AAAA;AACrB;AACH;AACF;AACF;AAGF;AAA+E;AAE3E;AAAoB;AACtB;AAEE;AAAmD;AACrD,kBACY;AACV;AAAgB,MAClB;AAAA;AAGF;AACE;AAAoB,IACtB;AAEA,yBAAqB;AACnB;AAAuB;AACvB,sBAAgB;AAChB;AAAc;AACd;AACA;AAEA,YAAM,aAAa;AACnB,YAAM;AACN,YAAM,wBAAwB,2BAA2B;AAEzD,YAAM,gDACiC;AAEvC,+BAAyB;AACzB;AAEA,yCACa;AAEb,qBAAe;AAEb;AACA;AACA,gBAAQ;AACR,4BAAoB;AAAA;AACrB;AAGH;AAKA;AAEE,uBAAiB;AACf;AACA;AACA;AAA+C,UAC7C;AAAA;AAGN;AACE;AACA;AAAc;AAEd;AACA;AACA,YAAM;AAEN;AAAoB;AACR;AACH;AACkB,QACzB;AAA2D,8CACrB;AAAkB,MAC1D;AAAA;AAGF;AACE;AACA;AACE,gBAAQ;AACR;AAAA;AAEF;AACA;AACA,UAAI;AACF;AACA;AACA;AAAA;AAEF;AAAgB;AAGlB;AAAa,MACX;AAAO,eACE;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}