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.
@@ -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, _c = _a.placeholder, placeholder = _c === void 0 ? "Type a message..." : _c, containerClassName = _a.containerClassName, inputContainerClassName = _a.inputContainerClassName, inputClassName = _a.inputClassName, sendBtnClassName = _a.sendBtnClassName, suggestionListClassName = _a.suggestionListClassName, suggestionItemClassName = _a.suggestionItemClassName, sendButtonIcon = _a.sendButtonIcon, onSendMessage = _a.onSendMessage, _d = _a.suggestionPosition, suggestionPosition = _d === void 0 ? 'bottom' : _d;
7
- var _e = useState(""), inputValue = _e[0], setInputValue = _e[1]; // Plain text
8
- var _f = useState([]), suggestions = _f[0], setSuggestions = _f[1];
9
- var _g = useState(false), showSuggestions = _g[0], setShowSuggestions = _g[1];
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, trailingSpace) {
64
+ highlightedText = highlightedText.replace(mentionPattern, function (match) {
24
65
  return "<span class=\"mention-highlight\">".concat(match.trim(), "</span>&nbsp;");
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) { return user.name.toLowerCase().startsWith(query_1); });
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
- var previousHTML = inputRef.current.innerHTML;
87
- var htmlWithHighlights = highlightMentionsAndLinks(plainText); // Updated function
88
- if (previousHTML !== htmlWithHighlights) {
89
- inputRef.current.innerHTML = htmlWithHighlights;
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()) || ""; }) // Take the first letter of each part
100
- .slice(0, 2) // Limit to 2 letters
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("ul", { className: "suggestion-list ".concat(suggestionListClassName || ''), ref: suggestionListRef, style: styles }, 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" },
131
- React.createElement("div", { className: "user-icon" }, getInitials(user === null || user === void 0 ? void 0 : user.name)),
132
- user.name)); })), window.document.body // Render in portal
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(); // Plain text
168
- var messageHTML = inputRef.current.innerHTML.trim(); // HTML with <span> highlighting
169
- if (messageText && onSendMessage) {
170
- onSendMessage({ messageText: messageText, messageHTML: messageHTML, userSelectListWithIds: userSelectListWithIdsRef.current, userSelectListName: userSelectListRef.current }); // Pass both plain text and HTML
171
- setInputValue(""); // Clear state
172
- setShowSuggestions(false); // Hide suggestions
173
- inputRef.current.innerText = ""; // Clear input field
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: "mention-input-container ".concat(inputContainerClassName || "") },
187
- (!inputValue || !inputRef.current || ((_b = inputRef.current) === null || _b === void 0 ? void 0 : _b.innerText.trim()) === "") ? (React.createElement("span", { className: "placeholder" }, placeholder)) : null,
188
- React.createElement("div", { ref: inputRef, contentEditable: true, suppressContentEditableWarning: true, className: "mention-input ".concat(inputClassName || ""), onInput: handleInputChange, onKeyDown: handleKeyDown }),
189
- React.createElement("button", { onClick: handleSendMessage, className: "send-button ".concat(sendBtnClassName || "") }, sendButtonIcon || "➤")),
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-mention-input",
3
- "version": "1.1.11",
3
+ "version": "1.1.12",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "scripts": {