@papyrus-sdk/ui-react 0.2.20 → 0.2.22
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/LICENSE +21 -0
- package/base.css +2 -2
- package/dist/index.js +491 -68
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +491 -68
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -9
package/dist/index.js
CHANGED
|
@@ -32,6 +32,8 @@ var import_react = require("react");
|
|
|
32
32
|
var import_react_dom = require("react-dom");
|
|
33
33
|
var import_core = require("@papyrus-sdk/core");
|
|
34
34
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
35
|
+
var MOBILE_LANDSCAPE_MAX_HEIGHT_PX = 500;
|
|
36
|
+
var MOBILE_VIEWPORT_QUERY = `(max-width: 639px), (orientation: landscape) and (max-height: ${MOBILE_LANDSCAPE_MAX_HEIGHT_PX}px)`;
|
|
35
37
|
var Topbar = ({
|
|
36
38
|
engine,
|
|
37
39
|
showBrand = false,
|
|
@@ -86,7 +88,7 @@ var Topbar = ({
|
|
|
86
88
|
}, [hasMobileMenu]);
|
|
87
89
|
(0, import_react.useEffect)(() => {
|
|
88
90
|
if (!canUseDOM || typeof window.matchMedia !== "function") return;
|
|
89
|
-
const mediaQuery = window.matchMedia(
|
|
91
|
+
const mediaQuery = window.matchMedia(MOBILE_VIEWPORT_QUERY);
|
|
90
92
|
const updateViewport = () => setIsMobileViewport(mediaQuery.matches);
|
|
91
93
|
updateViewport();
|
|
92
94
|
if (typeof mediaQuery.addEventListener === "function") {
|
|
@@ -1131,10 +1133,15 @@ var SidebarRight = ({ engine, style }) => {
|
|
|
1131
1133
|
setDocumentState,
|
|
1132
1134
|
triggerScrollToPage,
|
|
1133
1135
|
annotations,
|
|
1134
|
-
accentColor
|
|
1136
|
+
accentColor,
|
|
1137
|
+
updateAnnotation,
|
|
1138
|
+
addAnnotationReply,
|
|
1139
|
+
setSelectedAnnotation
|
|
1135
1140
|
} = (0, import_core3.useViewerStore)();
|
|
1136
1141
|
const [query, setQuery] = (0, import_react3.useState)("");
|
|
1137
1142
|
const [isSearching, setIsSearching] = (0, import_react3.useState)(false);
|
|
1143
|
+
const [contentDrafts, setContentDrafts] = (0, import_react3.useState)({});
|
|
1144
|
+
const [replyDrafts, setReplyDrafts] = (0, import_react3.useState)({});
|
|
1138
1145
|
const searchService = new import_core3.SearchService(engine);
|
|
1139
1146
|
const isDark = uiTheme === "dark";
|
|
1140
1147
|
const accentSoft = withAlpha2(accentColor, 0.12);
|
|
@@ -1150,6 +1157,41 @@ var SidebarRight = ({ engine, style }) => {
|
|
|
1150
1157
|
setSearch(query, results);
|
|
1151
1158
|
setIsSearching(false);
|
|
1152
1159
|
};
|
|
1160
|
+
const jumpToAnnotation = (annotation) => {
|
|
1161
|
+
const page = annotation.pageIndex + 1;
|
|
1162
|
+
engine.goToPage(page);
|
|
1163
|
+
setDocumentState({ currentPage: page });
|
|
1164
|
+
setSelectedAnnotation(annotation.id);
|
|
1165
|
+
triggerScrollToPage(annotation.pageIndex);
|
|
1166
|
+
};
|
|
1167
|
+
const getContentDraft = (annotation) => {
|
|
1168
|
+
if (Object.prototype.hasOwnProperty.call(contentDrafts, annotation.id)) {
|
|
1169
|
+
return contentDrafts[annotation.id];
|
|
1170
|
+
}
|
|
1171
|
+
return annotation.content ?? "";
|
|
1172
|
+
};
|
|
1173
|
+
const updateContentDraft = (annotationId, nextValue) => {
|
|
1174
|
+
setContentDrafts((prev) => ({ ...prev, [annotationId]: nextValue }));
|
|
1175
|
+
};
|
|
1176
|
+
const submitContent = (annotation) => {
|
|
1177
|
+
const nextContent = getContentDraft(annotation).trim();
|
|
1178
|
+
const currentContent = (annotation.content ?? "").trim();
|
|
1179
|
+
if (nextContent === currentContent) return;
|
|
1180
|
+
updateAnnotation(annotation.id, {
|
|
1181
|
+
content: nextContent,
|
|
1182
|
+
updatedAt: Date.now()
|
|
1183
|
+
});
|
|
1184
|
+
};
|
|
1185
|
+
const getReplyDraft = (annotationId) => replyDrafts[annotationId] ?? "";
|
|
1186
|
+
const updateReplyDraft = (annotationId, nextValue) => {
|
|
1187
|
+
setReplyDrafts((prev) => ({ ...prev, [annotationId]: nextValue }));
|
|
1188
|
+
};
|
|
1189
|
+
const submitReply = (annotationId) => {
|
|
1190
|
+
const nextReply = getReplyDraft(annotationId).trim();
|
|
1191
|
+
if (!nextReply) return;
|
|
1192
|
+
addAnnotationReply(annotationId, nextReply);
|
|
1193
|
+
setReplyDrafts((prev) => ({ ...prev, [annotationId]: "" }));
|
|
1194
|
+
};
|
|
1153
1195
|
if (!sidebarRightOpen) return null;
|
|
1154
1196
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1155
1197
|
"div",
|
|
@@ -1351,48 +1393,152 @@ var SidebarRight = ({ engine, style }) => {
|
|
|
1351
1393
|
}
|
|
1352
1394
|
) }),
|
|
1353
1395
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "text-[10px] font-bold text-gray-400 uppercase tracking-widest", children: "Sem anota\xE7\xF5es" })
|
|
1354
|
-
] }) : annotations.
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1396
|
+
] }) : annotations.slice().sort(
|
|
1397
|
+
(a, b) => (b.updatedAt ?? b.createdAt) - (a.updatedAt ?? a.createdAt)
|
|
1398
|
+
).map((ann) => {
|
|
1399
|
+
const isCommentThread = ann.type === "comment" || ann.type === "text";
|
|
1400
|
+
const replies = ann.replies ?? [];
|
|
1401
|
+
const contentDraft = getContentDraft(ann);
|
|
1402
|
+
const replyDraft = getReplyDraft(ann.id);
|
|
1403
|
+
const hasExistingContent = Boolean((ann.content ?? "").trim());
|
|
1404
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1405
|
+
"div",
|
|
1406
|
+
{
|
|
1407
|
+
className: "rounded-xl border p-4 transition-colors",
|
|
1408
|
+
style: {
|
|
1409
|
+
background: "var(--papyrus-surface-2-resolved, var(--papyrus-surface-2, #1f2937))",
|
|
1410
|
+
borderColor: "var(--papyrus-border-resolved, var(--papyrus-border, #374151))",
|
|
1411
|
+
color: "var(--papyrus-text-resolved, var(--papyrus-text, #e5e7eb))"
|
|
1412
|
+
},
|
|
1413
|
+
children: [
|
|
1414
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mb-3 flex items-center justify-between", children: [
|
|
1415
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center space-x-2", children: [
|
|
1416
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1417
|
+
"div",
|
|
1418
|
+
{
|
|
1419
|
+
className: "h-2.5 w-2.5 rounded-full",
|
|
1420
|
+
style: { backgroundColor: ann.color }
|
|
1421
|
+
}
|
|
1422
|
+
),
|
|
1423
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1424
|
+
"span",
|
|
1425
|
+
{
|
|
1426
|
+
className: "text-[10px] font-black uppercase tracking-wide",
|
|
1427
|
+
style: { color: accentColor },
|
|
1428
|
+
children: [
|
|
1429
|
+
"P",
|
|
1430
|
+
ann.pageIndex + 1
|
|
1431
|
+
]
|
|
1432
|
+
}
|
|
1433
|
+
),
|
|
1434
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "text-[10px] font-semibold opacity-70 uppercase tracking-wide", children: ann.type })
|
|
1435
|
+
] }),
|
|
1361
1436
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1362
|
-
"
|
|
1437
|
+
"button",
|
|
1438
|
+
{
|
|
1439
|
+
type: "button",
|
|
1440
|
+
className: "rounded-md border px-2 py-1 text-[10px] font-semibold uppercase tracking-wide",
|
|
1441
|
+
onClick: () => jumpToAnnotation(ann),
|
|
1442
|
+
style: {
|
|
1443
|
+
borderColor: accentColor,
|
|
1444
|
+
color: accentColor
|
|
1445
|
+
},
|
|
1446
|
+
children: "Ir para pagina"
|
|
1447
|
+
}
|
|
1448
|
+
)
|
|
1449
|
+
] }),
|
|
1450
|
+
isCommentThread ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "space-y-2", children: [
|
|
1451
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1452
|
+
"textarea",
|
|
1363
1453
|
{
|
|
1364
|
-
className: "w-
|
|
1365
|
-
style: {
|
|
1454
|
+
className: "w-full resize-none rounded-md border p-2 text-xs focus:outline-none",
|
|
1455
|
+
style: {
|
|
1456
|
+
background: "var(--papyrus-surface-resolved, var(--papyrus-surface, #111827))",
|
|
1457
|
+
borderColor: "var(--papyrus-border-resolved, var(--papyrus-border, #374151))",
|
|
1458
|
+
color: "var(--papyrus-text-resolved, var(--papyrus-text, #e5e7eb))"
|
|
1459
|
+
},
|
|
1460
|
+
rows: 3,
|
|
1461
|
+
placeholder: "Escreva seu comentario...",
|
|
1462
|
+
value: contentDraft,
|
|
1463
|
+
onChange: (event) => updateContentDraft(ann.id, event.target.value),
|
|
1464
|
+
onKeyDown: (event) => {
|
|
1465
|
+
if (event.key === "Enter" && !event.shiftKey) {
|
|
1466
|
+
event.preventDefault();
|
|
1467
|
+
submitContent(ann);
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1366
1470
|
}
|
|
1367
1471
|
),
|
|
1368
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.
|
|
1369
|
-
"
|
|
1472
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "flex justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1473
|
+
"button",
|
|
1370
1474
|
{
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1475
|
+
type: "button",
|
|
1476
|
+
className: "rounded-md px-3 py-1.5 text-[11px] font-semibold text-white",
|
|
1477
|
+
style: { backgroundColor: accentColor },
|
|
1478
|
+
onClick: () => submitContent(ann),
|
|
1479
|
+
children: hasExistingContent ? "Atualizar comentario" : "Enviar comentario"
|
|
1480
|
+
}
|
|
1481
|
+
) })
|
|
1482
|
+
] }) : null,
|
|
1483
|
+
replies.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mt-3 space-y-2", children: replies.map((reply) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1484
|
+
"div",
|
|
1485
|
+
{
|
|
1486
|
+
className: "rounded-md border p-2",
|
|
1487
|
+
style: {
|
|
1488
|
+
background: "var(--papyrus-surface-resolved, var(--papyrus-surface, #111827))",
|
|
1489
|
+
borderColor: "var(--papyrus-border-resolved, var(--papyrus-border, #374151))"
|
|
1490
|
+
},
|
|
1491
|
+
children: [
|
|
1492
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "text-xs leading-relaxed", children: reply.content }),
|
|
1493
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "mt-1 text-[10px] opacity-70", children: new Date(reply.createdAt).toLocaleTimeString(
|
|
1494
|
+
[],
|
|
1495
|
+
{
|
|
1496
|
+
hour: "2-digit",
|
|
1497
|
+
minute: "2-digit"
|
|
1498
|
+
}
|
|
1499
|
+
) })
|
|
1500
|
+
]
|
|
1501
|
+
},
|
|
1502
|
+
reply.id
|
|
1503
|
+
)) }) : null,
|
|
1504
|
+
isCommentThread ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mt-3 flex items-center gap-2", children: [
|
|
1505
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1506
|
+
"input",
|
|
1507
|
+
{
|
|
1508
|
+
type: "text",
|
|
1509
|
+
className: "flex-1 rounded-md border px-2 py-1.5 text-xs focus:outline-none",
|
|
1510
|
+
style: {
|
|
1511
|
+
background: "var(--papyrus-surface-resolved, var(--papyrus-surface, #111827))",
|
|
1512
|
+
borderColor: "var(--papyrus-border-resolved, var(--papyrus-border, #374151))",
|
|
1513
|
+
color: "var(--papyrus-text-resolved, var(--papyrus-text, #e5e7eb))"
|
|
1514
|
+
},
|
|
1515
|
+
value: replyDraft,
|
|
1516
|
+
placeholder: "Responder...",
|
|
1517
|
+
onChange: (event) => updateReplyDraft(ann.id, event.target.value),
|
|
1518
|
+
onKeyDown: (event) => {
|
|
1519
|
+
if (event.key === "Enter") {
|
|
1520
|
+
event.preventDefault();
|
|
1521
|
+
submitReply(ann.id);
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
),
|
|
1526
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1527
|
+
"button",
|
|
1528
|
+
{
|
|
1529
|
+
type: "button",
|
|
1530
|
+
className: "rounded-md px-3 py-1.5 text-[11px] font-semibold text-white",
|
|
1531
|
+
style: { backgroundColor: accentColor },
|
|
1532
|
+
onClick: () => submitReply(ann.id),
|
|
1533
|
+
children: "Responder"
|
|
1377
1534
|
}
|
|
1378
1535
|
)
|
|
1379
|
-
] })
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1386
|
-
"p",
|
|
1387
|
-
{
|
|
1388
|
-
className: `text-[11px] font-bold uppercase tracking-tight ${isDark ? "text-gray-200" : "text-gray-700"}`,
|
|
1389
|
-
children: ann.type
|
|
1390
|
-
}
|
|
1391
|
-
)
|
|
1392
|
-
]
|
|
1393
|
-
},
|
|
1394
|
-
ann.id
|
|
1395
|
-
))
|
|
1536
|
+
] }) : null
|
|
1537
|
+
]
|
|
1538
|
+
},
|
|
1539
|
+
ann.id
|
|
1540
|
+
);
|
|
1541
|
+
})
|
|
1396
1542
|
] }) })
|
|
1397
1543
|
]
|
|
1398
1544
|
}
|
|
@@ -1420,6 +1566,10 @@ var PageRenderer = ({
|
|
|
1420
1566
|
const canvasRef = (0, import_react4.useRef)(null);
|
|
1421
1567
|
const htmlLayerRef = (0, import_react4.useRef)(null);
|
|
1422
1568
|
const textLayerRef = (0, import_react4.useRef)(null);
|
|
1569
|
+
const skipNextAnnotationSelectRef = (0, import_react4.useRef)(false);
|
|
1570
|
+
const skipSelectResetTimerRef = (0, import_react4.useRef)(
|
|
1571
|
+
null
|
|
1572
|
+
);
|
|
1423
1573
|
const [loading, setLoading] = (0, import_react4.useState)(true);
|
|
1424
1574
|
const [pageSize, setPageSize] = (0, import_react4.useState)(null);
|
|
1425
1575
|
const [isDragging, setIsDragging] = (0, import_react4.useState)(false);
|
|
@@ -1437,6 +1587,8 @@ var PageRenderer = ({
|
|
|
1437
1587
|
setDocumentState,
|
|
1438
1588
|
annotations,
|
|
1439
1589
|
addAnnotation,
|
|
1590
|
+
addAnnotationReply,
|
|
1591
|
+
updateAnnotation,
|
|
1440
1592
|
activeTool,
|
|
1441
1593
|
removeAnnotation,
|
|
1442
1594
|
selectedAnnotationId,
|
|
@@ -1460,6 +1612,24 @@ var PageRenderer = ({
|
|
|
1460
1612
|
() => Boolean(searchQuery?.trim()) && searchResults.some((res) => res.pageIndex === pageIndex),
|
|
1461
1613
|
[searchQuery, searchResults, pageIndex]
|
|
1462
1614
|
);
|
|
1615
|
+
const suppressNextAnnotationSelect = () => {
|
|
1616
|
+
skipNextAnnotationSelectRef.current = true;
|
|
1617
|
+
if (skipSelectResetTimerRef.current) {
|
|
1618
|
+
clearTimeout(skipSelectResetTimerRef.current);
|
|
1619
|
+
}
|
|
1620
|
+
skipSelectResetTimerRef.current = setTimeout(() => {
|
|
1621
|
+
skipNextAnnotationSelectRef.current = false;
|
|
1622
|
+
skipSelectResetTimerRef.current = null;
|
|
1623
|
+
}, 0);
|
|
1624
|
+
};
|
|
1625
|
+
(0, import_react4.useEffect)(
|
|
1626
|
+
() => () => {
|
|
1627
|
+
if (skipSelectResetTimerRef.current) {
|
|
1628
|
+
clearTimeout(skipSelectResetTimerRef.current);
|
|
1629
|
+
}
|
|
1630
|
+
},
|
|
1631
|
+
[]
|
|
1632
|
+
);
|
|
1463
1633
|
(0, import_react4.useEffect)(() => {
|
|
1464
1634
|
let active = true;
|
|
1465
1635
|
const loadSize = async () => {
|
|
@@ -1629,13 +1799,27 @@ var PageRenderer = ({
|
|
|
1629
1799
|
activeSearchIndex,
|
|
1630
1800
|
textLayerVersion
|
|
1631
1801
|
]);
|
|
1632
|
-
const
|
|
1802
|
+
const getTouchPoint = (event) => {
|
|
1803
|
+
const touch = event.touches[0] ?? event.changedTouches[0];
|
|
1804
|
+
if (!touch) return null;
|
|
1805
|
+
return { x: touch.clientX, y: touch.clientY };
|
|
1806
|
+
};
|
|
1807
|
+
const handlePointerDown = (clientX, clientY, target) => {
|
|
1808
|
+
const clickedInsideAnnotation = Boolean(
|
|
1809
|
+
target?.closest("[data-papyrus-annotation-id]")
|
|
1810
|
+
);
|
|
1811
|
+
const clickedSelectionMenu = Boolean(
|
|
1812
|
+
target?.closest("[data-papyrus-selection-menu]")
|
|
1813
|
+
);
|
|
1814
|
+
if (!clickedInsideAnnotation && !clickedSelectionMenu) {
|
|
1815
|
+
setSelectedAnnotation(null);
|
|
1816
|
+
}
|
|
1633
1817
|
setSelectionMenu(null);
|
|
1634
1818
|
if (activeTool === "ink") {
|
|
1635
1819
|
const rect2 = containerRef.current?.getBoundingClientRect();
|
|
1636
1820
|
if (!rect2) return;
|
|
1637
|
-
const x2 = (
|
|
1638
|
-
const y2 = (
|
|
1821
|
+
const x2 = (clientX - rect2.left) / rect2.width;
|
|
1822
|
+
const y2 = (clientY - rect2.top) / rect2.height;
|
|
1639
1823
|
setIsInkDrawing(true);
|
|
1640
1824
|
setInkPoints([{ x: x2, y: y2 }]);
|
|
1641
1825
|
return;
|
|
@@ -1644,25 +1828,25 @@ var PageRenderer = ({
|
|
|
1644
1828
|
const rect = containerRef.current?.getBoundingClientRect();
|
|
1645
1829
|
if (!rect) return;
|
|
1646
1830
|
setIsDragging(true);
|
|
1647
|
-
const x =
|
|
1648
|
-
const y =
|
|
1831
|
+
const x = clientX - rect.left;
|
|
1832
|
+
const y = clientY - rect.top;
|
|
1649
1833
|
setStartPos({ x, y });
|
|
1650
1834
|
setCurrentRect({ x, y, w: 0, h: 0 });
|
|
1651
1835
|
};
|
|
1652
|
-
const
|
|
1836
|
+
const handlePointerMove = (clientX, clientY) => {
|
|
1653
1837
|
if (isInkDrawing) {
|
|
1654
1838
|
const rect2 = containerRef.current?.getBoundingClientRect();
|
|
1655
1839
|
if (!rect2) return;
|
|
1656
|
-
const x = (
|
|
1657
|
-
const y = (
|
|
1840
|
+
const x = (clientX - rect2.left) / rect2.width;
|
|
1841
|
+
const y = (clientY - rect2.top) / rect2.height;
|
|
1658
1842
|
setInkPoints((prev) => [...prev, { x, y }]);
|
|
1659
1843
|
return;
|
|
1660
1844
|
}
|
|
1661
1845
|
if (!isDragging) return;
|
|
1662
1846
|
const rect = containerRef.current?.getBoundingClientRect();
|
|
1663
1847
|
if (!rect) return;
|
|
1664
|
-
const currentX =
|
|
1665
|
-
const currentY =
|
|
1848
|
+
const currentX = clientX - rect.left;
|
|
1849
|
+
const currentY = clientY - rect.top;
|
|
1666
1850
|
setCurrentRect({
|
|
1667
1851
|
x: Math.min(startPos.x, currentX),
|
|
1668
1852
|
y: Math.min(startPos.y, currentY),
|
|
@@ -1670,7 +1854,7 @@ var PageRenderer = ({
|
|
|
1670
1854
|
h: Math.abs(currentY - startPos.y)
|
|
1671
1855
|
});
|
|
1672
1856
|
};
|
|
1673
|
-
const
|
|
1857
|
+
const handlePointerUp = () => {
|
|
1674
1858
|
if (isInkDrawing) {
|
|
1675
1859
|
setIsInkDrawing(false);
|
|
1676
1860
|
if (inkPoints.length > 1) {
|
|
@@ -1686,6 +1870,7 @@ var PageRenderer = ({
|
|
|
1686
1870
|
x: Math.max(0, Math.min(1, p.x)),
|
|
1687
1871
|
y: Math.max(0, Math.min(1, p.y))
|
|
1688
1872
|
}));
|
|
1873
|
+
suppressNextAnnotationSelect();
|
|
1689
1874
|
addAnnotation({
|
|
1690
1875
|
id: Math.random().toString(36).substr(2, 9),
|
|
1691
1876
|
pageIndex,
|
|
@@ -1766,6 +1951,7 @@ var PageRenderer = ({
|
|
|
1766
1951
|
height: Math.max(...ye) - Math.min(...ys)
|
|
1767
1952
|
};
|
|
1768
1953
|
if (textMarkupTools.has(activeTool)) {
|
|
1954
|
+
suppressNextAnnotationSelect();
|
|
1769
1955
|
addAnnotation({
|
|
1770
1956
|
id: Math.random().toString(36).substr(2, 9),
|
|
1771
1957
|
pageIndex,
|
|
@@ -1801,6 +1987,9 @@ var PageRenderer = ({
|
|
|
1801
1987
|
if (currentRect.w > 5 && currentRect.h > 5) {
|
|
1802
1988
|
const rect = containerRef.current?.getBoundingClientRect();
|
|
1803
1989
|
if (rect) {
|
|
1990
|
+
if (activeTool !== "text" && activeTool !== "comment") {
|
|
1991
|
+
suppressNextAnnotationSelect();
|
|
1992
|
+
}
|
|
1804
1993
|
addAnnotation({
|
|
1805
1994
|
id: Math.random().toString(36).substr(2, 9),
|
|
1806
1995
|
pageIndex,
|
|
@@ -1830,6 +2019,37 @@ var PageRenderer = ({
|
|
|
1830
2019
|
}
|
|
1831
2020
|
}
|
|
1832
2021
|
};
|
|
2022
|
+
const handleMouseDown = (e) => {
|
|
2023
|
+
handlePointerDown(e.clientX, e.clientY, e.target);
|
|
2024
|
+
};
|
|
2025
|
+
const handleMouseMove = (e) => {
|
|
2026
|
+
handlePointerMove(e.clientX, e.clientY);
|
|
2027
|
+
};
|
|
2028
|
+
const handleMouseUp = () => {
|
|
2029
|
+
handlePointerUp();
|
|
2030
|
+
};
|
|
2031
|
+
const handleTouchStart = (event) => {
|
|
2032
|
+
if (event.touches.length > 1) return;
|
|
2033
|
+
const point = getTouchPoint(event);
|
|
2034
|
+
if (!point) return;
|
|
2035
|
+
handlePointerDown(point.x, point.y, event.target);
|
|
2036
|
+
if ((activeTool === "ink" || !canSelectText) && event.cancelable) {
|
|
2037
|
+
event.preventDefault();
|
|
2038
|
+
}
|
|
2039
|
+
};
|
|
2040
|
+
const handleTouchMove = (event) => {
|
|
2041
|
+
if (event.touches.length > 1) return;
|
|
2042
|
+
const point = getTouchPoint(event);
|
|
2043
|
+
if (!point) return;
|
|
2044
|
+
handlePointerMove(point.x, point.y);
|
|
2045
|
+
if ((isInkDrawing || isDragging) && event.cancelable) {
|
|
2046
|
+
event.preventDefault();
|
|
2047
|
+
}
|
|
2048
|
+
};
|
|
2049
|
+
const handleTouchEnd = (event) => {
|
|
2050
|
+
if (event.touches.length > 0) return;
|
|
2051
|
+
handlePointerUp();
|
|
2052
|
+
};
|
|
1833
2053
|
const getPageFilter = () => {
|
|
1834
2054
|
switch (pageTheme) {
|
|
1835
2055
|
case "sepia":
|
|
@@ -1847,10 +2067,18 @@ var PageRenderer = ({
|
|
|
1847
2067
|
{
|
|
1848
2068
|
ref: containerRef,
|
|
1849
2069
|
className: `relative inline-block shadow-2xl bg-white mb-10 ${canSelectText ? "" : "no-select cursor-crosshair"}`,
|
|
1850
|
-
style: {
|
|
2070
|
+
style: {
|
|
2071
|
+
scrollMarginTop: "20px",
|
|
2072
|
+
minHeight: "100px",
|
|
2073
|
+
touchAction: activeTool === "ink" || activeTool === "text" || activeTool === "comment" ? "none" : "auto"
|
|
2074
|
+
},
|
|
1851
2075
|
onMouseDown: handleMouseDown,
|
|
1852
2076
|
onMouseMove: handleMouseMove,
|
|
1853
2077
|
onMouseUp: handleMouseUp,
|
|
2078
|
+
onTouchStart: handleTouchStart,
|
|
2079
|
+
onTouchMove: handleTouchMove,
|
|
2080
|
+
onTouchEnd: handleTouchEnd,
|
|
2081
|
+
onTouchCancel: handleTouchEnd,
|
|
1854
2082
|
children: [
|
|
1855
2083
|
loading && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "absolute inset-0 bg-gray-50 flex items-center justify-center z-10 animate-pulse", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-[10px] font-black text-gray-400 uppercase tracking-widest", children: "Sincronizando..." }) }),
|
|
1856
2084
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
@@ -1924,6 +2152,7 @@ var PageRenderer = ({
|
|
|
1924
2152
|
selectionMenu && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1925
2153
|
"div",
|
|
1926
2154
|
{
|
|
2155
|
+
"data-papyrus-selection-menu": "true",
|
|
1927
2156
|
className: "absolute z-[60] flex items-center gap-1 rounded-full border px-2 py-1 shadow-xl bg-white/95 backdrop-blur-md text-gray-700",
|
|
1928
2157
|
style: { left: selectionMenu.anchor.x, top: selectionMenu.anchor.y },
|
|
1929
2158
|
children: [
|
|
@@ -1936,6 +2165,7 @@ var PageRenderer = ({
|
|
|
1936
2165
|
{
|
|
1937
2166
|
className: "text-[10px] font-bold px-2 py-1 rounded-full hover:bg-gray-100",
|
|
1938
2167
|
onClick: () => {
|
|
2168
|
+
suppressNextAnnotationSelect();
|
|
1939
2169
|
addAnnotation({
|
|
1940
2170
|
id: Math.random().toString(36).substr(2, 9),
|
|
1941
2171
|
pageIndex,
|
|
@@ -1962,7 +2192,15 @@ var PageRenderer = ({
|
|
|
1962
2192
|
isSelected: selectedAnnotationId === ann.id,
|
|
1963
2193
|
accentColor,
|
|
1964
2194
|
onDelete: () => removeAnnotation(ann.id),
|
|
1965
|
-
onSelect: () =>
|
|
2195
|
+
onSelect: () => {
|
|
2196
|
+
if (skipNextAnnotationSelectRef.current) {
|
|
2197
|
+
skipNextAnnotationSelectRef.current = false;
|
|
2198
|
+
return;
|
|
2199
|
+
}
|
|
2200
|
+
setSelectedAnnotation(ann.id);
|
|
2201
|
+
},
|
|
2202
|
+
onUpdate: (updates) => updateAnnotation(ann.id, updates),
|
|
2203
|
+
onAddReply: (content) => addAnnotationReply(ann.id, content)
|
|
1966
2204
|
},
|
|
1967
2205
|
ann.id
|
|
1968
2206
|
)) })
|
|
@@ -1970,12 +2208,43 @@ var PageRenderer = ({
|
|
|
1970
2208
|
}
|
|
1971
2209
|
);
|
|
1972
2210
|
};
|
|
1973
|
-
var AnnotationItem = ({
|
|
2211
|
+
var AnnotationItem = ({
|
|
2212
|
+
ann,
|
|
2213
|
+
isSelected,
|
|
2214
|
+
accentColor,
|
|
2215
|
+
onDelete,
|
|
2216
|
+
onSelect,
|
|
2217
|
+
onUpdate,
|
|
2218
|
+
onAddReply
|
|
2219
|
+
}) => {
|
|
1974
2220
|
const isText = ann.type === "text" || ann.type === "comment";
|
|
1975
2221
|
const isHighlight = ann.type === "highlight";
|
|
1976
2222
|
const isMarkup = ann.type === "highlight" || ann.type === "underline" || ann.type === "squiggly" || ann.type === "strikeout";
|
|
1977
2223
|
const rects = ann.rects && ann.rects.length > 0 ? ann.rects : [ann.rect];
|
|
1978
2224
|
const isInk = ann.type === "ink" && ann.path && ann.path.length > 1;
|
|
2225
|
+
const [draftContent, setDraftContent] = (0, import_react4.useState)(ann.content ?? "");
|
|
2226
|
+
const [draftReply, setDraftReply] = (0, import_react4.useState)("");
|
|
2227
|
+
(0, import_react4.useEffect)(() => {
|
|
2228
|
+
setDraftContent(ann.content ?? "");
|
|
2229
|
+
}, [ann.id, ann.content]);
|
|
2230
|
+
(0, import_react4.useEffect)(() => {
|
|
2231
|
+
setDraftReply("");
|
|
2232
|
+
}, [ann.id]);
|
|
2233
|
+
const handleSaveContent = () => {
|
|
2234
|
+
const nextContent = draftContent.trim();
|
|
2235
|
+
const currentContent = (ann.content ?? "").trim();
|
|
2236
|
+
if (nextContent === currentContent) return;
|
|
2237
|
+
onUpdate({
|
|
2238
|
+
content: nextContent,
|
|
2239
|
+
updatedAt: Date.now()
|
|
2240
|
+
});
|
|
2241
|
+
};
|
|
2242
|
+
const handleReplySubmit = () => {
|
|
2243
|
+
const nextReply = draftReply.trim();
|
|
2244
|
+
if (!nextReply) return;
|
|
2245
|
+
onAddReply(nextReply);
|
|
2246
|
+
setDraftReply("");
|
|
2247
|
+
};
|
|
1979
2248
|
const renderMarkupRects = () => {
|
|
1980
2249
|
if (!isMarkup) return null;
|
|
1981
2250
|
return rects.map((r, idx) => {
|
|
@@ -2080,6 +2349,7 @@ var AnnotationItem = ({ ann, isSelected, accentColor, onDelete, onSelect }) => {
|
|
|
2080
2349
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
2081
2350
|
"div",
|
|
2082
2351
|
{
|
|
2352
|
+
"data-papyrus-annotation-id": ann.id,
|
|
2083
2353
|
className: `absolute pointer-events-auto transition-all ${isSelected ? "shadow-xl z-30" : "z-10"}`,
|
|
2084
2354
|
style: {
|
|
2085
2355
|
left: `${ann.rect.x * 100}%`,
|
|
@@ -2098,16 +2368,151 @@ var AnnotationItem = ({ ann, isSelected, accentColor, onDelete, onSelect }) => {
|
|
|
2098
2368
|
children: [
|
|
2099
2369
|
renderMarkupRects(),
|
|
2100
2370
|
renderInk(),
|
|
2101
|
-
isText && isSelected && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2102
|
-
"
|
|
2371
|
+
isText && !isSelected && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2372
|
+
"button",
|
|
2103
2373
|
{
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2374
|
+
type: "button",
|
|
2375
|
+
className: "absolute -top-2 -right-2 h-6 w-6 rounded-full flex items-center justify-center shadow-lg",
|
|
2376
|
+
style: {
|
|
2377
|
+
background: "var(--papyrus-surface-2-resolved, var(--papyrus-surface-2, #1f2937))",
|
|
2378
|
+
border: "1px solid var(--papyrus-border-resolved, #374151)",
|
|
2379
|
+
color: "var(--papyrus-text-resolved, #e5e7eb)"
|
|
2380
|
+
},
|
|
2381
|
+
title: "Abrir comentario",
|
|
2382
|
+
"aria-label": "Abrir comentario",
|
|
2383
|
+
onClick: (event) => {
|
|
2384
|
+
event.stopPropagation();
|
|
2385
|
+
onSelect();
|
|
2386
|
+
},
|
|
2387
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2388
|
+
"svg",
|
|
2389
|
+
{
|
|
2390
|
+
className: "h-3.5 w-3.5",
|
|
2391
|
+
fill: "none",
|
|
2392
|
+
stroke: "currentColor",
|
|
2393
|
+
viewBox: "0 0 24 24",
|
|
2394
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2395
|
+
"path",
|
|
2396
|
+
{
|
|
2397
|
+
strokeLinecap: "round",
|
|
2398
|
+
strokeLinejoin: "round",
|
|
2399
|
+
strokeWidth: 2,
|
|
2400
|
+
d: "M8 10h.01M12 10h.01M16 10h.01M7 16H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-4l-4 4v-4z"
|
|
2401
|
+
}
|
|
2402
|
+
)
|
|
2403
|
+
}
|
|
2404
|
+
)
|
|
2109
2405
|
}
|
|
2110
|
-
)
|
|
2406
|
+
),
|
|
2407
|
+
isText && isSelected && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
2408
|
+
"div",
|
|
2409
|
+
{
|
|
2410
|
+
className: "absolute top-full mt-2 w-72 rounded-xl p-3 z-50",
|
|
2411
|
+
style: {
|
|
2412
|
+
background: "var(--papyrus-popover-resolved, var(--papyrus-popover, #ffffff))",
|
|
2413
|
+
border: "1px solid var(--papyrus-border-resolved, #d1d5db)",
|
|
2414
|
+
color: "var(--papyrus-text-resolved, #111827)",
|
|
2415
|
+
boxShadow: "0 20px 40px var(--papyrus-shadow-resolved, rgba(0, 0, 0, 0.3))"
|
|
2416
|
+
},
|
|
2417
|
+
onClick: (event) => event.stopPropagation(),
|
|
2418
|
+
children: [
|
|
2419
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mb-2 flex items-center justify-between", children: [
|
|
2420
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-[10px] font-bold uppercase tracking-wider opacity-70", children: ann.type === "comment" ? "Comentario" : "Nota" }),
|
|
2421
|
+
ann.replies?.length ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "text-[10px] opacity-70", children: [
|
|
2422
|
+
ann.replies.length,
|
|
2423
|
+
" resposta",
|
|
2424
|
+
ann.replies.length > 1 ? "s" : ""
|
|
2425
|
+
] }) : null
|
|
2426
|
+
] }),
|
|
2427
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2428
|
+
"textarea",
|
|
2429
|
+
{
|
|
2430
|
+
className: "w-full rounded-md border p-2 text-xs font-medium resize-none focus:outline-none",
|
|
2431
|
+
style: {
|
|
2432
|
+
background: "var(--papyrus-surface-resolved, var(--papyrus-surface, #ffffff))",
|
|
2433
|
+
borderColor: "var(--papyrus-border-resolved, #d1d5db)",
|
|
2434
|
+
color: "var(--papyrus-text-resolved, #111827)"
|
|
2435
|
+
},
|
|
2436
|
+
placeholder: "Escreva seu comentario...",
|
|
2437
|
+
rows: 3,
|
|
2438
|
+
value: draftContent,
|
|
2439
|
+
onChange: (event) => setDraftContent(event.target.value),
|
|
2440
|
+
onKeyDown: (event) => {
|
|
2441
|
+
if (event.key === "Enter" && !event.shiftKey) {
|
|
2442
|
+
event.preventDefault();
|
|
2443
|
+
handleSaveContent();
|
|
2444
|
+
}
|
|
2445
|
+
},
|
|
2446
|
+
autoFocus: true
|
|
2447
|
+
}
|
|
2448
|
+
),
|
|
2449
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "mt-2 flex justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2450
|
+
"button",
|
|
2451
|
+
{
|
|
2452
|
+
type: "button",
|
|
2453
|
+
className: "rounded-md px-3 py-1.5 text-[11px] font-semibold text-white",
|
|
2454
|
+
style: { backgroundColor: accentColor },
|
|
2455
|
+
onClick: (event) => {
|
|
2456
|
+
event.stopPropagation();
|
|
2457
|
+
handleSaveContent();
|
|
2458
|
+
},
|
|
2459
|
+
children: (ann.content ?? "").trim() ? "Atualizar" : "Enviar"
|
|
2460
|
+
}
|
|
2461
|
+
) }),
|
|
2462
|
+
ann.replies && ann.replies.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "mt-3 space-y-2", children: ann.replies.map((reply) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
2463
|
+
"div",
|
|
2464
|
+
{
|
|
2465
|
+
className: "rounded-md border p-2",
|
|
2466
|
+
style: {
|
|
2467
|
+
background: "var(--papyrus-surface-resolved, var(--papyrus-surface, #ffffff))",
|
|
2468
|
+
borderColor: "var(--papyrus-border-resolved, #d1d5db)"
|
|
2469
|
+
},
|
|
2470
|
+
children: [
|
|
2471
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "text-xs", children: reply.content }),
|
|
2472
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "mt-1 text-[10px] opacity-70", children: new Date(reply.createdAt).toLocaleTimeString([], {
|
|
2473
|
+
hour: "2-digit",
|
|
2474
|
+
minute: "2-digit"
|
|
2475
|
+
}) })
|
|
2476
|
+
]
|
|
2477
|
+
},
|
|
2478
|
+
reply.id
|
|
2479
|
+
)) }) : null,
|
|
2480
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mt-3 flex items-center gap-2", children: [
|
|
2481
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2482
|
+
"input",
|
|
2483
|
+
{
|
|
2484
|
+
type: "text",
|
|
2485
|
+
className: "flex-1 rounded-md border px-2 py-1.5 text-xs focus:outline-none",
|
|
2486
|
+
style: {
|
|
2487
|
+
background: "var(--papyrus-surface-resolved, var(--papyrus-surface, #ffffff))",
|
|
2488
|
+
borderColor: "var(--papyrus-border-resolved, #d1d5db)",
|
|
2489
|
+
color: "var(--papyrus-text-resolved, #111827)"
|
|
2490
|
+
},
|
|
2491
|
+
placeholder: "Responder...",
|
|
2492
|
+
value: draftReply,
|
|
2493
|
+
onChange: (event) => setDraftReply(event.target.value),
|
|
2494
|
+
onKeyDown: (event) => {
|
|
2495
|
+
if (event.key === "Enter") {
|
|
2496
|
+
event.preventDefault();
|
|
2497
|
+
handleReplySubmit();
|
|
2498
|
+
}
|
|
2499
|
+
}
|
|
2500
|
+
}
|
|
2501
|
+
),
|
|
2502
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2503
|
+
"button",
|
|
2504
|
+
{
|
|
2505
|
+
type: "button",
|
|
2506
|
+
className: "rounded-md px-3 py-1.5 text-[11px] font-semibold text-white",
|
|
2507
|
+
style: { backgroundColor: accentColor },
|
|
2508
|
+
onClick: handleReplySubmit,
|
|
2509
|
+
children: "Responder"
|
|
2510
|
+
}
|
|
2511
|
+
)
|
|
2512
|
+
] })
|
|
2513
|
+
]
|
|
2514
|
+
}
|
|
2515
|
+
),
|
|
2111
2516
|
isSelected && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2112
2517
|
"button",
|
|
2113
2518
|
{
|
|
@@ -2149,9 +2554,12 @@ var MIN_ZOOM = 0.2;
|
|
|
2149
2554
|
var MAX_ZOOM = 5;
|
|
2150
2555
|
var WIDTH_SNAP_PX = 4;
|
|
2151
2556
|
var WIDTH_HYSTERESIS_PX = 6;
|
|
2557
|
+
var HEIGHT_SNAP_PX = 4;
|
|
2558
|
+
var HEIGHT_HYSTERESIS_PX = 6;
|
|
2152
2559
|
var MOBILE_HEADER_HIDE_DELTA_PX = 28;
|
|
2153
2560
|
var MOBILE_HEADER_SHOW_DELTA_PX = 16;
|
|
2154
2561
|
var MOBILE_HEADER_TOP_RESET_PX = 12;
|
|
2562
|
+
var MOBILE_LANDSCAPE_MAX_HEIGHT_PX2 = 500;
|
|
2155
2563
|
var Viewer = ({ engine, style }) => {
|
|
2156
2564
|
const viewerState = (0, import_core5.useViewerStore)();
|
|
2157
2565
|
const {
|
|
@@ -2177,6 +2585,7 @@ var Viewer = ({ engine, style }) => {
|
|
|
2177
2585
|
const jumpRef = (0, import_react5.useRef)(false);
|
|
2178
2586
|
const jumpTimerRef = (0, import_react5.useRef)(null);
|
|
2179
2587
|
const lastWidthRef = (0, import_react5.useRef)(null);
|
|
2588
|
+
const lastHeightRef = (0, import_react5.useRef)(null);
|
|
2180
2589
|
const lastScrollTopRef = (0, import_react5.useRef)(0);
|
|
2181
2590
|
const scrollDownAccumulatorRef = (0, import_react5.useRef)(0);
|
|
2182
2591
|
const scrollUpAccumulatorRef = (0, import_react5.useRef)(0);
|
|
@@ -2190,11 +2599,14 @@ var Viewer = ({ engine, style }) => {
|
|
|
2190
2599
|
rafId: null
|
|
2191
2600
|
});
|
|
2192
2601
|
const [availableWidth, setAvailableWidth] = (0, import_react5.useState)(null);
|
|
2602
|
+
const [availableHeight, setAvailableHeight] = (0, import_react5.useState)(null);
|
|
2193
2603
|
const [basePageSize, setBasePageSize] = (0, import_react5.useState)(null);
|
|
2194
2604
|
const [pageSizes, setPageSizes] = (0, import_react5.useState)({});
|
|
2195
2605
|
const [colorPickerOpen, setColorPickerOpen] = (0, import_react5.useState)(false);
|
|
2196
|
-
const
|
|
2197
|
-
const
|
|
2606
|
+
const isLandscape = availableWidth !== null && availableHeight !== null && availableWidth > availableHeight;
|
|
2607
|
+
const isLandscapeShort = isLandscape && availableHeight !== null && availableHeight <= MOBILE_LANDSCAPE_MAX_HEIGHT_PX2;
|
|
2608
|
+
const isCompact = availableWidth !== null && (availableWidth < 820 || isLandscapeShort);
|
|
2609
|
+
const isMobileViewport = availableWidth !== null && (availableWidth < 640 || isLandscapeShort);
|
|
2198
2610
|
const paddingY = isCompact ? "py-10" : "py-16";
|
|
2199
2611
|
const toolDockPosition = isCompact ? "bottom-4" : "bottom-8";
|
|
2200
2612
|
const colorPalette = [
|
|
@@ -2245,21 +2657,32 @@ var Viewer = ({ engine, style }) => {
|
|
|
2245
2657
|
const measurementTarget = viewerElement.parentElement ?? viewerElement;
|
|
2246
2658
|
let rafId = null;
|
|
2247
2659
|
const normalizeWidth = (rawWidth) => Math.max(0, Math.floor(rawWidth / WIDTH_SNAP_PX) * WIDTH_SNAP_PX);
|
|
2248
|
-
const
|
|
2660
|
+
const normalizeHeight = (rawHeight) => Math.max(0, Math.floor(rawHeight / HEIGHT_SNAP_PX) * HEIGHT_SNAP_PX);
|
|
2661
|
+
const updateSize = () => {
|
|
2249
2662
|
const rawWidth = measurementTarget.getBoundingClientRect?.().width ?? measurementTarget.clientWidth ?? measurementTarget.offsetWidth;
|
|
2663
|
+
const rawHeight = measurementTarget.getBoundingClientRect?.().height ?? measurementTarget.clientHeight ?? measurementTarget.offsetHeight;
|
|
2250
2664
|
const nextWidth = normalizeWidth(rawWidth);
|
|
2251
|
-
|
|
2665
|
+
const nextHeight = normalizeHeight(rawHeight);
|
|
2666
|
+
if (nextWidth <= 0 || nextHeight <= 0) return;
|
|
2252
2667
|
const previousWidth = lastWidthRef.current;
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2668
|
+
const previousHeight = lastHeightRef.current;
|
|
2669
|
+
const widthChanged = previousWidth == null || Math.abs(nextWidth - previousWidth) >= WIDTH_HYSTERESIS_PX;
|
|
2670
|
+
const heightChanged = previousHeight == null || Math.abs(nextHeight - previousHeight) >= HEIGHT_HYSTERESIS_PX;
|
|
2671
|
+
if (!widthChanged && !heightChanged) return;
|
|
2672
|
+
if (widthChanged) {
|
|
2673
|
+
lastWidthRef.current = nextWidth;
|
|
2674
|
+
setAvailableWidth(nextWidth);
|
|
2675
|
+
}
|
|
2676
|
+
if (heightChanged) {
|
|
2677
|
+
lastHeightRef.current = nextHeight;
|
|
2678
|
+
setAvailableHeight(nextHeight);
|
|
2679
|
+
}
|
|
2257
2680
|
};
|
|
2258
2681
|
const scheduleWidthUpdate = () => {
|
|
2259
2682
|
if (rafId != null) cancelAnimationFrame(rafId);
|
|
2260
2683
|
rafId = requestAnimationFrame(() => {
|
|
2261
2684
|
rafId = null;
|
|
2262
|
-
|
|
2685
|
+
updateSize();
|
|
2263
2686
|
});
|
|
2264
2687
|
};
|
|
2265
2688
|
scheduleWidthUpdate();
|