mention2.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. 'use strict';
  2. Object.defineProperty(exports, '__esModule', { value: true });
  3. var vue = require('vue');
  4. var lodashUnified = require('lodash-unified');
  5. var index$3 = require('../../input/index.js');
  6. var index$4 = require('../../tooltip/index.js');
  7. var mention = require('./mention.js');
  8. var helper = require('./helper.js');
  9. var mentionDropdown = require('./mention-dropdown2.js');
  10. var pluginVue_exportHelper = require('../../../_virtual/plugin-vue_export-helper.js');
  11. var input = require('../../input/src/input.js');
  12. var index = require('../../../hooks/use-namespace/index.js');
  13. var useFormCommonProps = require('../../form/src/hooks/use-form-common-props.js');
  14. var index$1 = require('../../../hooks/use-id/index.js');
  15. var index$2 = require('../../../hooks/use-focus-controller/index.js');
  16. var event = require('../../../constants/event.js');
  17. var aria = require('../../../constants/aria.js');
  18. var shared = require('@vue/shared');
  19. const __default__ = vue.defineComponent({
  20. name: "ElMention",
  21. inheritAttrs: false
  22. });
  23. const _sfc_main = /* @__PURE__ */ vue.defineComponent({
  24. ...__default__,
  25. props: mention.mentionProps,
  26. emits: mention.mentionEmits,
  27. setup(__props, { expose, emit }) {
  28. const props = __props;
  29. const passInputProps = vue.computed(() => lodashUnified.pick(props, Object.keys(input.inputProps)));
  30. const ns = index.useNamespace("mention");
  31. const disabled = useFormCommonProps.useFormDisabled();
  32. const contentId = index$1.useId();
  33. const elInputRef = vue.ref();
  34. const tooltipRef = vue.ref();
  35. const dropdownRef = vue.ref();
  36. const visible = vue.ref(false);
  37. const cursorStyle = vue.ref();
  38. const mentionCtx = vue.ref();
  39. const computedPlacement = vue.computed(() => props.showArrow ? props.placement : `${props.placement}-start`);
  40. const computedFallbackPlacements = vue.computed(() => props.showArrow ? ["bottom", "top"] : ["bottom-start", "top-start"]);
  41. const filteredOptions = vue.computed(() => {
  42. const { filterOption, options } = props;
  43. if (!mentionCtx.value || !filterOption)
  44. return options;
  45. return options.filter((option) => filterOption(mentionCtx.value.pattern, option));
  46. });
  47. const dropdownVisible = vue.computed(() => {
  48. return visible.value && (!!filteredOptions.value.length || props.loading);
  49. });
  50. const hoveringId = vue.computed(() => {
  51. var _a;
  52. return `${contentId.value}-${(_a = dropdownRef.value) == null ? void 0 : _a.hoveringIndex}`;
  53. });
  54. const handleInputChange = (value) => {
  55. emit(event.UPDATE_MODEL_EVENT, value);
  56. syncAfterCursorMove();
  57. };
  58. const handleInputKeyDown = (event$1) => {
  59. var _a, _b, _c, _d;
  60. if (!("code" in event$1) || ((_a = elInputRef.value) == null ? void 0 : _a.isComposing))
  61. return;
  62. switch (event$1.code) {
  63. case aria.EVENT_CODE.left:
  64. case aria.EVENT_CODE.right:
  65. syncAfterCursorMove();
  66. break;
  67. case aria.EVENT_CODE.up:
  68. case aria.EVENT_CODE.down:
  69. if (!visible.value)
  70. return;
  71. event$1.preventDefault();
  72. (_b = dropdownRef.value) == null ? void 0 : _b.navigateOptions(event$1.code === aria.EVENT_CODE.up ? "prev" : "next");
  73. break;
  74. case aria.EVENT_CODE.enter:
  75. case aria.EVENT_CODE.numpadEnter:
  76. if (!visible.value)
  77. return;
  78. event$1.preventDefault();
  79. if ((_c = dropdownRef.value) == null ? void 0 : _c.hoverOption) {
  80. (_d = dropdownRef.value) == null ? void 0 : _d.selectHoverOption();
  81. } else {
  82. visible.value = false;
  83. }
  84. break;
  85. case aria.EVENT_CODE.esc:
  86. if (!visible.value)
  87. return;
  88. event$1.preventDefault();
  89. visible.value = false;
  90. break;
  91. case aria.EVENT_CODE.backspace:
  92. if (props.whole && mentionCtx.value) {
  93. const { splitIndex, selectionEnd, pattern, prefixIndex, prefix } = mentionCtx.value;
  94. const inputEl = getInputEl();
  95. if (!inputEl)
  96. return;
  97. const inputValue = inputEl.value;
  98. const matchOption = props.options.find((item) => item.value === pattern);
  99. const isWhole = shared.isFunction(props.checkIsWhole) ? props.checkIsWhole(pattern, prefix) : matchOption;
  100. if (isWhole && splitIndex !== -1 && splitIndex + 1 === selectionEnd) {
  101. event$1.preventDefault();
  102. const newValue = inputValue.slice(0, prefixIndex) + inputValue.slice(splitIndex + 1);
  103. emit(event.UPDATE_MODEL_EVENT, newValue);
  104. const newSelectionEnd = prefixIndex;
  105. vue.nextTick(() => {
  106. inputEl.selectionStart = newSelectionEnd;
  107. inputEl.selectionEnd = newSelectionEnd;
  108. syncDropdownVisible();
  109. });
  110. }
  111. }
  112. }
  113. };
  114. const { wrapperRef } = index$2.useFocusController(elInputRef, {
  115. beforeFocus() {
  116. return disabled.value;
  117. },
  118. afterFocus() {
  119. syncAfterCursorMove();
  120. },
  121. beforeBlur(event) {
  122. var _a;
  123. return (_a = tooltipRef.value) == null ? void 0 : _a.isFocusInsideContent(event);
  124. },
  125. afterBlur() {
  126. visible.value = false;
  127. }
  128. });
  129. const handleInputMouseDown = () => {
  130. syncAfterCursorMove();
  131. };
  132. const handleSelect = (item) => {
  133. if (!mentionCtx.value)
  134. return;
  135. const inputEl = getInputEl();
  136. if (!inputEl)
  137. return;
  138. const inputValue = inputEl.value;
  139. const { split } = props;
  140. const newEndPart = inputValue.slice(mentionCtx.value.end);
  141. const alreadySeparated = newEndPart.startsWith(split);
  142. const newMiddlePart = `${item.value}${alreadySeparated ? "" : split}`;
  143. const newValue = inputValue.slice(0, mentionCtx.value.start) + newMiddlePart + newEndPart;
  144. emit(event.UPDATE_MODEL_EVENT, newValue);
  145. emit("select", item, mentionCtx.value.prefix);
  146. const newSelectionEnd = mentionCtx.value.start + newMiddlePart.length + (alreadySeparated ? 1 : 0);
  147. vue.nextTick(() => {
  148. inputEl.selectionStart = newSelectionEnd;
  149. inputEl.selectionEnd = newSelectionEnd;
  150. inputEl.focus();
  151. syncDropdownVisible();
  152. });
  153. };
  154. const getInputEl = () => {
  155. var _a, _b;
  156. return props.type === "textarea" ? (_a = elInputRef.value) == null ? void 0 : _a.textarea : (_b = elInputRef.value) == null ? void 0 : _b.input;
  157. };
  158. const syncAfterCursorMove = () => {
  159. setTimeout(() => {
  160. syncCursor();
  161. syncDropdownVisible();
  162. vue.nextTick(() => {
  163. var _a;
  164. return (_a = tooltipRef.value) == null ? void 0 : _a.updatePopper();
  165. });
  166. }, 0);
  167. };
  168. const syncCursor = () => {
  169. const inputEl = getInputEl();
  170. if (!inputEl)
  171. return;
  172. const caretPosition = helper.getCursorPosition(inputEl);
  173. const inputRect = inputEl.getBoundingClientRect();
  174. const elInputRect = elInputRef.value.$el.getBoundingClientRect();
  175. cursorStyle.value = {
  176. position: "absolute",
  177. width: 0,
  178. height: `${caretPosition.height}px`,
  179. left: `${caretPosition.left + inputRect.left - elInputRect.left}px`,
  180. top: `${caretPosition.top + inputRect.top - elInputRect.top}px`
  181. };
  182. };
  183. const syncDropdownVisible = () => {
  184. const inputEl = getInputEl();
  185. if (document.activeElement !== inputEl) {
  186. visible.value = false;
  187. return;
  188. }
  189. const { prefix, split } = props;
  190. mentionCtx.value = helper.getMentionCtx(inputEl, prefix, split);
  191. if (mentionCtx.value && mentionCtx.value.splitIndex === -1) {
  192. visible.value = true;
  193. emit("search", mentionCtx.value.pattern, mentionCtx.value.prefix);
  194. return;
  195. }
  196. visible.value = false;
  197. };
  198. expose({
  199. input: elInputRef,
  200. tooltip: tooltipRef,
  201. dropdownVisible
  202. });
  203. return (_ctx, _cache) => {
  204. return vue.openBlock(), vue.createElementBlock("div", {
  205. ref_key: "wrapperRef",
  206. ref: wrapperRef,
  207. class: vue.normalizeClass(vue.unref(ns).b())
  208. }, [
  209. vue.createVNode(vue.unref(index$3.ElInput), vue.mergeProps(vue.mergeProps(vue.unref(passInputProps), _ctx.$attrs), {
  210. ref_key: "elInputRef",
  211. ref: elInputRef,
  212. "model-value": _ctx.modelValue,
  213. disabled: vue.unref(disabled),
  214. role: vue.unref(dropdownVisible) ? "combobox" : void 0,
  215. "aria-activedescendant": vue.unref(dropdownVisible) ? vue.unref(hoveringId) || "" : void 0,
  216. "aria-controls": vue.unref(dropdownVisible) ? vue.unref(contentId) : void 0,
  217. "aria-expanded": vue.unref(dropdownVisible) || void 0,
  218. "aria-label": _ctx.ariaLabel,
  219. "aria-autocomplete": vue.unref(dropdownVisible) ? "none" : void 0,
  220. "aria-haspopup": vue.unref(dropdownVisible) ? "listbox" : void 0,
  221. onInput: handleInputChange,
  222. onKeydown: handleInputKeyDown,
  223. onMousedown: handleInputMouseDown
  224. }), vue.createSlots({
  225. _: 2
  226. }, [
  227. vue.renderList(_ctx.$slots, (_, name) => {
  228. return {
  229. name,
  230. fn: vue.withCtx((slotProps) => [
  231. vue.renderSlot(_ctx.$slots, name, vue.normalizeProps(vue.guardReactiveProps(slotProps)))
  232. ])
  233. };
  234. })
  235. ]), 1040, ["model-value", "disabled", "role", "aria-activedescendant", "aria-controls", "aria-expanded", "aria-label", "aria-autocomplete", "aria-haspopup"]),
  236. vue.createVNode(vue.unref(index$4.ElTooltip), {
  237. ref_key: "tooltipRef",
  238. ref: tooltipRef,
  239. visible: vue.unref(dropdownVisible),
  240. "popper-class": [vue.unref(ns).e("popper"), _ctx.popperClass],
  241. "popper-options": _ctx.popperOptions,
  242. placement: vue.unref(computedPlacement),
  243. "fallback-placements": vue.unref(computedFallbackPlacements),
  244. effect: "light",
  245. pure: "",
  246. offset: _ctx.offset,
  247. "show-arrow": _ctx.showArrow
  248. }, {
  249. default: vue.withCtx(() => [
  250. vue.createElementVNode("div", {
  251. style: vue.normalizeStyle(cursorStyle.value)
  252. }, null, 4)
  253. ]),
  254. content: vue.withCtx(() => {
  255. var _a;
  256. return [
  257. vue.createVNode(mentionDropdown["default"], {
  258. ref_key: "dropdownRef",
  259. ref: dropdownRef,
  260. options: vue.unref(filteredOptions),
  261. disabled: vue.unref(disabled),
  262. loading: _ctx.loading,
  263. "content-id": vue.unref(contentId),
  264. "aria-label": _ctx.ariaLabel,
  265. onSelect: handleSelect,
  266. onClick: vue.withModifiers((_a = elInputRef.value) == null ? void 0 : _a.focus, ["stop"])
  267. }, vue.createSlots({
  268. _: 2
  269. }, [
  270. vue.renderList(_ctx.$slots, (_, name) => {
  271. return {
  272. name,
  273. fn: vue.withCtx((slotProps) => [
  274. vue.renderSlot(_ctx.$slots, name, vue.normalizeProps(vue.guardReactiveProps(slotProps)))
  275. ])
  276. };
  277. })
  278. ]), 1032, ["options", "disabled", "loading", "content-id", "aria-label", "onClick"])
  279. ];
  280. }),
  281. _: 3
  282. }, 8, ["visible", "popper-class", "popper-options", "placement", "fallback-placements", "offset", "show-arrow"])
  283. ], 2);
  284. };
  285. }
  286. });
  287. var Mention = /* @__PURE__ */ pluginVue_exportHelper["default"](_sfc_main, [["__file", "mention.vue"]]);
  288. exports["default"] = Mention;
  289. //# sourceMappingURL=mention2.js.map