react-mention-input 1.1.28 → 1.1.30

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.
@@ -18,7 +18,7 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
18
18
  }
19
19
  return to.concat(ar || Array.prototype.slice.call(from));
20
20
  };
21
- import React, { useState } from "react";
21
+ import React, { useState, memo } from "react";
22
22
  import "./ShowMessageCard.css";
23
23
  import { useProtectedImage } from "./useProtectedImage";
24
24
  export var ShowMessageCard = function (_a) {
@@ -50,8 +50,8 @@ export var ShowMessageCard = function (_a) {
50
50
  .join("");
51
51
  return initials;
52
52
  };
53
- // Component to render protected images
54
- var ProtectedImage = function (_a) {
53
+ // Component to render protected images - memoized to prevent recreation on every render
54
+ var ProtectedImage = memo(function (_a) {
55
55
  var url = _a.url, alt = _a.alt, className = _a.className, style = _a.style, containerClassName = _a.containerClassName, containerStyle = _a.containerStyle, onError = _a.onError, _b = _a.renderInContainer, renderInContainer = _b === void 0 ? true : _b;
56
56
  var displayUrl = useProtectedImage({
57
57
  url: url,
@@ -66,7 +66,7 @@ export var ShowMessageCard = function (_a) {
66
66
  return (React.createElement("div", { className: containerClassName, style: containerStyle }, imgElement));
67
67
  }
68
68
  return imgElement;
69
- };
69
+ });
70
70
  // Helper function to extract hashtags and mentions from text
71
71
  var extractTagsAndMentions = function (text) {
72
72
  // First extract from HTML with spans
@@ -7,6 +7,7 @@ interface UseProtectedImageOptions {
7
7
  * Custom hook to handle protected image URLs that require authentication tokens in headers.
8
8
  * For protected URLs, it fetches the image with auth headers and converts it to a blob URL.
9
9
  * For non-protected URLs, it returns the URL as-is.
10
+ * Uses a module-level cache to prevent blinking on re-renders.
10
11
  */
11
12
  export declare const useProtectedImage: ({ url, isProtected, getAuthHeaders, }: UseProtectedImageOptions) => string | null;
12
13
  export {};
@@ -46,41 +46,86 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
46
46
  }
47
47
  };
48
48
  import { useState, useEffect, useRef } from 'react';
49
+ // Module-level cache to persist blob URLs across component re-renders
50
+ var blobUrlCache = new Map();
51
+ var fetchingUrls = new Set();
49
52
  /**
50
53
  * Custom hook to handle protected image URLs that require authentication tokens in headers.
51
54
  * For protected URLs, it fetches the image with auth headers and converts it to a blob URL.
52
55
  * For non-protected URLs, it returns the URL as-is.
56
+ * Uses a module-level cache to prevent blinking on re-renders.
53
57
  */
54
58
  export var useProtectedImage = function (_a) {
55
59
  var url = _a.url, isProtected = _a.isProtected, getAuthHeaders = _a.getAuthHeaders;
56
- var _b = useState(null), blobUrl = _b[0], setBlobUrl = _b[1];
57
- var blobUrlRef = useRef(null);
60
+ var _b = useState(function () {
61
+ // Initialize from cache if available
62
+ return url ? blobUrlCache.get(url) || null : null;
63
+ }), blobUrl = _b[0], setBlobUrl = _b[1];
64
+ var previousUrlRef = useRef(null);
65
+ var isProtectedRef = useRef(isProtected);
66
+ var getAuthHeadersRef = useRef(getAuthHeaders);
67
+ var mountedRef = useRef(true);
68
+ // Update refs when props change (but don't trigger re-fetch)
58
69
  useEffect(function () {
59
- // Cleanup previous blob URL
60
- if (blobUrlRef.current) {
61
- URL.revokeObjectURL(blobUrlRef.current);
62
- blobUrlRef.current = null;
70
+ isProtectedRef.current = isProtected;
71
+ getAuthHeadersRef.current = getAuthHeaders;
72
+ }, [isProtected, getAuthHeaders]);
73
+ useEffect(function () {
74
+ mountedRef.current = true;
75
+ return function () {
76
+ mountedRef.current = false;
77
+ };
78
+ }, []);
79
+ useEffect(function () {
80
+ // Always check cache first and restore if needed (synchronous)
81
+ if (url && blobUrlCache.has(url) && !blobUrl) {
82
+ var cachedBlobUrl = blobUrlCache.get(url);
83
+ setBlobUrl(cachedBlobUrl);
63
84
  }
64
- setBlobUrl(null);
85
+ // If URL hasn't changed, keep using cached blob URL
86
+ if (url === previousUrlRef.current) {
87
+ return;
88
+ }
89
+ var oldUrl = previousUrlRef.current;
90
+ previousUrlRef.current = url;
65
91
  if (!url) {
92
+ setBlobUrl(null);
66
93
  return;
67
94
  }
68
95
  // Check if URL is protected
69
- var shouldUseAuth = typeof isProtected === 'boolean'
70
- ? isProtected
71
- : isProtected ? isProtected(url) : false;
72
- // If not protected, don't fetch - will use original URL
73
- if (!shouldUseAuth || !getAuthHeaders) {
96
+ var shouldUseAuth = typeof isProtectedRef.current === 'boolean'
97
+ ? isProtectedRef.current
98
+ : isProtectedRef.current ? isProtectedRef.current(url) : false;
99
+ // If not protected, use original URL
100
+ if (!shouldUseAuth || !getAuthHeadersRef.current) {
101
+ // Clear any cached blob URL for this URL
102
+ if (blobUrlCache.has(url)) {
103
+ var cachedBlobUrl = blobUrlCache.get(url);
104
+ URL.revokeObjectURL(cachedBlobUrl);
105
+ blobUrlCache.delete(url);
106
+ }
107
+ setBlobUrl(null);
108
+ return;
109
+ }
110
+ // Check cache first - if we have a cached blob URL, use it immediately
111
+ if (blobUrlCache.has(url)) {
112
+ var cachedBlobUrl = blobUrlCache.get(url);
113
+ setBlobUrl(cachedBlobUrl);
114
+ return;
115
+ }
116
+ // If already fetching this URL, don't fetch again
117
+ if (fetchingUrls.has(url)) {
74
118
  return;
75
119
  }
76
120
  // Fetch protected image with auth headers
121
+ fetchingUrls.add(url);
77
122
  var fetchProtectedImage = function () { return __awaiter(void 0, void 0, void 0, function () {
78
123
  var headers, response, blob, newBlobUrl, error_1;
79
124
  return __generator(this, function (_a) {
80
125
  switch (_a.label) {
81
126
  case 0:
82
- _a.trys.push([0, 4, , 5]);
83
- return [4 /*yield*/, Promise.resolve(getAuthHeaders())];
127
+ _a.trys.push([0, 4, 5, 6]);
128
+ return [4 /*yield*/, Promise.resolve(getAuthHeadersRef.current())];
84
129
  case 1:
85
130
  headers = _a.sent();
86
131
  return [4 /*yield*/, fetch(url, {
@@ -96,34 +141,47 @@ export var useProtectedImage = function (_a) {
96
141
  case 3:
97
142
  blob = _a.sent();
98
143
  newBlobUrl = URL.createObjectURL(blob);
99
- blobUrlRef.current = newBlobUrl;
100
- setBlobUrl(newBlobUrl);
101
- return [3 /*break*/, 5];
144
+ // Only update if URL hasn't changed during fetch and component is still mounted
145
+ if (previousUrlRef.current === url && mountedRef.current) {
146
+ // Cache the blob URL
147
+ blobUrlCache.set(url, newBlobUrl);
148
+ setBlobUrl(newBlobUrl);
149
+ }
150
+ else {
151
+ // URL changed during fetch or component unmounted, revoke this blob URL
152
+ URL.revokeObjectURL(newBlobUrl);
153
+ }
154
+ return [3 /*break*/, 6];
102
155
  case 4:
103
156
  error_1 = _a.sent();
104
157
  console.error('Error fetching protected image:', error_1);
105
- // On error, fallback to original URL (might show broken image, but won't crash)
106
- setBlobUrl(null);
107
- return [3 /*break*/, 5];
108
- case 5: return [2 /*return*/];
158
+ // On error, only clear if URL hasn't changed and component is mounted
159
+ if (previousUrlRef.current === url && mountedRef.current) {
160
+ setBlobUrl(null);
161
+ }
162
+ return [3 /*break*/, 6];
163
+ case 5:
164
+ fetchingUrls.delete(url);
165
+ return [7 /*endfinally*/];
166
+ case 6: return [2 /*return*/];
109
167
  }
110
168
  });
111
169
  }); };
112
170
  fetchProtectedImage();
113
- // Cleanup function
114
- return function () {
115
- if (blobUrlRef.current) {
116
- URL.revokeObjectURL(blobUrlRef.current);
117
- blobUrlRef.current = null;
118
- }
119
- };
120
- }, [url, isProtected, getAuthHeaders]);
171
+ }, [url]); // Only depend on url
172
+ // Cleanup: Don't revoke blob URLs on unmount - keep them in cache for reuse
173
+ // They will be cleaned up when the URL changes or when the cache is cleared
121
174
  // Return blob URL if available, otherwise return original URL (or null)
122
175
  if (!url) {
123
176
  return null;
124
177
  }
125
- var isUrlProtected = typeof isProtected === 'boolean'
126
- ? isProtected
127
- : isProtected ? isProtected(url) : false;
128
- return blobUrl || (!isUrlProtected ? url : null);
178
+ var isUrlProtected = typeof isProtectedRef.current === 'boolean'
179
+ ? isProtectedRef.current
180
+ : isProtectedRef.current ? isProtectedRef.current(url) : false;
181
+ // For protected URLs, return cached blob URL or current blobUrl state
182
+ // For non-protected URLs, return the original URL
183
+ if (isUrlProtected) {
184
+ return blobUrl || blobUrlCache.get(url) || null;
185
+ }
186
+ return url;
129
187
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-mention-input",
3
- "version": "1.1.28",
3
+ "version": "1.1.30",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "scripts": {
@@ -25,7 +25,7 @@
25
25
  "description": "A React component for input with @mention functionality.",
26
26
  "repository": {
27
27
  "type": "git",
28
- "url": "https://github.com/yourusername/react-mention-input.git"
28
+ "url": "git+https://github.com/yourusername/react-mention-input.git"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@types/node": "^22.9.0",
@@ -1,4 +1,4 @@
1
- import React, { CSSProperties, useState, ReactNode } from "react";
1
+ import React, { CSSProperties, useState, ReactNode, useMemo, memo } from "react";
2
2
  import "./ShowMessageCard.css";
3
3
  import { useProtectedImage } from "./useProtectedImage";
4
4
 
@@ -112,8 +112,8 @@ export const ShowMessageCard: React.FC<ShowMessageCardProps> = ({
112
112
  return initials;
113
113
  };
114
114
 
115
- // Component to render protected images
116
- const ProtectedImage: React.FC<{
115
+ // Component to render protected images - memoized to prevent recreation on every render
116
+ const ProtectedImage = memo<{
117
117
  url: string;
118
118
  alt: string;
119
119
  className?: string;
@@ -122,7 +122,7 @@ export const ShowMessageCard: React.FC<ShowMessageCardProps> = ({
122
122
  containerStyle?: CSSProperties;
123
123
  onError?: () => void;
124
124
  renderInContainer?: boolean;
125
- }> = ({ url, alt, className, style, containerClassName, containerStyle, onError, renderInContainer = true }) => {
125
+ }>(({ url, alt, className, style, containerClassName, containerStyle, onError, renderInContainer = true }) => {
126
126
  const displayUrl = useProtectedImage({
127
127
  url,
128
128
  isProtected: isProtectedUrl,
@@ -152,7 +152,7 @@ export const ShowMessageCard: React.FC<ShowMessageCardProps> = ({
152
152
  }
153
153
 
154
154
  return imgElement;
155
- };
155
+ });
156
156
 
157
157
  // Helper function to extract hashtags and mentions from text
158
158
  const extractTagsAndMentions = (text: string) => {
@@ -6,45 +6,97 @@ interface UseProtectedImageOptions {
6
6
  getAuthHeaders?: () => Record<string, string> | Promise<Record<string, string>>;
7
7
  }
8
8
 
9
+ // Module-level cache to persist blob URLs across component re-renders
10
+ const blobUrlCache = new Map<string, string>();
11
+ const fetchingUrls = new Set<string>();
12
+
9
13
  /**
10
14
  * Custom hook to handle protected image URLs that require authentication tokens in headers.
11
15
  * For protected URLs, it fetches the image with auth headers and converts it to a blob URL.
12
16
  * For non-protected URLs, it returns the URL as-is.
17
+ * Uses a module-level cache to prevent blinking on re-renders.
13
18
  */
14
19
  export const useProtectedImage = ({
15
20
  url,
16
21
  isProtected,
17
22
  getAuthHeaders,
18
23
  }: UseProtectedImageOptions): string | null => {
19
- const [blobUrl, setBlobUrl] = useState<string | null>(null);
20
- const blobUrlRef = useRef<string | null>(null);
24
+ const [blobUrl, setBlobUrl] = useState<string | null>(() => {
25
+ // Initialize from cache if available
26
+ return url ? blobUrlCache.get(url) || null : null;
27
+ });
28
+ const previousUrlRef = useRef<string | null>(null);
29
+ const isProtectedRef = useRef(isProtected);
30
+ const getAuthHeadersRef = useRef(getAuthHeaders);
31
+ const mountedRef = useRef(true);
32
+
33
+ // Update refs when props change (but don't trigger re-fetch)
34
+ useEffect(() => {
35
+ isProtectedRef.current = isProtected;
36
+ getAuthHeadersRef.current = getAuthHeaders;
37
+ }, [isProtected, getAuthHeaders]);
38
+
39
+ useEffect(() => {
40
+ mountedRef.current = true;
41
+ return () => {
42
+ mountedRef.current = false;
43
+ };
44
+ }, []);
21
45
 
22
46
  useEffect(() => {
23
- // Cleanup previous blob URL
24
- if (blobUrlRef.current) {
25
- URL.revokeObjectURL(blobUrlRef.current);
26
- blobUrlRef.current = null;
47
+ // Always check cache first and restore if needed (synchronous)
48
+ if (url && blobUrlCache.has(url) && !blobUrl) {
49
+ const cachedBlobUrl = blobUrlCache.get(url)!;
50
+ setBlobUrl(cachedBlobUrl);
27
51
  }
28
- setBlobUrl(null);
52
+
53
+ // If URL hasn't changed, keep using cached blob URL
54
+ if (url === previousUrlRef.current) {
55
+ return;
56
+ }
57
+
58
+ const oldUrl = previousUrlRef.current;
59
+ previousUrlRef.current = url;
29
60
 
30
61
  if (!url) {
62
+ setBlobUrl(null);
31
63
  return;
32
64
  }
33
65
 
34
66
  // Check if URL is protected
35
- const shouldUseAuth = typeof isProtected === 'boolean'
36
- ? isProtected
37
- : isProtected ? isProtected(url) : false;
67
+ const shouldUseAuth = typeof isProtectedRef.current === 'boolean'
68
+ ? isProtectedRef.current
69
+ : isProtectedRef.current ? isProtectedRef.current(url) : false;
70
+
71
+ // If not protected, use original URL
72
+ if (!shouldUseAuth || !getAuthHeadersRef.current) {
73
+ // Clear any cached blob URL for this URL
74
+ if (blobUrlCache.has(url)) {
75
+ const cachedBlobUrl = blobUrlCache.get(url)!;
76
+ URL.revokeObjectURL(cachedBlobUrl);
77
+ blobUrlCache.delete(url);
78
+ }
79
+ setBlobUrl(null);
80
+ return;
81
+ }
38
82
 
39
- // If not protected, don't fetch - will use original URL
40
- if (!shouldUseAuth || !getAuthHeaders) {
83
+ // Check cache first - if we have a cached blob URL, use it immediately
84
+ if (blobUrlCache.has(url)) {
85
+ const cachedBlobUrl = blobUrlCache.get(url)!;
86
+ setBlobUrl(cachedBlobUrl);
87
+ return;
88
+ }
89
+
90
+ // If already fetching this URL, don't fetch again
91
+ if (fetchingUrls.has(url)) {
41
92
  return;
42
93
  }
43
94
 
44
95
  // Fetch protected image with auth headers
96
+ fetchingUrls.add(url);
45
97
  const fetchProtectedImage = async () => {
46
98
  try {
47
- const headers = await Promise.resolve(getAuthHeaders());
99
+ const headers = await Promise.resolve(getAuthHeadersRef.current!());
48
100
  const response = await fetch(url, {
49
101
  method: 'GET',
50
102
  headers: {
@@ -58,34 +110,48 @@ export const useProtectedImage = ({
58
110
 
59
111
  const blob = await response.blob();
60
112
  const newBlobUrl = URL.createObjectURL(blob);
61
- blobUrlRef.current = newBlobUrl;
62
- setBlobUrl(newBlobUrl);
113
+
114
+ // Only update if URL hasn't changed during fetch and component is still mounted
115
+ if (previousUrlRef.current === url && mountedRef.current) {
116
+ // Cache the blob URL
117
+ blobUrlCache.set(url, newBlobUrl);
118
+ setBlobUrl(newBlobUrl);
119
+ } else {
120
+ // URL changed during fetch or component unmounted, revoke this blob URL
121
+ URL.revokeObjectURL(newBlobUrl);
122
+ }
63
123
  } catch (error) {
64
124
  console.error('Error fetching protected image:', error);
65
- // On error, fallback to original URL (might show broken image, but won't crash)
66
- setBlobUrl(null);
125
+ // On error, only clear if URL hasn't changed and component is mounted
126
+ if (previousUrlRef.current === url && mountedRef.current) {
127
+ setBlobUrl(null);
128
+ }
129
+ } finally {
130
+ fetchingUrls.delete(url);
67
131
  }
68
132
  };
69
133
 
70
134
  fetchProtectedImage();
135
+ }, [url]); // Only depend on url
71
136
 
72
- // Cleanup function
73
- return () => {
74
- if (blobUrlRef.current) {
75
- URL.revokeObjectURL(blobUrlRef.current);
76
- blobUrlRef.current = null;
77
- }
78
- };
79
- }, [url, isProtected, getAuthHeaders]);
137
+ // Cleanup: Don't revoke blob URLs on unmount - keep them in cache for reuse
138
+ // They will be cleaned up when the URL changes or when the cache is cleared
80
139
 
81
140
  // Return blob URL if available, otherwise return original URL (or null)
82
141
  if (!url) {
83
142
  return null;
84
143
  }
85
144
 
86
- const isUrlProtected = typeof isProtected === 'boolean'
87
- ? isProtected
88
- : isProtected ? isProtected(url) : false;
89
- return blobUrl || (!isUrlProtected ? url : null);
145
+ const isUrlProtected = typeof isProtectedRef.current === 'boolean'
146
+ ? isProtectedRef.current
147
+ : isProtectedRef.current ? isProtectedRef.current(url) : false;
148
+
149
+ // For protected URLs, return cached blob URL or current blobUrl state
150
+ // For non-protected URLs, return the original URL
151
+ if (isUrlProtected) {
152
+ return blobUrl || blobUrlCache.get(url) || null;
153
+ }
154
+
155
+ return url;
90
156
  };
91
157
 
package/demo.tsx DELETED
@@ -1,167 +0,0 @@
1
- import React, { useState } from 'react';
2
- import { MentionInput, ShowMessageCard } from './src';
3
-
4
- const Demo: React.FC = () => {
5
- const [messages, setMessages] = useState<any[]>([
6
- {
7
- id: 1,
8
- name: 'John Doe',
9
- date: '14/07/2025 04:15 PM',
10
- comment: 'Updated the status to Draft. Need <span class="mention-highlight">@team-leads</span> review before proceeding. <span class="hashtag-highlight">#urgent</span> <span class="hashtag-highlight">#review</span>',
11
- objectName: '26may_item001',
12
- revision: 'A',
13
- object_type_icon: '📄',
14
- relatedObject: 'test_jeet_july2334 (A)',
15
- },
16
- {
17
- id: 2,
18
- name: 'Mike Johnson',
19
- date: '14/07/2025 04:15 PM',
20
- comment: 'Revision A completed successfully. Ready for next phase. <span class="hashtag-highlight">#milestone</span>',
21
- objectName: '26may_item001',
22
- revision: 'A',
23
- object_type_icon: '📋',
24
- relatedObject: 'test_jeet_july2334 (A)',
25
- }
26
- ]);
27
-
28
- // Sample users for mentions
29
- const users = [
30
- { id: 1, name: 'John Doe' },
31
- { id: 2, name: 'Jane Smith' },
32
- { id: 3, name: 'Mike Johnson' },
33
- { id: 4, name: 'Sarah Wilson' },
34
- ];
35
-
36
- const handleSendMessage = (messageData: {
37
- messageText: string;
38
- messageHTML: string;
39
- userSelectListWithIds: { id: number; name: string }[];
40
- userSelectListName: string[];
41
- tags: string[];
42
- images?: File[];
43
- imageUrl?: string | null;
44
- }) => {
45
- console.log('Message Data:', messageData);
46
- console.log('Extracted Tags:', messageData.tags);
47
-
48
- // Create a new message for display
49
- const newMessage = {
50
- id: Date.now(),
51
- name: 'You',
52
- date: new Date().toLocaleTimeString(),
53
- comment: messageData.messageHTML,
54
- imageUrl: messageData.imageUrl,
55
- tags: messageData.tags,
56
- mentions: messageData.userSelectListName,
57
- objectName: 'new_item001', // You can customize this or make it dynamic
58
- revision: 'A', // You can customize this or make it dynamic
59
- object_type_icon: '✨', // You can customize this or make it dynamic
60
- relatedObject: 'test_jeet_july2334 (A)', // You can customize this or make it dynamic
61
- };
62
-
63
- setMessages([...messages, newMessage]);
64
- };
65
-
66
- return (
67
- <div style={{ maxWidth: '600px', margin: '20px auto', padding: '20px' }}>
68
- <h1>React Mention Input with Hashtag Support</h1>
69
-
70
- <div style={{ marginBottom: '20px' }}>
71
- <h3>Features:</h3>
72
- <ul>
73
- <li>Type <strong>@</strong> to mention users (e.g., @John Doe)</li>
74
- <li>Type <strong>#</strong> to create hashtags (e.g., #urgent #review #milestone)</li>
75
- <li>Links are automatically detected and highlighted</li>
76
- <li>Hashtags are extracted and returned in the tags array</li>
77
- <li><strong>Custom Object Chip</strong> rendering with objectChipRender prop</li>
78
- <li><strong>Object Type Icons</strong> for visual identification</li>
79
- </ul>
80
- </div>
81
-
82
- <div style={{ marginBottom: '20px' }}>
83
- <MentionInput
84
- users={users}
85
- placeholder="Type @ to mention and inform someone"
86
- onSendMessage={handleSendMessage}
87
- suggestionPosition="bottom"
88
- />
89
- </div>
90
-
91
- <div>
92
- <h3>Messages (Default Object Chip):</h3>
93
- <ShowMessageCard data={messages} />
94
- </div>
95
-
96
- <div style={{ marginTop: '30px' }}>
97
- <h3>Messages (Custom Object Chip):</h3>
98
- <ShowMessageCard
99
- data={messages}
100
- objectChipRender={({ objectName, revision, objectTypeIcon, item }) => (
101
- <div style={{
102
- backgroundColor: '#007bff',
103
- color: 'white',
104
- padding: '6px 12px',
105
- borderRadius: '20px',
106
- fontSize: '12px',
107
- fontWeight: '600',
108
- boxShadow: '0 2px 4px rgba(0,123,255,0.3)',
109
- display: 'flex',
110
- alignItems: 'center',
111
- gap: '4px'
112
- }}>
113
- {objectTypeIcon && <span>{objectTypeIcon}</span>}
114
- {objectName && <span>{objectName}</span>}
115
- {revision && <span style={{ opacity: 0.8 }}> v{revision}</span>}
116
- </div>
117
- )}
118
- />
119
- </div>
120
-
121
- <div style={{ marginTop: '20px', padding: '15px', backgroundColor: '#f5f5f5', borderRadius: '8px' }}>
122
- <h4>Example Usage:</h4>
123
- <p>Try typing these examples:</p>
124
- <ul>
125
- <li>"Project update complete. Ready for @John Doe review #milestone #completed"</li>
126
- <li>"Need urgent help with deployment @Jane Smith @Mike Johnson #urgent #deployment #help"</li>
127
- <li>"Meeting scheduled for tomorrow #meeting #planning"</li>
128
- </ul>
129
-
130
- <h4 style={{ marginTop: '20px' }}>objectChipRender Example:</h4>
131
- <pre style={{ backgroundColor: '#2d3748', color: '#e2e8f0', padding: '12px', borderRadius: '6px', fontSize: '12px', overflow: 'auto' }}>
132
- {`<ShowMessageCard
133
- data={messages}
134
- objectChipRender={({ objectName, revision, objectTypeIcon, item }) => (
135
- <div style={{
136
- backgroundColor: '#007bff',
137
- color: 'white',
138
- padding: '6px 12px',
139
- borderRadius: '20px',
140
- display: 'flex',
141
- alignItems: 'center',
142
- gap: '4px'
143
- }}>
144
- {objectTypeIcon && <span>{objectTypeIcon}</span>}
145
- {objectName} v{revision}
146
- </div>
147
- )}
148
- />`}
149
- </pre>
150
-
151
- <h4 style={{ marginTop: '20px' }}>Data Structure with Icon:</h4>
152
- <pre style={{ backgroundColor: '#2d3748', color: '#e2e8f0', padding: '12px', borderRadius: '6px', fontSize: '12px', overflow: 'auto' }}>
153
- {`const messageData = {
154
- name: 'John Doe',
155
- objectName: '26may_item001',
156
- revision: 'A',
157
- object_type_icon: '📄', // Document icon
158
- relatedObject: 'test_jeet_july2334 (A)', // Related object
159
- comment: 'Message content...'
160
- };`}
161
- </pre>
162
- </div>
163
- </div>
164
- );
165
- };
166
-
167
- export default Demo;
@@ -1,38 +0,0 @@
1
- import React, { ReactNode } from "react";
2
- import "./MentionInput.css";
3
- interface User {
4
- id: number;
5
- name: string;
6
- }
7
- interface MentionInputProps {
8
- users: User[];
9
- placeholder?: string;
10
- containerClassName?: string;
11
- inputContainerClassName?: string;
12
- inputClassName?: string;
13
- sendBtnClassName?: string;
14
- suggestionListClassName?: string;
15
- suggestionItemClassName?: string;
16
- attachedImageContainerClassName?: string;
17
- attachedImageContainerStyle?: React.CSSProperties;
18
- imgClassName?: string;
19
- imgStyle?: React.CSSProperties;
20
- sendButtonIcon?: ReactNode;
21
- attachmentButtonIcon?: ReactNode;
22
- onSendMessage?: (obj: {
23
- messageText: string;
24
- messageHTML: string;
25
- userSelectListWithIds: {
26
- id: number;
27
- name: string;
28
- }[];
29
- userSelectListName: string[];
30
- tags: string[];
31
- images?: File[];
32
- imageUrl?: string | null;
33
- }) => void;
34
- suggestionPosition?: 'top' | 'bottom' | 'left' | 'right';
35
- onImageUpload?: (file: File) => Promise<string>;
36
- }
37
- declare const MentionInput: React.FC<MentionInputProps>;
38
- export default MentionInput;
@@ -1,376 +0,0 @@
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
- };
37
- import React, { useState, useRef } from "react";
38
- import ReactDOM from "react-dom";
39
- import "./MentionInput.css";
40
- var MentionInput = function (_a) {
41
- var _b;
42
- var users = _a.users, _c = _a.placeholder, placeholder = _c === void 0 ? "Type a message... (or drag & drop an image)" : _c, containerClassName = _a.containerClassName, inputContainerClassName = _a.inputContainerClassName, inputClassName = _a.inputClassName, sendBtnClassName = _a.sendBtnClassName, suggestionListClassName = _a.suggestionListClassName, suggestionItemClassName = _a.suggestionItemClassName, imgClassName = _a.imgClassName, imgStyle = _a.imgStyle, attachedImageContainerClassName = _a.attachedImageContainerClassName, attachedImageContainerStyle = _a.attachedImageContainerStyle, sendButtonIcon = _a.sendButtonIcon, attachmentButtonIcon = _a.attachmentButtonIcon, onSendMessage = _a.onSendMessage, _d = _a.suggestionPosition, suggestionPosition = _d === void 0 ? 'bottom' : _d, onImageUpload = _a.onImageUpload;
43
- var _e = useState(""), inputValue = _e[0], setInputValue = _e[1]; // Plain text
44
- var _f = useState([]), suggestions = _f[0], setSuggestions = _f[1];
45
- var _g = useState(false), showSuggestions = _g[0], setShowSuggestions = _g[1];
46
- var _h = useState(null), selectedImage = _h[0], setSelectedImage = _h[1];
47
- var _j = useState(null), imageUrl = _j[0], setImageUrl = _j[1];
48
- var _k = useState(false), isUploading = _k[0], setIsUploading = _k[1];
49
- var _l = useState(false), isDraggingOver = _l[0], setIsDraggingOver = _l[1];
50
- var inputRef = useRef(null);
51
- var suggestionListRef = useRef(null);
52
- var caretOffsetRef = useRef(0);
53
- var userSelectListRef = useRef([]); // Only unique names
54
- var userSelectListWithIdsRef = useRef([]); // Unique IDs with names
55
- var tagsListRef = useRef([]); // Store hashtags
56
- var fileInputRef = useRef(null);
57
- var highlightMentionsAndLinks = function (text) {
58
- // Regular expression for detecting links
59
- var linkRegex = /(https?:\/\/[^\s]+)/g;
60
- // Regular expression for detecting hashtags
61
- var hashtagRegex = /#[\w]+/g;
62
- // Highlight links
63
- var highlightedText = text.replace(linkRegex, '<a href="$1" target="_blank" rel="noopener noreferrer" class="link-highlight">$1</a>');
64
- // Highlight hashtags
65
- highlightedText = highlightedText.replace(hashtagRegex, function (match) {
66
- return "<span class=\"hashtag-highlight\">".concat(match, "</span>");
67
- });
68
- // Highlight mentions manually based on `userSelectListRef`
69
- userSelectListRef === null || userSelectListRef === void 0 ? void 0 : userSelectListRef.current.forEach(function (userName) {
70
- var mentionPattern = new RegExp("@".concat(userName, "(\\s|$)"), "g");
71
- highlightedText = highlightedText.replace(mentionPattern, function (match) {
72
- return "<span class=\"mention-highlight\">".concat(match.trim(), "</span>&nbsp;");
73
- });
74
- });
75
- return highlightedText;
76
- };
77
- var restoreCaretPosition = function (node, caretOffset) {
78
- var range = document.createRange();
79
- var sel = window.getSelection();
80
- var charCount = 0;
81
- var findCaret = function (currentNode) {
82
- var _a;
83
- for (var _i = 0, _b = Array.from(currentNode.childNodes); _i < _b.length; _i++) {
84
- var child = _b[_i];
85
- if (child.nodeType === Node.TEXT_NODE) {
86
- var textLength = ((_a = child.textContent) === null || _a === void 0 ? void 0 : _a.length) || 0;
87
- if (charCount + textLength >= caretOffset) {
88
- range.setStart(child, caretOffset - charCount);
89
- range.collapse(true);
90
- return true;
91
- }
92
- else {
93
- charCount += textLength;
94
- }
95
- }
96
- else if (child.nodeType === Node.ELEMENT_NODE) {
97
- if (findCaret(child))
98
- return true;
99
- }
100
- }
101
- return false;
102
- };
103
- findCaret(node);
104
- if (sel) {
105
- sel.removeAllRanges();
106
- sel.addRange(range);
107
- }
108
- };
109
- var handleInputChange = function () {
110
- if (!inputRef.current)
111
- return;
112
- // Store current selection before modifications
113
- var selection = window.getSelection();
114
- var range = selection === null || selection === void 0 ? void 0 : selection.getRangeAt(0);
115
- var newCaretOffset = 0;
116
- if (range && inputRef.current.contains(range.startContainer)) {
117
- var preCaretRange = range.cloneRange();
118
- preCaretRange.selectNodeContents(inputRef.current);
119
- preCaretRange.setEnd(range.startContainer, range.startOffset);
120
- newCaretOffset = preCaretRange.toString().length;
121
- }
122
- caretOffsetRef.current = newCaretOffset;
123
- var plainText = inputRef.current.innerText;
124
- setInputValue(plainText);
125
- // Process for mention suggestions
126
- var mentionMatch = plainText.slice(0, newCaretOffset).match(/@(\S*)$/);
127
- if (mentionMatch) {
128
- var query_1 = mentionMatch[1].toLowerCase();
129
- var filteredUsers = query_1 === "" ? users : users.filter(function (user) {
130
- return user.name.toLowerCase().includes(query_1);
131
- });
132
- setSuggestions(filteredUsers);
133
- setShowSuggestions(filteredUsers.length > 0);
134
- }
135
- else {
136
- setShowSuggestions(false);
137
- }
138
- // Extract and store hashtags
139
- var hashtagMatches = plainText.match(/#[\w]+/g);
140
- if (hashtagMatches) {
141
- var uniqueTags = Array.from(new Set(hashtagMatches));
142
- tagsListRef.current = uniqueTags;
143
- }
144
- else {
145
- tagsListRef.current = [];
146
- }
147
- // Only apply highlighting if we have mentions, hashtags, or links to highlight
148
- if (userSelectListRef.current.length > 0 || plainText.match(/(https?:\/\/[^\s]+)/g) || plainText.match(/#[\w]+/g)) {
149
- var currentHTML = inputRef.current.innerHTML;
150
- var htmlWithHighlights = highlightMentionsAndLinks(plainText);
151
- // Only update if the highlighted HTML is different to avoid cursor jumping
152
- if (currentHTML !== htmlWithHighlights) {
153
- inputRef.current.innerHTML = htmlWithHighlights;
154
- // Restore cursor position after changing innerHTML
155
- restoreCaretPosition(inputRef.current, newCaretOffset);
156
- }
157
- }
158
- };
159
- var renderSuggestions = function () {
160
- if (!showSuggestions || !inputRef.current)
161
- return null;
162
- var getInitials = function (name) {
163
- var nameParts = name.split(" ");
164
- var initials = nameParts
165
- .map(function (part) { var _a; return ((_a = part[0]) === null || _a === void 0 ? void 0 : _a.toUpperCase()) || ""; })
166
- .slice(0, 2)
167
- .join("");
168
- return initials;
169
- };
170
- var inputRect = inputRef.current.getBoundingClientRect();
171
- var styles = {
172
- position: 'absolute',
173
- zIndex: 1000,
174
- };
175
- // Use suggestionPosition prop to adjust tooltip position
176
- switch (suggestionPosition) {
177
- case 'top':
178
- styles.left = "".concat(inputRect.left, "px");
179
- styles.top = "".concat(inputRect.top - 150, "px");
180
- break;
181
- case 'bottom':
182
- styles.left = "".concat(inputRect.left, "px");
183
- styles.top = "".concat(inputRect.bottom, "px");
184
- break;
185
- case 'left':
186
- styles.left = "".concat(inputRect.left - 150, "px");
187
- styles.top = "".concat(inputRect.top, "px");
188
- break;
189
- case 'right':
190
- styles.left = "".concat(inputRect.right, "px");
191
- styles.top = "".concat(inputRect.top, "px");
192
- break;
193
- default:
194
- break;
195
- }
196
- return ReactDOM.createPortal(React.createElement("div", { className: "suggestion-container ".concat(suggestionListClassName || ''), style: styles },
197
- 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" },
198
- React.createElement("div", { className: "user-icon" }, getInitials(user === null || user === void 0 ? void 0 : user.name)),
199
- React.createElement("span", { className: "user-name" }, user.name))); }))), window.document.body);
200
- };
201
- var handleSuggestionClick = function (user) {
202
- if (!inputRef.current)
203
- return;
204
- var plainText = inputValue;
205
- var caretOffset = caretOffsetRef.current;
206
- var mentionMatch = plainText.slice(0, caretOffset).match(/@(\S*)$/);
207
- if (!userSelectListRef.current.includes(user.name)) {
208
- userSelectListRef.current.push(user.name);
209
- }
210
- // Check if the ID is already stored
211
- var isIdExists = userSelectListWithIdsRef.current.some(function (item) { return item.id === user.id; });
212
- if (!isIdExists) {
213
- userSelectListWithIdsRef.current.push(user);
214
- }
215
- if (!mentionMatch)
216
- return;
217
- var mentionIndex = plainText.slice(0, caretOffset).lastIndexOf("@");
218
- // Append space after the mention
219
- var newValue = plainText.substring(0, mentionIndex + 1) + user.name + " " + plainText.substring(caretOffset);
220
- setInputValue(newValue);
221
- inputRef.current.innerText = newValue;
222
- // Highlight mentions and links with &nbsp;
223
- var htmlWithHighlights = highlightMentionsAndLinks(newValue);
224
- // Set highlighted content
225
- inputRef.current.innerHTML = htmlWithHighlights;
226
- setShowSuggestions(false);
227
- // Adjust caret position after adding the mention and space
228
- var mentionEnd = mentionIndex + user.name.length + 1;
229
- restoreCaretPosition(inputRef.current, mentionEnd + 1); // +1 for the space
230
- };
231
- var handleImageSelect = function (event) { return __awaiter(void 0, void 0, void 0, function () {
232
- var files, file;
233
- return __generator(this, function (_a) {
234
- switch (_a.label) {
235
- case 0:
236
- files = Array.from(event.target.files || []);
237
- if (!(files.length > 0)) return [3 /*break*/, 2];
238
- file = files[0];
239
- if (!file.type.startsWith('image/')) return [3 /*break*/, 2];
240
- return [4 /*yield*/, uploadImage(file)];
241
- case 1:
242
- _a.sent();
243
- _a.label = 2;
244
- case 2: return [2 /*return*/];
245
- }
246
- });
247
- }); };
248
- var handleDragOver = function (e) {
249
- e.preventDefault();
250
- e.stopPropagation();
251
- // Only set dragging if files are being dragged
252
- if (e.dataTransfer.types.includes('Files')) {
253
- setIsDraggingOver(true);
254
- }
255
- };
256
- var handleDragLeave = function (e) {
257
- e.preventDefault();
258
- e.stopPropagation();
259
- // Check if we're leaving the container, not just moving between children
260
- var rect = e.currentTarget.getBoundingClientRect();
261
- var x = e.clientX;
262
- var y = e.clientY;
263
- if (x <= rect.left ||
264
- x >= rect.right ||
265
- y <= rect.top ||
266
- y >= rect.bottom) {
267
- setIsDraggingOver(false);
268
- }
269
- };
270
- var handleDrop = function (e) { return __awaiter(void 0, void 0, void 0, function () {
271
- var files, imageFiles;
272
- return __generator(this, function (_a) {
273
- switch (_a.label) {
274
- case 0:
275
- e.preventDefault();
276
- e.stopPropagation();
277
- setIsDraggingOver(false);
278
- files = Array.from(e.dataTransfer.files);
279
- if (!(files.length > 0)) return [3 /*break*/, 2];
280
- imageFiles = files.filter(function (file) { return file.type.startsWith('image/'); });
281
- if (!(imageFiles.length > 0)) return [3 /*break*/, 2];
282
- return [4 /*yield*/, uploadImage(imageFiles[0])];
283
- case 1:
284
- _a.sent();
285
- _a.label = 2;
286
- case 2: return [2 /*return*/];
287
- }
288
- });
289
- }); };
290
- var uploadImage = function (file) { return __awaiter(void 0, void 0, void 0, function () {
291
- var url, error_1;
292
- return __generator(this, function (_a) {
293
- switch (_a.label) {
294
- case 0:
295
- if (!onImageUpload) {
296
- // If no upload function provided, store the file directly
297
- setSelectedImage(file);
298
- setImageUrl(URL.createObjectURL(file));
299
- return [2 /*return*/];
300
- }
301
- _a.label = 1;
302
- case 1:
303
- _a.trys.push([1, 3, 4, 5]);
304
- setIsUploading(true);
305
- return [4 /*yield*/, onImageUpload(file)];
306
- case 2:
307
- url = _a.sent();
308
- setSelectedImage(file);
309
- setImageUrl(url);
310
- return [3 /*break*/, 5];
311
- case 3:
312
- error_1 = _a.sent();
313
- console.error('Error uploading image:', error_1);
314
- return [3 /*break*/, 5];
315
- case 4:
316
- setIsUploading(false);
317
- return [7 /*endfinally*/];
318
- case 5: return [2 /*return*/];
319
- }
320
- });
321
- }); };
322
- var removeImage = function () {
323
- setSelectedImage(null);
324
- setImageUrl(null);
325
- };
326
- var handleSendMessage = function () {
327
- if (inputRef.current) {
328
- var messageText = inputRef.current.innerText.trim();
329
- var messageHTML = inputRef.current.innerHTML.trim();
330
- if ((messageText || selectedImage) && onSendMessage) {
331
- onSendMessage({
332
- messageText: messageText,
333
- messageHTML: messageHTML,
334
- userSelectListWithIds: userSelectListWithIdsRef.current,
335
- userSelectListName: userSelectListRef.current,
336
- tags: tagsListRef.current,
337
- images: selectedImage ? [selectedImage] : [],
338
- imageUrl: imageUrl
339
- });
340
- setInputValue("");
341
- setShowSuggestions(false);
342
- inputRef.current.innerText = "";
343
- setSelectedImage(null);
344
- setImageUrl(null);
345
- userSelectListRef.current = [];
346
- userSelectListWithIdsRef.current = [];
347
- tagsListRef.current = [];
348
- }
349
- }
350
- };
351
- var handleKeyDown = function (event) {
352
- if (event.key === "Enter" && !event.shiftKey) {
353
- event.preventDefault(); // Prevent newline in content-editable
354
- handleSendMessage(); // Trigger the same function as the Send button
355
- }
356
- };
357
- return (React.createElement("div", { className: "mention-container ".concat(containerClassName || "") },
358
- imageUrl && selectedImage && (React.createElement("div", { className: "image-preview-card ".concat(attachedImageContainerClassName || ""), style: attachedImageContainerStyle },
359
- React.createElement("img", { src: imageUrl, alt: "Preview", className: imgClassName || "", style: imgStyle }),
360
- React.createElement("button", { onClick: removeImage, className: "remove-image-btn", "aria-label": "Remove image" }, "\u00D7"))),
361
- React.createElement("div", { className: "mention-input-container ".concat(inputContainerClassName || "", " ").concat(isDraggingOver ? 'dragging-over' : ''), onDragOver: handleDragOver, onDragLeave: handleDragLeave, onDragEnd: function () { return setIsDraggingOver(false); }, onDrop: handleDrop },
362
- isDraggingOver && (React.createElement("div", { className: "drag-overlay" },
363
- React.createElement("div", { className: "drag-message" },
364
- React.createElement("span", null, "Drop to upload")))),
365
- 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" },
366
- React.createElement("span", { className: "attachment-icon" }, attachmentButtonIcon || "📷")),
367
- React.createElement("div", { className: "mention-input-wrapper" },
368
- (!inputValue || !inputRef.current || ((_b = inputRef.current) === null || _b === void 0 ? void 0 : _b.innerText.trim()) === "") && (React.createElement("span", { className: "placeholder" }, placeholder)),
369
- 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'); } })),
370
- React.createElement("button", { onClick: handleSendMessage, className: "send-button ".concat(sendBtnClassName || ""), "aria-label": "Send message" }, sendButtonIcon || "➤"),
371
- React.createElement("input", { type: "file", ref: fileInputRef, accept: "image/*", onChange: handleImageSelect, style: { display: 'none' } }),
372
- isUploading && (React.createElement("div", { className: "upload-loading" },
373
- React.createElement("span", null, "Uploading...")))),
374
- renderSuggestions()));
375
- };
376
- export default MentionInput;
@@ -1,41 +0,0 @@
1
- import React, { CSSProperties, ReactNode } from "react";
2
- import "./ShowMessageCard.css";
3
- interface MessageCardProps {
4
- [key: string]: any;
5
- }
6
- interface ShowMessageCardProps {
7
- data: MessageCardProps[];
8
- nameKey?: string;
9
- dateKey?: string;
10
- commentKey?: string;
11
- imgSrcKey?: string;
12
- imageUrlKey?: string;
13
- objectNameKey?: string;
14
- containerClassName?: string;
15
- containerStyle?: CSSProperties;
16
- cardClassName?: string;
17
- cardStyle?: CSSProperties;
18
- headerClassName?: string;
19
- headerStyle?: CSSProperties;
20
- imgClassName?: string;
21
- imgStyle?: CSSProperties;
22
- infoClassName?: string;
23
- infoStyle?: CSSProperties;
24
- nameClassName?: string;
25
- nameStyle?: CSSProperties;
26
- dateClassName?: string;
27
- dateStyle?: CSSProperties;
28
- bodyClassName?: string;
29
- bodyStyle?: CSSProperties;
30
- commentClassName?: string;
31
- commentStyle?: CSSProperties;
32
- attachedImageClassName?: string;
33
- attachedImageStyle?: CSSProperties;
34
- attachedImageContainerClassName?: string;
35
- attachedImageContainerStyle?: CSSProperties;
36
- objectNameClassName?: string;
37
- objectNameStyle?: CSSProperties;
38
- renderItem?: (element: MessageCardProps) => ReactNode;
39
- }
40
- export declare const ShowMessageCard: React.FC<ShowMessageCardProps>;
41
- export {};
@@ -1,72 +0,0 @@
1
- var __assign = (this && this.__assign) || function () {
2
- __assign = Object.assign || function(t) {
3
- for (var s, i = 1, n = arguments.length; i < n; i++) {
4
- s = arguments[i];
5
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
6
- t[p] = s[p];
7
- }
8
- return t;
9
- };
10
- return __assign.apply(this, arguments);
11
- };
12
- import React, { useState } from "react";
13
- import "./ShowMessageCard.css";
14
- export var ShowMessageCard = function (_a) {
15
- var data = _a.data, _b = _a.nameKey, nameKey = _b === void 0 ? "name" : _b, _c = _a.dateKey, dateKey = _c === void 0 ? "date" : _c, _d = _a.commentKey, commentKey = _d === void 0 ? "comment" : _d, _e = _a.imgSrcKey, imgSrcKey = _e === void 0 ? "imgSrc" : _e, _f = _a.imageUrlKey, imageUrlKey = _f === void 0 ? "imageUrl" : _f, // Default key for attached image
16
- _g = _a.objectNameKey, // Default key for attached image
17
- objectNameKey = _g === void 0 ? "objectName" : _g, // Default key for object identifier
18
- containerClassName = _a.containerClassName, containerStyle = _a.containerStyle, cardClassName = _a.cardClassName, cardStyle = _a.cardStyle, headerClassName = _a.headerClassName, headerStyle = _a.headerStyle, imgClassName = _a.imgClassName, imgStyle = _a.imgStyle, infoClassName = _a.infoClassName, infoStyle = _a.infoStyle, nameClassName = _a.nameClassName, nameStyle = _a.nameStyle, dateClassName = _a.dateClassName, dateStyle = _a.dateStyle, bodyClassName = _a.bodyClassName, bodyStyle = _a.bodyStyle, commentClassName = _a.commentClassName, commentStyle = _a.commentStyle, attachedImageClassName = _a.attachedImageClassName, attachedImageStyle = _a.attachedImageStyle, attachedImageContainerClassName = _a.attachedImageContainerClassName, attachedImageContainerStyle = _a.attachedImageContainerStyle, objectNameClassName = _a.objectNameClassName, objectNameStyle = _a.objectNameStyle, renderItem = _a.renderItem;
19
- console.log(data, "data==");
20
- // State to manage initials for images that fail to load
21
- var _h = useState({}), initialsState = _h[0], setInitialsState = _h[1];
22
- // Handle image load failure
23
- var handleImageError = function (id) {
24
- setInitialsState(function (prevState) {
25
- var _a;
26
- return (__assign(__assign({}, prevState), (_a = {}, _a[id] = true, _a)));
27
- });
28
- };
29
- // Helper function to generate initials from the name
30
- var getInitials = function (name) {
31
- var nameParts = name.split(" ");
32
- var initials = nameParts
33
- .map(function (part) { var _a; return ((_a = part[0]) === null || _a === void 0 ? void 0 : _a.toUpperCase()) || ""; }) // Take the first letter of each part
34
- .slice(0, 2) // Limit to 2 letters
35
- .join("");
36
- return initials;
37
- };
38
- // Helper function to extract hashtags and mentions from text
39
- var extractTagsAndMentions = function (text) {
40
- var plainText = text.replace(/<[^>]*>/g, ''); // Remove HTML tags to get plain text
41
- var hashtags = plainText.match(/#[\w]+/g) || [];
42
- var mentions = plainText.match(/@[\w\s-]+/g) || [];
43
- return {
44
- hashtags: Array.from(new Set(hashtags)), // Remove duplicates
45
- mentions: Array.from(new Set(mentions.map(function (mention) { return mention.trim(); }))) // Remove duplicates and trim
46
- };
47
- };
48
- return (React.createElement("div", { className: "message-card-container ".concat(containerClassName || ""), style: containerStyle }, data.map(function (item, index) {
49
- if (renderItem !== undefined) {
50
- // Use custom render function if provided
51
- return (React.createElement(React.Fragment, { key: item.id || index }, renderItem(item)));
52
- }
53
- var showInitials = initialsState[item.id || index] || !item[imgSrcKey]; // Decide whether to show initials
54
- // Extract tags and mentions from the comment
55
- var _a = extractTagsAndMentions(item[commentKey] || ''), hashtags = _a.hashtags, mentions = _a.mentions;
56
- return (React.createElement("div", { key: item.id || index, className: "message-card ".concat(cardClassName || ""), style: cardStyle },
57
- React.createElement("div", { className: "message-card-header ".concat(headerClassName || ""), style: headerStyle },
58
- React.createElement("div", { className: "message-card-header-left" },
59
- showInitials ? (React.createElement("div", { className: "message-card-initials ".concat(imgClassName || ""), style: imgStyle }, getInitials(item[nameKey]))) : (React.createElement("img", { src: item[imgSrcKey], alt: item[nameKey], className: "message-card-img ".concat(imgClassName || ""), style: imgStyle, onError: function () { return handleImageError(item.id || index); } })),
60
- React.createElement("div", { className: "message-card-info ".concat(infoClassName || ""), style: infoStyle },
61
- React.createElement("h3", { className: "message-card-name ".concat(nameClassName || ""), style: nameStyle }, item[nameKey]),
62
- React.createElement("p", { className: "message-card-date ".concat(dateClassName || ""), style: dateStyle }, item[dateKey]))),
63
- item[objectNameKey] && (React.createElement("div", { className: "message-card-item-name ".concat(objectNameClassName || ""), style: objectNameStyle }, item[objectNameKey]))),
64
- React.createElement("div", { className: "message-card-body ".concat(bodyClassName || ""), style: bodyStyle },
65
- React.createElement("p", { className: "message-card-comment ".concat(commentClassName || ""), style: commentStyle, dangerouslySetInnerHTML: { __html: item[commentKey] } }),
66
- (item === null || item === void 0 ? void 0 : item[imageUrlKey]) && (React.createElement("div", { className: "message-card-attached-image-container ".concat(attachedImageContainerClassName || ""), style: attachedImageContainerStyle },
67
- React.createElement("img", { src: item[imageUrlKey], alt: "Attached", className: "message-card-attached-image ".concat(attachedImageClassName || ""), style: attachedImageStyle }))),
68
- (hashtags.length > 0 || mentions.length > 0) && (React.createElement("div", { className: "message-card-tags" },
69
- hashtags.map(function (tag, tagIndex) { return (React.createElement("span", { key: "hashtag-".concat(tagIndex), className: "tag-chip hashtag-chip" }, tag)); }),
70
- mentions.map(function (mention, mentionIndex) { return (React.createElement("span", { key: "mention-".concat(mentionIndex), className: "tag-chip mention-chip" }, mention)); }))))));
71
- })));
72
- };
@@ -1,2 +0,0 @@
1
- export { default as MentionInput } from './MentionInput';
2
- export { ShowMessageCard } from './ShowMessageCard';
package/dist/src/index.js DELETED
@@ -1,2 +0,0 @@
1
- export { default as MentionInput } from './MentionInput';
2
- export { ShowMessageCard } from './ShowMessageCard';
package/index.html DELETED
@@ -1,12 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>React Mention Input Demo</title>
7
- </head>
8
- <body>
9
- <div id="root"></div>
10
- <script type="module" src="/main.tsx"></script>
11
- </body>
12
- </html>
package/main.tsx DELETED
@@ -1,9 +0,0 @@
1
- import React from 'react'
2
- import ReactDOM from 'react-dom/client'
3
- import Demo from './demo'
4
-
5
- ReactDOM.createRoot(document.getElementById('root')!).render(
6
- <React.StrictMode>
7
- <Demo />
8
- </React.StrictMode>,
9
- )
package/tsconfig.json DELETED
@@ -1,28 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES5",
4
- "sourceMap": false,
5
- "module": "ESNext",
6
- "moduleResolution": "bundler",
7
- "jsx": "react",
8
- "lib": ["dom", "es6"],
9
- "declaration": true,
10
- "outDir": "./dist",
11
- "strict": true,
12
- "esModuleInterop": true,
13
- "allowSyntheticDefaultImports": true,
14
- "skipLibCheck": true
15
- },
16
- "include": [
17
- "src/**/*"
18
- ],
19
- "exclude": [
20
- "node_modules",
21
- "dist",
22
- "demo.tsx",
23
- "main.tsx",
24
- "vite.config.ts",
25
- "**/*.test.*",
26
- "**/*.spec.*"
27
- ]
28
- }
package/vite.config.ts DELETED
@@ -1,10 +0,0 @@
1
- import { defineConfig } from 'vite'
2
- import react from '@vitejs/plugin-react'
3
-
4
- export default defineConfig({
5
- plugins: [react()],
6
- server: {
7
- port: 3000,
8
- open: true
9
- }
10
- })