@mieweb/ui 0.9.0-dev.4 → 0.9.0-dev.6

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.
Files changed (50) hide show
  1. package/dist/{chunk-DG6LIEUJ.js → chunk-22CYDCXA.js} +13 -1183
  2. package/dist/chunk-22CYDCXA.js.map +1 -0
  3. package/dist/chunk-3SD7PDCV.js +1878 -0
  4. package/dist/chunk-3SD7PDCV.js.map +1 -0
  5. package/dist/{chunk-USZ3VEPE.js → chunk-NORSXAXL.js} +853 -779
  6. package/dist/chunk-NORSXAXL.js.map +1 -0
  7. package/dist/{chunk-Z3RYJWSV.cjs → chunk-PY7XAG4D.cjs} +3 -4
  8. package/dist/chunk-PY7XAG4D.cjs.map +1 -0
  9. package/dist/chunk-T6PTKWM6.cjs +1912 -0
  10. package/dist/chunk-T6PTKWM6.cjs.map +1 -0
  11. package/dist/{chunk-DH7I5HMK.cjs → chunk-WIAQQNFU.cjs} +12 -1184
  12. package/dist/chunk-WIAQQNFU.cjs.map +1 -0
  13. package/dist/{chunk-XEZCHQHB.js → chunk-X5YM3KXY.js} +3 -4
  14. package/dist/chunk-X5YM3KXY.js.map +1 -0
  15. package/dist/{chunk-3MHG4HYB.cjs → chunk-XCNDZNY7.cjs} +855 -780
  16. package/dist/chunk-XCNDZNY7.cjs.map +1 -0
  17. package/dist/components/SuperChat/index.cjs +8 -8
  18. package/dist/components/SuperChat/index.d.cts +4 -4
  19. package/dist/components/SuperChat/index.d.ts +4 -4
  20. package/dist/components/SuperChat/index.js +2 -2
  21. package/dist/components/SuperChat/plugins/index.cjs +10 -3
  22. package/dist/components/SuperChat/plugins/index.cjs.map +1 -1
  23. package/dist/components/SuperChat/plugins/index.d.cts +3 -3
  24. package/dist/components/SuperChat/plugins/index.d.ts +3 -3
  25. package/dist/components/SuperChat/plugins/index.js +9 -2
  26. package/dist/components/SuperChat/plugins/index.js.map +1 -1
  27. package/dist/index.cjs +171 -114
  28. package/dist/index.cjs.map +1 -1
  29. package/dist/index.d.cts +471 -410
  30. package/dist/index.d.ts +471 -410
  31. package/dist/index.js +95 -41
  32. package/dist/index.js.map +1 -1
  33. package/dist/styles.css +1 -1
  34. package/dist/tailwind-preset.cjs +4 -4
  35. package/dist/tailwind-preset.js +1 -1
  36. package/dist/{types-DZDroUW8.d.cts → types-B97JNRNQ.d.cts} +1 -1
  37. package/dist/{types-B2RLmdVd.d.cts → types-CnPEwzlp.d.cts} +6 -2
  38. package/dist/{types-B2RLmdVd.d.ts → types-CnPEwzlp.d.ts} +6 -2
  39. package/dist/{types-9vK_YdEb.d.ts → types-gZXrfLj6.d.ts} +1 -1
  40. package/package.json +1 -1
  41. package/dist/chunk-3MHG4HYB.cjs.map +0 -1
  42. package/dist/chunk-5VXRKOPR.cjs +0 -735
  43. package/dist/chunk-5VXRKOPR.cjs.map +0 -1
  44. package/dist/chunk-DG6LIEUJ.js.map +0 -1
  45. package/dist/chunk-DH7I5HMK.cjs.map +0 -1
  46. package/dist/chunk-IF3X2WZP.js +0 -705
  47. package/dist/chunk-IF3X2WZP.js.map +0 -1
  48. package/dist/chunk-USZ3VEPE.js.map +0 -1
  49. package/dist/chunk-XEZCHQHB.js.map +0 -1
  50. package/dist/chunk-Z3RYJWSV.cjs.map +0 -1
@@ -1,705 +0,0 @@
1
- import { cn } from './chunk-TQONQSJO.js';
2
- import { useAnchoredPosition } from './chunk-WSNMQQ4P.js';
3
- import * as React from 'react';
4
- import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
5
- import { createPortal } from 'react-dom';
6
-
7
- function getFileType(mimeType) {
8
- if (mimeType.startsWith("image/")) return "image";
9
- if (mimeType.startsWith("video/")) return "video";
10
- if (mimeType.startsWith("audio/")) return "audio";
11
- if (mimeType.includes("pdf") || mimeType.includes("document") || mimeType.includes("text")) {
12
- return "document";
13
- }
14
- return "file";
15
- }
16
- function formatFileSize(bytes) {
17
- if (bytes < 1024) return `${bytes} B`;
18
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
19
- return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
20
- }
21
- function validateFile(file, acceptedTypes, maxSize) {
22
- if (acceptedTypes && acceptedTypes.length > 0) {
23
- const isAccepted = acceptedTypes.some((type) => {
24
- if (type.startsWith(".")) {
25
- return file.name.toLowerCase().endsWith(type.toLowerCase());
26
- }
27
- if (type.endsWith("/*")) {
28
- return file.type.startsWith(type.replace("/*", "/"));
29
- }
30
- return file.type === type;
31
- });
32
- if (!isAccepted) {
33
- return { valid: false, error: "File type not supported" };
34
- }
35
- }
36
- if (maxSize && file.size > maxSize) {
37
- return {
38
- valid: false,
39
- error: `File too large (max ${formatFileSize(maxSize)})`
40
- };
41
- }
42
- return { valid: true };
43
- }
44
- function generateAttachmentId() {
45
- return `attachment-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
46
- }
47
- function AttachmentPreviewItem({
48
- attachment,
49
- onRemove,
50
- onRetry,
51
- disabled = false,
52
- className
53
- }) {
54
- const { file, previewUrl, type, state, progress } = attachment;
55
- const isImage = type === "image";
56
- const isVideo = type === "video";
57
- const isUploading = state === "uploading";
58
- const isFailed = state === "failed";
59
- return /* @__PURE__ */ jsxs(
60
- "div",
61
- {
62
- className: cn(
63
- "group relative overflow-hidden rounded-lg",
64
- "bg-neutral-100 dark:bg-neutral-800",
65
- "border border-neutral-200 dark:border-neutral-700",
66
- isFailed && "border-red-500",
67
- className
68
- ),
69
- children: [
70
- (isImage || isVideo) && previewUrl ? /* @__PURE__ */ jsxs("div", { className: "relative h-20 w-20", children: [
71
- isVideo ? /* @__PURE__ */ jsx(
72
- "video",
73
- {
74
- src: previewUrl,
75
- "aria-label": file.name,
76
- muted: true,
77
- playsInline: true,
78
- preload: "metadata",
79
- className: cn(
80
- "h-full w-full object-cover",
81
- (isUploading || isFailed) && "opacity-50"
82
- )
83
- }
84
- ) : /* @__PURE__ */ jsx(
85
- "img",
86
- {
87
- src: previewUrl,
88
- alt: file.name,
89
- className: cn(
90
- "h-full w-full object-cover",
91
- (isUploading || isFailed) && "opacity-50"
92
- )
93
- }
94
- ),
95
- isVideo && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ jsx(
96
- "svg",
97
- {
98
- "aria-hidden": "true",
99
- className: "h-6 w-6 text-white drop-shadow",
100
- fill: "currentColor",
101
- viewBox: "0 0 24 24",
102
- children: /* @__PURE__ */ jsx("path", { d: "M8 5v14l11-7z" })
103
- }
104
- ) })
105
- ] }) : (
106
- /* File preview */
107
- /* @__PURE__ */ jsxs("div", { className: "flex h-20 w-20 flex-col items-center justify-center p-2", children: [
108
- /* @__PURE__ */ jsx(
109
- "svg",
110
- {
111
- "aria-hidden": "true",
112
- className: "h-8 w-8 text-neutral-500",
113
- fill: "none",
114
- viewBox: "0 0 24 24",
115
- stroke: "currentColor",
116
- children: /* @__PURE__ */ jsx(
117
- "path",
118
- {
119
- strokeLinecap: "round",
120
- strokeLinejoin: "round",
121
- strokeWidth: 2,
122
- d: "M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
123
- }
124
- )
125
- }
126
- ),
127
- /* @__PURE__ */ jsx("span", { className: "mt-1 max-w-full truncate px-1 text-xs text-neutral-600 dark:text-neutral-400", children: file.name.split(".").pop()?.toUpperCase() })
128
- ] })
129
- ),
130
- isUploading && progress !== void 0 && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-black/50", children: /* @__PURE__ */ jsxs("div", { className: "text-center text-white", children: [
131
- /* @__PURE__ */ jsxs(
132
- "svg",
133
- {
134
- "aria-hidden": "true",
135
- className: "mx-auto h-6 w-6 animate-spin",
136
- fill: "none",
137
- viewBox: "0 0 24 24",
138
- children: [
139
- /* @__PURE__ */ jsx(
140
- "circle",
141
- {
142
- className: "opacity-25",
143
- cx: "12",
144
- cy: "12",
145
- r: "10",
146
- stroke: "currentColor",
147
- strokeWidth: "4"
148
- }
149
- ),
150
- /* @__PURE__ */ jsx(
151
- "path",
152
- {
153
- className: "opacity-75",
154
- fill: "currentColor",
155
- d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
156
- }
157
- )
158
- ]
159
- }
160
- ),
161
- /* @__PURE__ */ jsxs("span", { className: "mt-1 text-xs", children: [
162
- progress,
163
- "%"
164
- ] })
165
- ] }) }),
166
- isFailed && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-black/50", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
167
- /* @__PURE__ */ jsx(
168
- "svg",
169
- {
170
- "aria-hidden": "true",
171
- className: "mx-auto h-6 w-6 text-red-400",
172
- fill: "none",
173
- viewBox: "0 0 24 24",
174
- stroke: "currentColor",
175
- children: /* @__PURE__ */ jsx(
176
- "path",
177
- {
178
- strokeLinecap: "round",
179
- strokeLinejoin: "round",
180
- strokeWidth: 2,
181
- d: "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
182
- }
183
- )
184
- }
185
- ),
186
- onRetry && /* @__PURE__ */ jsx(
187
- "button",
188
- {
189
- type: "button",
190
- onClick: onRetry,
191
- disabled,
192
- className: "mt-1 text-xs text-white underline hover:no-underline disabled:no-underline disabled:opacity-50",
193
- children: "Retry"
194
- }
195
- )
196
- ] }) }),
197
- /* @__PURE__ */ jsx(
198
- "button",
199
- {
200
- type: "button",
201
- onClick: onRemove,
202
- disabled,
203
- className: cn(
204
- "absolute -end-1 -top-1 z-10",
205
- "rounded-full p-1",
206
- "bg-neutral-900 text-white",
207
- "opacity-0 group-hover:opacity-100",
208
- "focus:ring-primary-500 focus:opacity-100 focus:ring-2 focus:outline-none",
209
- "transition-opacity",
210
- "disabled:cursor-not-allowed disabled:group-hover:opacity-0"
211
- ),
212
- "aria-label": `Remove ${file.name}`,
213
- children: /* @__PURE__ */ jsx(
214
- "svg",
215
- {
216
- "aria-hidden": "true",
217
- className: "h-3 w-3",
218
- fill: "none",
219
- viewBox: "0 0 24 24",
220
- stroke: "currentColor",
221
- children: /* @__PURE__ */ jsx(
222
- "path",
223
- {
224
- strokeLinecap: "round",
225
- strokeLinejoin: "round",
226
- strokeWidth: 2,
227
- d: "M6 18L18 6M6 6l12 12"
228
- }
229
- )
230
- }
231
- )
232
- }
233
- ),
234
- /* @__PURE__ */ jsx(
235
- "div",
236
- {
237
- className: cn(
238
- "absolute start-0 end-0 bottom-0",
239
- "bg-black/70 px-1 py-0.5",
240
- "opacity-0 group-hover:opacity-100",
241
- "transition-opacity"
242
- ),
243
- children: /* @__PURE__ */ jsx("p", { className: "truncate text-xs text-white", children: file.name })
244
- }
245
- )
246
- ]
247
- }
248
- );
249
- }
250
- AttachmentPreviewItem.displayName = "AttachmentPreviewItem";
251
- var AttachmentPicker = React.forwardRef(
252
- ({
253
- onFilesSelected,
254
- acceptedTypes = ["image/*", "video/*", ".pdf", ".doc", ".docx"],
255
- maxFileSize = 25 * 1024 * 1024,
256
- // 25MB
257
- maxFiles = 10,
258
- multiple = true,
259
- disabled = false,
260
- onError,
261
- className,
262
- children
263
- }, ref) => {
264
- const inputRef = React.useRef(null);
265
- React.useImperativeHandle(ref, () => inputRef.current);
266
- const handleClick = () => {
267
- inputRef.current?.click();
268
- };
269
- const handleChange = (event) => {
270
- const files = Array.from(event.target.files || []);
271
- if (files.length === 0) return;
272
- const validFiles = [];
273
- for (const file of files.slice(0, maxFiles)) {
274
- const validation = validateFile(file, acceptedTypes, maxFileSize);
275
- if (validation.valid) {
276
- validFiles.push(file);
277
- } else if (onError) {
278
- onError(`${file.name}: ${validation.error}`);
279
- }
280
- }
281
- if (files.length > maxFiles && onError) {
282
- onError(`Maximum ${maxFiles} files allowed`);
283
- }
284
- if (validFiles.length > 0) {
285
- onFilesSelected(validFiles);
286
- }
287
- event.target.value = "";
288
- };
289
- return /* @__PURE__ */ jsxs(Fragment, { children: [
290
- /* @__PURE__ */ jsx(
291
- "input",
292
- {
293
- ref: inputRef,
294
- type: "file",
295
- accept: acceptedTypes.join(","),
296
- multiple,
297
- onChange: handleChange,
298
- disabled,
299
- className: "sr-only",
300
- "aria-label": "Select files to attach"
301
- }
302
- ),
303
- /* @__PURE__ */ jsx(
304
- "button",
305
- {
306
- type: "button",
307
- onClick: handleClick,
308
- disabled,
309
- "data-slot": "attachment-picker-button",
310
- className: cn(
311
- "inline-flex items-center justify-center",
312
- "rounded-full p-2",
313
- "text-neutral-500 hover:text-neutral-700",
314
- "dark:text-neutral-400 dark:hover:text-neutral-200",
315
- "hover:bg-neutral-100 dark:hover:bg-neutral-800",
316
- "focus:ring-primary-500 focus:ring-2 focus:outline-none",
317
- "disabled:cursor-not-allowed disabled:opacity-50",
318
- "transition-colors",
319
- className
320
- ),
321
- "aria-label": "Attach files",
322
- children: children || /* @__PURE__ */ jsx(
323
- "svg",
324
- {
325
- "aria-hidden": "true",
326
- className: "h-5 w-5",
327
- fill: "none",
328
- viewBox: "0 0 24 24",
329
- stroke: "currentColor",
330
- children: /* @__PURE__ */ jsx(
331
- "path",
332
- {
333
- strokeLinecap: "round",
334
- strokeLinejoin: "round",
335
- strokeWidth: 2,
336
- d: "M15.172 7l-6.586 6.586a2 2 0 102.828 2.828l6.414-6.586a4 4 0 00-5.656-5.656l-6.415 6.585a6 6 0 108.486 8.486L20.5 13"
337
- }
338
- )
339
- }
340
- )
341
- }
342
- )
343
- ] });
344
- }
345
- );
346
- AttachmentPicker.displayName = "AttachmentPicker";
347
- function DragDropZone({
348
- onFilesDropped,
349
- acceptedTypes,
350
- maxFileSize,
351
- maxFiles = 10,
352
- disabled = false,
353
- onError,
354
- overlayLabel = "Drop files here",
355
- children,
356
- className
357
- }) {
358
- const [isDragging, setIsDragging] = React.useState(false);
359
- const dragCounterRef = React.useRef(0);
360
- const handleDragEnter = (event) => {
361
- event.preventDefault();
362
- event.stopPropagation();
363
- if (disabled) return;
364
- dragCounterRef.current++;
365
- if (event.dataTransfer.items && event.dataTransfer.items.length > 0) {
366
- setIsDragging(true);
367
- }
368
- };
369
- const handleDragLeave = (event) => {
370
- event.preventDefault();
371
- event.stopPropagation();
372
- if (disabled) return;
373
- dragCounterRef.current--;
374
- if (dragCounterRef.current === 0) {
375
- setIsDragging(false);
376
- }
377
- };
378
- const handleDragOver = (event) => {
379
- event.preventDefault();
380
- event.stopPropagation();
381
- };
382
- const handleDrop = (event) => {
383
- event.preventDefault();
384
- event.stopPropagation();
385
- if (disabled) return;
386
- setIsDragging(false);
387
- dragCounterRef.current = 0;
388
- const files = Array.from(event.dataTransfer.files);
389
- if (files.length === 0) return;
390
- const validFiles = [];
391
- for (const file of files.slice(0, maxFiles)) {
392
- const validation = validateFile(file, acceptedTypes, maxFileSize);
393
- if (validation.valid) {
394
- validFiles.push(file);
395
- } else if (onError) {
396
- onError(`${file.name}: ${validation.error}`);
397
- }
398
- }
399
- if (files.length > maxFiles && onError) {
400
- onError(`Maximum ${maxFiles} files allowed`);
401
- }
402
- if (validFiles.length > 0) {
403
- onFilesDropped(validFiles);
404
- }
405
- };
406
- return /* @__PURE__ */ jsxs(
407
- "div",
408
- {
409
- onDragEnter: handleDragEnter,
410
- onDragLeave: handleDragLeave,
411
- onDragOver: handleDragOver,
412
- onDrop: handleDrop,
413
- className: cn("relative", className),
414
- children: [
415
- children,
416
- isDragging && /* @__PURE__ */ jsx(
417
- "div",
418
- {
419
- className: cn(
420
- "absolute inset-0 z-50",
421
- "flex items-center justify-center",
422
- "bg-primary-50/90 dark:bg-primary-900/90",
423
- "border-primary-500 border-2 border-dashed",
424
- "rounded-lg"
425
- ),
426
- children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
427
- /* @__PURE__ */ jsx(
428
- "svg",
429
- {
430
- "aria-hidden": "true",
431
- className: "text-primary-800 mx-auto h-12 w-12",
432
- fill: "none",
433
- viewBox: "0 0 24 24",
434
- stroke: "currentColor",
435
- children: /* @__PURE__ */ jsx(
436
- "path",
437
- {
438
- strokeLinecap: "round",
439
- strokeLinejoin: "round",
440
- strokeWidth: 2,
441
- d: "M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"
442
- }
443
- )
444
- }
445
- ),
446
- /* @__PURE__ */ jsx("p", { className: "text-primary-700 dark:text-primary-300 mt-2 text-sm font-medium", children: overlayLabel })
447
- ] })
448
- }
449
- )
450
- ]
451
- }
452
- );
453
- }
454
- DragDropZone.displayName = "DragDropZone";
455
- function CameraButton({
456
- onCapture,
457
- useFrontCamera = false,
458
- disabled = false,
459
- className
460
- }) {
461
- const inputRef = React.useRef(null);
462
- const handleClick = () => {
463
- inputRef.current?.click();
464
- };
465
- const handleChange = (event) => {
466
- const file = event.target.files?.[0];
467
- if (file) {
468
- onCapture(file);
469
- }
470
- event.target.value = "";
471
- };
472
- return /* @__PURE__ */ jsxs(Fragment, { children: [
473
- /* @__PURE__ */ jsx(
474
- "input",
475
- {
476
- ref: inputRef,
477
- type: "file",
478
- accept: "image/*",
479
- capture: useFrontCamera ? "user" : "environment",
480
- onChange: handleChange,
481
- disabled,
482
- className: "sr-only",
483
- "aria-label": "Take a photo"
484
- }
485
- ),
486
- /* @__PURE__ */ jsx(
487
- "button",
488
- {
489
- type: "button",
490
- onClick: handleClick,
491
- disabled,
492
- className: cn(
493
- "inline-flex items-center justify-center",
494
- "rounded-full p-2",
495
- "text-neutral-500 hover:text-neutral-700",
496
- "dark:text-neutral-400 dark:hover:text-neutral-200",
497
- "hover:bg-neutral-100 dark:hover:bg-neutral-800",
498
- "focus:ring-primary-500 focus:ring-2 focus:outline-none",
499
- "disabled:cursor-not-allowed disabled:opacity-50",
500
- "transition-colors",
501
- className
502
- ),
503
- "aria-label": "Take a photo",
504
- children: /* @__PURE__ */ jsxs(
505
- "svg",
506
- {
507
- "aria-hidden": "true",
508
- className: "h-5 w-5",
509
- fill: "none",
510
- viewBox: "0 0 24 24",
511
- stroke: "currentColor",
512
- children: [
513
- /* @__PURE__ */ jsx(
514
- "path",
515
- {
516
- strokeLinecap: "round",
517
- strokeLinejoin: "round",
518
- strokeWidth: 2,
519
- d: "M3 9a2 2 0 012-2h.93a2 2 0 001.664-.89l.812-1.22A2 2 0 0110.07 4h3.86a2 2 0 011.664.89l.812 1.22A2 2 0 0018.07 7H19a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V9z"
520
- }
521
- ),
522
- /* @__PURE__ */ jsx(
523
- "path",
524
- {
525
- strokeLinecap: "round",
526
- strokeLinejoin: "round",
527
- strokeWidth: 2,
528
- d: "M15 13a3 3 0 11-6 0 3 3 0 016 0z"
529
- }
530
- )
531
- ]
532
- }
533
- )
534
- }
535
- )
536
- ] });
537
- }
538
- CameraButton.displayName = "CameraButton";
539
- function activeMentionQuery(value, caret) {
540
- const upToCaret = value.slice(0, caret);
541
- const match = /(^|\s)@([^\s@]*)$/.exec(upToCaret);
542
- if (!match) return null;
543
- const query = match[2];
544
- return { query, start: caret - query.length - 1 };
545
- }
546
- function useMentionAutocomplete({
547
- options,
548
- value,
549
- setValue,
550
- textareaRef
551
- }) {
552
- const enabled = !!options && options.length > 0;
553
- const [mention, setMention] = React.useState(null);
554
- const [highlight, setHighlight] = React.useState(0);
555
- const listId = React.useId();
556
- const optionId = React.useCallback(
557
- (index) => `${listId}-option-${index}`,
558
- [listId]
559
- );
560
- const suggestions = React.useMemo(() => {
561
- if (!mention || !options) return [];
562
- const q = mention.query.toLowerCase();
563
- return options.filter((o) => o.label.toLowerCase().includes(q));
564
- }, [mention, options]);
565
- const menuOpen = enabled && mention !== null && suggestions.length > 0;
566
- const {
567
- anchorRef,
568
- floatingRef,
569
- style: menuStyle
570
- } = useAnchoredPosition({
571
- open: menuOpen,
572
- placement: "top-start",
573
- maxHeight: 224
574
- });
575
- const clampedHighlight = menuOpen ? Math.min(highlight, suggestions.length - 1) : -1;
576
- const activeOptionId = menuOpen ? optionId(clampedHighlight) : void 0;
577
- const sync = React.useCallback(
578
- (nextValue, caret) => {
579
- if (!enabled) return;
580
- setMention(activeMentionQuery(nextValue, caret));
581
- setHighlight(0);
582
- },
583
- [enabled]
584
- );
585
- const insert = (option) => {
586
- if (!mention) return;
587
- const insertValue = option.value ?? option.label.split(" ")[0];
588
- const before = value.slice(0, mention.start);
589
- const after = value.slice(mention.start + 1 + mention.query.length);
590
- const insertText = `@${insertValue} `;
591
- setValue(before + insertText + after);
592
- setMention(null);
593
- const caret = before.length + insertText.length;
594
- requestAnimationFrame(() => {
595
- const el = textareaRef.current;
596
- if (el) {
597
- el.focus();
598
- el.setSelectionRange(caret, caret);
599
- }
600
- });
601
- };
602
- const handleKeyDown = (event) => {
603
- if (!menuOpen) return false;
604
- if (event.key === "ArrowDown") {
605
- event.preventDefault();
606
- setHighlight(
607
- (h) => (Math.min(h, suggestions.length - 1) + 1) % suggestions.length
608
- );
609
- return true;
610
- }
611
- if (event.key === "ArrowUp") {
612
- event.preventDefault();
613
- setHighlight(
614
- (h) => (Math.min(h, suggestions.length - 1) - 1 + suggestions.length) % suggestions.length
615
- );
616
- return true;
617
- }
618
- if (event.key === "Enter" || event.key === "Tab") {
619
- event.preventDefault();
620
- const chosen = suggestions[clampedHighlight];
621
- if (chosen) insert(chosen);
622
- return true;
623
- }
624
- if (event.key === "Escape") {
625
- event.preventDefault();
626
- setMention(null);
627
- return true;
628
- }
629
- return false;
630
- };
631
- const inputProps = enabled ? {
632
- "aria-controls": menuOpen ? listId : void 0,
633
- "aria-activedescendant": activeOptionId,
634
- "aria-autocomplete": "list"
635
- } : {};
636
- return {
637
- enabled,
638
- menuOpen,
639
- suggestions,
640
- highlight: clampedHighlight,
641
- listId,
642
- optionId,
643
- anchorRef,
644
- floatingRef,
645
- menuStyle,
646
- sync,
647
- insert,
648
- setHighlight,
649
- handleKeyDown,
650
- inputProps
651
- };
652
- }
653
- function MentionMenu({
654
- mention,
655
- label = "Mention",
656
- dataSlot = "composer-mention-list"
657
- }) {
658
- if (!mention.menuOpen) return null;
659
- return createPortal(
660
- /* @__PURE__ */ jsx(
661
- "ul",
662
- {
663
- ref: mention.floatingRef,
664
- style: mention.menuStyle,
665
- id: mention.listId,
666
- role: "listbox",
667
- "aria-label": label,
668
- "data-slot": dataSlot,
669
- className: "w-64 overflow-y-auto rounded-lg border border-neutral-200 bg-white py-1 shadow-lg dark:border-neutral-700 dark:bg-neutral-800",
670
- children: mention.suggestions.map((option, i) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
671
- "button",
672
- {
673
- type: "button",
674
- id: mention.optionId(i),
675
- role: "option",
676
- "aria-selected": i === mention.highlight,
677
- onMouseDown: (e) => {
678
- e.preventDefault();
679
- mention.insert(option);
680
- },
681
- onMouseEnter: () => mention.setHighlight(i),
682
- className: cn(
683
- "flex w-full items-center gap-2 px-3 py-1.5 text-start text-sm",
684
- i === mention.highlight ? "bg-primary-100 text-primary-900 dark:bg-primary-900/40 dark:text-primary-100" : "text-neutral-700 dark:text-neutral-200"
685
- ),
686
- children: [
687
- option.icon,
688
- /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1 truncate", children: [
689
- /* @__PURE__ */ jsx("span", { className: "font-medium", children: option.label }),
690
- option.description && /* @__PURE__ */ jsx("span", { className: "ms-1 text-xs text-neutral-400", children: option.description })
691
- ] }),
692
- option.meta && /* @__PURE__ */ jsx("span", { className: "text-[10px] text-neutral-400", children: option.meta })
693
- ]
694
- }
695
- ) }, option.id))
696
- }
697
- ),
698
- document.body
699
- );
700
- }
701
- MentionMenu.displayName = "MentionMenu";
702
-
703
- export { AttachmentPicker, AttachmentPreviewItem, CameraButton, DragDropZone, MentionMenu, generateAttachmentId, getFileType, useMentionAutocomplete, validateFile };
704
- //# sourceMappingURL=chunk-IF3X2WZP.js.map
705
- //# sourceMappingURL=chunk-IF3X2WZP.js.map