@papyrus-sdk/ui-react 0.2.18 → 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/base.css +89 -48
- package/dist/index.d.mts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +128 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +136 -13
- 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";
|
|
@@ -8,6 +8,7 @@ var Topbar = ({
|
|
|
8
8
|
showBrand = false,
|
|
9
9
|
brand,
|
|
10
10
|
title,
|
|
11
|
+
style,
|
|
11
12
|
showSidebarLeftToggle = true,
|
|
12
13
|
showPageControls = true,
|
|
13
14
|
showZoomControls = true,
|
|
@@ -16,6 +17,7 @@ var Topbar = ({
|
|
|
16
17
|
showUpload = true,
|
|
17
18
|
showSearch = true
|
|
18
19
|
}) => {
|
|
20
|
+
const viewerState = useViewerStore();
|
|
19
21
|
const {
|
|
20
22
|
currentPage,
|
|
21
23
|
pageCount,
|
|
@@ -28,13 +30,15 @@ var Topbar = ({
|
|
|
28
30
|
toggleSidebarLeft,
|
|
29
31
|
toggleSidebarRight,
|
|
30
32
|
triggerScrollToPage
|
|
31
|
-
} =
|
|
33
|
+
} = viewerState;
|
|
34
|
+
const mobileTopbarVisible = viewerState.mobileTopbarVisible ?? true;
|
|
32
35
|
const fileInputRef = useRef(null);
|
|
33
36
|
const zoomTimerRef = useRef(null);
|
|
34
37
|
const pendingZoomRef = useRef(null);
|
|
35
38
|
const [pageInput, setPageInput] = useState(currentPage.toString());
|
|
36
39
|
const [showPageThemes, setShowPageThemes] = useState(false);
|
|
37
40
|
const [showMobileMenu, setShowMobileMenu] = useState(false);
|
|
41
|
+
const [isMobileViewport, setIsMobileViewport] = useState(false);
|
|
38
42
|
const pageDigits = Math.max(2, String(pageCount || 1).length);
|
|
39
43
|
const isDark = uiTheme === "dark";
|
|
40
44
|
const canUseDOM = typeof document !== "undefined";
|
|
@@ -51,6 +55,18 @@ var Topbar = ({
|
|
|
51
55
|
useEffect(() => {
|
|
52
56
|
if (!hasMobileMenu) setShowMobileMenu(false);
|
|
53
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]);
|
|
54
70
|
useEffect(() => {
|
|
55
71
|
if (!showMobileMenu || !canUseDOM) return;
|
|
56
72
|
const previousOverflow = document.body.style.overflow;
|
|
@@ -64,6 +80,30 @@ var Topbar = ({
|
|
|
64
80
|
window.removeEventListener("keydown", handleKeyDown);
|
|
65
81
|
};
|
|
66
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]);
|
|
67
107
|
const handleZoom = (delta) => {
|
|
68
108
|
const baseZoom = pendingZoomRef.current ?? zoom;
|
|
69
109
|
const nextZoom = Math.max(0.2, Math.min(5, baseZoom + delta));
|
|
@@ -354,6 +394,7 @@ var Topbar = ({
|
|
|
354
394
|
{
|
|
355
395
|
"data-papyrus-theme": uiTheme,
|
|
356
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"}`,
|
|
397
|
+
style: topbarStyle,
|
|
357
398
|
children: [
|
|
358
399
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 min-w-0 z-10", children: [
|
|
359
400
|
showSidebarLeftToggle && /* @__PURE__ */ jsx(
|
|
@@ -886,7 +927,7 @@ var OutlineNode = ({ item, engine, isDark, accentColor, depth = 0 }) => {
|
|
|
886
927
|
)) })
|
|
887
928
|
] });
|
|
888
929
|
};
|
|
889
|
-
var SidebarLeft = ({ engine }) => {
|
|
930
|
+
var SidebarLeft = ({ engine, style }) => {
|
|
890
931
|
const {
|
|
891
932
|
pageCount,
|
|
892
933
|
currentPage,
|
|
@@ -908,6 +949,7 @@ var SidebarLeft = ({ engine }) => {
|
|
|
908
949
|
{
|
|
909
950
|
"data-papyrus-theme": uiTheme,
|
|
910
951
|
className: `papyrus-sidebar-left papyrus-theme absolute left-0 top-0 bottom-0 z-[120] w-[85vw] max-w-72 border-r flex flex-col h-full overflow-hidden transition-colors duration-200 ${isDark ? "bg-[#2a2a2a] border-[#3a3a3a]" : "bg-[#fcfcfc] border-gray-200"}`,
|
|
952
|
+
style,
|
|
911
953
|
children: [
|
|
912
954
|
/* @__PURE__ */ jsxs2(
|
|
913
955
|
"div",
|
|
@@ -1048,7 +1090,7 @@ var withAlpha2 = (hex, alpha) => {
|
|
|
1048
1090
|
const b = parseInt(value.slice(4, 6), 16);
|
|
1049
1091
|
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
|
1050
1092
|
};
|
|
1051
|
-
var SidebarRight = ({ engine }) => {
|
|
1093
|
+
var SidebarRight = ({ engine, style }) => {
|
|
1052
1094
|
const {
|
|
1053
1095
|
sidebarRightOpen,
|
|
1054
1096
|
sidebarRightTab,
|
|
@@ -1085,6 +1127,7 @@ var SidebarRight = ({ engine }) => {
|
|
|
1085
1127
|
{
|
|
1086
1128
|
"data-papyrus-theme": uiTheme,
|
|
1087
1129
|
className: `papyrus-sidebar-right papyrus-theme absolute right-0 top-0 bottom-0 z-[120] w-[88vw] max-w-80 border-l flex flex-col h-full transition-colors duration-200 shadow-2xl ${isDark ? "bg-[#1a1a1a] border-[#333]" : "bg-white border-gray-200"}`,
|
|
1130
|
+
style,
|
|
1088
1131
|
children: [
|
|
1089
1132
|
/* @__PURE__ */ jsxs3(
|
|
1090
1133
|
"div",
|
|
@@ -1329,11 +1372,11 @@ var SidebarRight = ({ engine }) => {
|
|
|
1329
1372
|
var SidebarRight_default = SidebarRight;
|
|
1330
1373
|
|
|
1331
1374
|
// components/Viewer.tsx
|
|
1332
|
-
import { useEffect as useEffect4, useMemo as
|
|
1375
|
+
import { useEffect as useEffect4, useMemo as useMemo3, useRef as useRef4, useState as useState5 } from "react";
|
|
1333
1376
|
import { useViewerStore as useViewerStore5 } from "@papyrus-sdk/core";
|
|
1334
1377
|
|
|
1335
1378
|
// components/PageRenderer.tsx
|
|
1336
|
-
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";
|
|
1337
1380
|
import { useViewerStore as useViewerStore4, papyrusEvents } from "@papyrus-sdk/core";
|
|
1338
1381
|
import {
|
|
1339
1382
|
PapyrusEventType
|
|
@@ -1386,7 +1429,7 @@ var PageRenderer = ({
|
|
|
1386
1429
|
"strikeout"
|
|
1387
1430
|
]);
|
|
1388
1431
|
const canSelectText = activeTool === "select" || textMarkupTools.has(activeTool);
|
|
1389
|
-
const hasSearchHits =
|
|
1432
|
+
const hasSearchHits = useMemo2(
|
|
1390
1433
|
() => Boolean(searchQuery?.trim()) && searchResults.some((res) => res.pageIndex === pageIndex),
|
|
1391
1434
|
[searchQuery, searchResults, pageIndex]
|
|
1392
1435
|
);
|
|
@@ -1407,14 +1450,14 @@ var PageRenderer = ({
|
|
|
1407
1450
|
active = false;
|
|
1408
1451
|
};
|
|
1409
1452
|
}, [engine, pageIndex]);
|
|
1410
|
-
const fitScale =
|
|
1453
|
+
const fitScale = useMemo2(() => {
|
|
1411
1454
|
if (!availableWidth || !pageSize?.width) return 1;
|
|
1412
1455
|
const targetWidth = Math.max(0, availableWidth - 48);
|
|
1413
1456
|
if (!targetWidth) return 1;
|
|
1414
1457
|
const rawScale = Math.min(1, targetWidth / pageSize.width);
|
|
1415
1458
|
return Math.round(rawScale * SCALE_PRECISION) / SCALE_PRECISION;
|
|
1416
1459
|
}, [availableWidth, pageSize]);
|
|
1417
|
-
const displaySize =
|
|
1460
|
+
const displaySize = useMemo2(() => {
|
|
1418
1461
|
if (!pageSize) return null;
|
|
1419
1462
|
const scale = zoom * fitScale;
|
|
1420
1463
|
return {
|
|
@@ -2079,7 +2122,11 @@ var MIN_ZOOM = 0.2;
|
|
|
2079
2122
|
var MAX_ZOOM = 5;
|
|
2080
2123
|
var WIDTH_SNAP_PX = 4;
|
|
2081
2124
|
var WIDTH_HYSTERESIS_PX = 6;
|
|
2082
|
-
var
|
|
2125
|
+
var MOBILE_HEADER_HIDE_DELTA_PX = 28;
|
|
2126
|
+
var MOBILE_HEADER_SHOW_DELTA_PX = 16;
|
|
2127
|
+
var MOBILE_HEADER_TOP_RESET_PX = 12;
|
|
2128
|
+
var Viewer = ({ engine, style }) => {
|
|
2129
|
+
const viewerState = useViewerStore5();
|
|
2083
2130
|
const {
|
|
2084
2131
|
pageCount,
|
|
2085
2132
|
currentPage,
|
|
@@ -2092,7 +2139,8 @@ var Viewer = ({ engine }) => {
|
|
|
2092
2139
|
annotationColor,
|
|
2093
2140
|
setAnnotationColor,
|
|
2094
2141
|
toolDockOpen
|
|
2095
|
-
} =
|
|
2142
|
+
} = viewerState;
|
|
2143
|
+
const mobileTopbarVisible = viewerState.mobileTopbarVisible ?? true;
|
|
2096
2144
|
const isDark = uiTheme === "dark";
|
|
2097
2145
|
const viewerRef = useRef4(null);
|
|
2098
2146
|
const colorPickerRef = useRef4(null);
|
|
@@ -2102,6 +2150,11 @@ var Viewer = ({ engine }) => {
|
|
|
2102
2150
|
const jumpRef = useRef4(false);
|
|
2103
2151
|
const jumpTimerRef = useRef4(null);
|
|
2104
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);
|
|
2105
2158
|
const pinchRef = useRef4({
|
|
2106
2159
|
active: false,
|
|
2107
2160
|
startDistance: 0,
|
|
@@ -2114,6 +2167,7 @@ var Viewer = ({ engine }) => {
|
|
|
2114
2167
|
const [pageSizes, setPageSizes] = useState5({});
|
|
2115
2168
|
const [colorPickerOpen, setColorPickerOpen] = useState5(false);
|
|
2116
2169
|
const isCompact = availableWidth !== null && availableWidth < 820;
|
|
2170
|
+
const isMobileViewport = availableWidth !== null && availableWidth < 640;
|
|
2117
2171
|
const paddingY = isCompact ? "py-10" : "py-16";
|
|
2118
2172
|
const toolDockPosition = isCompact ? "bottom-4" : "bottom-8";
|
|
2119
2173
|
const colorPalette = [
|
|
@@ -2126,6 +2180,13 @@ var Viewer = ({ engine }) => {
|
|
|
2126
2180
|
"#8b5cf6",
|
|
2127
2181
|
"#111827"
|
|
2128
2182
|
];
|
|
2183
|
+
const setMobileTopbarVisibility = (visible) => {
|
|
2184
|
+
if (mobileTopbarVisibleRef.current === visible) return;
|
|
2185
|
+
mobileTopbarVisibleRef.current = visible;
|
|
2186
|
+
setDocumentState({
|
|
2187
|
+
mobileTopbarVisible: visible
|
|
2188
|
+
});
|
|
2189
|
+
};
|
|
2129
2190
|
useEffect4(() => {
|
|
2130
2191
|
if (!colorPickerOpen) return;
|
|
2131
2192
|
const handleClick = (event) => {
|
|
@@ -2140,6 +2201,9 @@ var Viewer = ({ engine }) => {
|
|
|
2140
2201
|
useEffect4(() => {
|
|
2141
2202
|
if (!toolDockOpen && colorPickerOpen) setColorPickerOpen(false);
|
|
2142
2203
|
}, [toolDockOpen, colorPickerOpen]);
|
|
2204
|
+
useEffect4(() => {
|
|
2205
|
+
mobileTopbarVisibleRef.current = mobileTopbarVisible;
|
|
2206
|
+
}, [mobileTopbarVisible]);
|
|
2143
2207
|
useEffect4(
|
|
2144
2208
|
() => () => {
|
|
2145
2209
|
if (pinchRef.current.rafId != null) {
|
|
@@ -2191,6 +2255,64 @@ var Viewer = ({ engine }) => {
|
|
|
2191
2255
|
observer.disconnect();
|
|
2192
2256
|
};
|
|
2193
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]);
|
|
2194
2316
|
useEffect4(() => {
|
|
2195
2317
|
let active = true;
|
|
2196
2318
|
if (!pageCount) return;
|
|
@@ -2305,7 +2427,7 @@ var Viewer = ({ engine }) => {
|
|
|
2305
2427
|
const virtualAnchor = currentPage - 1;
|
|
2306
2428
|
const virtualStart = Math.max(0, virtualAnchor - virtualOverscan);
|
|
2307
2429
|
const virtualEnd = Math.min(pageCount - 1, virtualAnchor + virtualOverscan);
|
|
2308
|
-
const fallbackSize =
|
|
2430
|
+
const fallbackSize = useMemo3(() => {
|
|
2309
2431
|
if (basePageSize && availableWidth) {
|
|
2310
2432
|
const fitScale = Math.min(
|
|
2311
2433
|
1,
|
|
@@ -2322,7 +2444,7 @@ var Viewer = ({ engine }) => {
|
|
|
2322
2444
|
height: Math.round(base * zoom)
|
|
2323
2445
|
};
|
|
2324
2446
|
}, [basePageSize, availableWidth, zoom]);
|
|
2325
|
-
const averagePageHeight =
|
|
2447
|
+
const averagePageHeight = useMemo3(() => {
|
|
2326
2448
|
const heights = Object.values(pageSizes).map((size) => size.height);
|
|
2327
2449
|
if (!heights.length)
|
|
2328
2450
|
return availableWidth ? Math.max(680, availableWidth * 1.3) : 1100;
|
|
@@ -2422,6 +2544,7 @@ var Viewer = ({ engine }) => {
|
|
|
2422
2544
|
onTouchEnd: handleTouchEnd,
|
|
2423
2545
|
onTouchCancel: handleTouchEnd,
|
|
2424
2546
|
className: `papyrus-viewer papyrus-theme min-w-0 w-full flex-1 overflow-y-scroll overflow-x-hidden flex flex-col items-center ${paddingY} relative custom-scrollbar scroll-smooth ${isDark ? "bg-[#121212]" : "bg-[#e9ecef]"}`,
|
|
2547
|
+
style,
|
|
2425
2548
|
children: [
|
|
2426
2549
|
/* @__PURE__ */ jsx5("div", { className: "flex flex-col items-center gap-6 w-full min-w-0", children: pages.map((idx) => /* @__PURE__ */ jsx5(
|
|
2427
2550
|
"div",
|