@papyrus-sdk/ui-react 0.2.19 → 0.2.20
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.js +121 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +129 -11
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// components/Topbar.tsx
|
|
2
|
-
import { useEffect, useRef, useState } from "react";
|
|
2
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
3
3
|
import { createPortal } from "react-dom";
|
|
4
4
|
import { useViewerStore } from "@papyrus-sdk/core";
|
|
5
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -17,6 +17,7 @@ var Topbar = ({
|
|
|
17
17
|
showUpload = true,
|
|
18
18
|
showSearch = true
|
|
19
19
|
}) => {
|
|
20
|
+
const viewerState = useViewerStore();
|
|
20
21
|
const {
|
|
21
22
|
currentPage,
|
|
22
23
|
pageCount,
|
|
@@ -29,13 +30,15 @@ var Topbar = ({
|
|
|
29
30
|
toggleSidebarLeft,
|
|
30
31
|
toggleSidebarRight,
|
|
31
32
|
triggerScrollToPage
|
|
32
|
-
} =
|
|
33
|
+
} = viewerState;
|
|
34
|
+
const mobileTopbarVisible = viewerState.mobileTopbarVisible ?? true;
|
|
33
35
|
const fileInputRef = useRef(null);
|
|
34
36
|
const zoomTimerRef = useRef(null);
|
|
35
37
|
const pendingZoomRef = useRef(null);
|
|
36
38
|
const [pageInput, setPageInput] = useState(currentPage.toString());
|
|
37
39
|
const [showPageThemes, setShowPageThemes] = useState(false);
|
|
38
40
|
const [showMobileMenu, setShowMobileMenu] = useState(false);
|
|
41
|
+
const [isMobileViewport, setIsMobileViewport] = useState(false);
|
|
39
42
|
const pageDigits = Math.max(2, String(pageCount || 1).length);
|
|
40
43
|
const isDark = uiTheme === "dark";
|
|
41
44
|
const canUseDOM = typeof document !== "undefined";
|
|
@@ -52,6 +55,18 @@ var Topbar = ({
|
|
|
52
55
|
useEffect(() => {
|
|
53
56
|
if (!hasMobileMenu) setShowMobileMenu(false);
|
|
54
57
|
}, [hasMobileMenu]);
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
if (!canUseDOM || typeof window.matchMedia !== "function") return;
|
|
60
|
+
const mediaQuery = window.matchMedia("(max-width: 639px)");
|
|
61
|
+
const updateViewport = () => setIsMobileViewport(mediaQuery.matches);
|
|
62
|
+
updateViewport();
|
|
63
|
+
if (typeof mediaQuery.addEventListener === "function") {
|
|
64
|
+
mediaQuery.addEventListener("change", updateViewport);
|
|
65
|
+
return () => mediaQuery.removeEventListener("change", updateViewport);
|
|
66
|
+
}
|
|
67
|
+
mediaQuery.addListener(updateViewport);
|
|
68
|
+
return () => mediaQuery.removeListener(updateViewport);
|
|
69
|
+
}, [canUseDOM]);
|
|
55
70
|
useEffect(() => {
|
|
56
71
|
if (!showMobileMenu || !canUseDOM) return;
|
|
57
72
|
const previousOverflow = document.body.style.overflow;
|
|
@@ -65,6 +80,30 @@ var Topbar = ({
|
|
|
65
80
|
window.removeEventListener("keydown", handleKeyDown);
|
|
66
81
|
};
|
|
67
82
|
}, [showMobileMenu, canUseDOM]);
|
|
83
|
+
const topbarStyle = useMemo(() => {
|
|
84
|
+
const mergedStyle = { ...style ?? {} };
|
|
85
|
+
if (!isMobileViewport) {
|
|
86
|
+
mergedStyle.transition = "height 180ms ease, opacity 160ms ease, padding 180ms ease, border-width 180ms ease";
|
|
87
|
+
return mergedStyle;
|
|
88
|
+
}
|
|
89
|
+
mergedStyle.transition = "height 180ms ease, opacity 160ms ease, padding 180ms ease, border-width 180ms ease";
|
|
90
|
+
mergedStyle.overflow = "hidden";
|
|
91
|
+
if (!mobileTopbarVisible) {
|
|
92
|
+
mergedStyle.height = 0;
|
|
93
|
+
mergedStyle.minHeight = 0;
|
|
94
|
+
mergedStyle.paddingTop = 0;
|
|
95
|
+
mergedStyle.paddingBottom = 0;
|
|
96
|
+
mergedStyle.borderBottomWidth = 0;
|
|
97
|
+
mergedStyle.opacity = 0;
|
|
98
|
+
mergedStyle.pointerEvents = "none";
|
|
99
|
+
return mergedStyle;
|
|
100
|
+
}
|
|
101
|
+
mergedStyle.height = 56;
|
|
102
|
+
mergedStyle.minHeight = 56;
|
|
103
|
+
mergedStyle.opacity = 1;
|
|
104
|
+
mergedStyle.pointerEvents = "auto";
|
|
105
|
+
return mergedStyle;
|
|
106
|
+
}, [isMobileViewport, mobileTopbarVisible, style]);
|
|
68
107
|
const handleZoom = (delta) => {
|
|
69
108
|
const baseZoom = pendingZoomRef.current ?? zoom;
|
|
70
109
|
const nextZoom = Math.max(0.2, Math.min(5, baseZoom + delta));
|
|
@@ -355,7 +394,7 @@ var Topbar = ({
|
|
|
355
394
|
{
|
|
356
395
|
"data-papyrus-theme": uiTheme,
|
|
357
396
|
className: `papyrus-topbar papyrus-theme relative h-14 border-b flex items-center px-3 sm:px-4 z-50 transition-colors duration-200 ${isDark ? "bg-[#1a1a1a] border-[#333] text-white" : "bg-white border-gray-200 text-gray-800"}`,
|
|
358
|
-
style,
|
|
397
|
+
style: topbarStyle,
|
|
359
398
|
children: [
|
|
360
399
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 min-w-0 z-10", children: [
|
|
361
400
|
showSidebarLeftToggle && /* @__PURE__ */ jsx(
|
|
@@ -1333,11 +1372,11 @@ var SidebarRight = ({ engine, style }) => {
|
|
|
1333
1372
|
var SidebarRight_default = SidebarRight;
|
|
1334
1373
|
|
|
1335
1374
|
// components/Viewer.tsx
|
|
1336
|
-
import { useEffect as useEffect4, useMemo as
|
|
1375
|
+
import { useEffect as useEffect4, useMemo as useMemo3, useRef as useRef4, useState as useState5 } from "react";
|
|
1337
1376
|
import { useViewerStore as useViewerStore5 } from "@papyrus-sdk/core";
|
|
1338
1377
|
|
|
1339
1378
|
// components/PageRenderer.tsx
|
|
1340
|
-
import { useEffect as useEffect3, useMemo, useRef as useRef3, useState as useState4 } from "react";
|
|
1379
|
+
import { useEffect as useEffect3, useMemo as useMemo2, useRef as useRef3, useState as useState4 } from "react";
|
|
1341
1380
|
import { useViewerStore as useViewerStore4, papyrusEvents } from "@papyrus-sdk/core";
|
|
1342
1381
|
import {
|
|
1343
1382
|
PapyrusEventType
|
|
@@ -1390,7 +1429,7 @@ var PageRenderer = ({
|
|
|
1390
1429
|
"strikeout"
|
|
1391
1430
|
]);
|
|
1392
1431
|
const canSelectText = activeTool === "select" || textMarkupTools.has(activeTool);
|
|
1393
|
-
const hasSearchHits =
|
|
1432
|
+
const hasSearchHits = useMemo2(
|
|
1394
1433
|
() => Boolean(searchQuery?.trim()) && searchResults.some((res) => res.pageIndex === pageIndex),
|
|
1395
1434
|
[searchQuery, searchResults, pageIndex]
|
|
1396
1435
|
);
|
|
@@ -1411,14 +1450,14 @@ var PageRenderer = ({
|
|
|
1411
1450
|
active = false;
|
|
1412
1451
|
};
|
|
1413
1452
|
}, [engine, pageIndex]);
|
|
1414
|
-
const fitScale =
|
|
1453
|
+
const fitScale = useMemo2(() => {
|
|
1415
1454
|
if (!availableWidth || !pageSize?.width) return 1;
|
|
1416
1455
|
const targetWidth = Math.max(0, availableWidth - 48);
|
|
1417
1456
|
if (!targetWidth) return 1;
|
|
1418
1457
|
const rawScale = Math.min(1, targetWidth / pageSize.width);
|
|
1419
1458
|
return Math.round(rawScale * SCALE_PRECISION) / SCALE_PRECISION;
|
|
1420
1459
|
}, [availableWidth, pageSize]);
|
|
1421
|
-
const displaySize =
|
|
1460
|
+
const displaySize = useMemo2(() => {
|
|
1422
1461
|
if (!pageSize) return null;
|
|
1423
1462
|
const scale = zoom * fitScale;
|
|
1424
1463
|
return {
|
|
@@ -2083,7 +2122,11 @@ var MIN_ZOOM = 0.2;
|
|
|
2083
2122
|
var MAX_ZOOM = 5;
|
|
2084
2123
|
var WIDTH_SNAP_PX = 4;
|
|
2085
2124
|
var WIDTH_HYSTERESIS_PX = 6;
|
|
2125
|
+
var MOBILE_HEADER_HIDE_DELTA_PX = 28;
|
|
2126
|
+
var MOBILE_HEADER_SHOW_DELTA_PX = 16;
|
|
2127
|
+
var MOBILE_HEADER_TOP_RESET_PX = 12;
|
|
2086
2128
|
var Viewer = ({ engine, style }) => {
|
|
2129
|
+
const viewerState = useViewerStore5();
|
|
2087
2130
|
const {
|
|
2088
2131
|
pageCount,
|
|
2089
2132
|
currentPage,
|
|
@@ -2096,7 +2139,8 @@ var Viewer = ({ engine, style }) => {
|
|
|
2096
2139
|
annotationColor,
|
|
2097
2140
|
setAnnotationColor,
|
|
2098
2141
|
toolDockOpen
|
|
2099
|
-
} =
|
|
2142
|
+
} = viewerState;
|
|
2143
|
+
const mobileTopbarVisible = viewerState.mobileTopbarVisible ?? true;
|
|
2100
2144
|
const isDark = uiTheme === "dark";
|
|
2101
2145
|
const viewerRef = useRef4(null);
|
|
2102
2146
|
const colorPickerRef = useRef4(null);
|
|
@@ -2106,6 +2150,11 @@ var Viewer = ({ engine, style }) => {
|
|
|
2106
2150
|
const jumpRef = useRef4(false);
|
|
2107
2151
|
const jumpTimerRef = useRef4(null);
|
|
2108
2152
|
const lastWidthRef = useRef4(null);
|
|
2153
|
+
const lastScrollTopRef = useRef4(0);
|
|
2154
|
+
const scrollDownAccumulatorRef = useRef4(0);
|
|
2155
|
+
const scrollUpAccumulatorRef = useRef4(0);
|
|
2156
|
+
const previousCurrentPageRef = useRef4(currentPage);
|
|
2157
|
+
const mobileTopbarVisibleRef = useRef4(mobileTopbarVisible);
|
|
2109
2158
|
const pinchRef = useRef4({
|
|
2110
2159
|
active: false,
|
|
2111
2160
|
startDistance: 0,
|
|
@@ -2118,6 +2167,7 @@ var Viewer = ({ engine, style }) => {
|
|
|
2118
2167
|
const [pageSizes, setPageSizes] = useState5({});
|
|
2119
2168
|
const [colorPickerOpen, setColorPickerOpen] = useState5(false);
|
|
2120
2169
|
const isCompact = availableWidth !== null && availableWidth < 820;
|
|
2170
|
+
const isMobileViewport = availableWidth !== null && availableWidth < 640;
|
|
2121
2171
|
const paddingY = isCompact ? "py-10" : "py-16";
|
|
2122
2172
|
const toolDockPosition = isCompact ? "bottom-4" : "bottom-8";
|
|
2123
2173
|
const colorPalette = [
|
|
@@ -2130,6 +2180,13 @@ var Viewer = ({ engine, style }) => {
|
|
|
2130
2180
|
"#8b5cf6",
|
|
2131
2181
|
"#111827"
|
|
2132
2182
|
];
|
|
2183
|
+
const setMobileTopbarVisibility = (visible) => {
|
|
2184
|
+
if (mobileTopbarVisibleRef.current === visible) return;
|
|
2185
|
+
mobileTopbarVisibleRef.current = visible;
|
|
2186
|
+
setDocumentState({
|
|
2187
|
+
mobileTopbarVisible: visible
|
|
2188
|
+
});
|
|
2189
|
+
};
|
|
2133
2190
|
useEffect4(() => {
|
|
2134
2191
|
if (!colorPickerOpen) return;
|
|
2135
2192
|
const handleClick = (event) => {
|
|
@@ -2144,6 +2201,9 @@ var Viewer = ({ engine, style }) => {
|
|
|
2144
2201
|
useEffect4(() => {
|
|
2145
2202
|
if (!toolDockOpen && colorPickerOpen) setColorPickerOpen(false);
|
|
2146
2203
|
}, [toolDockOpen, colorPickerOpen]);
|
|
2204
|
+
useEffect4(() => {
|
|
2205
|
+
mobileTopbarVisibleRef.current = mobileTopbarVisible;
|
|
2206
|
+
}, [mobileTopbarVisible]);
|
|
2147
2207
|
useEffect4(
|
|
2148
2208
|
() => () => {
|
|
2149
2209
|
if (pinchRef.current.rafId != null) {
|
|
@@ -2195,6 +2255,64 @@ var Viewer = ({ engine, style }) => {
|
|
|
2195
2255
|
observer.disconnect();
|
|
2196
2256
|
};
|
|
2197
2257
|
}, []);
|
|
2258
|
+
useEffect4(() => {
|
|
2259
|
+
const root = viewerRef.current;
|
|
2260
|
+
if (!root) return;
|
|
2261
|
+
if (!isMobileViewport) {
|
|
2262
|
+
lastScrollTopRef.current = root.scrollTop;
|
|
2263
|
+
scrollDownAccumulatorRef.current = 0;
|
|
2264
|
+
scrollUpAccumulatorRef.current = 0;
|
|
2265
|
+
setMobileTopbarVisibility(true);
|
|
2266
|
+
return;
|
|
2267
|
+
}
|
|
2268
|
+
lastScrollTopRef.current = root.scrollTop;
|
|
2269
|
+
scrollDownAccumulatorRef.current = 0;
|
|
2270
|
+
scrollUpAccumulatorRef.current = 0;
|
|
2271
|
+
const handleScroll = () => {
|
|
2272
|
+
const nextScrollTop = root.scrollTop;
|
|
2273
|
+
const delta = nextScrollTop - lastScrollTopRef.current;
|
|
2274
|
+
lastScrollTopRef.current = nextScrollTop;
|
|
2275
|
+
if (Math.abs(delta) < 1) return;
|
|
2276
|
+
if (nextScrollTop <= MOBILE_HEADER_TOP_RESET_PX) {
|
|
2277
|
+
scrollDownAccumulatorRef.current = 0;
|
|
2278
|
+
scrollUpAccumulatorRef.current = 0;
|
|
2279
|
+
setMobileTopbarVisibility(true);
|
|
2280
|
+
return;
|
|
2281
|
+
}
|
|
2282
|
+
if (delta > 0) {
|
|
2283
|
+
scrollDownAccumulatorRef.current += delta;
|
|
2284
|
+
scrollUpAccumulatorRef.current = 0;
|
|
2285
|
+
} else {
|
|
2286
|
+
scrollUpAccumulatorRef.current += -delta;
|
|
2287
|
+
scrollDownAccumulatorRef.current = 0;
|
|
2288
|
+
}
|
|
2289
|
+
if (scrollDownAccumulatorRef.current >= MOBILE_HEADER_HIDE_DELTA_PX && mobileTopbarVisibleRef.current) {
|
|
2290
|
+
scrollDownAccumulatorRef.current = 0;
|
|
2291
|
+
scrollUpAccumulatorRef.current = 0;
|
|
2292
|
+
setMobileTopbarVisibility(false);
|
|
2293
|
+
return;
|
|
2294
|
+
}
|
|
2295
|
+
if (scrollUpAccumulatorRef.current >= MOBILE_HEADER_SHOW_DELTA_PX && !mobileTopbarVisibleRef.current) {
|
|
2296
|
+
scrollDownAccumulatorRef.current = 0;
|
|
2297
|
+
scrollUpAccumulatorRef.current = 0;
|
|
2298
|
+
setMobileTopbarVisibility(true);
|
|
2299
|
+
}
|
|
2300
|
+
};
|
|
2301
|
+
root.addEventListener("scroll", handleScroll, { passive: true });
|
|
2302
|
+
return () => {
|
|
2303
|
+
root.removeEventListener("scroll", handleScroll);
|
|
2304
|
+
};
|
|
2305
|
+
}, [isMobileViewport, setDocumentState]);
|
|
2306
|
+
useEffect4(() => {
|
|
2307
|
+
const previousPage = previousCurrentPageRef.current;
|
|
2308
|
+
previousCurrentPageRef.current = currentPage;
|
|
2309
|
+
if (!isMobileViewport) return;
|
|
2310
|
+
if (currentPage < previousPage && !mobileTopbarVisibleRef.current) {
|
|
2311
|
+
scrollDownAccumulatorRef.current = 0;
|
|
2312
|
+
scrollUpAccumulatorRef.current = 0;
|
|
2313
|
+
setMobileTopbarVisibility(true);
|
|
2314
|
+
}
|
|
2315
|
+
}, [currentPage, isMobileViewport, setDocumentState]);
|
|
2198
2316
|
useEffect4(() => {
|
|
2199
2317
|
let active = true;
|
|
2200
2318
|
if (!pageCount) return;
|
|
@@ -2309,7 +2427,7 @@ var Viewer = ({ engine, style }) => {
|
|
|
2309
2427
|
const virtualAnchor = currentPage - 1;
|
|
2310
2428
|
const virtualStart = Math.max(0, virtualAnchor - virtualOverscan);
|
|
2311
2429
|
const virtualEnd = Math.min(pageCount - 1, virtualAnchor + virtualOverscan);
|
|
2312
|
-
const fallbackSize =
|
|
2430
|
+
const fallbackSize = useMemo3(() => {
|
|
2313
2431
|
if (basePageSize && availableWidth) {
|
|
2314
2432
|
const fitScale = Math.min(
|
|
2315
2433
|
1,
|
|
@@ -2326,7 +2444,7 @@ var Viewer = ({ engine, style }) => {
|
|
|
2326
2444
|
height: Math.round(base * zoom)
|
|
2327
2445
|
};
|
|
2328
2446
|
}, [basePageSize, availableWidth, zoom]);
|
|
2329
|
-
const averagePageHeight =
|
|
2447
|
+
const averagePageHeight = useMemo3(() => {
|
|
2330
2448
|
const heights = Object.values(pageSizes).map((size) => size.height);
|
|
2331
2449
|
if (!heights.length)
|
|
2332
2450
|
return availableWidth ? Math.max(680, availableWidth * 1.3) : 1100;
|