@mieweb/ui 0.9.0-dev.0 → 0.9.0-dev.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/dist/{chunk-WIAQQNFU.cjs → chunk-2A2FLQJC.cjs} +1138 -12
- package/dist/chunk-2A2FLQJC.cjs.map +1 -0
- package/dist/{chunk-5BVFVXHN.cjs → chunk-5VXRKOPR.cjs} +16 -525
- package/dist/chunk-5VXRKOPR.cjs.map +1 -0
- package/dist/{chunk-O6GJ4IYS.js → chunk-IF3X2WZP.js} +16 -521
- package/dist/chunk-IF3X2WZP.js.map +1 -0
- package/dist/{chunk-22CYDCXA.js → chunk-KATEJG2F.js} +1137 -13
- package/dist/chunk-KATEJG2F.js.map +1 -0
- package/dist/{chunk-FQBTGS3E.cjs → chunk-O425FKDI.cjs} +563 -55
- package/dist/chunk-O425FKDI.cjs.map +1 -0
- package/dist/{chunk-MXQ7J4BB.js → chunk-RPG2ZP2T.js} +557 -53
- package/dist/chunk-RPG2ZP2T.js.map +1 -0
- package/dist/components/SuperChat/index.cjs +36 -17
- package/dist/components/SuperChat/index.cjs.map +1 -1
- package/dist/components/SuperChat/index.d.cts +16 -3
- package/dist/components/SuperChat/index.d.ts +16 -3
- package/dist/components/SuperChat/index.js +31 -12
- package/dist/components/SuperChat/index.js.map +1 -1
- package/dist/components/SuperChat/plugins/index.cjs +3 -3
- package/dist/components/SuperChat/plugins/index.js +2 -2
- package/dist/index.cjs +820 -1930
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +663 -1780
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-22CYDCXA.js.map +0 -1
- package/dist/chunk-5BVFVXHN.cjs.map +0 -1
- package/dist/chunk-FQBTGS3E.cjs.map +0 -1
- package/dist/chunk-MXQ7J4BB.js.map +0 -1
- package/dist/chunk-O6GJ4IYS.js.map +0 -1
- package/dist/chunk-WIAQQNFU.cjs.map +0 -1
|
@@ -1,9 +1,513 @@
|
|
|
1
|
-
import { DragDropZone,
|
|
1
|
+
import { useMentionAutocomplete, DragDropZone, AttachmentPreviewItem, AttachmentPicker, CameraButton, MentionMenu, getFileType, generateAttachmentId } from './chunk-IF3X2WZP.js';
|
|
2
2
|
import { cn } from './chunk-TQONQSJO.js';
|
|
3
|
-
import * as
|
|
3
|
+
import * as React6 from 'react';
|
|
4
4
|
import { cva } from 'class-variance-authority';
|
|
5
5
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
6
6
|
|
|
7
|
+
function isFileAccepted(file, accepted) {
|
|
8
|
+
if (!accepted || accepted.length === 0) return true;
|
|
9
|
+
const mime = file.type;
|
|
10
|
+
const name = file.name.toLowerCase();
|
|
11
|
+
return accepted.some((token) => {
|
|
12
|
+
if (token.startsWith(".")) return name.endsWith(token.toLowerCase());
|
|
13
|
+
if (token.endsWith("/*")) return mime.startsWith(token.slice(0, -1));
|
|
14
|
+
if (token.includes("/")) return mime === token;
|
|
15
|
+
return false;
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
function CharacterCounter({
|
|
19
|
+
current,
|
|
20
|
+
max,
|
|
21
|
+
showWarningAt = 0.9,
|
|
22
|
+
className
|
|
23
|
+
}) {
|
|
24
|
+
const percentage = current / max;
|
|
25
|
+
const isWarning = percentage >= showWarningAt && percentage < 1;
|
|
26
|
+
const isOver = current > max;
|
|
27
|
+
return /* @__PURE__ */ jsxs(
|
|
28
|
+
"span",
|
|
29
|
+
{
|
|
30
|
+
className: cn(
|
|
31
|
+
"text-xs tabular-nums",
|
|
32
|
+
isOver ? "font-medium text-red-700 dark:text-red-400" : isWarning ? "text-amber-700 dark:text-amber-400" : "text-neutral-600 dark:text-neutral-400",
|
|
33
|
+
className
|
|
34
|
+
),
|
|
35
|
+
"aria-live": "polite",
|
|
36
|
+
"aria-label": `${current} of ${max} characters`,
|
|
37
|
+
children: [
|
|
38
|
+
current,
|
|
39
|
+
"/",
|
|
40
|
+
max
|
|
41
|
+
]
|
|
42
|
+
}
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
CharacterCounter.displayName = "CharacterCounter";
|
|
46
|
+
var sendButtonVariants = cva(
|
|
47
|
+
[
|
|
48
|
+
"inline-flex items-center justify-center",
|
|
49
|
+
"rounded-full p-3 self-start",
|
|
50
|
+
"transition-all duration-200",
|
|
51
|
+
"focus:outline-none focus:ring-2 focus:ring-primary-500",
|
|
52
|
+
"disabled:opacity-50 disabled:cursor-not-allowed"
|
|
53
|
+
],
|
|
54
|
+
{
|
|
55
|
+
variants: {
|
|
56
|
+
variant: {
|
|
57
|
+
primary: [
|
|
58
|
+
"bg-primary-800 text-white",
|
|
59
|
+
"hover:bg-primary-900",
|
|
60
|
+
"active:scale-95"
|
|
61
|
+
],
|
|
62
|
+
subtle: [
|
|
63
|
+
"bg-transparent text-primary-800",
|
|
64
|
+
"hover:bg-primary-50 dark:hover:bg-primary-900/20"
|
|
65
|
+
]
|
|
66
|
+
},
|
|
67
|
+
canSend: {
|
|
68
|
+
true: "",
|
|
69
|
+
false: "opacity-50 cursor-not-allowed"
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
defaultVariants: {
|
|
73
|
+
variant: "primary",
|
|
74
|
+
canSend: false
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
);
|
|
78
|
+
var SendButton = React6.forwardRef(
|
|
79
|
+
({ className, variant, canSend, isLoading, disabled, ...props }, ref) => {
|
|
80
|
+
return /* @__PURE__ */ jsx(
|
|
81
|
+
"button",
|
|
82
|
+
{
|
|
83
|
+
ref,
|
|
84
|
+
type: "submit",
|
|
85
|
+
disabled: disabled || !canSend || isLoading,
|
|
86
|
+
"data-slot": "composer-send-button",
|
|
87
|
+
className: cn(sendButtonVariants({ variant, canSend }), className),
|
|
88
|
+
"aria-label": isLoading ? "Sending message" : "Send message",
|
|
89
|
+
...props,
|
|
90
|
+
children: isLoading ? /* @__PURE__ */ jsxs(
|
|
91
|
+
"svg",
|
|
92
|
+
{
|
|
93
|
+
"aria-hidden": "true",
|
|
94
|
+
className: "h-5 w-5 animate-spin",
|
|
95
|
+
fill: "none",
|
|
96
|
+
viewBox: "0 0 24 24",
|
|
97
|
+
children: [
|
|
98
|
+
/* @__PURE__ */ jsx(
|
|
99
|
+
"circle",
|
|
100
|
+
{
|
|
101
|
+
className: "opacity-25",
|
|
102
|
+
cx: "12",
|
|
103
|
+
cy: "12",
|
|
104
|
+
r: "10",
|
|
105
|
+
stroke: "currentColor",
|
|
106
|
+
strokeWidth: "4"
|
|
107
|
+
}
|
|
108
|
+
),
|
|
109
|
+
/* @__PURE__ */ jsx(
|
|
110
|
+
"path",
|
|
111
|
+
{
|
|
112
|
+
className: "opacity-75",
|
|
113
|
+
fill: "currentColor",
|
|
114
|
+
d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
|
|
115
|
+
}
|
|
116
|
+
)
|
|
117
|
+
]
|
|
118
|
+
}
|
|
119
|
+
) : /* @__PURE__ */ jsx(
|
|
120
|
+
"svg",
|
|
121
|
+
{
|
|
122
|
+
"aria-hidden": "true",
|
|
123
|
+
className: "h-5 w-5",
|
|
124
|
+
fill: "none",
|
|
125
|
+
viewBox: "0 0 24 24",
|
|
126
|
+
stroke: "currentColor",
|
|
127
|
+
children: /* @__PURE__ */ jsx(
|
|
128
|
+
"path",
|
|
129
|
+
{
|
|
130
|
+
strokeLinecap: "round",
|
|
131
|
+
strokeLinejoin: "round",
|
|
132
|
+
strokeWidth: 2,
|
|
133
|
+
d: "M12 19l9 2-9-18-9 18 9-2zm0 0v-8"
|
|
134
|
+
}
|
|
135
|
+
)
|
|
136
|
+
}
|
|
137
|
+
)
|
|
138
|
+
}
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
);
|
|
142
|
+
SendButton.displayName = "SendButton";
|
|
143
|
+
var MessageComposer = React6.forwardRef(
|
|
144
|
+
({
|
|
145
|
+
onSend,
|
|
146
|
+
onTypingStart,
|
|
147
|
+
onTypingStop,
|
|
148
|
+
value: controlledValue,
|
|
149
|
+
onValueChange,
|
|
150
|
+
placeholder = "Type a message...",
|
|
151
|
+
maxLength = 1600,
|
|
152
|
+
showCharacterCount = false,
|
|
153
|
+
disabled = false,
|
|
154
|
+
isSending = false,
|
|
155
|
+
showAttachmentPicker = true,
|
|
156
|
+
showCameraButton = false,
|
|
157
|
+
acceptedFileTypes = ["image/*", "video/*", ".pdf", ".doc", ".docx"],
|
|
158
|
+
maxFileSize = 25 * 1024 * 1024,
|
|
159
|
+
maxAttachments = 10,
|
|
160
|
+
onError,
|
|
161
|
+
autoFocus = false,
|
|
162
|
+
replyTo = null,
|
|
163
|
+
onCancelReply,
|
|
164
|
+
variant = "default",
|
|
165
|
+
inputTrailing,
|
|
166
|
+
mentionOptions,
|
|
167
|
+
className
|
|
168
|
+
}, ref) => {
|
|
169
|
+
const textareaRef = React6.useRef(null);
|
|
170
|
+
const [internalContent, setInternalContent] = React6.useState("");
|
|
171
|
+
const isControlled = controlledValue !== void 0;
|
|
172
|
+
const content = isControlled ? controlledValue : internalContent;
|
|
173
|
+
const setContent = React6.useCallback(
|
|
174
|
+
(val) => {
|
|
175
|
+
if (isControlled) {
|
|
176
|
+
const newVal = typeof val === "function" ? val(controlledValue) : val;
|
|
177
|
+
onValueChange?.(newVal);
|
|
178
|
+
} else {
|
|
179
|
+
setInternalContent(val);
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
[isControlled, controlledValue, onValueChange]
|
|
183
|
+
);
|
|
184
|
+
const [attachments, setAttachments] = React6.useState(
|
|
185
|
+
[]
|
|
186
|
+
);
|
|
187
|
+
const [isTyping, setIsTyping] = React6.useState(false);
|
|
188
|
+
const typingTimeoutRef = React6.useRef(null);
|
|
189
|
+
React6.useImperativeHandle(ref, () => textareaRef.current);
|
|
190
|
+
React6.useEffect(() => {
|
|
191
|
+
const textarea = textareaRef.current;
|
|
192
|
+
if (textarea) {
|
|
193
|
+
textarea.style.height = "auto";
|
|
194
|
+
textarea.style.height = `${Math.min(textarea.scrollHeight, 150)}px`;
|
|
195
|
+
}
|
|
196
|
+
}, [content]);
|
|
197
|
+
React6.useEffect(() => {
|
|
198
|
+
if (content.length > 0 && !isTyping) {
|
|
199
|
+
setIsTyping(true);
|
|
200
|
+
onTypingStart?.();
|
|
201
|
+
}
|
|
202
|
+
if (typingTimeoutRef.current) {
|
|
203
|
+
clearTimeout(typingTimeoutRef.current);
|
|
204
|
+
}
|
|
205
|
+
typingTimeoutRef.current = setTimeout(() => {
|
|
206
|
+
if (isTyping) {
|
|
207
|
+
setIsTyping(false);
|
|
208
|
+
onTypingStop?.();
|
|
209
|
+
}
|
|
210
|
+
}, 2e3);
|
|
211
|
+
return () => {
|
|
212
|
+
if (typingTimeoutRef.current) {
|
|
213
|
+
clearTimeout(typingTimeoutRef.current);
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
}, [content, isTyping, onTypingStart, onTypingStop]);
|
|
217
|
+
React6.useEffect(() => {
|
|
218
|
+
if (autoFocus) {
|
|
219
|
+
textareaRef.current?.focus();
|
|
220
|
+
}
|
|
221
|
+
}, [autoFocus]);
|
|
222
|
+
React6.useEffect(() => {
|
|
223
|
+
if (replyTo) {
|
|
224
|
+
textareaRef.current?.focus();
|
|
225
|
+
}
|
|
226
|
+
}, [replyTo]);
|
|
227
|
+
const mention = useMentionAutocomplete({
|
|
228
|
+
options: mentionOptions,
|
|
229
|
+
value: content,
|
|
230
|
+
setValue: setContent,
|
|
231
|
+
textareaRef
|
|
232
|
+
});
|
|
233
|
+
const canSend = (content.trim().length > 0 || attachments.length > 0) && content.length <= maxLength && !disabled && !isSending;
|
|
234
|
+
const handleSubmit = async (event) => {
|
|
235
|
+
event.preventDefault();
|
|
236
|
+
if (!canSend) return;
|
|
237
|
+
const message = {
|
|
238
|
+
content: content.trim(),
|
|
239
|
+
attachments: attachments.map((a) => a.file),
|
|
240
|
+
replyToId: replyTo?.id
|
|
241
|
+
};
|
|
242
|
+
setContent("");
|
|
243
|
+
setAttachments([]);
|
|
244
|
+
setIsTyping(false);
|
|
245
|
+
onTypingStop?.();
|
|
246
|
+
try {
|
|
247
|
+
await onSend(message);
|
|
248
|
+
} catch {
|
|
249
|
+
setContent(message.content);
|
|
250
|
+
onError?.("Failed to send message");
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
const handleKeyDown = (event) => {
|
|
254
|
+
if (mention.handleKeyDown(event)) return;
|
|
255
|
+
if (event.key === "Enter" && !event.shiftKey) {
|
|
256
|
+
event.preventDefault();
|
|
257
|
+
if (canSend) {
|
|
258
|
+
handleSubmit(event);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
const handleFilesSelected = (files) => {
|
|
263
|
+
const remainingSlots = maxAttachments - attachments.length;
|
|
264
|
+
const filesToAdd = files.slice(0, remainingSlots);
|
|
265
|
+
if (files.length > remainingSlots) {
|
|
266
|
+
onError?.(`Maximum ${maxAttachments} attachments allowed`);
|
|
267
|
+
}
|
|
268
|
+
const newAttachments = filesToAdd.map((file) => {
|
|
269
|
+
const type = getFileType(file.type);
|
|
270
|
+
let previewUrl;
|
|
271
|
+
if (type === "image" || type === "video") {
|
|
272
|
+
previewUrl = URL.createObjectURL(file);
|
|
273
|
+
}
|
|
274
|
+
return {
|
|
275
|
+
id: generateAttachmentId(),
|
|
276
|
+
file,
|
|
277
|
+
previewUrl,
|
|
278
|
+
type,
|
|
279
|
+
state: "pending"
|
|
280
|
+
};
|
|
281
|
+
});
|
|
282
|
+
setAttachments((prev) => [...prev, ...newAttachments]);
|
|
283
|
+
};
|
|
284
|
+
const handlePaste = (event) => {
|
|
285
|
+
if (!showAttachmentPicker || disabled) return;
|
|
286
|
+
const pasted = Array.from(event.clipboardData.items).filter((item) => item.kind === "file").map((item) => item.getAsFile()).filter((f) => f !== null).filter((f) => isFileAccepted(f, acceptedFileTypes));
|
|
287
|
+
if (pasted.length === 0) return;
|
|
288
|
+
event.preventDefault();
|
|
289
|
+
handleFilesSelected(pasted);
|
|
290
|
+
};
|
|
291
|
+
const handleRemoveAttachment = (attachmentId) => {
|
|
292
|
+
setAttachments((prev) => {
|
|
293
|
+
const attachment = prev.find((a) => a.id === attachmentId);
|
|
294
|
+
if (attachment?.previewUrl) {
|
|
295
|
+
URL.revokeObjectURL(attachment.previewUrl);
|
|
296
|
+
}
|
|
297
|
+
return prev.filter((a) => a.id !== attachmentId);
|
|
298
|
+
});
|
|
299
|
+
};
|
|
300
|
+
React6.useEffect(() => {
|
|
301
|
+
const currentAttachments = attachments;
|
|
302
|
+
return () => {
|
|
303
|
+
currentAttachments.forEach((a) => {
|
|
304
|
+
if (a.previewUrl) {
|
|
305
|
+
URL.revokeObjectURL(a.previewUrl);
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
};
|
|
309
|
+
}, [attachments]);
|
|
310
|
+
return /* @__PURE__ */ jsx(
|
|
311
|
+
DragDropZone,
|
|
312
|
+
{
|
|
313
|
+
onFilesDropped: handleFilesSelected,
|
|
314
|
+
acceptedTypes: acceptedFileTypes,
|
|
315
|
+
maxFileSize,
|
|
316
|
+
maxFiles: maxAttachments - attachments.length,
|
|
317
|
+
disabled: disabled || attachments.length >= maxAttachments,
|
|
318
|
+
onError,
|
|
319
|
+
className: cn("w-full", className),
|
|
320
|
+
children: /* @__PURE__ */ jsxs(
|
|
321
|
+
"form",
|
|
322
|
+
{
|
|
323
|
+
onSubmit: handleSubmit,
|
|
324
|
+
"data-slot": "message-composer",
|
|
325
|
+
className: "w-full",
|
|
326
|
+
children: [
|
|
327
|
+
replyTo && /* @__PURE__ */ jsxs(
|
|
328
|
+
"div",
|
|
329
|
+
{
|
|
330
|
+
"data-slot": "composer-reply-preview",
|
|
331
|
+
className: cn(
|
|
332
|
+
"flex items-center gap-2 px-4 py-2",
|
|
333
|
+
"bg-neutral-50 dark:bg-neutral-800/50",
|
|
334
|
+
"border-primary-500 border-s-4"
|
|
335
|
+
),
|
|
336
|
+
children: [
|
|
337
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
338
|
+
/* @__PURE__ */ jsxs("span", { className: "text-primary-800 dark:text-primary-400 text-xs font-medium", children: [
|
|
339
|
+
"Replying to ",
|
|
340
|
+
replyTo.senderName
|
|
341
|
+
] }),
|
|
342
|
+
/* @__PURE__ */ jsx("p", { className: "truncate text-sm text-neutral-600 dark:text-neutral-300", children: replyTo.content })
|
|
343
|
+
] }),
|
|
344
|
+
/* @__PURE__ */ jsx(
|
|
345
|
+
"button",
|
|
346
|
+
{
|
|
347
|
+
type: "button",
|
|
348
|
+
onClick: onCancelReply,
|
|
349
|
+
className: cn(
|
|
350
|
+
"shrink-0 rounded p-1",
|
|
351
|
+
"text-neutral-500 hover:text-neutral-700",
|
|
352
|
+
"dark:text-neutral-400 dark:hover:text-neutral-200",
|
|
353
|
+
"focus:ring-primary-500 focus:ring-2 focus:outline-none"
|
|
354
|
+
),
|
|
355
|
+
"aria-label": "Cancel reply",
|
|
356
|
+
children: /* @__PURE__ */ jsx(
|
|
357
|
+
"svg",
|
|
358
|
+
{
|
|
359
|
+
"aria-hidden": "true",
|
|
360
|
+
className: "h-4 w-4",
|
|
361
|
+
fill: "none",
|
|
362
|
+
viewBox: "0 0 24 24",
|
|
363
|
+
stroke: "currentColor",
|
|
364
|
+
children: /* @__PURE__ */ jsx(
|
|
365
|
+
"path",
|
|
366
|
+
{
|
|
367
|
+
strokeLinecap: "round",
|
|
368
|
+
strokeLinejoin: "round",
|
|
369
|
+
strokeWidth: 2,
|
|
370
|
+
d: "M6 18L18 6M6 6l12 12"
|
|
371
|
+
}
|
|
372
|
+
)
|
|
373
|
+
}
|
|
374
|
+
)
|
|
375
|
+
}
|
|
376
|
+
)
|
|
377
|
+
]
|
|
378
|
+
}
|
|
379
|
+
),
|
|
380
|
+
attachments.length > 0 && /* @__PURE__ */ jsx(
|
|
381
|
+
"div",
|
|
382
|
+
{
|
|
383
|
+
"data-slot": "composer-attachments",
|
|
384
|
+
className: cn(
|
|
385
|
+
"flex flex-wrap gap-2 p-3",
|
|
386
|
+
"border-t border-neutral-200 dark:border-neutral-700"
|
|
387
|
+
),
|
|
388
|
+
children: attachments.map((attachment) => /* @__PURE__ */ jsx(
|
|
389
|
+
AttachmentPreviewItem,
|
|
390
|
+
{
|
|
391
|
+
attachment,
|
|
392
|
+
onRemove: () => handleRemoveAttachment(attachment.id)
|
|
393
|
+
},
|
|
394
|
+
attachment.id
|
|
395
|
+
))
|
|
396
|
+
}
|
|
397
|
+
),
|
|
398
|
+
/* @__PURE__ */ jsxs(
|
|
399
|
+
"div",
|
|
400
|
+
{
|
|
401
|
+
"data-slot": "composer-input-area",
|
|
402
|
+
className: cn(
|
|
403
|
+
"flex items-center gap-2 p-3",
|
|
404
|
+
"bg-white dark:bg-neutral-900",
|
|
405
|
+
variant === "default" && "border-t border-neutral-200 dark:border-neutral-700"
|
|
406
|
+
),
|
|
407
|
+
children: [
|
|
408
|
+
showAttachmentPicker && /* @__PURE__ */ jsx(
|
|
409
|
+
AttachmentPicker,
|
|
410
|
+
{
|
|
411
|
+
onFilesSelected: handleFilesSelected,
|
|
412
|
+
acceptedTypes: acceptedFileTypes,
|
|
413
|
+
maxFileSize,
|
|
414
|
+
maxFiles: maxAttachments - attachments.length,
|
|
415
|
+
disabled: disabled || attachments.length >= maxAttachments,
|
|
416
|
+
onError
|
|
417
|
+
}
|
|
418
|
+
),
|
|
419
|
+
showCameraButton && /* @__PURE__ */ jsx(
|
|
420
|
+
CameraButton,
|
|
421
|
+
{
|
|
422
|
+
onCapture: (file) => handleFilesSelected([file]),
|
|
423
|
+
disabled: disabled || attachments.length >= maxAttachments
|
|
424
|
+
}
|
|
425
|
+
),
|
|
426
|
+
/* @__PURE__ */ jsxs(
|
|
427
|
+
"div",
|
|
428
|
+
{
|
|
429
|
+
"data-slot": "composer-input-wrapper",
|
|
430
|
+
className: "relative flex-1",
|
|
431
|
+
ref: mention.anchorRef,
|
|
432
|
+
children: [
|
|
433
|
+
/* @__PURE__ */ jsx(MentionMenu, { mention }),
|
|
434
|
+
/* @__PURE__ */ jsx(
|
|
435
|
+
"textarea",
|
|
436
|
+
{
|
|
437
|
+
ref: textareaRef,
|
|
438
|
+
"data-slot": "composer-input",
|
|
439
|
+
value: content,
|
|
440
|
+
onChange: (e) => {
|
|
441
|
+
setContent(e.target.value);
|
|
442
|
+
mention.sync(
|
|
443
|
+
e.target.value,
|
|
444
|
+
e.target.selectionStart ?? e.target.value.length
|
|
445
|
+
);
|
|
446
|
+
},
|
|
447
|
+
onClick: (e) => {
|
|
448
|
+
const el = e.currentTarget;
|
|
449
|
+
mention.sync(el.value, el.selectionStart ?? el.value.length);
|
|
450
|
+
},
|
|
451
|
+
onKeyDown: handleKeyDown,
|
|
452
|
+
onPaste: handlePaste,
|
|
453
|
+
placeholder,
|
|
454
|
+
disabled: disabled || isSending,
|
|
455
|
+
rows: 1,
|
|
456
|
+
className: cn(
|
|
457
|
+
"w-full resize-none rounded-2xl py-2.5",
|
|
458
|
+
inputTrailing ? "ps-4 pe-10" : "px-4",
|
|
459
|
+
"bg-neutral-100 dark:bg-neutral-800",
|
|
460
|
+
"text-neutral-900 dark:text-neutral-100",
|
|
461
|
+
"placeholder:text-neutral-400 dark:placeholder:text-neutral-500",
|
|
462
|
+
"focus:ring-primary-500 focus:ring-2 focus:outline-none",
|
|
463
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
464
|
+
"transition-colors",
|
|
465
|
+
"max-h-[150px]"
|
|
466
|
+
),
|
|
467
|
+
"aria-label": "Message",
|
|
468
|
+
"aria-describedby": showCharacterCount ? "char-count" : void 0,
|
|
469
|
+
...mention.inputProps
|
|
470
|
+
}
|
|
471
|
+
),
|
|
472
|
+
inputTrailing && /* @__PURE__ */ jsx(
|
|
473
|
+
"div",
|
|
474
|
+
{
|
|
475
|
+
"data-slot": "composer-input-trailing",
|
|
476
|
+
className: "pointer-events-none absolute end-1 top-0 flex h-[44px] items-center [&>*]:pointer-events-auto",
|
|
477
|
+
children: inputTrailing
|
|
478
|
+
}
|
|
479
|
+
),
|
|
480
|
+
showCharacterCount && /* @__PURE__ */ jsx(
|
|
481
|
+
"div",
|
|
482
|
+
{
|
|
483
|
+
"data-slot": "composer-char-count",
|
|
484
|
+
id: "char-count",
|
|
485
|
+
className: "absolute end-3 bottom-1.5",
|
|
486
|
+
children: /* @__PURE__ */ jsx(CharacterCounter, { current: content.length, max: maxLength })
|
|
487
|
+
}
|
|
488
|
+
)
|
|
489
|
+
]
|
|
490
|
+
}
|
|
491
|
+
),
|
|
492
|
+
/* @__PURE__ */ jsx(
|
|
493
|
+
SendButton,
|
|
494
|
+
{
|
|
495
|
+
canSend,
|
|
496
|
+
isLoading: isSending,
|
|
497
|
+
disabled
|
|
498
|
+
}
|
|
499
|
+
)
|
|
500
|
+
]
|
|
501
|
+
}
|
|
502
|
+
)
|
|
503
|
+
]
|
|
504
|
+
}
|
|
505
|
+
)
|
|
506
|
+
}
|
|
507
|
+
);
|
|
508
|
+
}
|
|
509
|
+
);
|
|
510
|
+
MessageComposer.displayName = "MessageComposer";
|
|
7
511
|
var statusIconVariants = cva(
|
|
8
512
|
"inline-flex items-center gap-0.5 text-current",
|
|
9
513
|
{
|
|
@@ -384,7 +888,7 @@ function defaultFormatTimestamp(timestamp) {
|
|
|
384
888
|
minute: "2-digit"
|
|
385
889
|
});
|
|
386
890
|
}
|
|
387
|
-
var MessageBubble =
|
|
891
|
+
var MessageBubble = React6.forwardRef(
|
|
388
892
|
({
|
|
389
893
|
className,
|
|
390
894
|
message,
|
|
@@ -859,7 +1363,7 @@ function LoadMoreButton({
|
|
|
859
1363
|
);
|
|
860
1364
|
}
|
|
861
1365
|
LoadMoreButton.displayName = "LoadMoreButton";
|
|
862
|
-
var MessageList =
|
|
1366
|
+
var MessageList = React6.forwardRef(
|
|
863
1367
|
({
|
|
864
1368
|
messages,
|
|
865
1369
|
currentUser,
|
|
@@ -878,19 +1382,19 @@ var MessageList = React5.forwardRef(
|
|
|
878
1382
|
className,
|
|
879
1383
|
autoScroll = "onNewMessage"
|
|
880
1384
|
}, ref) => {
|
|
881
|
-
const scrollContainerRef =
|
|
882
|
-
const bottomRef =
|
|
883
|
-
const [isUserScrolled, setIsUserScrolled] =
|
|
884
|
-
const prevMessageCountRef =
|
|
885
|
-
|
|
886
|
-
const handleScroll =
|
|
1385
|
+
const scrollContainerRef = React6.useRef(null);
|
|
1386
|
+
const bottomRef = React6.useRef(null);
|
|
1387
|
+
const [isUserScrolled, setIsUserScrolled] = React6.useState(false);
|
|
1388
|
+
const prevMessageCountRef = React6.useRef(messages.length);
|
|
1389
|
+
React6.useImperativeHandle(ref, () => scrollContainerRef.current);
|
|
1390
|
+
const handleScroll = React6.useCallback(() => {
|
|
887
1391
|
const container = scrollContainerRef.current;
|
|
888
1392
|
if (!container) return;
|
|
889
1393
|
const { scrollTop, scrollHeight, clientHeight } = container;
|
|
890
1394
|
const isAtBottom = scrollHeight - scrollTop - clientHeight < 100;
|
|
891
1395
|
setIsUserScrolled(!isAtBottom);
|
|
892
1396
|
}, []);
|
|
893
|
-
|
|
1397
|
+
React6.useEffect(() => {
|
|
894
1398
|
const container = scrollContainerRef.current;
|
|
895
1399
|
const bottom = bottomRef.current;
|
|
896
1400
|
if (!container || !bottom) return;
|
|
@@ -906,7 +1410,7 @@ var MessageList = React5.forwardRef(
|
|
|
906
1410
|
}
|
|
907
1411
|
}
|
|
908
1412
|
}, [messages, currentUser.id, autoScroll, isUserScrolled]);
|
|
909
|
-
|
|
1413
|
+
React6.useEffect(() => {
|
|
910
1414
|
const bottom = bottomRef.current;
|
|
911
1415
|
if (bottom && !isLoading) {
|
|
912
1416
|
bottom.scrollIntoView();
|
|
@@ -959,7 +1463,7 @@ var MessageList = React5.forwardRef(
|
|
|
959
1463
|
{
|
|
960
1464
|
"data-slot": "message-list-content",
|
|
961
1465
|
className: "flex flex-col gap-1 p-4",
|
|
962
|
-
children: messageGroups.map((group) => /* @__PURE__ */ jsxs(
|
|
1466
|
+
children: messageGroups.map((group) => /* @__PURE__ */ jsxs(React6.Fragment, { children: [
|
|
963
1467
|
groupByDate && group.label && /* @__PURE__ */ jsx(DateSeparator, { label: group.label }),
|
|
964
1468
|
group.messages.map((message, index) => {
|
|
965
1469
|
const prevMessage = group.messages[index - 1];
|
|
@@ -1098,7 +1602,7 @@ function formatLastSeen(date) {
|
|
|
1098
1602
|
if (diffDays < 7) return `${diffDays}d ago`;
|
|
1099
1603
|
return date.toLocaleDateString();
|
|
1100
1604
|
}
|
|
1101
|
-
var ConversationHeader =
|
|
1605
|
+
var ConversationHeader = React6.forwardRef(
|
|
1102
1606
|
({
|
|
1103
1607
|
className,
|
|
1104
1608
|
size,
|
|
@@ -1220,7 +1724,7 @@ var ConversationHeader = React5.forwardRef(
|
|
|
1220
1724
|
}
|
|
1221
1725
|
);
|
|
1222
1726
|
ConversationHeader.displayName = "ConversationHeader";
|
|
1223
|
-
var ConversationListItem =
|
|
1727
|
+
var ConversationListItem = React6.forwardRef(({ className, conversation, isSelected, onSelect, ...props }, ref) => {
|
|
1224
1728
|
const participant = conversation.participants.find((p) => !p.isCurrentUser);
|
|
1225
1729
|
const title = getConversationTitle(conversation, participant);
|
|
1226
1730
|
const avatarUrl = conversation.avatarUrl || participant?.avatarUrl;
|
|
@@ -1428,7 +1932,7 @@ function ConversationListSkeleton({
|
|
|
1428
1932
|
}
|
|
1429
1933
|
ConversationListSkeleton.displayName = "ConversationListSkeleton";
|
|
1430
1934
|
function LightboxModal({ attachment, onClose }) {
|
|
1431
|
-
|
|
1935
|
+
React6.useEffect(() => {
|
|
1432
1936
|
const handleKeyDown = (event) => {
|
|
1433
1937
|
if (event.key === "Escape") {
|
|
1434
1938
|
onClose();
|
|
@@ -1525,7 +2029,7 @@ function LightboxModal({ attachment, onClose }) {
|
|
|
1525
2029
|
);
|
|
1526
2030
|
}
|
|
1527
2031
|
LightboxModal.displayName = "LightboxModal";
|
|
1528
|
-
var MessageThread =
|
|
2032
|
+
var MessageThread = React6.forwardRef(
|
|
1529
2033
|
({
|
|
1530
2034
|
conversation,
|
|
1531
2035
|
messages,
|
|
@@ -1556,8 +2060,8 @@ var MessageThread = React5.forwardRef(
|
|
|
1556
2060
|
onError,
|
|
1557
2061
|
className
|
|
1558
2062
|
}, ref) => {
|
|
1559
|
-
const [lightboxAttachment, setLightboxAttachment] =
|
|
1560
|
-
const [replyTo, setReplyTo] =
|
|
2063
|
+
const [lightboxAttachment, setLightboxAttachment] = React6.useState(null);
|
|
2064
|
+
const [replyTo, setReplyTo] = React6.useState(null);
|
|
1561
2065
|
const participant = conversation?.type === "direct" ? conversation.participants.find((p) => p.id !== currentUser.id) : void 0;
|
|
1562
2066
|
const handleAttachmentClick = (attachment, message) => {
|
|
1563
2067
|
if (attachment.type === "image" || attachment.type === "video") {
|
|
@@ -1729,13 +2233,13 @@ function useMessages(options) {
|
|
|
1729
2233
|
onRetry,
|
|
1730
2234
|
onLoadMore
|
|
1731
2235
|
} = options;
|
|
1732
|
-
const [messages, setMessages] =
|
|
1733
|
-
const [isSending, setIsSending] =
|
|
1734
|
-
const [isLoadingMore, setIsLoadingMore] =
|
|
1735
|
-
|
|
2236
|
+
const [messages, setMessages] = React6.useState(initialMessages);
|
|
2237
|
+
const [isSending, setIsSending] = React6.useState(false);
|
|
2238
|
+
const [isLoadingMore, setIsLoadingMore] = React6.useState(false);
|
|
2239
|
+
React6.useEffect(() => {
|
|
1736
2240
|
setMessages(initialMessages);
|
|
1737
2241
|
}, [initialMessages]);
|
|
1738
|
-
const addMessage =
|
|
2242
|
+
const addMessage = React6.useCallback((message) => {
|
|
1739
2243
|
setMessages((prev) => {
|
|
1740
2244
|
if (prev.some((m) => m.id === message.id)) {
|
|
1741
2245
|
return prev;
|
|
@@ -1743,7 +2247,7 @@ function useMessages(options) {
|
|
|
1743
2247
|
return [...prev, message];
|
|
1744
2248
|
});
|
|
1745
2249
|
}, []);
|
|
1746
|
-
const updateMessage =
|
|
2250
|
+
const updateMessage = React6.useCallback(
|
|
1747
2251
|
(messageId, updates) => {
|
|
1748
2252
|
setMessages(
|
|
1749
2253
|
(prev) => prev.map((m) => m.id === messageId ? { ...m, ...updates } : m)
|
|
@@ -1751,22 +2255,22 @@ function useMessages(options) {
|
|
|
1751
2255
|
},
|
|
1752
2256
|
[]
|
|
1753
2257
|
);
|
|
1754
|
-
const removeMessage =
|
|
2258
|
+
const removeMessage = React6.useCallback((messageId) => {
|
|
1755
2259
|
setMessages((prev) => prev.filter((m) => m.id !== messageId));
|
|
1756
2260
|
}, []);
|
|
1757
|
-
const updateStatus =
|
|
2261
|
+
const updateStatus = React6.useCallback(
|
|
1758
2262
|
(messageId, status) => {
|
|
1759
2263
|
updateMessage(messageId, { status });
|
|
1760
2264
|
},
|
|
1761
2265
|
[updateMessage]
|
|
1762
2266
|
);
|
|
1763
|
-
const markAsRead =
|
|
2267
|
+
const markAsRead = React6.useCallback(
|
|
1764
2268
|
(messageId) => {
|
|
1765
2269
|
updateStatus(messageId, "read");
|
|
1766
2270
|
},
|
|
1767
2271
|
[updateStatus]
|
|
1768
2272
|
);
|
|
1769
|
-
const sendMessage =
|
|
2273
|
+
const sendMessage = React6.useCallback(
|
|
1770
2274
|
async (newMessage) => {
|
|
1771
2275
|
const optimisticId = `optimistic-${Date.now()}`;
|
|
1772
2276
|
const optimisticMessage = {
|
|
@@ -1798,7 +2302,7 @@ function useMessages(options) {
|
|
|
1798
2302
|
},
|
|
1799
2303
|
[currentUser, onSend, addMessage, updateStatus]
|
|
1800
2304
|
);
|
|
1801
|
-
const retryMessage =
|
|
2305
|
+
const retryMessage = React6.useCallback(
|
|
1802
2306
|
async (messageId) => {
|
|
1803
2307
|
updateStatus(messageId, "sending");
|
|
1804
2308
|
try {
|
|
@@ -1812,7 +2316,7 @@ function useMessages(options) {
|
|
|
1812
2316
|
},
|
|
1813
2317
|
[onRetry, updateStatus]
|
|
1814
2318
|
);
|
|
1815
|
-
const loadMore =
|
|
2319
|
+
const loadMore = React6.useCallback(async () => {
|
|
1816
2320
|
if (isLoadingMore || !onLoadMore) return;
|
|
1817
2321
|
setIsLoadingMore(true);
|
|
1818
2322
|
try {
|
|
@@ -1843,13 +2347,13 @@ function useTypingIndicator(options = {}) {
|
|
|
1843
2347
|
onTypingStart,
|
|
1844
2348
|
onTypingStop
|
|
1845
2349
|
} = options;
|
|
1846
|
-
const [participants, setParticipants] =
|
|
1847
|
-
const [isLocalTyping, setIsLocalTyping] =
|
|
1848
|
-
const typingTimeoutRef =
|
|
1849
|
-
|
|
2350
|
+
const [participants, setParticipants] = React6.useState(initialParticipants);
|
|
2351
|
+
const [isLocalTyping, setIsLocalTyping] = React6.useState(false);
|
|
2352
|
+
const typingTimeoutRef = React6.useRef(null);
|
|
2353
|
+
React6.useEffect(() => {
|
|
1850
2354
|
setParticipants(initialParticipants);
|
|
1851
2355
|
}, [initialParticipants]);
|
|
1852
|
-
const startTyping =
|
|
2356
|
+
const startTyping = React6.useCallback(() => {
|
|
1853
2357
|
if (!isLocalTyping) {
|
|
1854
2358
|
setIsLocalTyping(true);
|
|
1855
2359
|
onTypingStart?.();
|
|
@@ -1862,21 +2366,21 @@ function useTypingIndicator(options = {}) {
|
|
|
1862
2366
|
onTypingStop?.();
|
|
1863
2367
|
}, debounceTime);
|
|
1864
2368
|
}, [isLocalTyping, debounceTime, onTypingStart, onTypingStop]);
|
|
1865
|
-
const stopTyping =
|
|
2369
|
+
const stopTyping = React6.useCallback(() => {
|
|
1866
2370
|
if (typingTimeoutRef.current) {
|
|
1867
2371
|
clearTimeout(typingTimeoutRef.current);
|
|
1868
2372
|
}
|
|
1869
2373
|
setIsLocalTyping(false);
|
|
1870
2374
|
onTypingStop?.();
|
|
1871
2375
|
}, [onTypingStop]);
|
|
1872
|
-
|
|
2376
|
+
React6.useEffect(() => {
|
|
1873
2377
|
return () => {
|
|
1874
2378
|
if (typingTimeoutRef.current) {
|
|
1875
2379
|
clearTimeout(typingTimeoutRef.current);
|
|
1876
2380
|
}
|
|
1877
2381
|
};
|
|
1878
2382
|
}, []);
|
|
1879
|
-
const typingState =
|
|
2383
|
+
const typingState = React6.useMemo(
|
|
1880
2384
|
() => ({
|
|
1881
2385
|
participants,
|
|
1882
2386
|
lastUpdated: /* @__PURE__ */ new Date()
|
|
@@ -1892,11 +2396,11 @@ function useTypingIndicator(options = {}) {
|
|
|
1892
2396
|
}
|
|
1893
2397
|
function useMessageScroll(options) {
|
|
1894
2398
|
const { messages, currentUserId, threshold = 100 } = options;
|
|
1895
|
-
const scrollContainerRef =
|
|
1896
|
-
const bottomRef =
|
|
1897
|
-
const [isScrolledUp, setIsScrolledUp] =
|
|
1898
|
-
const prevMessageCountRef =
|
|
1899
|
-
|
|
2399
|
+
const scrollContainerRef = React6.useRef(null);
|
|
2400
|
+
const bottomRef = React6.useRef(null);
|
|
2401
|
+
const [isScrolledUp, setIsScrolledUp] = React6.useState(false);
|
|
2402
|
+
const prevMessageCountRef = React6.useRef(messages.length);
|
|
2403
|
+
React6.useEffect(() => {
|
|
1900
2404
|
const container = scrollContainerRef.current;
|
|
1901
2405
|
if (!container) return;
|
|
1902
2406
|
const handleScroll = () => {
|
|
@@ -1907,12 +2411,12 @@ function useMessageScroll(options) {
|
|
|
1907
2411
|
container.addEventListener("scroll", handleScroll);
|
|
1908
2412
|
return () => container.removeEventListener("scroll", handleScroll);
|
|
1909
2413
|
}, [threshold]);
|
|
1910
|
-
const scrollToBottom =
|
|
2414
|
+
const scrollToBottom = React6.useCallback((smooth = true) => {
|
|
1911
2415
|
bottomRef.current?.scrollIntoView({
|
|
1912
2416
|
behavior: smooth ? "smooth" : "auto"
|
|
1913
2417
|
});
|
|
1914
2418
|
}, []);
|
|
1915
|
-
|
|
2419
|
+
React6.useEffect(() => {
|
|
1916
2420
|
const messageCountChanged = messages.length !== prevMessageCountRef.current;
|
|
1917
2421
|
prevMessageCountRef.current = messages.length;
|
|
1918
2422
|
if (!messageCountChanged) return;
|
|
@@ -1931,9 +2435,9 @@ function useMessageScroll(options) {
|
|
|
1931
2435
|
}
|
|
1932
2436
|
function useReadReceipts(options) {
|
|
1933
2437
|
const { currentUserId, onMarkRead, threshold = 0.5 } = options;
|
|
1934
|
-
const observerRef =
|
|
1935
|
-
const observedMessagesRef =
|
|
1936
|
-
|
|
2438
|
+
const observerRef = React6.useRef(null);
|
|
2439
|
+
const observedMessagesRef = React6.useRef(/* @__PURE__ */ new Set());
|
|
2440
|
+
React6.useEffect(() => {
|
|
1937
2441
|
observerRef.current = new IntersectionObserver(
|
|
1938
2442
|
(entries) => {
|
|
1939
2443
|
entries.forEach((entry) => {
|
|
@@ -1952,7 +2456,7 @@ function useReadReceipts(options) {
|
|
|
1952
2456
|
observerRef.current?.disconnect();
|
|
1953
2457
|
};
|
|
1954
2458
|
}, [onMarkRead, threshold]);
|
|
1955
|
-
const observeMessage =
|
|
2459
|
+
const observeMessage = React6.useCallback(
|
|
1956
2460
|
(element, message) => {
|
|
1957
2461
|
if (!element || !observerRef.current) return;
|
|
1958
2462
|
if (message.sender.id !== currentUserId && message.status !== "read" && !observedMessagesRef.current.has(message.id)) {
|
|
@@ -1965,6 +2469,6 @@ function useReadReceipts(options) {
|
|
|
1965
2469
|
return { observeMessage };
|
|
1966
2470
|
}
|
|
1967
2471
|
|
|
1968
|
-
export { AttachmentPreview, ConversationHeader, ConversationListItem, ConversationListSkeleton, DateSeparator, EmptyState, LightboxModal, LoadMoreButton, MessageBubble, MessageList, MessageStatusIcon, MessageThread, MessagingSplitView, ReadReceiptIndicator, SkeletonMessage, TypingIndicator, bubbleVariants, formatDateLabel, formatFileSize, formatLastSeen, getConversationSubtitle, getConversationTitle, groupMessagesByDate, headerVariants, isSameSenderGroup, useMessageScroll, useMessages, useReadReceipts, useTypingIndicator };
|
|
1969
|
-
//# sourceMappingURL=chunk-
|
|
1970
|
-
//# sourceMappingURL=chunk-
|
|
2472
|
+
export { AttachmentPreview, CharacterCounter, ConversationHeader, ConversationListItem, ConversationListSkeleton, DateSeparator, EmptyState, LightboxModal, LoadMoreButton, MessageBubble, MessageComposer, MessageList, MessageStatusIcon, MessageThread, MessagingSplitView, ReadReceiptIndicator, SendButton, SkeletonMessage, TypingIndicator, bubbleVariants, formatDateLabel, formatFileSize, formatLastSeen, getConversationSubtitle, getConversationTitle, groupMessagesByDate, headerVariants, isSameSenderGroup, sendButtonVariants, useMessageScroll, useMessages, useReadReceipts, useTypingIndicator };
|
|
2473
|
+
//# sourceMappingURL=chunk-RPG2ZP2T.js.map
|
|
2474
|
+
//# sourceMappingURL=chunk-RPG2ZP2T.js.map
|