react-mention-input 1.1.11 → 1.1.12
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/dist/MentionInput.css +290 -74
- package/dist/MentionInput.d.ts +3 -0
- package/dist/MentionInput.js +196 -29
- package/package.json +1 -1
- package/src/MentionInput.css +290 -74
- package/src/MentionInput.tsx +223 -60
package/dist/MentionInput.js
CHANGED
|
@@ -1,17 +1,58 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
11
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
12
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
13
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
14
|
+
function step(op) {
|
|
15
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
16
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
17
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
18
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
19
|
+
switch (op[0]) {
|
|
20
|
+
case 0: case 1: t = op; break;
|
|
21
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
22
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
23
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
24
|
+
default:
|
|
25
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
26
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
27
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
28
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
29
|
+
if (t[2]) _.ops.pop();
|
|
30
|
+
_.trys.pop(); continue;
|
|
31
|
+
}
|
|
32
|
+
op = body.call(thisArg, _);
|
|
33
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
34
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
35
|
+
}
|
|
36
|
+
};
|
|
1
37
|
import React, { useState, useRef } from "react";
|
|
2
38
|
import ReactDOM from "react-dom";
|
|
3
39
|
import "./MentionInput.css";
|
|
4
40
|
var MentionInput = function (_a) {
|
|
5
|
-
var _b;
|
|
6
|
-
var users = _a.users,
|
|
7
|
-
var
|
|
8
|
-
var
|
|
9
|
-
var
|
|
41
|
+
var _b, _c;
|
|
42
|
+
var users = _a.users, _d = _a.placeholder, placeholder = _d === void 0 ? "Type a message... (or drag & drop an image)" : _d, containerClassName = _a.containerClassName, inputContainerClassName = _a.inputContainerClassName, inputClassName = _a.inputClassName, sendBtnClassName = _a.sendBtnClassName, suggestionListClassName = _a.suggestionListClassName, suggestionItemClassName = _a.suggestionItemClassName, sendButtonIcon = _a.sendButtonIcon, onSendMessage = _a.onSendMessage, _e = _a.suggestionPosition, suggestionPosition = _e === void 0 ? 'bottom' : _e, onImageUpload = _a.onImageUpload;
|
|
43
|
+
var _f = useState(""), inputValue = _f[0], setInputValue = _f[1]; // Plain text
|
|
44
|
+
var _g = useState([]), suggestions = _g[0], setSuggestions = _g[1];
|
|
45
|
+
var _h = useState(false), showSuggestions = _h[0], setShowSuggestions = _h[1];
|
|
46
|
+
var _j = useState(null), selectedImage = _j[0], setSelectedImage = _j[1];
|
|
47
|
+
var _k = useState(null), imageUrl = _k[0], setImageUrl = _k[1];
|
|
48
|
+
var _l = useState(false), isUploading = _l[0], setIsUploading = _l[1];
|
|
49
|
+
var _m = useState(false), isDraggingOver = _m[0], setIsDraggingOver = _m[1];
|
|
10
50
|
var inputRef = useRef(null);
|
|
11
51
|
var suggestionListRef = useRef(null);
|
|
12
52
|
var caretOffsetRef = useRef(0);
|
|
13
53
|
var userSelectListRef = useRef([]); // Only unique names
|
|
14
54
|
var userSelectListWithIdsRef = useRef([]); // Unique IDs with names
|
|
55
|
+
var fileInputRef = useRef(null);
|
|
15
56
|
var highlightMentionsAndLinks = function (text) {
|
|
16
57
|
// Regular expression for detecting links
|
|
17
58
|
var linkRegex = /(https?:\/\/[^\s]+)/g;
|
|
@@ -20,7 +61,7 @@ var MentionInput = function (_a) {
|
|
|
20
61
|
// Highlight mentions manually based on `userSelectListRef`
|
|
21
62
|
userSelectListRef === null || userSelectListRef === void 0 ? void 0 : userSelectListRef.current.forEach(function (userName) {
|
|
22
63
|
var mentionPattern = new RegExp("@".concat(userName, "(\\s|$)"), "g");
|
|
23
|
-
highlightedText = highlightedText.replace(mentionPattern, function (match
|
|
64
|
+
highlightedText = highlightedText.replace(mentionPattern, function (match) {
|
|
24
65
|
return "<span class=\"mention-highlight\">".concat(match.trim(), "</span> ");
|
|
25
66
|
});
|
|
26
67
|
});
|
|
@@ -61,6 +102,7 @@ var MentionInput = function (_a) {
|
|
|
61
102
|
var handleInputChange = function () {
|
|
62
103
|
if (!inputRef.current)
|
|
63
104
|
return;
|
|
105
|
+
// Store current selection before modifications
|
|
64
106
|
var selection = window.getSelection();
|
|
65
107
|
var range = selection === null || selection === void 0 ? void 0 : selection.getRangeAt(0);
|
|
66
108
|
var newCaretOffset = 0;
|
|
@@ -73,22 +115,30 @@ var MentionInput = function (_a) {
|
|
|
73
115
|
caretOffsetRef.current = newCaretOffset;
|
|
74
116
|
var plainText = inputRef.current.innerText;
|
|
75
117
|
setInputValue(plainText);
|
|
118
|
+
// Process for mention suggestions
|
|
76
119
|
var mentionMatch = plainText.slice(0, newCaretOffset).match(/@(\S*)$/);
|
|
77
120
|
if (mentionMatch) {
|
|
78
121
|
var query_1 = mentionMatch[1].toLowerCase();
|
|
79
|
-
var filteredUsers = query_1 === "" ? users : users.filter(function (user) {
|
|
122
|
+
var filteredUsers = query_1 === "" ? users : users.filter(function (user) {
|
|
123
|
+
return user.name.toLowerCase().includes(query_1);
|
|
124
|
+
});
|
|
80
125
|
setSuggestions(filteredUsers);
|
|
81
126
|
setShowSuggestions(filteredUsers.length > 0);
|
|
82
127
|
}
|
|
83
128
|
else {
|
|
84
129
|
setShowSuggestions(false);
|
|
85
130
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
131
|
+
// Only apply highlighting if we have mentions or links to highlight
|
|
132
|
+
if (userSelectListRef.current.length > 0 || plainText.match(/(https?:\/\/[^\s]+)/g)) {
|
|
133
|
+
var currentHTML = inputRef.current.innerHTML;
|
|
134
|
+
var htmlWithHighlights = highlightMentionsAndLinks(plainText);
|
|
135
|
+
// Only update if the highlighted HTML is different to avoid cursor jumping
|
|
136
|
+
if (currentHTML !== htmlWithHighlights) {
|
|
137
|
+
inputRef.current.innerHTML = htmlWithHighlights;
|
|
138
|
+
// Restore cursor position after changing innerHTML
|
|
139
|
+
restoreCaretPosition(inputRef.current, newCaretOffset);
|
|
140
|
+
}
|
|
90
141
|
}
|
|
91
|
-
restoreCaretPosition(inputRef.current, newCaretOffset);
|
|
92
142
|
};
|
|
93
143
|
var renderSuggestions = function () {
|
|
94
144
|
if (!showSuggestions || !inputRef.current)
|
|
@@ -96,8 +146,8 @@ var MentionInput = function (_a) {
|
|
|
96
146
|
var getInitials = function (name) {
|
|
97
147
|
var nameParts = name.split(" ");
|
|
98
148
|
var initials = nameParts
|
|
99
|
-
.map(function (part) { var _a; return ((_a = part[0]) === null || _a === void 0 ? void 0 : _a.toUpperCase()) || ""; })
|
|
100
|
-
.slice(0, 2)
|
|
149
|
+
.map(function (part) { var _a; return ((_a = part[0]) === null || _a === void 0 ? void 0 : _a.toUpperCase()) || ""; })
|
|
150
|
+
.slice(0, 2)
|
|
101
151
|
.join("");
|
|
102
152
|
return initials;
|
|
103
153
|
};
|
|
@@ -127,10 +177,10 @@ var MentionInput = function (_a) {
|
|
|
127
177
|
default:
|
|
128
178
|
break;
|
|
129
179
|
}
|
|
130
|
-
return ReactDOM.createPortal(React.createElement("
|
|
131
|
-
React.createElement("
|
|
132
|
-
|
|
133
|
-
|
|
180
|
+
return ReactDOM.createPortal(React.createElement("div", { className: "suggestion-container ".concat(suggestionListClassName || ''), style: styles },
|
|
181
|
+
React.createElement("ul", { className: "suggestion-list", ref: suggestionListRef }, suggestions.map(function (user) { return (React.createElement("li", { key: user.id, onClick: function () { return handleSuggestionClick(user); }, className: "suggestion-item ".concat(suggestionItemClassName || ''), role: "option", tabIndex: 0, "aria-selected": "false" },
|
|
182
|
+
React.createElement("div", { className: "user-icon" }, getInitials(user === null || user === void 0 ? void 0 : user.name)),
|
|
183
|
+
React.createElement("span", { className: "user-name" }, user.name))); }))), window.document.body);
|
|
134
184
|
};
|
|
135
185
|
var handleSuggestionClick = function (user) {
|
|
136
186
|
if (!inputRef.current)
|
|
@@ -162,15 +212,119 @@ var MentionInput = function (_a) {
|
|
|
162
212
|
var mentionEnd = mentionIndex + user.name.length + 1;
|
|
163
213
|
restoreCaretPosition(inputRef.current, mentionEnd + 1); // +1 for the space
|
|
164
214
|
};
|
|
215
|
+
var handleImageSelect = function (event) { return __awaiter(void 0, void 0, void 0, function () {
|
|
216
|
+
var files, file;
|
|
217
|
+
return __generator(this, function (_a) {
|
|
218
|
+
switch (_a.label) {
|
|
219
|
+
case 0:
|
|
220
|
+
files = Array.from(event.target.files || []);
|
|
221
|
+
if (!(files.length > 0)) return [3 /*break*/, 2];
|
|
222
|
+
file = files[0];
|
|
223
|
+
if (!file.type.startsWith('image/')) return [3 /*break*/, 2];
|
|
224
|
+
return [4 /*yield*/, uploadImage(file)];
|
|
225
|
+
case 1:
|
|
226
|
+
_a.sent();
|
|
227
|
+
_a.label = 2;
|
|
228
|
+
case 2: return [2 /*return*/];
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
}); };
|
|
232
|
+
var handleDragOver = function (e) {
|
|
233
|
+
e.preventDefault();
|
|
234
|
+
e.stopPropagation();
|
|
235
|
+
// Only set dragging if files are being dragged
|
|
236
|
+
if (e.dataTransfer.types.includes('Files')) {
|
|
237
|
+
setIsDraggingOver(true);
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
var handleDragLeave = function (e) {
|
|
241
|
+
e.preventDefault();
|
|
242
|
+
e.stopPropagation();
|
|
243
|
+
// Check if we're leaving the container, not just moving between children
|
|
244
|
+
var rect = e.currentTarget.getBoundingClientRect();
|
|
245
|
+
var x = e.clientX;
|
|
246
|
+
var y = e.clientY;
|
|
247
|
+
if (x <= rect.left ||
|
|
248
|
+
x >= rect.right ||
|
|
249
|
+
y <= rect.top ||
|
|
250
|
+
y >= rect.bottom) {
|
|
251
|
+
setIsDraggingOver(false);
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
var handleDrop = function (e) { return __awaiter(void 0, void 0, void 0, function () {
|
|
255
|
+
var files, imageFiles;
|
|
256
|
+
return __generator(this, function (_a) {
|
|
257
|
+
switch (_a.label) {
|
|
258
|
+
case 0:
|
|
259
|
+
e.preventDefault();
|
|
260
|
+
e.stopPropagation();
|
|
261
|
+
setIsDraggingOver(false);
|
|
262
|
+
files = Array.from(e.dataTransfer.files);
|
|
263
|
+
if (!(files.length > 0)) return [3 /*break*/, 2];
|
|
264
|
+
imageFiles = files.filter(function (file) { return file.type.startsWith('image/'); });
|
|
265
|
+
if (!(imageFiles.length > 0)) return [3 /*break*/, 2];
|
|
266
|
+
return [4 /*yield*/, uploadImage(imageFiles[0])];
|
|
267
|
+
case 1:
|
|
268
|
+
_a.sent();
|
|
269
|
+
_a.label = 2;
|
|
270
|
+
case 2: return [2 /*return*/];
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
}); };
|
|
274
|
+
var uploadImage = function (file) { return __awaiter(void 0, void 0, void 0, function () {
|
|
275
|
+
var url, error_1;
|
|
276
|
+
return __generator(this, function (_a) {
|
|
277
|
+
switch (_a.label) {
|
|
278
|
+
case 0:
|
|
279
|
+
if (!onImageUpload) {
|
|
280
|
+
// If no upload function provided, store the file directly
|
|
281
|
+
setSelectedImage(file);
|
|
282
|
+
setImageUrl(URL.createObjectURL(file));
|
|
283
|
+
return [2 /*return*/];
|
|
284
|
+
}
|
|
285
|
+
_a.label = 1;
|
|
286
|
+
case 1:
|
|
287
|
+
_a.trys.push([1, 3, 4, 5]);
|
|
288
|
+
setIsUploading(true);
|
|
289
|
+
return [4 /*yield*/, onImageUpload(file)];
|
|
290
|
+
case 2:
|
|
291
|
+
url = _a.sent();
|
|
292
|
+
setSelectedImage(file);
|
|
293
|
+
setImageUrl(url);
|
|
294
|
+
return [3 /*break*/, 5];
|
|
295
|
+
case 3:
|
|
296
|
+
error_1 = _a.sent();
|
|
297
|
+
console.error('Error uploading image:', error_1);
|
|
298
|
+
return [3 /*break*/, 5];
|
|
299
|
+
case 4:
|
|
300
|
+
setIsUploading(false);
|
|
301
|
+
return [7 /*endfinally*/];
|
|
302
|
+
case 5: return [2 /*return*/];
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
}); };
|
|
306
|
+
var removeImage = function () {
|
|
307
|
+
setSelectedImage(null);
|
|
308
|
+
setImageUrl(null);
|
|
309
|
+
};
|
|
165
310
|
var handleSendMessage = function () {
|
|
166
311
|
if (inputRef.current) {
|
|
167
|
-
var messageText = inputRef.current.innerText.trim();
|
|
168
|
-
var messageHTML = inputRef.current.innerHTML.trim();
|
|
169
|
-
if (messageText && onSendMessage) {
|
|
170
|
-
onSendMessage({
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
312
|
+
var messageText = inputRef.current.innerText.trim();
|
|
313
|
+
var messageHTML = inputRef.current.innerHTML.trim();
|
|
314
|
+
if ((messageText || selectedImage) && onSendMessage) {
|
|
315
|
+
onSendMessage({
|
|
316
|
+
messageText: messageText,
|
|
317
|
+
messageHTML: messageHTML,
|
|
318
|
+
userSelectListWithIds: userSelectListWithIdsRef.current,
|
|
319
|
+
userSelectListName: userSelectListRef.current,
|
|
320
|
+
images: selectedImage ? [selectedImage] : [],
|
|
321
|
+
imageUrl: imageUrl
|
|
322
|
+
});
|
|
323
|
+
setInputValue("");
|
|
324
|
+
setShowSuggestions(false);
|
|
325
|
+
inputRef.current.innerText = "";
|
|
326
|
+
setSelectedImage(null);
|
|
327
|
+
setImageUrl(null);
|
|
174
328
|
userSelectListRef.current = [];
|
|
175
329
|
userSelectListWithIdsRef.current = [];
|
|
176
330
|
}
|
|
@@ -182,11 +336,24 @@ var MentionInput = function (_a) {
|
|
|
182
336
|
handleSendMessage(); // Trigger the same function as the Send button
|
|
183
337
|
}
|
|
184
338
|
};
|
|
339
|
+
console.log(inputValue, (_b = inputRef.current) === null || _b === void 0 ? void 0 : _b.innerText.trim(), "inputValue====");
|
|
185
340
|
return (React.createElement("div", { className: "mention-container ".concat(containerClassName || "") },
|
|
186
|
-
React.createElement("div", { className: "
|
|
187
|
-
|
|
188
|
-
React.createElement("
|
|
189
|
-
|
|
341
|
+
imageUrl && selectedImage && (React.createElement("div", { className: "image-preview-card" },
|
|
342
|
+
React.createElement("img", { src: imageUrl, alt: "Preview" }),
|
|
343
|
+
React.createElement("button", { onClick: removeImage, className: "remove-image-btn", "aria-label": "Remove image" }, "\u00D7"))),
|
|
344
|
+
React.createElement("div", { className: "mention-input-container ".concat(inputContainerClassName || "", " ").concat(isDraggingOver ? 'dragging-over' : ''), onDragOver: handleDragOver, onDragLeave: handleDragLeave, onDragEnd: function () { return setIsDraggingOver(false); }, onDrop: handleDrop },
|
|
345
|
+
isDraggingOver && (React.createElement("div", { className: "drag-overlay" },
|
|
346
|
+
React.createElement("div", { className: "drag-message" },
|
|
347
|
+
React.createElement("span", null, "Drop to upload")))),
|
|
348
|
+
React.createElement("button", { onClick: function () { var _a; return (_a = fileInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, className: "attachment-button", type: "button", "aria-label": "Attach image" },
|
|
349
|
+
React.createElement("span", { className: "attachment-icon" }, "\uD83D\uDCF7")),
|
|
350
|
+
React.createElement("div", { className: "mention-input-wrapper" },
|
|
351
|
+
(!inputValue || !inputRef.current || ((_c = inputRef.current) === null || _c === void 0 ? void 0 : _c.innerText.trim()) === "") && (React.createElement("span", { className: "placeholder" }, placeholder)),
|
|
352
|
+
React.createElement("div", { ref: inputRef, contentEditable: true, suppressContentEditableWarning: true, className: "mention-input ".concat(inputClassName || ""), onInput: handleInputChange, onKeyDown: handleKeyDown, onFocus: function () { return document.execCommand('styleWithCSS', false, 'false'); } })),
|
|
353
|
+
React.createElement("button", { onClick: handleSendMessage, className: "send-button ".concat(sendBtnClassName || ""), "aria-label": "Send message" }, sendButtonIcon || "➤"),
|
|
354
|
+
React.createElement("input", { type: "file", ref: fileInputRef, accept: "image/*", onChange: handleImageSelect, style: { display: 'none' } }),
|
|
355
|
+
isUploading && (React.createElement("div", { className: "upload-loading" },
|
|
356
|
+
React.createElement("span", null, "Uploading...")))),
|
|
190
357
|
renderSuggestions()));
|
|
191
358
|
};
|
|
192
359
|
export default MentionInput;
|