Mentions.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. "use strict";
  2. var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
  3. var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
  4. Object.defineProperty(exports, "__esModule", {
  5. value: true
  6. });
  7. exports.default = void 0;
  8. var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
  9. var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread2"));
  10. var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
  11. var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
  12. var _classnames = _interopRequireDefault(require("classnames"));
  13. var _rcInput = require("rc-input");
  14. var _rcTextarea = _interopRequireDefault(require("rc-textarea"));
  15. var _toArray = _interopRequireDefault(require("rc-util/lib/Children/toArray"));
  16. var _useMergedState5 = _interopRequireDefault(require("rc-util/lib/hooks/useMergedState"));
  17. var _KeyCode = _interopRequireDefault(require("rc-util/lib/KeyCode"));
  18. var _warning = _interopRequireDefault(require("rc-util/lib/warning"));
  19. var _react = _interopRequireWildcard(require("react"));
  20. var _useEffectState = _interopRequireDefault(require("./hooks/useEffectState"));
  21. var _KeywordTrigger = _interopRequireDefault(require("./KeywordTrigger"));
  22. var _MentionsContext = _interopRequireDefault(require("./MentionsContext"));
  23. var _Option = _interopRequireDefault(require("./Option"));
  24. var _util = require("./util");
  25. var _excluded = ["prefixCls", "className", "style", "prefix", "split", "notFoundContent", "value", "defaultValue", "children", "options", "open", "allowClear", "silent", "validateSearch", "filterOption", "onChange", "onKeyDown", "onKeyUp", "onPressEnter", "onSearch", "onSelect", "onFocus", "onBlur", "transitionName", "placement", "direction", "getPopupContainer", "dropdownClassName", "rows", "visible", "onPopupScroll"],
  26. _excluded2 = ["suffix", "prefixCls", "defaultValue", "value", "allowClear", "onChange", "classNames", "className", "disabled", "onClear"];
  27. var InternalMentions = /*#__PURE__*/(0, _react.forwardRef)(function (props, ref) {
  28. var prefixCls = props.prefixCls,
  29. className = props.className,
  30. style = props.style,
  31. _props$prefix = props.prefix,
  32. prefix = _props$prefix === void 0 ? '@' : _props$prefix,
  33. _props$split = props.split,
  34. split = _props$split === void 0 ? ' ' : _props$split,
  35. _props$notFoundConten = props.notFoundContent,
  36. notFoundContent = _props$notFoundConten === void 0 ? 'Not Found' : _props$notFoundConten,
  37. value = props.value,
  38. defaultValue = props.defaultValue,
  39. children = props.children,
  40. options = props.options,
  41. open = props.open,
  42. allowClear = props.allowClear,
  43. silent = props.silent,
  44. _props$validateSearch = props.validateSearch,
  45. validateSearch = _props$validateSearch === void 0 ? _util.validateSearch : _props$validateSearch,
  46. _props$filterOption = props.filterOption,
  47. filterOption = _props$filterOption === void 0 ? _util.filterOption : _props$filterOption,
  48. onChange = props.onChange,
  49. onKeyDown = props.onKeyDown,
  50. onKeyUp = props.onKeyUp,
  51. onPressEnter = props.onPressEnter,
  52. onSearch = props.onSearch,
  53. onSelect = props.onSelect,
  54. onFocus = props.onFocus,
  55. onBlur = props.onBlur,
  56. transitionName = props.transitionName,
  57. placement = props.placement,
  58. direction = props.direction,
  59. getPopupContainer = props.getPopupContainer,
  60. dropdownClassName = props.dropdownClassName,
  61. _props$rows = props.rows,
  62. rows = _props$rows === void 0 ? 1 : _props$rows,
  63. visible = props.visible,
  64. onPopupScroll = props.onPopupScroll,
  65. restProps = (0, _objectWithoutProperties2.default)(props, _excluded);
  66. var mergedPrefix = (0, _react.useMemo)(function () {
  67. return Array.isArray(prefix) ? prefix : [prefix];
  68. }, [prefix]);
  69. // =============================== Refs ===============================
  70. var containerRef = (0, _react.useRef)(null);
  71. var textareaRef = (0, _react.useRef)(null);
  72. var measureRef = (0, _react.useRef)(null);
  73. var getTextArea = function getTextArea() {
  74. var _textareaRef$current;
  75. return (_textareaRef$current = textareaRef.current) === null || _textareaRef$current === void 0 || (_textareaRef$current = _textareaRef$current.resizableTextArea) === null || _textareaRef$current === void 0 ? void 0 : _textareaRef$current.textArea;
  76. };
  77. _react.default.useImperativeHandle(ref, function () {
  78. var _textareaRef$current4;
  79. return {
  80. focus: function focus() {
  81. var _textareaRef$current2;
  82. return (_textareaRef$current2 = textareaRef.current) === null || _textareaRef$current2 === void 0 ? void 0 : _textareaRef$current2.focus();
  83. },
  84. blur: function blur() {
  85. var _textareaRef$current3;
  86. return (_textareaRef$current3 = textareaRef.current) === null || _textareaRef$current3 === void 0 ? void 0 : _textareaRef$current3.blur();
  87. },
  88. textarea: (_textareaRef$current4 = textareaRef.current) === null || _textareaRef$current4 === void 0 || (_textareaRef$current4 = _textareaRef$current4.resizableTextArea) === null || _textareaRef$current4 === void 0 ? void 0 : _textareaRef$current4.textArea,
  89. nativeElement: containerRef.current
  90. };
  91. });
  92. // ============================== State ===============================
  93. var _useState = (0, _react.useState)(false),
  94. _useState2 = (0, _slicedToArray2.default)(_useState, 2),
  95. measuring = _useState2[0],
  96. setMeasuring = _useState2[1];
  97. var _useState3 = (0, _react.useState)(''),
  98. _useState4 = (0, _slicedToArray2.default)(_useState3, 2),
  99. measureText = _useState4[0],
  100. setMeasureText = _useState4[1];
  101. var _useState5 = (0, _react.useState)(''),
  102. _useState6 = (0, _slicedToArray2.default)(_useState5, 2),
  103. measurePrefix = _useState6[0],
  104. setMeasurePrefix = _useState6[1];
  105. var _useState7 = (0, _react.useState)(0),
  106. _useState8 = (0, _slicedToArray2.default)(_useState7, 2),
  107. measureLocation = _useState8[0],
  108. setMeasureLocation = _useState8[1];
  109. var _useState9 = (0, _react.useState)(0),
  110. _useState10 = (0, _slicedToArray2.default)(_useState9, 2),
  111. activeIndex = _useState10[0],
  112. setActiveIndex = _useState10[1];
  113. var _useState11 = (0, _react.useState)(false),
  114. _useState12 = (0, _slicedToArray2.default)(_useState11, 2),
  115. isFocus = _useState12[0],
  116. setIsFocus = _useState12[1];
  117. // ============================== Value ===============================
  118. var _useMergedState = (0, _useMergedState5.default)('', {
  119. defaultValue: defaultValue,
  120. value: value
  121. }),
  122. _useMergedState2 = (0, _slicedToArray2.default)(_useMergedState, 2),
  123. mergedValue = _useMergedState2[0],
  124. setMergedValue = _useMergedState2[1];
  125. // =============================== Open ===============================
  126. (0, _react.useEffect)(function () {
  127. // Sync measure div top with textarea for rc-trigger usage
  128. if (measuring && measureRef.current) {
  129. measureRef.current.scrollTop = getTextArea().scrollTop;
  130. }
  131. }, [measuring]);
  132. var _React$useMemo = _react.default.useMemo(function () {
  133. if (open) {
  134. if (process.env.NODE_ENV !== 'production') {
  135. (0, _warning.default)(false, '`open` of Mentions is only used for debug usage. Do not use in you production.');
  136. }
  137. for (var i = 0; i < mergedPrefix.length; i += 1) {
  138. var curPrefix = mergedPrefix[i];
  139. var index = mergedValue.lastIndexOf(curPrefix);
  140. if (index >= 0) {
  141. return [true, '', curPrefix, index];
  142. }
  143. }
  144. }
  145. return [measuring, measureText, measurePrefix, measureLocation];
  146. }, [open, measuring, mergedPrefix, mergedValue, measureText, measurePrefix, measureLocation]),
  147. _React$useMemo2 = (0, _slicedToArray2.default)(_React$useMemo, 4),
  148. mergedMeasuring = _React$useMemo2[0],
  149. mergedMeasureText = _React$useMemo2[1],
  150. mergedMeasurePrefix = _React$useMemo2[2],
  151. mergedMeasureLocation = _React$useMemo2[3];
  152. // ============================== Option ==============================
  153. var getOptions = _react.default.useCallback(function (targetMeasureText) {
  154. var list;
  155. if (options && options.length > 0) {
  156. list = options.map(function (item) {
  157. var _item$key;
  158. return (0, _objectSpread2.default)((0, _objectSpread2.default)({}, item), {}, {
  159. key: (_item$key = item === null || item === void 0 ? void 0 : item.key) !== null && _item$key !== void 0 ? _item$key : item.value
  160. });
  161. });
  162. } else {
  163. list = (0, _toArray.default)(children).map(function (_ref) {
  164. var optionProps = _ref.props,
  165. key = _ref.key;
  166. return (0, _objectSpread2.default)((0, _objectSpread2.default)({}, optionProps), {}, {
  167. label: optionProps.children,
  168. key: key || optionProps.value
  169. });
  170. });
  171. }
  172. return list.filter(function (option) {
  173. /** Return all result if `filterOption` is false. */
  174. if (filterOption === false) {
  175. return true;
  176. }
  177. return filterOption(targetMeasureText, option);
  178. });
  179. }, [children, options, filterOption]);
  180. var mergedOptions = _react.default.useMemo(function () {
  181. return getOptions(mergedMeasureText);
  182. }, [getOptions, mergedMeasureText]);
  183. // ============================= Measure ==============================
  184. // Mark that we will reset input selection to target position when user select option
  185. var onSelectionEffect = (0, _useEffectState.default)();
  186. var startMeasure = function startMeasure(nextMeasureText, nextMeasurePrefix, nextMeasureLocation) {
  187. setMeasuring(true);
  188. setMeasureText(nextMeasureText);
  189. setMeasurePrefix(nextMeasurePrefix);
  190. setMeasureLocation(nextMeasureLocation);
  191. setActiveIndex(0);
  192. };
  193. var stopMeasure = function stopMeasure(callback) {
  194. setMeasuring(false);
  195. setMeasureLocation(0);
  196. setMeasureText('');
  197. onSelectionEffect(callback);
  198. };
  199. // ============================== Change ==============================
  200. var triggerChange = function triggerChange(nextValue) {
  201. setMergedValue(nextValue);
  202. onChange === null || onChange === void 0 || onChange(nextValue);
  203. };
  204. var onInternalChange = function onInternalChange(_ref2) {
  205. var nextValue = _ref2.target.value;
  206. triggerChange(nextValue);
  207. };
  208. var selectOption = function selectOption(option) {
  209. var _getTextArea;
  210. var _option$value = option.value,
  211. mentionValue = _option$value === void 0 ? '' : _option$value;
  212. var _replaceWithMeasure = (0, _util.replaceWithMeasure)(mergedValue, {
  213. measureLocation: mergedMeasureLocation,
  214. targetText: mentionValue,
  215. prefix: mergedMeasurePrefix,
  216. selectionStart: (_getTextArea = getTextArea()) === null || _getTextArea === void 0 ? void 0 : _getTextArea.selectionStart,
  217. split: split
  218. }),
  219. text = _replaceWithMeasure.text,
  220. selectionLocation = _replaceWithMeasure.selectionLocation;
  221. triggerChange(text);
  222. stopMeasure(function () {
  223. // We need restore the selection position
  224. (0, _util.setInputSelection)(getTextArea(), selectionLocation);
  225. });
  226. onSelect === null || onSelect === void 0 || onSelect(option, mergedMeasurePrefix);
  227. };
  228. // ============================= KeyEvent =============================
  229. // Check if hit the measure keyword
  230. var onInternalKeyDown = function onInternalKeyDown(event) {
  231. var which = event.which;
  232. onKeyDown === null || onKeyDown === void 0 || onKeyDown(event);
  233. // Skip if not measuring
  234. if (!mergedMeasuring) {
  235. return;
  236. }
  237. if (which === _KeyCode.default.UP || which === _KeyCode.default.DOWN) {
  238. // Control arrow function
  239. var optionLen = mergedOptions.length;
  240. var offset = which === _KeyCode.default.UP ? -1 : 1;
  241. var newActiveIndex = (activeIndex + offset + optionLen) % optionLen;
  242. setActiveIndex(newActiveIndex);
  243. event.preventDefault();
  244. } else if (which === _KeyCode.default.ESC) {
  245. stopMeasure();
  246. } else if (which === _KeyCode.default.ENTER) {
  247. // Measure hit
  248. event.preventDefault();
  249. // loading skip
  250. if (silent) {
  251. return;
  252. }
  253. if (!mergedOptions.length) {
  254. stopMeasure();
  255. return;
  256. }
  257. var _option = mergedOptions[activeIndex];
  258. selectOption(_option);
  259. }
  260. };
  261. /**
  262. * When to start measure:
  263. * 1. When user press `prefix`
  264. * 2. When measureText !== prevMeasureText
  265. * - If measure hit
  266. * - If measuring
  267. *
  268. * When to stop measure:
  269. * 1. Selection is out of range
  270. * 2. Contains `space`
  271. * 3. ESC or select one
  272. */
  273. var onInternalKeyUp = function onInternalKeyUp(event) {
  274. var key = event.key,
  275. which = event.which;
  276. var target = event.target;
  277. var selectionStartText = (0, _util.getBeforeSelectionText)(target);
  278. var _getLastMeasureIndex = (0, _util.getLastMeasureIndex)(selectionStartText, mergedPrefix),
  279. measureIndex = _getLastMeasureIndex.location,
  280. nextMeasurePrefix = _getLastMeasureIndex.prefix;
  281. // If the client implements an onKeyUp handler, call it
  282. onKeyUp === null || onKeyUp === void 0 || onKeyUp(event);
  283. // Skip if match the white key list
  284. if ([_KeyCode.default.ESC, _KeyCode.default.UP, _KeyCode.default.DOWN, _KeyCode.default.ENTER].indexOf(which) !== -1) {
  285. return;
  286. }
  287. if (measureIndex !== -1) {
  288. var nextMeasureText = selectionStartText.slice(measureIndex + nextMeasurePrefix.length);
  289. var validateMeasure = validateSearch(nextMeasureText, split);
  290. var matchOption = !!getOptions(nextMeasureText).length;
  291. if (validateMeasure) {
  292. // adding AltGraph also fort azert keyboard
  293. if (key === nextMeasurePrefix || key === 'Shift' || which === _KeyCode.default.ALT || key === 'AltGraph' || mergedMeasuring || nextMeasureText !== mergedMeasureText && matchOption) {
  294. startMeasure(nextMeasureText, nextMeasurePrefix, measureIndex);
  295. }
  296. } else if (mergedMeasuring) {
  297. // Stop if measureText is invalidate
  298. stopMeasure();
  299. }
  300. /**
  301. * We will trigger `onSearch` to developer since they may use for async update.
  302. * If met `space` means user finished searching.
  303. */
  304. if (onSearch && validateMeasure) {
  305. onSearch(nextMeasureText, nextMeasurePrefix);
  306. }
  307. } else if (mergedMeasuring) {
  308. stopMeasure();
  309. }
  310. };
  311. var onInternalPressEnter = function onInternalPressEnter(event) {
  312. if (!mergedMeasuring && onPressEnter) {
  313. onPressEnter(event);
  314. }
  315. };
  316. // ============================ Focus Blur ============================
  317. var focusRef = (0, _react.useRef)();
  318. var onInternalFocus = function onInternalFocus(event) {
  319. window.clearTimeout(focusRef.current);
  320. if (!isFocus && event && onFocus) {
  321. onFocus(event);
  322. }
  323. setIsFocus(true);
  324. };
  325. var onInternalBlur = function onInternalBlur(event) {
  326. focusRef.current = window.setTimeout(function () {
  327. setIsFocus(false);
  328. stopMeasure();
  329. onBlur === null || onBlur === void 0 || onBlur(event);
  330. }, 0);
  331. };
  332. var onDropdownFocus = function onDropdownFocus() {
  333. onInternalFocus();
  334. };
  335. var onDropdownBlur = function onDropdownBlur() {
  336. onInternalBlur();
  337. };
  338. // ============================== Scroll ===============================
  339. var onInternalPopupScroll = function onInternalPopupScroll(event) {
  340. onPopupScroll === null || onPopupScroll === void 0 || onPopupScroll(event);
  341. };
  342. // ============================== Render ==============================
  343. return /*#__PURE__*/_react.default.createElement("div", {
  344. className: (0, _classnames.default)(prefixCls, className),
  345. style: style,
  346. ref: containerRef
  347. }, /*#__PURE__*/_react.default.createElement(_rcTextarea.default, (0, _extends2.default)({
  348. ref: textareaRef,
  349. value: mergedValue
  350. }, restProps, {
  351. rows: rows,
  352. onChange: onInternalChange,
  353. onKeyDown: onInternalKeyDown,
  354. onKeyUp: onInternalKeyUp,
  355. onPressEnter: onInternalPressEnter,
  356. onFocus: onInternalFocus,
  357. onBlur: onInternalBlur
  358. })), mergedMeasuring && /*#__PURE__*/_react.default.createElement("div", {
  359. ref: measureRef,
  360. className: "".concat(prefixCls, "-measure")
  361. }, mergedValue.slice(0, mergedMeasureLocation), /*#__PURE__*/_react.default.createElement(_MentionsContext.default.Provider, {
  362. value: {
  363. notFoundContent: notFoundContent,
  364. activeIndex: activeIndex,
  365. setActiveIndex: setActiveIndex,
  366. selectOption: selectOption,
  367. onFocus: onDropdownFocus,
  368. onBlur: onDropdownBlur,
  369. onScroll: onInternalPopupScroll
  370. }
  371. }, /*#__PURE__*/_react.default.createElement(_KeywordTrigger.default, {
  372. prefixCls: prefixCls,
  373. transitionName: transitionName,
  374. placement: placement,
  375. direction: direction,
  376. options: mergedOptions,
  377. visible: true,
  378. getPopupContainer: getPopupContainer,
  379. dropdownClassName: dropdownClassName
  380. }, /*#__PURE__*/_react.default.createElement("span", null, mergedMeasurePrefix))), mergedValue.slice(mergedMeasureLocation + mergedMeasurePrefix.length)));
  381. });
  382. var Mentions = /*#__PURE__*/(0, _react.forwardRef)(function (_ref3, ref) {
  383. var suffix = _ref3.suffix,
  384. _ref3$prefixCls = _ref3.prefixCls,
  385. prefixCls = _ref3$prefixCls === void 0 ? 'rc-mentions' : _ref3$prefixCls,
  386. defaultValue = _ref3.defaultValue,
  387. customValue = _ref3.value,
  388. allowClear = _ref3.allowClear,
  389. onChange = _ref3.onChange,
  390. classes = _ref3.classNames,
  391. className = _ref3.className,
  392. disabled = _ref3.disabled,
  393. onClear = _ref3.onClear,
  394. rest = (0, _objectWithoutProperties2.default)(_ref3, _excluded2);
  395. // =============================== Ref ================================
  396. var holderRef = (0, _react.useRef)(null);
  397. var mentionRef = (0, _react.useRef)(null);
  398. (0, _react.useImperativeHandle)(ref, function () {
  399. var _holderRef$current, _mentionRef$current;
  400. return (0, _objectSpread2.default)((0, _objectSpread2.default)({}, mentionRef.current), {}, {
  401. nativeElement: ((_holderRef$current = holderRef.current) === null || _holderRef$current === void 0 ? void 0 : _holderRef$current.nativeElement) || ((_mentionRef$current = mentionRef.current) === null || _mentionRef$current === void 0 ? void 0 : _mentionRef$current.nativeElement)
  402. });
  403. });
  404. // ============================== Value ===============================
  405. var _useMergedState3 = (0, _useMergedState5.default)('', {
  406. defaultValue: defaultValue,
  407. value: customValue
  408. }),
  409. _useMergedState4 = (0, _slicedToArray2.default)(_useMergedState3, 2),
  410. mergedValue = _useMergedState4[0],
  411. setMergedValue = _useMergedState4[1];
  412. // ============================== Change ==============================
  413. var triggerChange = function triggerChange(currentValue) {
  414. setMergedValue(currentValue);
  415. onChange === null || onChange === void 0 || onChange(currentValue);
  416. };
  417. // ============================== Reset ===============================
  418. var handleReset = function handleReset() {
  419. triggerChange('');
  420. };
  421. return /*#__PURE__*/_react.default.createElement(_rcInput.BaseInput, {
  422. suffix: suffix,
  423. prefixCls: prefixCls,
  424. value: mergedValue,
  425. allowClear: allowClear,
  426. handleReset: handleReset,
  427. className: className,
  428. classNames: classes,
  429. disabled: disabled,
  430. ref: holderRef,
  431. onClear: onClear
  432. }, /*#__PURE__*/_react.default.createElement(InternalMentions, (0, _extends2.default)({
  433. className: classes === null || classes === void 0 ? void 0 : classes.mentions,
  434. prefixCls: prefixCls,
  435. ref: mentionRef,
  436. onChange: triggerChange,
  437. disabled: disabled
  438. }, rest)));
  439. });
  440. Mentions.Option = _Option.default;
  441. var _default = exports.default = Mentions;