mention2.js 13 KB

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