@papyrus-sdk/ui-react 0.2.21 → 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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Papyrus Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/base.css CHANGED
@@ -100,7 +100,7 @@
100
100
  display: none;
101
101
  }
102
102
 
103
- @media (min-width: 640px) {
103
+ @media (min-width: 640px) and (min-height: 501px) {
104
104
  .papyrus-topbar .sm\:inline {
105
105
  display: inline !important;
106
106
  }
@@ -122,7 +122,7 @@
122
122
  }
123
123
  }
124
124
 
125
- @media (max-width: 720px) {
125
+ @media (max-width: 720px), (orientation: landscape) and (max-height: 500px) {
126
126
  .papyrus-topbar {
127
127
  flex-wrap: wrap;
128
128
  height: auto;
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("(max-width: 639px)");
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") {
@@ -1797,8 +1799,12 @@ var PageRenderer = ({
1797
1799
  activeSearchIndex,
1798
1800
  textLayerVersion
1799
1801
  ]);
1800
- const handleMouseDown = (e) => {
1801
- const target = e.target;
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) => {
1802
1808
  const clickedInsideAnnotation = Boolean(
1803
1809
  target?.closest("[data-papyrus-annotation-id]")
1804
1810
  );
@@ -1812,8 +1818,8 @@ var PageRenderer = ({
1812
1818
  if (activeTool === "ink") {
1813
1819
  const rect2 = containerRef.current?.getBoundingClientRect();
1814
1820
  if (!rect2) return;
1815
- const x2 = (e.clientX - rect2.left) / rect2.width;
1816
- const y2 = (e.clientY - rect2.top) / rect2.height;
1821
+ const x2 = (clientX - rect2.left) / rect2.width;
1822
+ const y2 = (clientY - rect2.top) / rect2.height;
1817
1823
  setIsInkDrawing(true);
1818
1824
  setInkPoints([{ x: x2, y: y2 }]);
1819
1825
  return;
@@ -1822,25 +1828,25 @@ var PageRenderer = ({
1822
1828
  const rect = containerRef.current?.getBoundingClientRect();
1823
1829
  if (!rect) return;
1824
1830
  setIsDragging(true);
1825
- const x = e.clientX - rect.left;
1826
- const y = e.clientY - rect.top;
1831
+ const x = clientX - rect.left;
1832
+ const y = clientY - rect.top;
1827
1833
  setStartPos({ x, y });
1828
1834
  setCurrentRect({ x, y, w: 0, h: 0 });
1829
1835
  };
1830
- const handleMouseMove = (e) => {
1836
+ const handlePointerMove = (clientX, clientY) => {
1831
1837
  if (isInkDrawing) {
1832
1838
  const rect2 = containerRef.current?.getBoundingClientRect();
1833
1839
  if (!rect2) return;
1834
- const x = (e.clientX - rect2.left) / rect2.width;
1835
- const y = (e.clientY - rect2.top) / rect2.height;
1840
+ const x = (clientX - rect2.left) / rect2.width;
1841
+ const y = (clientY - rect2.top) / rect2.height;
1836
1842
  setInkPoints((prev) => [...prev, { x, y }]);
1837
1843
  return;
1838
1844
  }
1839
1845
  if (!isDragging) return;
1840
1846
  const rect = containerRef.current?.getBoundingClientRect();
1841
1847
  if (!rect) return;
1842
- const currentX = e.clientX - rect.left;
1843
- const currentY = e.clientY - rect.top;
1848
+ const currentX = clientX - rect.left;
1849
+ const currentY = clientY - rect.top;
1844
1850
  setCurrentRect({
1845
1851
  x: Math.min(startPos.x, currentX),
1846
1852
  y: Math.min(startPos.y, currentY),
@@ -1848,7 +1854,7 @@ var PageRenderer = ({
1848
1854
  h: Math.abs(currentY - startPos.y)
1849
1855
  });
1850
1856
  };
1851
- const handleMouseUp = (e) => {
1857
+ const handlePointerUp = () => {
1852
1858
  if (isInkDrawing) {
1853
1859
  setIsInkDrawing(false);
1854
1860
  if (inkPoints.length > 1) {
@@ -2013,6 +2019,37 @@ var PageRenderer = ({
2013
2019
  }
2014
2020
  }
2015
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
+ };
2016
2053
  const getPageFilter = () => {
2017
2054
  switch (pageTheme) {
2018
2055
  case "sepia":
@@ -2030,10 +2067,18 @@ var PageRenderer = ({
2030
2067
  {
2031
2068
  ref: containerRef,
2032
2069
  className: `relative inline-block shadow-2xl bg-white mb-10 ${canSelectText ? "" : "no-select cursor-crosshair"}`,
2033
- style: { scrollMarginTop: "20px", minHeight: "100px" },
2070
+ style: {
2071
+ scrollMarginTop: "20px",
2072
+ minHeight: "100px",
2073
+ touchAction: activeTool === "ink" || activeTool === "text" || activeTool === "comment" ? "none" : "auto"
2074
+ },
2034
2075
  onMouseDown: handleMouseDown,
2035
2076
  onMouseMove: handleMouseMove,
2036
2077
  onMouseUp: handleMouseUp,
2078
+ onTouchStart: handleTouchStart,
2079
+ onTouchMove: handleTouchMove,
2080
+ onTouchEnd: handleTouchEnd,
2081
+ onTouchCancel: handleTouchEnd,
2037
2082
  children: [
2038
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..." }) }),
2039
2084
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
@@ -2509,9 +2554,12 @@ var MIN_ZOOM = 0.2;
2509
2554
  var MAX_ZOOM = 5;
2510
2555
  var WIDTH_SNAP_PX = 4;
2511
2556
  var WIDTH_HYSTERESIS_PX = 6;
2557
+ var HEIGHT_SNAP_PX = 4;
2558
+ var HEIGHT_HYSTERESIS_PX = 6;
2512
2559
  var MOBILE_HEADER_HIDE_DELTA_PX = 28;
2513
2560
  var MOBILE_HEADER_SHOW_DELTA_PX = 16;
2514
2561
  var MOBILE_HEADER_TOP_RESET_PX = 12;
2562
+ var MOBILE_LANDSCAPE_MAX_HEIGHT_PX2 = 500;
2515
2563
  var Viewer = ({ engine, style }) => {
2516
2564
  const viewerState = (0, import_core5.useViewerStore)();
2517
2565
  const {
@@ -2537,6 +2585,7 @@ var Viewer = ({ engine, style }) => {
2537
2585
  const jumpRef = (0, import_react5.useRef)(false);
2538
2586
  const jumpTimerRef = (0, import_react5.useRef)(null);
2539
2587
  const lastWidthRef = (0, import_react5.useRef)(null);
2588
+ const lastHeightRef = (0, import_react5.useRef)(null);
2540
2589
  const lastScrollTopRef = (0, import_react5.useRef)(0);
2541
2590
  const scrollDownAccumulatorRef = (0, import_react5.useRef)(0);
2542
2591
  const scrollUpAccumulatorRef = (0, import_react5.useRef)(0);
@@ -2550,11 +2599,14 @@ var Viewer = ({ engine, style }) => {
2550
2599
  rafId: null
2551
2600
  });
2552
2601
  const [availableWidth, setAvailableWidth] = (0, import_react5.useState)(null);
2602
+ const [availableHeight, setAvailableHeight] = (0, import_react5.useState)(null);
2553
2603
  const [basePageSize, setBasePageSize] = (0, import_react5.useState)(null);
2554
2604
  const [pageSizes, setPageSizes] = (0, import_react5.useState)({});
2555
2605
  const [colorPickerOpen, setColorPickerOpen] = (0, import_react5.useState)(false);
2556
- const isCompact = availableWidth !== null && availableWidth < 820;
2557
- const isMobileViewport = availableWidth !== null && availableWidth < 640;
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);
2558
2610
  const paddingY = isCompact ? "py-10" : "py-16";
2559
2611
  const toolDockPosition = isCompact ? "bottom-4" : "bottom-8";
2560
2612
  const colorPalette = [
@@ -2605,21 +2657,32 @@ var Viewer = ({ engine, style }) => {
2605
2657
  const measurementTarget = viewerElement.parentElement ?? viewerElement;
2606
2658
  let rafId = null;
2607
2659
  const normalizeWidth = (rawWidth) => Math.max(0, Math.floor(rawWidth / WIDTH_SNAP_PX) * WIDTH_SNAP_PX);
2608
- const updateWidth = () => {
2660
+ const normalizeHeight = (rawHeight) => Math.max(0, Math.floor(rawHeight / HEIGHT_SNAP_PX) * HEIGHT_SNAP_PX);
2661
+ const updateSize = () => {
2609
2662
  const rawWidth = measurementTarget.getBoundingClientRect?.().width ?? measurementTarget.clientWidth ?? measurementTarget.offsetWidth;
2663
+ const rawHeight = measurementTarget.getBoundingClientRect?.().height ?? measurementTarget.clientHeight ?? measurementTarget.offsetHeight;
2610
2664
  const nextWidth = normalizeWidth(rawWidth);
2611
- if (nextWidth <= 0) return;
2665
+ const nextHeight = normalizeHeight(rawHeight);
2666
+ if (nextWidth <= 0 || nextHeight <= 0) return;
2612
2667
  const previousWidth = lastWidthRef.current;
2613
- if (previousWidth != null && Math.abs(nextWidth - previousWidth) < WIDTH_HYSTERESIS_PX)
2614
- return;
2615
- lastWidthRef.current = nextWidth;
2616
- setAvailableWidth(nextWidth);
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
+ }
2617
2680
  };
2618
2681
  const scheduleWidthUpdate = () => {
2619
2682
  if (rafId != null) cancelAnimationFrame(rafId);
2620
2683
  rafId = requestAnimationFrame(() => {
2621
2684
  rafId = null;
2622
- updateWidth();
2685
+ updateSize();
2623
2686
  });
2624
2687
  };
2625
2688
  scheduleWidthUpdate();