@natoe/colab 0.1.4 → 0.1.5
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/index.d.mts +10 -5
- package/dist/index.d.ts +10 -5
- package/dist/index.js +287 -107
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +288 -108
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React4, { createContext, forwardRef, useRef, useImperativeHandle, useEffect, useCallback, useState, useContext, useMemo } from 'react';
|
|
1
|
+
import React4, { createContext, forwardRef, useRef, useImperativeHandle, useEffect, useCallback, useState, useContext, useId, useMemo } from 'react';
|
|
2
2
|
import { Socket, Presence } from 'phoenix';
|
|
3
3
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
4
4
|
|
|
@@ -1373,91 +1373,247 @@ var styles3 = {
|
|
|
1373
1373
|
textDecoration: "underline"
|
|
1374
1374
|
}
|
|
1375
1375
|
};
|
|
1376
|
+
function ReplyIcon({ size = 18, color = "currentColor" }) {
|
|
1377
|
+
return /* @__PURE__ */ jsx(
|
|
1378
|
+
"svg",
|
|
1379
|
+
{
|
|
1380
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1381
|
+
width: size,
|
|
1382
|
+
height: size,
|
|
1383
|
+
viewBox: "0 0 24 24",
|
|
1384
|
+
fill: color,
|
|
1385
|
+
"aria-hidden": "true",
|
|
1386
|
+
children: /* @__PURE__ */ jsx("path", { d: "M10 9V5l-7 7 7 7v-4.1c5 0 8.5 1.6 11 5.1-1-5-4-10-11-11z" })
|
|
1387
|
+
}
|
|
1388
|
+
);
|
|
1389
|
+
}
|
|
1390
|
+
function PinIcon({ size = 14, color = "currentColor" }) {
|
|
1391
|
+
return /* @__PURE__ */ jsx(
|
|
1392
|
+
"svg",
|
|
1393
|
+
{
|
|
1394
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1395
|
+
width: size,
|
|
1396
|
+
height: size,
|
|
1397
|
+
viewBox: "0 0 24 24",
|
|
1398
|
+
fill: color,
|
|
1399
|
+
"aria-hidden": "true",
|
|
1400
|
+
children: /* @__PURE__ */ jsx("path", { d: "M16 9V4l1 0c.55 0 1-.45 1-1s-.45-1-1-1H7c-.55 0-1 .45-1 1s.45 1 1 1l1 0v5c0 1.66-1.34 3-3 3v2h5.97v7l1 1 1-1v-7H19v-2c-1.66 0-3-1.34-3-3z" })
|
|
1401
|
+
}
|
|
1402
|
+
);
|
|
1403
|
+
}
|
|
1404
|
+
function CopyIcon({ size = 18, color = "currentColor" }) {
|
|
1405
|
+
return /* @__PURE__ */ jsx(
|
|
1406
|
+
"svg",
|
|
1407
|
+
{
|
|
1408
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1409
|
+
width: size,
|
|
1410
|
+
height: size,
|
|
1411
|
+
viewBox: "0 0 24 24",
|
|
1412
|
+
fill: color,
|
|
1413
|
+
"aria-hidden": "true",
|
|
1414
|
+
children: /* @__PURE__ */ jsx("path", { d: "M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z" })
|
|
1415
|
+
}
|
|
1416
|
+
);
|
|
1417
|
+
}
|
|
1418
|
+
function MoreIcon({ size = 18, color = "currentColor" }) {
|
|
1419
|
+
return /* @__PURE__ */ jsxs(
|
|
1420
|
+
"svg",
|
|
1421
|
+
{
|
|
1422
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1423
|
+
width: size,
|
|
1424
|
+
height: size,
|
|
1425
|
+
viewBox: "0 0 24 24",
|
|
1426
|
+
fill: color,
|
|
1427
|
+
"aria-hidden": "true",
|
|
1428
|
+
children: [
|
|
1429
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "5", r: "2" }),
|
|
1430
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "2" }),
|
|
1431
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "19", r: "2" })
|
|
1432
|
+
]
|
|
1433
|
+
}
|
|
1434
|
+
);
|
|
1435
|
+
}
|
|
1376
1436
|
function MessageActionsMenu({
|
|
1377
1437
|
message,
|
|
1378
1438
|
onReply,
|
|
1379
1439
|
onPin,
|
|
1380
1440
|
onUnpin,
|
|
1381
1441
|
onCopy,
|
|
1382
|
-
visible,
|
|
1383
1442
|
pinDisabled = false,
|
|
1443
|
+
anchorRight = false,
|
|
1384
1444
|
className
|
|
1385
1445
|
}) {
|
|
1386
|
-
|
|
1446
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
1447
|
+
const wrapperRef = useRef(null);
|
|
1448
|
+
const triggerRef = useRef(null);
|
|
1387
1449
|
const isPinned = !!message.isPinned;
|
|
1388
1450
|
const canCopy = message.type === "text" && !!message.body;
|
|
1389
|
-
|
|
1390
|
-
if (!
|
|
1391
|
-
|
|
1451
|
+
useEffect(() => {
|
|
1452
|
+
if (!isOpen) return;
|
|
1453
|
+
const handleClick = (e) => {
|
|
1454
|
+
if (!wrapperRef.current?.contains(e.target)) {
|
|
1455
|
+
setIsOpen(false);
|
|
1456
|
+
}
|
|
1457
|
+
};
|
|
1458
|
+
const handleKey = (e) => {
|
|
1459
|
+
if (e.key === "Escape") {
|
|
1460
|
+
setIsOpen(false);
|
|
1461
|
+
triggerRef.current?.focus();
|
|
1462
|
+
}
|
|
1463
|
+
};
|
|
1464
|
+
document.addEventListener("mousedown", handleClick);
|
|
1465
|
+
document.addEventListener("keydown", handleKey);
|
|
1466
|
+
return () => {
|
|
1467
|
+
document.removeEventListener("mousedown", handleClick);
|
|
1468
|
+
document.removeEventListener("keydown", handleKey);
|
|
1469
|
+
};
|
|
1470
|
+
}, [isOpen]);
|
|
1471
|
+
const run = (fn) => {
|
|
1472
|
+
setIsOpen(false);
|
|
1473
|
+
fn();
|
|
1392
1474
|
};
|
|
1393
|
-
return /* @__PURE__ */ jsxs("div", { className, style: styles4.
|
|
1475
|
+
return /* @__PURE__ */ jsxs("div", { ref: wrapperRef, className, style: styles4.wrapper, children: [
|
|
1394
1476
|
/* @__PURE__ */ jsx(
|
|
1395
1477
|
"button",
|
|
1396
1478
|
{
|
|
1397
|
-
|
|
1398
|
-
style: styles4.button,
|
|
1399
|
-
title: "Reply",
|
|
1479
|
+
ref: triggerRef,
|
|
1400
1480
|
type: "button",
|
|
1401
|
-
|
|
1481
|
+
onClick: (e) => {
|
|
1482
|
+
e.stopPropagation();
|
|
1483
|
+
setIsOpen((v) => !v);
|
|
1484
|
+
},
|
|
1485
|
+
"aria-label": "Message actions",
|
|
1486
|
+
"aria-haspopup": "menu",
|
|
1487
|
+
"aria-expanded": isOpen ? "true" : "false",
|
|
1488
|
+
title: "Message actions",
|
|
1489
|
+
style: styles4.trigger,
|
|
1490
|
+
children: /* @__PURE__ */ jsx(MoreIcon, { size: 18, color: "#4b5563" })
|
|
1402
1491
|
}
|
|
1403
1492
|
),
|
|
1404
|
-
|
|
1405
|
-
"
|
|
1406
|
-
{
|
|
1407
|
-
onClick: () => onUnpin(message.id),
|
|
1408
|
-
style: styles4.button,
|
|
1409
|
-
title: "Unpin",
|
|
1410
|
-
type: "button",
|
|
1411
|
-
children: "\u{1F4CC}"
|
|
1412
|
-
}
|
|
1413
|
-
) : /* @__PURE__ */ jsx(
|
|
1414
|
-
"button",
|
|
1493
|
+
isOpen && /* @__PURE__ */ jsxs(
|
|
1494
|
+
"div",
|
|
1415
1495
|
{
|
|
1416
|
-
|
|
1496
|
+
role: "menu",
|
|
1497
|
+
"aria-label": "Message actions",
|
|
1417
1498
|
style: {
|
|
1418
|
-
...styles4.
|
|
1419
|
-
|
|
1420
|
-
cursor: pinDisabled ? "not-allowed" : "pointer"
|
|
1499
|
+
...styles4.menu,
|
|
1500
|
+
...anchorRight ? styles4.menuRight : styles4.menuLeft
|
|
1421
1501
|
},
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1502
|
+
children: [
|
|
1503
|
+
/* @__PURE__ */ jsxs(
|
|
1504
|
+
"button",
|
|
1505
|
+
{
|
|
1506
|
+
type: "button",
|
|
1507
|
+
role: "menuitem",
|
|
1508
|
+
onClick: () => run(() => onReply(message)),
|
|
1509
|
+
style: styles4.item,
|
|
1510
|
+
children: [
|
|
1511
|
+
/* @__PURE__ */ jsx(ReplyIcon, { size: 18, color: "#374151" }),
|
|
1512
|
+
/* @__PURE__ */ jsx("span", { children: "Reply" })
|
|
1513
|
+
]
|
|
1514
|
+
}
|
|
1515
|
+
),
|
|
1516
|
+
isPinned ? /* @__PURE__ */ jsxs(
|
|
1517
|
+
"button",
|
|
1518
|
+
{
|
|
1519
|
+
type: "button",
|
|
1520
|
+
role: "menuitem",
|
|
1521
|
+
onClick: () => run(() => onUnpin(message.id)),
|
|
1522
|
+
style: styles4.item,
|
|
1523
|
+
children: [
|
|
1524
|
+
/* @__PURE__ */ jsx(PinIcon, { size: 16, color: "#374151" }),
|
|
1525
|
+
/* @__PURE__ */ jsx("span", { children: "Unpin" })
|
|
1526
|
+
]
|
|
1527
|
+
}
|
|
1528
|
+
) : /* @__PURE__ */ jsxs(
|
|
1529
|
+
"button",
|
|
1530
|
+
{
|
|
1531
|
+
type: "button",
|
|
1532
|
+
role: "menuitem",
|
|
1533
|
+
onClick: () => run(() => onPin(message.id)),
|
|
1534
|
+
disabled: pinDisabled,
|
|
1535
|
+
style: {
|
|
1536
|
+
...styles4.item,
|
|
1537
|
+
...pinDisabled ? styles4.itemDisabled : {}
|
|
1538
|
+
},
|
|
1539
|
+
title: pinDisabled ? "Pin limit reached (max 3)" : void 0,
|
|
1540
|
+
children: [
|
|
1541
|
+
/* @__PURE__ */ jsx(PinIcon, { size: 16, color: "#374151" }),
|
|
1542
|
+
/* @__PURE__ */ jsx("span", { children: pinDisabled ? "Pin (limit reached)" : "Pin" })
|
|
1543
|
+
]
|
|
1544
|
+
}
|
|
1545
|
+
),
|
|
1546
|
+
canCopy && onCopy && /* @__PURE__ */ jsxs(
|
|
1547
|
+
"button",
|
|
1548
|
+
{
|
|
1549
|
+
type: "button",
|
|
1550
|
+
role: "menuitem",
|
|
1551
|
+
onClick: () => run(() => onCopy(message.body)),
|
|
1552
|
+
style: styles4.item,
|
|
1553
|
+
children: [
|
|
1554
|
+
/* @__PURE__ */ jsx(CopyIcon, { size: 16, color: "#374151" }),
|
|
1555
|
+
/* @__PURE__ */ jsx("span", { children: "Copy text" })
|
|
1556
|
+
]
|
|
1557
|
+
}
|
|
1558
|
+
)
|
|
1559
|
+
]
|
|
1436
1560
|
}
|
|
1437
1561
|
)
|
|
1438
1562
|
] });
|
|
1439
1563
|
}
|
|
1440
1564
|
var styles4 = {
|
|
1441
|
-
|
|
1565
|
+
wrapper: {
|
|
1566
|
+
position: "relative",
|
|
1567
|
+
display: "inline-block"
|
|
1568
|
+
},
|
|
1569
|
+
trigger: {
|
|
1570
|
+
width: "36px",
|
|
1571
|
+
height: "36px",
|
|
1442
1572
|
display: "flex",
|
|
1443
|
-
|
|
1444
|
-
|
|
1573
|
+
alignItems: "center",
|
|
1574
|
+
justifyContent: "center",
|
|
1575
|
+
backgroundColor: "transparent",
|
|
1576
|
+
border: "none",
|
|
1577
|
+
borderRadius: "50%",
|
|
1578
|
+
cursor: "pointer",
|
|
1579
|
+
color: "#4b5563"
|
|
1580
|
+
},
|
|
1581
|
+
menu: {
|
|
1582
|
+
position: "absolute",
|
|
1583
|
+
top: "100%",
|
|
1584
|
+
marginTop: "4px",
|
|
1585
|
+
minWidth: "160px",
|
|
1445
1586
|
backgroundColor: "#ffffff",
|
|
1446
1587
|
border: "1px solid #e5e7eb",
|
|
1447
|
-
borderRadius: "
|
|
1448
|
-
boxShadow: "0
|
|
1588
|
+
borderRadius: "10px",
|
|
1589
|
+
boxShadow: "0 6px 18px rgba(0, 0, 0, 0.12)",
|
|
1590
|
+
padding: "4px",
|
|
1591
|
+
zIndex: 20
|
|
1449
1592
|
},
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1593
|
+
menuLeft: {
|
|
1594
|
+
left: 0
|
|
1595
|
+
},
|
|
1596
|
+
menuRight: {
|
|
1597
|
+
right: 0
|
|
1598
|
+
},
|
|
1599
|
+
item: {
|
|
1453
1600
|
display: "flex",
|
|
1454
1601
|
alignItems: "center",
|
|
1455
|
-
|
|
1456
|
-
|
|
1602
|
+
gap: "10px",
|
|
1603
|
+
width: "100%",
|
|
1604
|
+
minHeight: "40px",
|
|
1605
|
+
padding: "8px 12px",
|
|
1606
|
+
fontSize: "14px",
|
|
1607
|
+
color: "#111827",
|
|
1457
1608
|
backgroundColor: "transparent",
|
|
1458
1609
|
border: "none",
|
|
1459
|
-
borderRadius: "
|
|
1460
|
-
cursor: "pointer"
|
|
1610
|
+
borderRadius: "6px",
|
|
1611
|
+
cursor: "pointer",
|
|
1612
|
+
textAlign: "left"
|
|
1613
|
+
},
|
|
1614
|
+
itemDisabled: {
|
|
1615
|
+
color: "#9ca3af",
|
|
1616
|
+
cursor: "not-allowed"
|
|
1461
1617
|
}
|
|
1462
1618
|
};
|
|
1463
1619
|
function FileIcon({ size = 18, color = "currentColor" }) {
|
|
@@ -1474,20 +1630,6 @@ function FileIcon({ size = 18, color = "currentColor" }) {
|
|
|
1474
1630
|
}
|
|
1475
1631
|
);
|
|
1476
1632
|
}
|
|
1477
|
-
function PinIcon({ size = 14, color = "currentColor" }) {
|
|
1478
|
-
return /* @__PURE__ */ jsx(
|
|
1479
|
-
"svg",
|
|
1480
|
-
{
|
|
1481
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
1482
|
-
width: size,
|
|
1483
|
-
height: size,
|
|
1484
|
-
viewBox: "0 0 24 24",
|
|
1485
|
-
fill: color,
|
|
1486
|
-
"aria-hidden": "true",
|
|
1487
|
-
children: /* @__PURE__ */ jsx("path", { d: "M16 9V4l1 0c.55 0 1-.45 1-1s-.45-1-1-1H7c-.55 0-1 .45-1 1s.45 1 1 1l1 0v5c0 1.66-1.34 3-3 3v2h5.97v7l1 1 1-1v-7H19v-2c-1.66 0-3-1.34-3-3z" })
|
|
1488
|
-
}
|
|
1489
|
-
);
|
|
1490
|
-
}
|
|
1491
1633
|
function AlertIcon({ size = 14, color = "currentColor" }) {
|
|
1492
1634
|
return /* @__PURE__ */ jsx(
|
|
1493
1635
|
"svg",
|
|
@@ -1552,12 +1694,26 @@ function MessageBubble({
|
|
|
1552
1694
|
pinDisabled = false,
|
|
1553
1695
|
className
|
|
1554
1696
|
}) {
|
|
1555
|
-
const [isHovered, setIsHovered] = useState(false);
|
|
1556
1697
|
if (message.type === "system") {
|
|
1557
1698
|
return /* @__PURE__ */ jsx(SystemBubble, { message });
|
|
1558
1699
|
}
|
|
1559
1700
|
const hasActions = !!(onReply || onPin || onUnpin || onCopy);
|
|
1560
|
-
|
|
1701
|
+
const actionsSlot = hasActions ? /* @__PURE__ */ jsx("div", { style: styles5.actionsSlot, children: /* @__PURE__ */ jsx(
|
|
1702
|
+
MessageActionsMenu,
|
|
1703
|
+
{
|
|
1704
|
+
message,
|
|
1705
|
+
onReply: onReply ?? (() => {
|
|
1706
|
+
}),
|
|
1707
|
+
onPin: onPin ?? (() => {
|
|
1708
|
+
}),
|
|
1709
|
+
onUnpin: onUnpin ?? (() => {
|
|
1710
|
+
}),
|
|
1711
|
+
onCopy,
|
|
1712
|
+
pinDisabled,
|
|
1713
|
+
anchorRight: !isOwn
|
|
1714
|
+
}
|
|
1715
|
+
) }) : null;
|
|
1716
|
+
return /* @__PURE__ */ jsxs(
|
|
1561
1717
|
"div",
|
|
1562
1718
|
{
|
|
1563
1719
|
className,
|
|
@@ -1565,10 +1721,9 @@ function MessageBubble({
|
|
|
1565
1721
|
...styles5.wrapper,
|
|
1566
1722
|
justifyContent: isOwn ? "flex-end" : "flex-start"
|
|
1567
1723
|
},
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
/* @__PURE__ */ jsxs(
|
|
1724
|
+
children: [
|
|
1725
|
+
isOwn && actionsSlot,
|
|
1726
|
+
/* @__PURE__ */ jsx("div", { style: styles5.bubbleWrapper, children: /* @__PURE__ */ jsxs(
|
|
1572
1727
|
"div",
|
|
1573
1728
|
{
|
|
1574
1729
|
style: {
|
|
@@ -1627,33 +1782,9 @@ function MessageBubble({
|
|
|
1627
1782
|
)
|
|
1628
1783
|
]
|
|
1629
1784
|
}
|
|
1630
|
-
),
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
{
|
|
1634
|
-
style: {
|
|
1635
|
-
...styles5.actionsWrapper,
|
|
1636
|
-
[isOwn ? "right" : "left"]: "100%",
|
|
1637
|
-
[isOwn ? "marginRight" : "marginLeft"]: "6px"
|
|
1638
|
-
},
|
|
1639
|
-
children: /* @__PURE__ */ jsx(
|
|
1640
|
-
MessageActionsMenu,
|
|
1641
|
-
{
|
|
1642
|
-
message,
|
|
1643
|
-
onReply: onReply ?? (() => {
|
|
1644
|
-
}),
|
|
1645
|
-
onPin: onPin ?? (() => {
|
|
1646
|
-
}),
|
|
1647
|
-
onUnpin: onUnpin ?? (() => {
|
|
1648
|
-
}),
|
|
1649
|
-
onCopy,
|
|
1650
|
-
visible: isHovered,
|
|
1651
|
-
pinDisabled
|
|
1652
|
-
}
|
|
1653
|
-
)
|
|
1654
|
-
}
|
|
1655
|
-
)
|
|
1656
|
-
] })
|
|
1785
|
+
) }),
|
|
1786
|
+
!isOwn && actionsSlot
|
|
1787
|
+
]
|
|
1657
1788
|
}
|
|
1658
1789
|
);
|
|
1659
1790
|
}
|
|
@@ -1750,6 +1881,8 @@ function escapeRegex2(string) {
|
|
|
1750
1881
|
var styles5 = {
|
|
1751
1882
|
wrapper: {
|
|
1752
1883
|
display: "flex",
|
|
1884
|
+
alignItems: "flex-end",
|
|
1885
|
+
gap: "4px",
|
|
1753
1886
|
marginBottom: "8px",
|
|
1754
1887
|
paddingLeft: "16px",
|
|
1755
1888
|
paddingRight: "16px"
|
|
@@ -1758,6 +1891,10 @@ var styles5 = {
|
|
|
1758
1891
|
position: "relative",
|
|
1759
1892
|
maxWidth: "75%"
|
|
1760
1893
|
},
|
|
1894
|
+
actionsSlot: {
|
|
1895
|
+
flexShrink: 0,
|
|
1896
|
+
alignSelf: "flex-end"
|
|
1897
|
+
},
|
|
1761
1898
|
bubble: {
|
|
1762
1899
|
padding: "10px 14px",
|
|
1763
1900
|
borderRadius: "12px",
|
|
@@ -1833,12 +1970,6 @@ var styles5 = {
|
|
|
1833
1970
|
cursor: "pointer",
|
|
1834
1971
|
padding: "0 2px"
|
|
1835
1972
|
},
|
|
1836
|
-
actionsWrapper: {
|
|
1837
|
-
position: "absolute",
|
|
1838
|
-
top: "50%",
|
|
1839
|
-
transform: "translateY(-50%)",
|
|
1840
|
-
zIndex: 10
|
|
1841
|
-
},
|
|
1842
1973
|
audioContainer: {
|
|
1843
1974
|
display: "flex",
|
|
1844
1975
|
alignItems: "center",
|
|
@@ -2488,7 +2619,18 @@ function MessageInput({
|
|
|
2488
2619
|
type: "file",
|
|
2489
2620
|
onChange: handleFileSelect,
|
|
2490
2621
|
style: { display: "none" },
|
|
2491
|
-
accept: [
|
|
2622
|
+
accept: [
|
|
2623
|
+
...SUPPORTED_IMAGE_TYPES,
|
|
2624
|
+
"application/pdf",
|
|
2625
|
+
".doc",
|
|
2626
|
+
".docx",
|
|
2627
|
+
".xls",
|
|
2628
|
+
".xlsx",
|
|
2629
|
+
".csv",
|
|
2630
|
+
".txt",
|
|
2631
|
+
".heic",
|
|
2632
|
+
".heif"
|
|
2633
|
+
].join(",")
|
|
2492
2634
|
}
|
|
2493
2635
|
),
|
|
2494
2636
|
/* @__PURE__ */ jsx(
|
|
@@ -3500,6 +3642,7 @@ function ExpandIcon({ size = 18, color = "currentColor" }) {
|
|
|
3500
3642
|
}
|
|
3501
3643
|
);
|
|
3502
3644
|
}
|
|
3645
|
+
var FOCUSABLE_SELECTOR = 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
3503
3646
|
function CollabPopup({
|
|
3504
3647
|
orderId,
|
|
3505
3648
|
patientData,
|
|
@@ -3521,6 +3664,8 @@ function CollabPopup({
|
|
|
3521
3664
|
const [isMinimized, setIsMinimized] = useState(false);
|
|
3522
3665
|
const dragOffset = useRef({ x: 0, y: 0 });
|
|
3523
3666
|
const containerRef = useRef(null);
|
|
3667
|
+
const previouslyFocused = useRef(null);
|
|
3668
|
+
const titleId = useId();
|
|
3524
3669
|
const handleMouseDown = useCallback(
|
|
3525
3670
|
(e) => {
|
|
3526
3671
|
if (!e.target.closest("[data-drag-handle]")) return;
|
|
@@ -3535,13 +3680,44 @@ function CollabPopup({
|
|
|
3535
3680
|
);
|
|
3536
3681
|
useEffect(() => {
|
|
3537
3682
|
if (!isOpen) return;
|
|
3683
|
+
previouslyFocused.current = document.activeElement;
|
|
3684
|
+
const focusFirst = () => {
|
|
3685
|
+
const root = containerRef.current;
|
|
3686
|
+
if (!root) return;
|
|
3687
|
+
const first = root.querySelector(FOCUSABLE_SELECTOR);
|
|
3688
|
+
(first ?? root).focus();
|
|
3689
|
+
};
|
|
3690
|
+
const raf = requestAnimationFrame(focusFirst);
|
|
3538
3691
|
const handleKey = (e) => {
|
|
3539
3692
|
if (e.key === "Escape") {
|
|
3540
3693
|
onClose();
|
|
3694
|
+
return;
|
|
3695
|
+
}
|
|
3696
|
+
if (e.key !== "Tab") return;
|
|
3697
|
+
const root = containerRef.current;
|
|
3698
|
+
if (!root) return;
|
|
3699
|
+
const focusables = Array.from(root.querySelectorAll(FOCUSABLE_SELECTOR));
|
|
3700
|
+
if (focusables.length === 0) {
|
|
3701
|
+
e.preventDefault();
|
|
3702
|
+
return;
|
|
3703
|
+
}
|
|
3704
|
+
const first = focusables[0];
|
|
3705
|
+
const last = focusables[focusables.length - 1];
|
|
3706
|
+
const active = document.activeElement;
|
|
3707
|
+
if (e.shiftKey && active === first) {
|
|
3708
|
+
e.preventDefault();
|
|
3709
|
+
last.focus();
|
|
3710
|
+
} else if (!e.shiftKey && active === last) {
|
|
3711
|
+
e.preventDefault();
|
|
3712
|
+
first.focus();
|
|
3541
3713
|
}
|
|
3542
3714
|
};
|
|
3543
3715
|
window.addEventListener("keydown", handleKey);
|
|
3544
|
-
return () =>
|
|
3716
|
+
return () => {
|
|
3717
|
+
cancelAnimationFrame(raf);
|
|
3718
|
+
window.removeEventListener("keydown", handleKey);
|
|
3719
|
+
previouslyFocused.current?.focus?.();
|
|
3720
|
+
};
|
|
3545
3721
|
}, [isOpen, onClose]);
|
|
3546
3722
|
useEffect(() => {
|
|
3547
3723
|
if (!isDragging) return;
|
|
@@ -3567,6 +3743,10 @@ function CollabPopup({
|
|
|
3567
3743
|
{
|
|
3568
3744
|
ref: containerRef,
|
|
3569
3745
|
className,
|
|
3746
|
+
role: "dialog",
|
|
3747
|
+
"aria-modal": "true",
|
|
3748
|
+
"aria-labelledby": titleId,
|
|
3749
|
+
tabIndex: -1,
|
|
3570
3750
|
style: {
|
|
3571
3751
|
...styles13.container,
|
|
3572
3752
|
left: position.x,
|
|
@@ -3578,7 +3758,7 @@ function CollabPopup({
|
|
|
3578
3758
|
onMouseDown: handleMouseDown,
|
|
3579
3759
|
children: [
|
|
3580
3760
|
/* @__PURE__ */ jsxs("div", { "data-drag-handle": true, style: styles13.titleBar, children: [
|
|
3581
|
-
/* @__PURE__ */ jsx("span", { style: styles13.titleText, children: buildChannelName(patientData) }),
|
|
3761
|
+
/* @__PURE__ */ jsx("span", { id: titleId, style: styles13.titleText, children: buildChannelName(patientData) }),
|
|
3582
3762
|
/* @__PURE__ */ jsxs("div", { style: styles13.titleActions, children: [
|
|
3583
3763
|
/* @__PURE__ */ jsx(
|
|
3584
3764
|
"button",
|