mention2.js 12 KB

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