@lumel/mention 5.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +286 -0
- package/LICENSE +7 -0
- package/README.md +9 -0
- package/lib/Mention.d.ts +22 -0
- package/lib/MentionSuggestions/Entry/Avatar/Avatar.d.ts +9 -0
- package/lib/MentionSuggestions/Entry/DefaultEntryComponent.d.ts +3 -0
- package/lib/MentionSuggestions/Entry/Entry.d.ts +38 -0
- package/lib/MentionSuggestions/MentionSuggestions.d.ts +80 -0
- package/lib/MentionSuggestions/Popover.d.ts +10 -0
- package/lib/MentionSuggestions/__test__/MentionSuggestions.test.d.ts +1 -0
- package/lib/MentionSuggestionsPortal.d.ts +11 -0
- package/lib/__test__/Mention.test.d.ts +1 -0
- package/lib/__test__/mentionSuggestionsStrategy.test.d.ts +1 -0
- package/lib/defaultRegExp.d.ts +2 -0
- package/lib/index.cjs.js +1215 -0
- package/lib/index.d.ts +66 -0
- package/lib/index.esm.js +1198 -0
- package/lib/mentionStrategy.d.ts +3 -0
- package/lib/mentionSuggestionsStrategy.d.ts +6 -0
- package/lib/modifiers/addMention.d.ts +3 -0
- package/lib/plugin.css +8 -0
- package/lib/theme.d.ts +12 -0
- package/lib/utils/__test__/getSearchTextAt.test.d.ts +1 -0
- package/lib/utils/__test__/getTriggerForMention.test.d.ts +1 -0
- package/lib/utils/__test__/getTypeByTrigger.test.d.ts +1 -0
- package/lib/utils/decodeOffsetKey.d.ts +7 -0
- package/lib/utils/defaultSuggestionsFilter.d.ts +3 -0
- package/lib/utils/getSearchText.d.ts +4 -0
- package/lib/utils/getSearchTextAt.d.ts +9 -0
- package/lib/utils/getTriggerForMention.d.ts +8 -0
- package/lib/utils/getTypeByTrigger.d.ts +1 -0
- package/lib/utils/positionSuggestions.d.ts +13 -0
- package/lib/utils/warning.d.ts +1 -0
- package/package.json +60 -0
package/lib/index.cjs.js
ADDED
|
@@ -0,0 +1,1215 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var immutable = require('immutable');
|
|
6
|
+
var React = require('react');
|
|
7
|
+
var clsx = require('clsx');
|
|
8
|
+
var draftJs = require('draft-js');
|
|
9
|
+
var PropTypes = require('prop-types');
|
|
10
|
+
var escapeRegExp = require('lodash/escapeRegExp');
|
|
11
|
+
var once = require('lodash/once');
|
|
12
|
+
var reactPopper = require('react-popper');
|
|
13
|
+
|
|
14
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
15
|
+
|
|
16
|
+
var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
|
|
17
|
+
var clsx__default = /*#__PURE__*/_interopDefaultLegacy(clsx);
|
|
18
|
+
var PropTypes__default = /*#__PURE__*/_interopDefaultLegacy(PropTypes);
|
|
19
|
+
var escapeRegExp__default = /*#__PURE__*/_interopDefaultLegacy(escapeRegExp);
|
|
20
|
+
var once__default = /*#__PURE__*/_interopDefaultLegacy(once);
|
|
21
|
+
|
|
22
|
+
function _extends() {
|
|
23
|
+
_extends = Object.assign ? Object.assign.bind() : function (target) {
|
|
24
|
+
for (var i = 1; i < arguments.length; i++) {
|
|
25
|
+
var source = arguments[i];
|
|
26
|
+
|
|
27
|
+
for (var key in source) {
|
|
28
|
+
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
29
|
+
target[key] = source[key];
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return target;
|
|
35
|
+
};
|
|
36
|
+
return _extends.apply(this, arguments);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function _inheritsLoose(subClass, superClass) {
|
|
40
|
+
subClass.prototype = Object.create(superClass.prototype);
|
|
41
|
+
subClass.prototype.constructor = subClass;
|
|
42
|
+
|
|
43
|
+
_setPrototypeOf(subClass, superClass);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function _setPrototypeOf(o, p) {
|
|
47
|
+
_setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) {
|
|
48
|
+
o.__proto__ = p;
|
|
49
|
+
return o;
|
|
50
|
+
};
|
|
51
|
+
return _setPrototypeOf(o, p);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function _objectWithoutPropertiesLoose(source, excluded) {
|
|
55
|
+
if (source == null) return {};
|
|
56
|
+
var target = {};
|
|
57
|
+
var sourceKeys = Object.keys(source);
|
|
58
|
+
var key, i;
|
|
59
|
+
|
|
60
|
+
for (i = 0; i < sourceKeys.length; i++) {
|
|
61
|
+
key = sourceKeys[i];
|
|
62
|
+
if (excluded.indexOf(key) >= 0) continue;
|
|
63
|
+
target[key] = source[key];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return target;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function _unsupportedIterableToArray(o, minLen) {
|
|
70
|
+
if (!o) return;
|
|
71
|
+
if (typeof o === "string") return _arrayLikeToArray(o, minLen);
|
|
72
|
+
var n = Object.prototype.toString.call(o).slice(8, -1);
|
|
73
|
+
if (n === "Object" && o.constructor) n = o.constructor.name;
|
|
74
|
+
if (n === "Map" || n === "Set") return Array.from(o);
|
|
75
|
+
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function _arrayLikeToArray(arr, len) {
|
|
79
|
+
if (len == null || len > arr.length) len = arr.length;
|
|
80
|
+
|
|
81
|
+
for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
|
|
82
|
+
|
|
83
|
+
return arr2;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function _createForOfIteratorHelperLoose(o, allowArrayLike) {
|
|
87
|
+
var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"];
|
|
88
|
+
if (it) return (it = it.call(o)).next.bind(it);
|
|
89
|
+
|
|
90
|
+
if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") {
|
|
91
|
+
if (it) o = it;
|
|
92
|
+
var i = 0;
|
|
93
|
+
return function () {
|
|
94
|
+
if (i >= o.length) return {
|
|
95
|
+
done: true
|
|
96
|
+
};
|
|
97
|
+
return {
|
|
98
|
+
done: false,
|
|
99
|
+
value: o[i++]
|
|
100
|
+
};
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function MentionLink(_ref) {
|
|
108
|
+
var mention = _ref.mention,
|
|
109
|
+
children = _ref.children,
|
|
110
|
+
className = _ref.className;
|
|
111
|
+
return /*#__PURE__*/React__default["default"].createElement("a", {
|
|
112
|
+
href: mention.link,
|
|
113
|
+
className: className,
|
|
114
|
+
spellCheck: false,
|
|
115
|
+
"data-testid": "mentionLink"
|
|
116
|
+
}, children);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function MentionText(_ref2) {
|
|
120
|
+
var children = _ref2.children,
|
|
121
|
+
className = _ref2.className;
|
|
122
|
+
return /*#__PURE__*/React__default["default"].createElement("span", {
|
|
123
|
+
className: className,
|
|
124
|
+
spellCheck: false,
|
|
125
|
+
"data-testid": "mentionText"
|
|
126
|
+
}, children);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function Mention(props) {
|
|
130
|
+
var entityKey = props.entityKey,
|
|
131
|
+
_props$theme = props.theme,
|
|
132
|
+
theme = _props$theme === void 0 ? {} : _props$theme,
|
|
133
|
+
mentionComponent = props.mentionComponent,
|
|
134
|
+
children = props.children,
|
|
135
|
+
decoratedText = props.decoratedText,
|
|
136
|
+
className = props.className,
|
|
137
|
+
contentState = props.contentState;
|
|
138
|
+
var combinedClassName = clsx__default["default"](theme.mention, className);
|
|
139
|
+
var mention = contentState.getEntity(entityKey).getData().mention;
|
|
140
|
+
var Component = mentionComponent || (mention.link ? MentionLink : MentionText);
|
|
141
|
+
return /*#__PURE__*/React__default["default"].createElement(Component, {
|
|
142
|
+
entityKey: entityKey,
|
|
143
|
+
mention: mention,
|
|
144
|
+
theme: theme,
|
|
145
|
+
className: combinedClassName,
|
|
146
|
+
decoratedText: decoratedText
|
|
147
|
+
}, children);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Return tail end of the string matching trigger upto the position.
|
|
152
|
+
*/
|
|
153
|
+
function getSearchTextAt(blockText, position, triggers) {
|
|
154
|
+
var str = blockText.substr(0, position);
|
|
155
|
+
var triggerPattern = triggers.map(function (trigger) {
|
|
156
|
+
return escapeRegExp__default["default"](trigger);
|
|
157
|
+
}).join('|');
|
|
158
|
+
var TRIGGER_REGEX = new RegExp("(\\s|^)(" + triggerPattern + ")", 'g');
|
|
159
|
+
var matches = str.matchAll(TRIGGER_REGEX);
|
|
160
|
+
var triggerStartIndex = 0;
|
|
161
|
+
var valueStartIndex = 0;
|
|
162
|
+
|
|
163
|
+
for (var _iterator = _createForOfIteratorHelperLoose(matches), _step; !(_step = _iterator()).done;) {
|
|
164
|
+
var match = _step.value;
|
|
165
|
+
var spaceLen = match[1].length;
|
|
166
|
+
var matchLen = match[2].length;
|
|
167
|
+
triggerStartIndex = (match.index || 0) + spaceLen;
|
|
168
|
+
valueStartIndex = triggerStartIndex + matchLen;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
var matchingString = str.slice(valueStartIndex);
|
|
172
|
+
var end = str.length;
|
|
173
|
+
return {
|
|
174
|
+
begin: triggerStartIndex,
|
|
175
|
+
end: end,
|
|
176
|
+
matchingString: matchingString
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
var getSearchText = (function (editorState, selection, triggers) {
|
|
181
|
+
var anchorKey = selection.getAnchorKey();
|
|
182
|
+
var anchorOffset = selection.getAnchorOffset();
|
|
183
|
+
var currentContent = editorState.getCurrentContent();
|
|
184
|
+
var currentBlock = currentContent.getBlockForKey(anchorKey);
|
|
185
|
+
var blockText = currentBlock.getText();
|
|
186
|
+
return getSearchTextAt(blockText, anchorOffset, triggers);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
function getTypeByTrigger(trigger) {
|
|
190
|
+
return trigger === '@' ? 'mention' : trigger + "mention";
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function addMention(editorState, mention, mentionPrefix, mentionTrigger, entityMutability) {
|
|
194
|
+
var contentStateWithEntity = editorState.getCurrentContent().createEntity(getTypeByTrigger(mentionTrigger), entityMutability, {
|
|
195
|
+
mention: mention
|
|
196
|
+
});
|
|
197
|
+
var entityKey = contentStateWithEntity.getLastCreatedEntityKey();
|
|
198
|
+
var currentSelectionState = editorState.getSelection();
|
|
199
|
+
|
|
200
|
+
var _getSearchText = getSearchText(editorState, currentSelectionState, [mentionTrigger]),
|
|
201
|
+
begin = _getSearchText.begin,
|
|
202
|
+
end = _getSearchText.end; // get selection of the @mention search text
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
var mentionTextSelection = currentSelectionState.merge({
|
|
206
|
+
anchorOffset: begin,
|
|
207
|
+
focusOffset: end
|
|
208
|
+
});
|
|
209
|
+
var mentionReplacedContent = draftJs.Modifier.replaceText(editorState.getCurrentContent(), mentionTextSelection, "" + mentionPrefix + mention.name, editorState.getCurrentInlineStyle(), entityKey); // If the mention is inserted at the end, a space is appended right after for
|
|
210
|
+
// a smooth writing experience.
|
|
211
|
+
|
|
212
|
+
var blockKey = mentionTextSelection.getAnchorKey();
|
|
213
|
+
var blockSize = editorState.getCurrentContent().getBlockForKey(blockKey).getLength();
|
|
214
|
+
|
|
215
|
+
if (blockSize === end) {
|
|
216
|
+
mentionReplacedContent = draftJs.Modifier.insertText(mentionReplacedContent, mentionReplacedContent.getSelectionAfter(), ' ');
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
var newEditorState = draftJs.EditorState.push(editorState, mentionReplacedContent, 'insert-fragment');
|
|
220
|
+
return draftJs.EditorState.forceSelection(newEditorState, mentionReplacedContent.getSelectionAfter());
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
var decodeOffsetKey = function decodeOffsetKey(offsetKey) {
|
|
224
|
+
var _offsetKey$split = offsetKey.split('-'),
|
|
225
|
+
blockKey = _offsetKey$split[0],
|
|
226
|
+
decoratorKey = _offsetKey$split[1],
|
|
227
|
+
leafKey = _offsetKey$split[2];
|
|
228
|
+
|
|
229
|
+
return {
|
|
230
|
+
blockKey: blockKey,
|
|
231
|
+
decoratorKey: parseInt(decoratorKey, 10),
|
|
232
|
+
leafKey: parseInt(leafKey, 10)
|
|
233
|
+
};
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
var decodeOffsetKey$1 = decodeOffsetKey;
|
|
237
|
+
|
|
238
|
+
function filterUndefineds(value) {
|
|
239
|
+
return value !== undefined;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function getTriggerForMention(editorState, searches, mentionTriggers) {
|
|
243
|
+
// get the current selection
|
|
244
|
+
var selection = editorState.getSelection();
|
|
245
|
+
var anchorKey = selection.getAnchorKey();
|
|
246
|
+
var anchorOffset = selection.getAnchorOffset(); // the list should not be visible if a range is selected or the editor has no focus
|
|
247
|
+
|
|
248
|
+
if (!selection.isCollapsed() || !selection.getHasFocus()) {
|
|
249
|
+
return null;
|
|
250
|
+
} // identify the start & end positon of each search-text
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
var offsetDetails = searches.map(function (offsetKey) {
|
|
254
|
+
return decodeOffsetKey$1(offsetKey);
|
|
255
|
+
}); // a leave can be empty when it is removed due event.g. using backspace
|
|
256
|
+
// do not check leaves, use full decorated portal text
|
|
257
|
+
|
|
258
|
+
var leaves = offsetDetails.filter(function (offsetDetail) {
|
|
259
|
+
return offsetDetail.blockKey === anchorKey;
|
|
260
|
+
}).map(function (offsetDetail) {
|
|
261
|
+
return editorState.getBlockTree(offsetDetail.blockKey).getIn([offsetDetail.decoratorKey]);
|
|
262
|
+
}); // if all leaves are undefined the popover should be removed
|
|
263
|
+
|
|
264
|
+
if (leaves.every(function (leave) {
|
|
265
|
+
return leave === undefined;
|
|
266
|
+
})) {
|
|
267
|
+
return null;
|
|
268
|
+
} // Checks that the cursor is after the @ character but still somewhere in
|
|
269
|
+
// the word (search term). Setting it to allow the cursor to be left of
|
|
270
|
+
// the @ causes troubles due selection confusion.
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
var blockText = editorState.getCurrentContent().getBlockForKey(anchorKey).getText();
|
|
274
|
+
var triggerForSelectionInsideWord = leaves.filter(filterUndefineds).map(function (_ref) {
|
|
275
|
+
var start = _ref.start,
|
|
276
|
+
end = _ref.end;
|
|
277
|
+
return mentionTriggers.map(function (trigger) {
|
|
278
|
+
return (// @ is the first character
|
|
279
|
+
start === 0 && anchorOffset >= start + trigger.length && //should not trigger if the cursor is before the trigger
|
|
280
|
+
blockText.substr(0, trigger.length) === trigger && anchorOffset <= end || // @ is in the text or at the end, multi triggers
|
|
281
|
+
mentionTriggers.length > 1 && anchorOffset >= start + trigger.length && (blockText.substr(start + 1, trigger.length) === trigger || blockText.substr(start, trigger.length) === trigger) && anchorOffset <= end || // @ is in the text or at the end, single trigger
|
|
282
|
+
mentionTriggers.length === 1 && anchorOffset >= start + trigger.length && anchorOffset <= end ? trigger : undefined
|
|
283
|
+
);
|
|
284
|
+
}).filter(filterUndefineds)[0];
|
|
285
|
+
}).filter(filterUndefineds);
|
|
286
|
+
|
|
287
|
+
if (triggerForSelectionInsideWord.isEmpty()) {
|
|
288
|
+
return null;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
var _triggerForSelectionI = triggerForSelectionInsideWord.entrySeq().first(),
|
|
292
|
+
activeOffsetKey = _triggerForSelectionI[0],
|
|
293
|
+
activeTrigger = _triggerForSelectionI[1];
|
|
294
|
+
|
|
295
|
+
return {
|
|
296
|
+
activeOffsetKey: activeOffsetKey,
|
|
297
|
+
activeTrigger: activeTrigger
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
var getRelativeParent = function getRelativeParent(element) {
|
|
302
|
+
if (!element) {
|
|
303
|
+
return null;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
var position = window.getComputedStyle(element).getPropertyValue('position');
|
|
307
|
+
|
|
308
|
+
if (position !== 'static') {
|
|
309
|
+
return element;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return getRelativeParent(element.parentElement);
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
function positionSuggestions(_ref) {
|
|
316
|
+
var decoratorRect = _ref.decoratorRect,
|
|
317
|
+
popover = _ref.popover,
|
|
318
|
+
props = _ref.props;
|
|
319
|
+
var relativeParent = getRelativeParent(popover.parentElement);
|
|
320
|
+
var relativeRect;
|
|
321
|
+
|
|
322
|
+
if (relativeParent) {
|
|
323
|
+
var relativeParentRect = relativeParent.getBoundingClientRect();
|
|
324
|
+
relativeRect = {
|
|
325
|
+
scrollLeft: relativeParent.scrollLeft,
|
|
326
|
+
scrollTop: relativeParent.scrollTop,
|
|
327
|
+
left: decoratorRect.left - relativeParentRect.left,
|
|
328
|
+
top: decoratorRect.bottom - relativeParentRect.top
|
|
329
|
+
};
|
|
330
|
+
} else {
|
|
331
|
+
relativeRect = {
|
|
332
|
+
scrollTop: window.pageYOffset || document.documentElement.scrollTop,
|
|
333
|
+
scrollLeft: window.pageXOffset || document.documentElement.scrollLeft,
|
|
334
|
+
top: decoratorRect.bottom,
|
|
335
|
+
left: decoratorRect.left
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
var left = relativeRect.left + relativeRect.scrollLeft;
|
|
340
|
+
var top = relativeRect.top + relativeRect.scrollTop;
|
|
341
|
+
var transform;
|
|
342
|
+
var transition;
|
|
343
|
+
|
|
344
|
+
if (props.open) {
|
|
345
|
+
if (props.suggestions.length > 0) {
|
|
346
|
+
transform = 'scale(1)';
|
|
347
|
+
transition = 'all 0.25s cubic-bezier(.3,1.2,.2,1)';
|
|
348
|
+
} else {
|
|
349
|
+
transform = 'scale(0)';
|
|
350
|
+
transition = 'all 0.35s cubic-bezier(.3,1,.2,1)';
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
return {
|
|
355
|
+
left: left + "px",
|
|
356
|
+
top: top + "px",
|
|
357
|
+
transform: transform,
|
|
358
|
+
transformOrigin: '1em 0%',
|
|
359
|
+
transition: transition
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
var warning = once__default["default"](function (text) {
|
|
364
|
+
if (process.env.NODE_ENV === 'development') {
|
|
365
|
+
// eslint-disable-next-line no-console
|
|
366
|
+
console.warn(text);
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
function Avatar(_ref) {
|
|
371
|
+
var mention = _ref.mention,
|
|
372
|
+
_ref$theme = _ref.theme,
|
|
373
|
+
theme = _ref$theme === void 0 ? {} : _ref$theme;
|
|
374
|
+
|
|
375
|
+
if (mention.avatar) {
|
|
376
|
+
return /*#__PURE__*/React__default["default"].createElement("img", {
|
|
377
|
+
src: mention.avatar,
|
|
378
|
+
className: theme.mentionSuggestionsEntryAvatar,
|
|
379
|
+
role: "presentation"
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
return null;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
var _excluded$1 = ["mention", "theme", "isFocused", "searchValue", "selectMention"];
|
|
387
|
+
function DefaultEntryComponent(props) {
|
|
388
|
+
var mention = props.mention,
|
|
389
|
+
theme = props.theme,
|
|
390
|
+
isFocused = props.isFocused;
|
|
391
|
+
props.searchValue;
|
|
392
|
+
props.selectMention;
|
|
393
|
+
var parentProps = _objectWithoutPropertiesLoose(props, _excluded$1);
|
|
394
|
+
|
|
395
|
+
return /*#__PURE__*/React__default["default"].createElement("div", _extends({}, parentProps, {
|
|
396
|
+
"aria-selected": isFocused
|
|
397
|
+
}), /*#__PURE__*/React__default["default"].createElement(Avatar, {
|
|
398
|
+
mention: mention,
|
|
399
|
+
theme: theme
|
|
400
|
+
}), /*#__PURE__*/React__default["default"].createElement("span", {
|
|
401
|
+
className: theme == null ? void 0 : theme.mentionSuggestionsEntryText
|
|
402
|
+
}, mention.name));
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
var Entry = function Entry(_ref) {
|
|
406
|
+
var onMentionSelect = _ref.onMentionSelect,
|
|
407
|
+
mention = _ref.mention,
|
|
408
|
+
theme = _ref.theme,
|
|
409
|
+
index = _ref.index,
|
|
410
|
+
onMentionFocus = _ref.onMentionFocus,
|
|
411
|
+
isFocused = _ref.isFocused,
|
|
412
|
+
id = _ref.id,
|
|
413
|
+
searchValue = _ref.searchValue,
|
|
414
|
+
EntryComponent = _ref.entryComponent;
|
|
415
|
+
var mouseDown = React.useRef(false);
|
|
416
|
+
var ref = React.useRef(null);
|
|
417
|
+
React.useEffect(function () {
|
|
418
|
+
if (isFocused) {
|
|
419
|
+
//delay the scrolling as the popup needs some time for positioning
|
|
420
|
+
requestAnimationFrame(function () {
|
|
421
|
+
var _ref$current;
|
|
422
|
+
|
|
423
|
+
return (_ref$current = ref.current) == null ? void 0 : _ref$current.scrollIntoView({
|
|
424
|
+
behavior: 'smooth',
|
|
425
|
+
block: 'nearest'
|
|
426
|
+
});
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
}, [isFocused]);
|
|
430
|
+
React.useEffect(function () {
|
|
431
|
+
mouseDown.current = false;
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
var onMouseUp = function onMouseUp() {
|
|
435
|
+
if (mouseDown.current) {
|
|
436
|
+
onMentionSelect(mention);
|
|
437
|
+
mouseDown.current = false;
|
|
438
|
+
}
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
var onMouseDown = function onMouseDown(event) {
|
|
442
|
+
// Note: important to avoid a content edit change
|
|
443
|
+
event.preventDefault();
|
|
444
|
+
mouseDown.current = true;
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
var onMouseEnter = function onMouseEnter() {
|
|
448
|
+
onMentionFocus(index);
|
|
449
|
+
};
|
|
450
|
+
|
|
451
|
+
var className = isFocused ? theme.mentionSuggestionsEntryFocused : theme.mentionSuggestionsEntry;
|
|
452
|
+
return /*#__PURE__*/React__default["default"].createElement("div", {
|
|
453
|
+
ref: ref
|
|
454
|
+
}, /*#__PURE__*/React__default["default"].createElement(EntryComponent, {
|
|
455
|
+
className: className,
|
|
456
|
+
onMouseDown: onMouseDown,
|
|
457
|
+
onMouseUp: onMouseUp,
|
|
458
|
+
onMouseEnter: onMouseEnter,
|
|
459
|
+
role: "option",
|
|
460
|
+
id: id,
|
|
461
|
+
"aria-selected": isFocused ? 'true' : undefined,
|
|
462
|
+
theme: theme,
|
|
463
|
+
mention: mention,
|
|
464
|
+
isFocused: isFocused,
|
|
465
|
+
searchValue: searchValue,
|
|
466
|
+
selectMention: onMentionSelect
|
|
467
|
+
}));
|
|
468
|
+
};
|
|
469
|
+
|
|
470
|
+
Entry.propTypes = {
|
|
471
|
+
entryComponent: PropTypes__default["default"].any.isRequired,
|
|
472
|
+
searchValue: PropTypes__default["default"].string,
|
|
473
|
+
// eslint-disable-next-line react/no-unused-prop-types
|
|
474
|
+
onMentionSelect: PropTypes__default["default"].func
|
|
475
|
+
};
|
|
476
|
+
var Entry$1 = Entry;
|
|
477
|
+
|
|
478
|
+
function Popover(_ref) {
|
|
479
|
+
var store = _ref.store,
|
|
480
|
+
children = _ref.children,
|
|
481
|
+
theme = _ref.theme,
|
|
482
|
+
_ref$popperOptions = _ref.popperOptions,
|
|
483
|
+
popperOptions = _ref$popperOptions === void 0 ? {
|
|
484
|
+
placement: 'bottom-start'
|
|
485
|
+
} : _ref$popperOptions;
|
|
486
|
+
|
|
487
|
+
var _useState = React.useState(function () {
|
|
488
|
+
return clsx__default["default"](theme.mentionSuggestions, theme.mentionSuggestionsPopup);
|
|
489
|
+
}),
|
|
490
|
+
className = _useState[0],
|
|
491
|
+
setClassName = _useState[1];
|
|
492
|
+
|
|
493
|
+
var _useState2 = React.useState(null),
|
|
494
|
+
popperElement = _useState2[0],
|
|
495
|
+
setPopperElement = _useState2[1];
|
|
496
|
+
|
|
497
|
+
var _usePopper = reactPopper.usePopper(store.getReferenceElement(), popperElement, popperOptions),
|
|
498
|
+
styles = _usePopper.styles,
|
|
499
|
+
attributes = _usePopper.attributes;
|
|
500
|
+
|
|
501
|
+
React.useEffect(function () {
|
|
502
|
+
requestAnimationFrame(function () {
|
|
503
|
+
return setClassName(clsx__default["default"](theme.mentionSuggestions, theme.mentionSuggestionsPopup, theme.mentionSuggestionsPopupVisible));
|
|
504
|
+
});
|
|
505
|
+
}, [theme]);
|
|
506
|
+
return /*#__PURE__*/React__default["default"].createElement("div", _extends({
|
|
507
|
+
ref: setPopperElement,
|
|
508
|
+
style: styles.popper
|
|
509
|
+
}, attributes.popper, {
|
|
510
|
+
className: className,
|
|
511
|
+
role: "listbox"
|
|
512
|
+
}), children);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
var _excluded = ["entryComponent", "popoverComponent", "popperOptions", "popoverContainer", "onOpenChange", "onAddMention", "onSearchChange", "suggestions", "ariaProps", "callbacks", "theme", "store", "entityMutability", "positionSuggestions", "mentionTriggers", "mentionPrefix"];
|
|
516
|
+
var MentionSuggestions = /*#__PURE__*/function (_Component) {
|
|
517
|
+
_inheritsLoose(MentionSuggestions, _Component);
|
|
518
|
+
|
|
519
|
+
function MentionSuggestions(props) {
|
|
520
|
+
var _this;
|
|
521
|
+
|
|
522
|
+
_this = _Component.call(this, props) || this;
|
|
523
|
+
_this.state = {
|
|
524
|
+
focusedOptionIndex: 0
|
|
525
|
+
};
|
|
526
|
+
_this.key = draftJs.genKey();
|
|
527
|
+
_this.popover = void 0;
|
|
528
|
+
_this.activeOffsetKey = void 0;
|
|
529
|
+
_this.lastSearchValue = void 0;
|
|
530
|
+
_this.lastActiveTrigger = '';
|
|
531
|
+
_this.lastSelectionIsInsideWord = void 0;
|
|
532
|
+
|
|
533
|
+
_this.onEditorStateChange = function (editorState) {
|
|
534
|
+
var searches = _this.props.store.getAllSearches(); // if no search portal is active there is no need to show the popover
|
|
535
|
+
|
|
536
|
+
|
|
537
|
+
if (searches.size === 0) {
|
|
538
|
+
return editorState;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
var removeList = function removeList() {
|
|
542
|
+
_this.props.store.resetEscapedSearch();
|
|
543
|
+
|
|
544
|
+
_this.closeDropdown();
|
|
545
|
+
|
|
546
|
+
return editorState;
|
|
547
|
+
};
|
|
548
|
+
|
|
549
|
+
var triggerForMention = getTriggerForMention(editorState, searches, _this.props.mentionTriggers);
|
|
550
|
+
|
|
551
|
+
if (!triggerForMention) {
|
|
552
|
+
return removeList();
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
var lastActiveOffsetKey = _this.activeOffsetKey;
|
|
556
|
+
_this.activeOffsetKey = triggerForMention.activeOffsetKey;
|
|
557
|
+
|
|
558
|
+
_this.onSearchChange(editorState, editorState.getSelection(), _this.activeOffsetKey, lastActiveOffsetKey, triggerForMention.activeTrigger); // make sure the escaped search is reseted in the cursor since the user
|
|
559
|
+
// already switched to another mention search
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
if (!_this.props.store.isEscaped(_this.activeOffsetKey || '')) {
|
|
563
|
+
_this.props.store.resetEscapedSearch();
|
|
564
|
+
} // If none of the above triggered to close the window, it's safe to assume
|
|
565
|
+
// the dropdown should be open. This is useful when a user focuses on another
|
|
566
|
+
// input field and then comes back: the dropdown will show again.
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
if (!_this.props.open && !_this.props.store.isEscaped(_this.activeOffsetKey || '')) {
|
|
570
|
+
_this.openDropdown();
|
|
571
|
+
} // makes sure the focused index is reseted every time a new selection opens
|
|
572
|
+
// or the selection was moved to another mention search
|
|
573
|
+
|
|
574
|
+
|
|
575
|
+
if (lastActiveOffsetKey !== _this.activeOffsetKey) {
|
|
576
|
+
_this.setState({
|
|
577
|
+
focusedOptionIndex: 0
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
return editorState;
|
|
582
|
+
};
|
|
583
|
+
|
|
584
|
+
_this.onSearchChange = function (editorState, selection, activeOffsetKey, lastActiveOffsetKey, trigger) {
|
|
585
|
+
var _getSearchText = getSearchText(editorState, selection, [trigger]),
|
|
586
|
+
searchValue = _getSearchText.matchingString;
|
|
587
|
+
|
|
588
|
+
if (_this.lastActiveTrigger !== trigger || _this.lastSearchValue !== searchValue || activeOffsetKey !== lastActiveOffsetKey) {
|
|
589
|
+
_this.lastActiveTrigger = trigger;
|
|
590
|
+
_this.lastSearchValue = searchValue;
|
|
591
|
+
|
|
592
|
+
_this.props.onSearchChange({
|
|
593
|
+
trigger: trigger,
|
|
594
|
+
value: searchValue
|
|
595
|
+
}); //reset focus item if search is cahnged
|
|
596
|
+
|
|
597
|
+
|
|
598
|
+
_this.setState({
|
|
599
|
+
focusedOptionIndex: 0
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
};
|
|
603
|
+
|
|
604
|
+
_this.onDownArrow = function (keyboardEvent) {
|
|
605
|
+
keyboardEvent.preventDefault();
|
|
606
|
+
var newIndex = _this.state.focusedOptionIndex + 1;
|
|
607
|
+
|
|
608
|
+
_this.onMentionFocus(newIndex >= _this.props.suggestions.length ? 0 : newIndex);
|
|
609
|
+
};
|
|
610
|
+
|
|
611
|
+
_this.onTab = function (keyboardEvent) {
|
|
612
|
+
keyboardEvent.preventDefault();
|
|
613
|
+
|
|
614
|
+
_this.commitSelection();
|
|
615
|
+
};
|
|
616
|
+
|
|
617
|
+
_this.onUpArrow = function (keyboardEvent) {
|
|
618
|
+
keyboardEvent.preventDefault();
|
|
619
|
+
|
|
620
|
+
if (_this.props.suggestions.length > 0) {
|
|
621
|
+
var newIndex = _this.state.focusedOptionIndex - 1;
|
|
622
|
+
|
|
623
|
+
_this.onMentionFocus(newIndex < 0 ? _this.props.suggestions.length - 1 : newIndex);
|
|
624
|
+
}
|
|
625
|
+
};
|
|
626
|
+
|
|
627
|
+
_this.onEscape = function (keyboardEvent) {
|
|
628
|
+
keyboardEvent.preventDefault();
|
|
629
|
+
|
|
630
|
+
_this.props.store.escapeSearch(_this.activeOffsetKey || '');
|
|
631
|
+
|
|
632
|
+
_this.closeDropdown(); // to force a re-render of the outer component to change the aria props
|
|
633
|
+
|
|
634
|
+
|
|
635
|
+
_this.props.store.setEditorState(_this.props.store.getEditorState());
|
|
636
|
+
};
|
|
637
|
+
|
|
638
|
+
_this.onMentionSelect = function (mention) {
|
|
639
|
+
// Note: This can happen in case a user typed @xxx (invalid mention) and
|
|
640
|
+
// then hit Enter. Then the mention will be undefined.
|
|
641
|
+
if (!mention) {
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
if (_this.props.onAddMention) {
|
|
646
|
+
_this.props.onAddMention(mention);
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
_this.closeDropdown();
|
|
650
|
+
|
|
651
|
+
var newEditorState = addMention(_this.props.store.getEditorState(), mention, _this.props.mentionPrefix, _this.lastActiveTrigger || '', _this.props.entityMutability);
|
|
652
|
+
|
|
653
|
+
_this.props.store.setEditorState(newEditorState);
|
|
654
|
+
};
|
|
655
|
+
|
|
656
|
+
_this.onMentionFocus = function (index) {
|
|
657
|
+
var descendant = "mention-option-" + _this.key + "-" + index;
|
|
658
|
+
_this.props.ariaProps.ariaActiveDescendantID = descendant;
|
|
659
|
+
|
|
660
|
+
_this.setState({
|
|
661
|
+
focusedOptionIndex: index
|
|
662
|
+
}); // to force a re-render of the outer component to change the aria props
|
|
663
|
+
|
|
664
|
+
|
|
665
|
+
_this.props.store.setEditorState(_this.props.store.getEditorState());
|
|
666
|
+
};
|
|
667
|
+
|
|
668
|
+
_this.commitSelection = function () {
|
|
669
|
+
var mention = _this.props.suggestions[_this.state.focusedOptionIndex];
|
|
670
|
+
|
|
671
|
+
if (!_this.props.store.getIsOpened() || !mention) {
|
|
672
|
+
return 'not-handled';
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
_this.onMentionSelect(mention);
|
|
676
|
+
|
|
677
|
+
return 'handled';
|
|
678
|
+
};
|
|
679
|
+
|
|
680
|
+
_this.openDropdown = function () {
|
|
681
|
+
// This is a really nasty way of attaching & releasing the key related functions.
|
|
682
|
+
// It assumes that the keyFunctions object will not loose its reference and
|
|
683
|
+
// by this we can replace inner parameters spread over different modules.
|
|
684
|
+
// This better be some registering & unregistering logic. PRs are welcome :)
|
|
685
|
+
_this.props.callbacks.handleReturn = _this.commitSelection;
|
|
686
|
+
|
|
687
|
+
_this.props.callbacks.keyBindingFn = function (keyboardEvent) {
|
|
688
|
+
// arrow down
|
|
689
|
+
if (keyboardEvent.keyCode === 40) {
|
|
690
|
+
_this.onDownArrow(keyboardEvent);
|
|
691
|
+
} // arrow up
|
|
692
|
+
|
|
693
|
+
|
|
694
|
+
if (keyboardEvent.keyCode === 38) {
|
|
695
|
+
_this.onUpArrow(keyboardEvent);
|
|
696
|
+
} // escape
|
|
697
|
+
|
|
698
|
+
|
|
699
|
+
if (keyboardEvent.keyCode === 27) {
|
|
700
|
+
_this.onEscape(keyboardEvent);
|
|
701
|
+
} // tab
|
|
702
|
+
|
|
703
|
+
|
|
704
|
+
if (keyboardEvent.keyCode === 9) {
|
|
705
|
+
_this.onTab(keyboardEvent);
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
return undefined;
|
|
709
|
+
};
|
|
710
|
+
|
|
711
|
+
var descendant = "mention-option-" + _this.key + "-" + _this.state.focusedOptionIndex;
|
|
712
|
+
_this.props.ariaProps.ariaActiveDescendantID = descendant;
|
|
713
|
+
_this.props.ariaProps.ariaOwneeID = "mentions-list-" + _this.key;
|
|
714
|
+
_this.props.ariaProps.ariaHasPopup = 'true';
|
|
715
|
+
_this.props.ariaProps.ariaExpanded = true;
|
|
716
|
+
|
|
717
|
+
_this.props.onOpenChange(true);
|
|
718
|
+
};
|
|
719
|
+
|
|
720
|
+
_this.closeDropdown = function () {
|
|
721
|
+
// make sure none of these callbacks are triggered
|
|
722
|
+
_this.props.callbacks.handleReturn = undefined;
|
|
723
|
+
_this.props.callbacks.keyBindingFn = undefined;
|
|
724
|
+
_this.props.ariaProps.ariaHasPopup = 'false';
|
|
725
|
+
_this.props.ariaProps.ariaExpanded = false;
|
|
726
|
+
_this.props.ariaProps.ariaActiveDescendantID = undefined;
|
|
727
|
+
_this.props.ariaProps.ariaOwneeID = undefined;
|
|
728
|
+
|
|
729
|
+
_this.props.onOpenChange(false);
|
|
730
|
+
};
|
|
731
|
+
|
|
732
|
+
_this.props.callbacks.onChange = _this.onEditorStateChange;
|
|
733
|
+
return _this;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
var _proto = MentionSuggestions.prototype;
|
|
737
|
+
|
|
738
|
+
_proto.componentDidUpdate = function componentDidUpdate() {
|
|
739
|
+
if (this.popover) {
|
|
740
|
+
// In case the list shrinks there should be still an option focused.
|
|
741
|
+
// Note: this might run multiple times and deduct 1 until the condition is
|
|
742
|
+
// not fullfilled anymore.
|
|
743
|
+
var size = this.props.suggestions.length;
|
|
744
|
+
|
|
745
|
+
if (size > 0 && this.state.focusedOptionIndex >= size) {
|
|
746
|
+
this.setState({
|
|
747
|
+
focusedOptionIndex: size - 1
|
|
748
|
+
});
|
|
749
|
+
} // Note: this is a simple protection for the error when componentDidUpdate
|
|
750
|
+
// try to get new getPortalClientRect, but the key already was deleted by
|
|
751
|
+
// previous action. (right now, it only can happened when set the mention
|
|
752
|
+
// trigger to be multi-characters which not supported anyway!)
|
|
753
|
+
|
|
754
|
+
|
|
755
|
+
if (!this.props.store.getAllSearches().has(this.activeOffsetKey)) {
|
|
756
|
+
return;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
var decoratorRect = this.props.store.getPortalClientRect(this.activeOffsetKey);
|
|
760
|
+
var positionSuggestions$1 = this.props.positionSuggestions || positionSuggestions;
|
|
761
|
+
var newStyles = positionSuggestions$1({
|
|
762
|
+
decoratorRect: decoratorRect,
|
|
763
|
+
props: this.props,
|
|
764
|
+
popover: this.popover
|
|
765
|
+
});
|
|
766
|
+
|
|
767
|
+
for (var _i = 0, _Object$entries = Object.entries(newStyles); _i < _Object$entries.length; _i++) {
|
|
768
|
+
var _Object$entries$_i = _Object$entries[_i],
|
|
769
|
+
key = _Object$entries$_i[0],
|
|
770
|
+
value = _Object$entries$_i[1];
|
|
771
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
772
|
+
this.popover.style[key] = value;
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
};
|
|
776
|
+
|
|
777
|
+
_proto.componentWillUnmount = function componentWillUnmount() {
|
|
778
|
+
this.props.callbacks.onChange = undefined;
|
|
779
|
+
};
|
|
780
|
+
|
|
781
|
+
_proto.render = function render() {
|
|
782
|
+
var _this2 = this;
|
|
783
|
+
|
|
784
|
+
if (!this.props.open) {
|
|
785
|
+
return null;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
var _this$props = this.props,
|
|
789
|
+
entryComponent = _this$props.entryComponent,
|
|
790
|
+
popoverComponent = _this$props.popoverComponent,
|
|
791
|
+
popperOptions = _this$props.popperOptions,
|
|
792
|
+
_this$props$popoverCo = _this$props.popoverContainer,
|
|
793
|
+
PopoverContainer = _this$props$popoverCo === void 0 ? Popover : _this$props$popoverCo;
|
|
794
|
+
_this$props.onOpenChange;
|
|
795
|
+
_this$props.onAddMention;
|
|
796
|
+
_this$props.onSearchChange;
|
|
797
|
+
_this$props.suggestions;
|
|
798
|
+
_this$props.ariaProps;
|
|
799
|
+
_this$props.callbacks;
|
|
800
|
+
var _this$props$theme = _this$props.theme,
|
|
801
|
+
theme = _this$props$theme === void 0 ? {} : _this$props$theme;
|
|
802
|
+
_this$props.store;
|
|
803
|
+
_this$props.entityMutability;
|
|
804
|
+
var positionSuggestions = _this$props.positionSuggestions;
|
|
805
|
+
_this$props.mentionTriggers;
|
|
806
|
+
_this$props.mentionPrefix;
|
|
807
|
+
var elementProps = _objectWithoutPropertiesLoose(_this$props, _excluded);
|
|
808
|
+
|
|
809
|
+
if (popoverComponent || positionSuggestions) {
|
|
810
|
+
warning('The properties `popoverComponent` and `positionSuggestions` are deprecated and will be removed in @draft-js-plugins/mentions 6.0 . Use `popperOptions` instead');
|
|
811
|
+
return /*#__PURE__*/React__default["default"].cloneElement(popoverComponent || /*#__PURE__*/React__default["default"].createElement("div", null), _extends({}, elementProps, {
|
|
812
|
+
className: theme.mentionSuggestions,
|
|
813
|
+
role: 'listbox',
|
|
814
|
+
id: "mentions-list-" + this.key,
|
|
815
|
+
ref: function ref(element) {
|
|
816
|
+
_this2.popover = element;
|
|
817
|
+
}
|
|
818
|
+
}), this.props.suggestions.map(function (mention, index) {
|
|
819
|
+
return /*#__PURE__*/React__default["default"].createElement(Entry$1, {
|
|
820
|
+
key: mention.id != null ? mention.id : mention.name,
|
|
821
|
+
onMentionSelect: _this2.onMentionSelect,
|
|
822
|
+
onMentionFocus: _this2.onMentionFocus,
|
|
823
|
+
isFocused: _this2.state.focusedOptionIndex === index,
|
|
824
|
+
mention: mention,
|
|
825
|
+
index: index,
|
|
826
|
+
id: "mention-option-" + _this2.key + "-" + index,
|
|
827
|
+
theme: theme,
|
|
828
|
+
searchValue: _this2.lastSearchValue,
|
|
829
|
+
entryComponent: entryComponent || DefaultEntryComponent
|
|
830
|
+
});
|
|
831
|
+
}));
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
if (!this.props.renderEmptyPopup && this.props.suggestions.length === 0) {
|
|
835
|
+
return null;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
return /*#__PURE__*/React__default["default"].createElement(PopoverContainer, {
|
|
839
|
+
store: this.props.store,
|
|
840
|
+
popperOptions: popperOptions,
|
|
841
|
+
theme: theme
|
|
842
|
+
}, this.props.suggestions.map(function (mention, index) {
|
|
843
|
+
return /*#__PURE__*/React__default["default"].createElement(Entry$1, {
|
|
844
|
+
key: mention.id != null ? mention.id : mention.name,
|
|
845
|
+
onMentionSelect: _this2.onMentionSelect,
|
|
846
|
+
onMentionFocus: _this2.onMentionFocus,
|
|
847
|
+
isFocused: _this2.state.focusedOptionIndex === index,
|
|
848
|
+
mention: mention,
|
|
849
|
+
index: index,
|
|
850
|
+
id: "mention-option-" + _this2.key + "-" + index,
|
|
851
|
+
theme: theme,
|
|
852
|
+
searchValue: _this2.lastSearchValue,
|
|
853
|
+
entryComponent: entryComponent || DefaultEntryComponent
|
|
854
|
+
});
|
|
855
|
+
}));
|
|
856
|
+
};
|
|
857
|
+
|
|
858
|
+
return MentionSuggestions;
|
|
859
|
+
}(React.Component);
|
|
860
|
+
MentionSuggestions.propTypes = {
|
|
861
|
+
open: PropTypes__default["default"].bool.isRequired,
|
|
862
|
+
onOpenChange: PropTypes__default["default"].func.isRequired,
|
|
863
|
+
entityMutability: PropTypes__default["default"].oneOf(['SEGMENTED', 'IMMUTABLE', 'MUTABLE']),
|
|
864
|
+
entryComponent: PropTypes__default["default"].func,
|
|
865
|
+
onAddMention: PropTypes__default["default"].func,
|
|
866
|
+
suggestions: PropTypes__default["default"].array.isRequired
|
|
867
|
+
};
|
|
868
|
+
var MentionSuggestions$1 = MentionSuggestions;
|
|
869
|
+
|
|
870
|
+
var useIsomorphicLayoutEffect = typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect;
|
|
871
|
+
function MentionSuggestionsPortal(props) {
|
|
872
|
+
var searchPortal = React.useRef(); // Note: this is a workaround for an obscure issue: https://github.com/draft-js-plugins/draft-js-plugins/pull/667/files
|
|
873
|
+
// Ideally we can remove this in the future.
|
|
874
|
+
|
|
875
|
+
var searchPortalRef = function searchPortalRef(element) {
|
|
876
|
+
searchPortal.current = element;
|
|
877
|
+
props.store.setReferenceElement(element);
|
|
878
|
+
};
|
|
879
|
+
|
|
880
|
+
var updatePortalClientRect = function updatePortalClientRect(currentProps) {
|
|
881
|
+
currentProps.store.updatePortalClientRect(currentProps.offsetKey, function () {
|
|
882
|
+
return searchPortal.current.getBoundingClientRect();
|
|
883
|
+
});
|
|
884
|
+
}; // When inputting Japanese characters (or any complex alphabet which requires
|
|
885
|
+
// hitting enter to commit the characters), that action was causing a race
|
|
886
|
+
// condition when we used UNSAFE_componentWillMount. By using componentDidMount
|
|
887
|
+
// instead of UNSAFE_componentWillMount, the component will unmount unregister and
|
|
888
|
+
// then properly mount and register after. Prior to this change,
|
|
889
|
+
// UNSAFE_componentWillMount would not fire after componentWillUnmount even though it
|
|
890
|
+
// was still in the DOM, so it wasn't re-registering the offsetkey.
|
|
891
|
+
|
|
892
|
+
|
|
893
|
+
useIsomorphicLayoutEffect(function () {
|
|
894
|
+
props.store.register(props.offsetKey);
|
|
895
|
+
props.store.setIsOpened(true);
|
|
896
|
+
updatePortalClientRect(props); // trigger a re-render so the MentionSuggestions becomes active
|
|
897
|
+
|
|
898
|
+
props.store.setEditorState(props.store.getEditorState());
|
|
899
|
+
return function () {
|
|
900
|
+
props.store.unregister(props.offsetKey);
|
|
901
|
+
props.store.setIsOpened(false);
|
|
902
|
+
props.store.setReferenceElement(null);
|
|
903
|
+
};
|
|
904
|
+
}, []);
|
|
905
|
+
React.useEffect(function () {
|
|
906
|
+
updatePortalClientRect(props);
|
|
907
|
+
});
|
|
908
|
+
return /*#__PURE__*/React__default["default"].createElement("span", {
|
|
909
|
+
ref: searchPortalRef
|
|
910
|
+
}, props.children);
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
var defaultRegExp = '[' + '\\w-' + // Latin-1 Supplement (letters only) - https://en.wikipedia.org/wiki/List_of_Unicode_characters#Latin-1_Supplement
|
|
914
|
+
"\xC0-\xD6" + "\xD8-\xF6" + "\xF8-\xFF" + // Latin Extended-A (without deprecated character) - https://en.wikipedia.org/wiki/List_of_Unicode_characters#Latin_Extended-A
|
|
915
|
+
"\u0100-\u0148" + "\u014A-\u017F" + // Cyrillic symbols: \u0410-\u044F - https://en.wikipedia.org/wiki/Cyrillic_script_in_Unicode
|
|
916
|
+
"\u0410-\u044F" + // Symbols that are sometimes used in Japanese names: \u3005-\u3006
|
|
917
|
+
"\u3005-\u3006" + // hiragana (japanese): \u3040-\u309F - https://gist.github.com/ryanmcgrath/982242#file-japaneseregex-js
|
|
918
|
+
"\u3040-\u309F" + // katakana (japanese): \u30A0-\u30FF - https://gist.github.com/ryanmcgrath/982242#file-japaneseregex-js
|
|
919
|
+
"\u30A0-\u30FF" + // For an advanced explaination about Hangul see https://github.com/draft-js-plugins/draft-js-plugins/pull/480#issuecomment-254055437
|
|
920
|
+
// Hangul Jamo (korean): \u3130-\u318F - https://en.wikipedia.org/wiki/Korean_language_and_computers#Hangul_in_Unicode
|
|
921
|
+
// Hangul Syllables (korean): \uAC00-\uD7A3 - https://en.wikipedia.org/wiki/Korean_language_and_computers#Hangul_in_Unicode
|
|
922
|
+
"\u3130-\u318F" + "\uAC00-\uD7A3" + // common chinese symbols: \u4e00-\u9eff - http://stackoverflow.com/a/1366113/837709
|
|
923
|
+
// extended to \u9fa5 https://github.com/draft-js-plugins/draft-js-plugins/issues/1888
|
|
924
|
+
"\u4E00-\u9FA5" + // Arabic https://en.wikipedia.org/wiki/Arabic_(Unicode_block)
|
|
925
|
+
"\u0600-\u06FF" + // Vietnamese http://vietunicode.sourceforge.net/charset/
|
|
926
|
+
"\xC0-\u1EF9" + ']';
|
|
927
|
+
|
|
928
|
+
var defaultTheme = {
|
|
929
|
+
mention: "m6zwb4v",
|
|
930
|
+
// CSS class for suggestions component
|
|
931
|
+
mentionSuggestions: "mnw6qvm",
|
|
932
|
+
mentionSuggestionsPopup: "m1ymsnxd",
|
|
933
|
+
mentionSuggestionsPopupVisible: "m126ak5t",
|
|
934
|
+
// CSS classes for an entry in the suggestions component
|
|
935
|
+
mentionSuggestionsEntry: "mtiwdxc",
|
|
936
|
+
mentionSuggestionsEntryFocused: "myz2dw1",
|
|
937
|
+
mentionSuggestionsEntryText: "mpqdcgq",
|
|
938
|
+
mentionSuggestionsEntryAvatar: "m1mfvffo"
|
|
939
|
+
};
|
|
940
|
+
|
|
941
|
+
var findMentionEntities = function findMentionEntities(triggers) {
|
|
942
|
+
return function (contentBlock, callback, contentState) {
|
|
943
|
+
contentBlock.findEntityRanges(function (character) {
|
|
944
|
+
var entityKey = character.getEntity();
|
|
945
|
+
return entityKey !== null && triggers.some(function (trigger) {
|
|
946
|
+
return contentState.getEntity(entityKey).getType() === getTypeByTrigger(trigger);
|
|
947
|
+
});
|
|
948
|
+
}, callback);
|
|
949
|
+
};
|
|
950
|
+
};
|
|
951
|
+
|
|
952
|
+
var mentionStrategy = findMentionEntities;
|
|
953
|
+
|
|
954
|
+
var whitespaceRegEx = /\s/;
|
|
955
|
+
|
|
956
|
+
function checkForWhiteSpaceBeforeTrigger(text, index) {
|
|
957
|
+
if (index === 0) {
|
|
958
|
+
return true;
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
return whitespaceRegEx.test(text[index - 1]);
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
function findInContentBlock(regex, text, nonEntityStart, callback) {
|
|
965
|
+
var matchArr;
|
|
966
|
+
var start;
|
|
967
|
+
var prevLastIndex = regex.lastIndex; // Go through all matches in the text and return the indices to the callback
|
|
968
|
+
// Break the loop if lastIndex is not changed
|
|
969
|
+
|
|
970
|
+
while ((matchArr = regex.exec(text)) !== null) {
|
|
971
|
+
if (regex.lastIndex === prevLastIndex) {
|
|
972
|
+
break;
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
prevLastIndex = regex.lastIndex;
|
|
976
|
+
start = nonEntityStart + matchArr.index;
|
|
977
|
+
|
|
978
|
+
var _end = start + matchArr[0].length;
|
|
979
|
+
|
|
980
|
+
if (whitespaceRegEx.test(text[start])) {
|
|
981
|
+
//trim the result so that we have no whitespaces
|
|
982
|
+
start += 1;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
callback(start, _end);
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
function findInContentBlockWithWhitespace(regex, text, nonEntityStart, callback) {
|
|
990
|
+
var matchArr;
|
|
991
|
+
var start;
|
|
992
|
+
var prevLastIndex = regex.lastIndex; // Go through all matches in the text and return the indices to the callback
|
|
993
|
+
// Break the loop if lastIndex is not changed
|
|
994
|
+
|
|
995
|
+
while ((matchArr = regex.exec(text)) !== null) {
|
|
996
|
+
if (regex.lastIndex === prevLastIndex) {
|
|
997
|
+
break;
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
prevLastIndex = regex.lastIndex;
|
|
1001
|
+
start = nonEntityStart + matchArr.index;
|
|
1002
|
+
|
|
1003
|
+
var _end2 = start + matchArr[0].length; //check if whitespace support is active that the char before the trigger is a white space #1844
|
|
1004
|
+
|
|
1005
|
+
|
|
1006
|
+
if (checkForWhiteSpaceBeforeTrigger(text, matchArr.index)) {
|
|
1007
|
+
callback(start, _end2);
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
var findWithRegex = function findWithRegex(regex, contentBlock, supportWhiteSpace, callback) {
|
|
1013
|
+
var contentBlockText = contentBlock.getText(); // exclude entities, when matching
|
|
1014
|
+
|
|
1015
|
+
contentBlock.findEntityRanges(function (character) {
|
|
1016
|
+
return !character.getEntity();
|
|
1017
|
+
}, function (nonEntityStart, nonEntityEnd) {
|
|
1018
|
+
var text = contentBlockText.slice(nonEntityStart, nonEntityEnd);
|
|
1019
|
+
|
|
1020
|
+
if (supportWhiteSpace) {
|
|
1021
|
+
findInContentBlockWithWhitespace(regex, text, nonEntityStart, callback);
|
|
1022
|
+
} else {
|
|
1023
|
+
findInContentBlock(regex, text, nonEntityStart, callback);
|
|
1024
|
+
}
|
|
1025
|
+
});
|
|
1026
|
+
};
|
|
1027
|
+
|
|
1028
|
+
var mentionSuggestionsStrategy = (function (triggers, supportWhiteSpace, regExp) {
|
|
1029
|
+
var triggerPattern = "(" + triggers.map(function (trigger) {
|
|
1030
|
+
return escapeRegExp__default["default"](trigger);
|
|
1031
|
+
}).join('|') + ")";
|
|
1032
|
+
var MENTION_REGEX = supportWhiteSpace ? new RegExp(triggerPattern + "(" + regExp + "|\\s)*", 'g') : new RegExp("(\\s|^)" + triggerPattern + regExp + "*", 'g');
|
|
1033
|
+
return function (contentBlock, callback) {
|
|
1034
|
+
findWithRegex(MENTION_REGEX, contentBlock, supportWhiteSpace, callback);
|
|
1035
|
+
};
|
|
1036
|
+
});
|
|
1037
|
+
|
|
1038
|
+
// Get the first 5 suggestions that match
|
|
1039
|
+
var defaultSuggestionsFilter$1 = function defaultSuggestionsFilter(searchValue, suggestions, trigger) {
|
|
1040
|
+
var value = searchValue.toLowerCase();
|
|
1041
|
+
var triggerSuggestions = trigger && !Array.isArray(suggestions) ? suggestions[trigger] : suggestions;
|
|
1042
|
+
var filteredSuggestions = triggerSuggestions.filter(function (suggestion) {
|
|
1043
|
+
return !value || suggestion.name.toLowerCase().indexOf(value) > -1;
|
|
1044
|
+
});
|
|
1045
|
+
var length = filteredSuggestions.length < 5 ? filteredSuggestions.length : 5;
|
|
1046
|
+
return filteredSuggestions.slice(0, length);
|
|
1047
|
+
};
|
|
1048
|
+
|
|
1049
|
+
var suggestionsFilter = defaultSuggestionsFilter$1;
|
|
1050
|
+
|
|
1051
|
+
var index = (function (config) {
|
|
1052
|
+
if (config === void 0) {
|
|
1053
|
+
config = {};
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
var callbacks = {
|
|
1057
|
+
keyBindingFn: undefined,
|
|
1058
|
+
handleKeyCommand: undefined,
|
|
1059
|
+
handleReturn: undefined,
|
|
1060
|
+
onChange: undefined
|
|
1061
|
+
};
|
|
1062
|
+
var ariaProps = {
|
|
1063
|
+
ariaHasPopup: 'false',
|
|
1064
|
+
ariaExpanded: false,
|
|
1065
|
+
ariaOwneeID: undefined,
|
|
1066
|
+
ariaActiveDescendantID: undefined
|
|
1067
|
+
};
|
|
1068
|
+
var searches = immutable.Map();
|
|
1069
|
+
var escapedSearch;
|
|
1070
|
+
var clientRectFunctions = immutable.Map();
|
|
1071
|
+
var isOpened = false;
|
|
1072
|
+
var referenceElement;
|
|
1073
|
+
var store = {
|
|
1074
|
+
getEditorState: undefined,
|
|
1075
|
+
setEditorState: undefined,
|
|
1076
|
+
getPortalClientRect: function getPortalClientRect(offsetKey) {
|
|
1077
|
+
return clientRectFunctions.get(offsetKey)();
|
|
1078
|
+
},
|
|
1079
|
+
getAllSearches: function getAllSearches() {
|
|
1080
|
+
return searches;
|
|
1081
|
+
},
|
|
1082
|
+
isEscaped: function isEscaped(offsetKey) {
|
|
1083
|
+
return escapedSearch === offsetKey;
|
|
1084
|
+
},
|
|
1085
|
+
escapeSearch: function escapeSearch(offsetKey) {
|
|
1086
|
+
escapedSearch = offsetKey;
|
|
1087
|
+
},
|
|
1088
|
+
resetEscapedSearch: function resetEscapedSearch() {
|
|
1089
|
+
escapedSearch = undefined;
|
|
1090
|
+
},
|
|
1091
|
+
register: function register(offsetKey) {
|
|
1092
|
+
searches = searches.set(offsetKey, offsetKey);
|
|
1093
|
+
},
|
|
1094
|
+
updatePortalClientRect: function updatePortalClientRect(offsetKey, func) {
|
|
1095
|
+
clientRectFunctions = clientRectFunctions.set(offsetKey, func);
|
|
1096
|
+
},
|
|
1097
|
+
unregister: function unregister(offsetKey) {
|
|
1098
|
+
searches = searches["delete"](offsetKey);
|
|
1099
|
+
clientRectFunctions = clientRectFunctions["delete"](offsetKey);
|
|
1100
|
+
},
|
|
1101
|
+
getIsOpened: function getIsOpened() {
|
|
1102
|
+
return isOpened;
|
|
1103
|
+
},
|
|
1104
|
+
setIsOpened: function setIsOpened(nextIsOpened) {
|
|
1105
|
+
isOpened = nextIsOpened;
|
|
1106
|
+
},
|
|
1107
|
+
getReferenceElement: function getReferenceElement() {
|
|
1108
|
+
return referenceElement;
|
|
1109
|
+
},
|
|
1110
|
+
setReferenceElement: function setReferenceElement(element) {
|
|
1111
|
+
referenceElement = element;
|
|
1112
|
+
}
|
|
1113
|
+
}; // Styles are overwritten instead of merged as merging causes a lot of confusion.
|
|
1114
|
+
//
|
|
1115
|
+
// Why? Because when merging a developer needs to know all of the underlying
|
|
1116
|
+
// styles which needs a deep dive into the code. Merging also makes it prone to
|
|
1117
|
+
// errors when upgrading as basically every styling change would become a major
|
|
1118
|
+
// breaking change. 1px of an increased padding can break a whole layout.
|
|
1119
|
+
|
|
1120
|
+
var _config = config,
|
|
1121
|
+
_config$mentionPrefix = _config.mentionPrefix,
|
|
1122
|
+
mentionPrefix = _config$mentionPrefix === void 0 ? '' : _config$mentionPrefix,
|
|
1123
|
+
_config$theme = _config.theme,
|
|
1124
|
+
theme = _config$theme === void 0 ? defaultTheme : _config$theme,
|
|
1125
|
+
positionSuggestions = _config.positionSuggestions,
|
|
1126
|
+
mentionComponent = _config.mentionComponent,
|
|
1127
|
+
_config$mentionSugges = _config.mentionSuggestionsComponent,
|
|
1128
|
+
MentionSuggestionsComponent = _config$mentionSugges === void 0 ? MentionSuggestions$1 : _config$mentionSugges,
|
|
1129
|
+
_config$entityMutabil = _config.entityMutability,
|
|
1130
|
+
entityMutability = _config$entityMutabil === void 0 ? 'SEGMENTED' : _config$entityMutabil,
|
|
1131
|
+
_config$mentionTrigge = _config.mentionTrigger,
|
|
1132
|
+
mentionTrigger = _config$mentionTrigge === void 0 ? '@' : _config$mentionTrigge,
|
|
1133
|
+
_config$mentionRegExp = _config.mentionRegExp,
|
|
1134
|
+
mentionRegExp = _config$mentionRegExp === void 0 ? defaultRegExp : _config$mentionRegExp,
|
|
1135
|
+
_config$supportWhites = _config.supportWhitespace,
|
|
1136
|
+
supportWhitespace = _config$supportWhites === void 0 ? false : _config$supportWhites,
|
|
1137
|
+
popperOptions = _config.popperOptions;
|
|
1138
|
+
var mentionTriggers = typeof mentionTrigger === 'string' ? [mentionTrigger] : mentionTrigger;
|
|
1139
|
+
var mentionSearchProps = {
|
|
1140
|
+
ariaProps: ariaProps,
|
|
1141
|
+
callbacks: callbacks,
|
|
1142
|
+
theme: theme,
|
|
1143
|
+
store: store,
|
|
1144
|
+
entityMutability: entityMutability,
|
|
1145
|
+
positionSuggestions: positionSuggestions,
|
|
1146
|
+
mentionTriggers: mentionTriggers,
|
|
1147
|
+
mentionPrefix: mentionPrefix,
|
|
1148
|
+
popperOptions: popperOptions
|
|
1149
|
+
};
|
|
1150
|
+
|
|
1151
|
+
var DecoratedMentionSuggestionsComponent = function DecoratedMentionSuggestionsComponent(props) {
|
|
1152
|
+
return /*#__PURE__*/React__default["default"].createElement(MentionSuggestionsComponent, _extends({}, props, mentionSearchProps));
|
|
1153
|
+
};
|
|
1154
|
+
|
|
1155
|
+
var DecoratedMention = function DecoratedMention(props) {
|
|
1156
|
+
return /*#__PURE__*/React__default["default"].createElement(Mention, _extends({}, props, {
|
|
1157
|
+
theme: theme,
|
|
1158
|
+
mentionComponent: mentionComponent
|
|
1159
|
+
}));
|
|
1160
|
+
};
|
|
1161
|
+
|
|
1162
|
+
var DecoratedMentionSuggestionsPortal = function DecoratedMentionSuggestionsPortal(props) {
|
|
1163
|
+
return /*#__PURE__*/React__default["default"].createElement(MentionSuggestionsPortal, _extends({}, props, {
|
|
1164
|
+
store: store
|
|
1165
|
+
}));
|
|
1166
|
+
};
|
|
1167
|
+
|
|
1168
|
+
return {
|
|
1169
|
+
MentionSuggestions: DecoratedMentionSuggestionsComponent,
|
|
1170
|
+
decorators: [{
|
|
1171
|
+
strategy: mentionStrategy(mentionTriggers),
|
|
1172
|
+
component: DecoratedMention
|
|
1173
|
+
}, {
|
|
1174
|
+
strategy: mentionSuggestionsStrategy(mentionTriggers, supportWhitespace, mentionRegExp),
|
|
1175
|
+
component: DecoratedMentionSuggestionsPortal
|
|
1176
|
+
}],
|
|
1177
|
+
getAccessibilityProps: function getAccessibilityProps() {
|
|
1178
|
+
return {
|
|
1179
|
+
role: 'combobox',
|
|
1180
|
+
ariaAutoComplete: 'list',
|
|
1181
|
+
ariaHasPopup: ariaProps.ariaHasPopup,
|
|
1182
|
+
ariaExpanded: ariaProps.ariaExpanded,
|
|
1183
|
+
ariaActiveDescendantID: ariaProps.ariaActiveDescendantID,
|
|
1184
|
+
ariaOwneeID: ariaProps.ariaOwneeID
|
|
1185
|
+
};
|
|
1186
|
+
},
|
|
1187
|
+
initialize: function initialize(_ref) {
|
|
1188
|
+
var getEditorState = _ref.getEditorState,
|
|
1189
|
+
setEditorState = _ref.setEditorState;
|
|
1190
|
+
store.getEditorState = getEditorState;
|
|
1191
|
+
store.setEditorState = setEditorState;
|
|
1192
|
+
},
|
|
1193
|
+
keyBindingFn: function keyBindingFn(keyboardEvent) {
|
|
1194
|
+
return callbacks.keyBindingFn && callbacks.keyBindingFn(keyboardEvent);
|
|
1195
|
+
},
|
|
1196
|
+
handleReturn: function handleReturn(keyboardEvent) {
|
|
1197
|
+
return callbacks.handleReturn && callbacks.handleReturn(keyboardEvent);
|
|
1198
|
+
},
|
|
1199
|
+
onChange: function onChange(editorState) {
|
|
1200
|
+
if (callbacks.onChange) {
|
|
1201
|
+
return callbacks.onChange(editorState);
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
return editorState;
|
|
1205
|
+
}
|
|
1206
|
+
};
|
|
1207
|
+
});
|
|
1208
|
+
var defaultSuggestionsFilter = suggestionsFilter;
|
|
1209
|
+
|
|
1210
|
+
exports.MentionSuggestions = MentionSuggestions$1;
|
|
1211
|
+
exports.Popover = Popover;
|
|
1212
|
+
exports.addMention = addMention;
|
|
1213
|
+
exports["default"] = index;
|
|
1214
|
+
exports.defaultSuggestionsFilter = defaultSuggestionsFilter;
|
|
1215
|
+
exports.defaultTheme = defaultTheme;
|