From 85c5595256ad84b9aabece7ef5f5c6ca0397c727 Mon Sep 17 00:00:00 2001 From: Abe Jellinek Date: Fri, 15 Sep 2023 16:11:31 -0400 Subject: [PATCH] Fix resizing with touch And move some annotation-overlay styles to CSS. --- .../components/overlay/annotation-overlay.tsx | 62 +++++++++---------- src/dom/common/dom-view.tsx | 6 ++ .../common/stylesheets/annotation-overlay.css | 25 ++++++++ 3 files changed, 61 insertions(+), 32 deletions(-) create mode 100644 src/dom/common/stylesheets/annotation-overlay.css diff --git a/src/dom/common/components/overlay/annotation-overlay.tsx b/src/dom/common/components/overlay/annotation-overlay.tsx index f3e39ff9..9ae855a1 100644 --- a/src/dom/common/components/overlay/annotation-overlay.tsx +++ b/src/dom/common/components/overlay/annotation-overlay.tsx @@ -110,18 +110,7 @@ export const AnnotationOverlay: React.FC = (props) => { let widgetContainer = useRef(null); return <> - + {annotations.filter(annotation => annotation.type == 'highlight' || annotation.type == 'underline').map((annotation) => { if (annotation.id) { return ( @@ -159,14 +148,6 @@ export const AnnotationOverlay: React.FC = (props) => { = (props) => { highlightRects = Array.from(highlightRects) .sort((a, b) => (a.bottom - b.bottom) || (a.left - b.left)); - let handlePointerDown = (event: React.PointerEvent, isStart: boolean) => { + let handlePointerDown = (event: React.PointerEvent) => { if (event.button !== 0) { return; } + event.preventDefault(); (event.target as Element).setPointerCapture(event.pointerId); - setResizingSide(isStart ? 'start' : 'end'); - setPointerCapture({ elem: event.target as Element, pointerId: event.pointerId }); - onResizeStart(annotation); }; let handlePointerUp = (event: React.PointerEvent) => { @@ -532,9 +511,20 @@ const Resizer: React.FC = (props) => { return; } (event.target as Element).releasePointerCapture(event.pointerId); + }; + + let handleGotPointerCapture = (event: React.PointerEvent, side: 'start' | 'end') => { + setResizingSide(side); + setPointerCapture({ elem: event.target as Element, pointerId: event.pointerId }); + onResizeStart(annotation); + }; + + let handleLostPointerCapture = () => { setResizingSide(false); - setPointerCapture(null); - onResizeEnd(annotation, false); + if (pointerCapture) { + setPointerCapture(null); + onResizeEnd(annotation, false); + } }; let handleKeyDown = useCallback((event: KeyboardEvent) => { @@ -631,10 +621,14 @@ const Resizer: React.FC = (props) => { width={WIDTH} height={topLeftRect.height} fill={annotation.color} - style={{ pointerEvents: pointerEventsSuppressed ? 'none' : 'all', cursor: 'col-resize' }} - onPointerDown={event => handlePointerDown(event, true)} - onPointerUp={event => handlePointerUp(event)} + className="resizer" + style={{ pointerEvents: pointerEventsSuppressed ? 'none' : 'auto' }} + onPointerDown={handlePointerDown} + onPointerUp={handlePointerUp} + onPointerCancel={handlePointerUp} onPointerMove={resizingSide == 'start' ? (event => handlePointerMove(event, !rtl)) : undefined} + onGotPointerCapture={event => handleGotPointerCapture(event, 'start')} + onLostPointerCapture={handleLostPointerCapture} /> = (props) => { width={WIDTH} height={bottomRightRect.height} fill={annotation.color} - style={{ pointerEvents: pointerEventsSuppressed ? 'none' : 'all', cursor: 'col-resize' }} - onPointerDown={event => handlePointerDown(event, false)} - onPointerUp={event => handlePointerUp(event)} + className="resizer" + style={{ pointerEvents: pointerEventsSuppressed ? 'none' : 'auto' }} + onPointerDown={handlePointerDown} + onPointerUp={handlePointerUp} + onPointerCancel={handlePointerUp} onPointerMove={resizingSide == 'end' ? (event => handlePointerMove(event, rtl)) : undefined} + onGotPointerCapture={event => handleGotPointerCapture(event, 'end')} + onLostPointerCapture={handleLostPointerCapture} /> ; }; diff --git a/src/dom/common/dom-view.tsx b/src/dom/common/dom-view.tsx index fcdf5ae4..7964f92d 100644 --- a/src/dom/common/dom-view.tsx +++ b/src/dom/common/dom-view.tsx @@ -35,6 +35,8 @@ import { SELECTION_COLOR } from "../../common/defines"; import { isSafari } from "../../common/lib/utilities"; import { isElement } from "./lib/nodes"; import { debounce } from "../../common/lib/debounce"; +// @ts-ignore +import annotationOverlayCSS from '!!raw-loader!./stylesheets/annotation-overlay.css'; abstract class DOMView { initializedPromise: Promise; @@ -442,6 +444,10 @@ abstract class DOMView { this._iframeDocument.addEventListener('scroll', this._handleScroll.bind(this), { passive: true }); this._iframeDocument.addEventListener('selectionchange', this._handleSelectionChange.bind(this)); + let style = this._iframeDocument.createElement('style'); + style.innerHTML = annotationOverlayCSS; + this._iframeDocument.head.append(style); + // Pass options to setters that were delayed until iframe initialization this.setAnnotations(this._options.annotations); this.setTool(this._options.tool); diff --git a/src/dom/common/stylesheets/annotation-overlay.css b/src/dom/common/stylesheets/annotation-overlay.css new file mode 100644 index 00000000..ae6c239e --- /dev/null +++ b/src/dom/common/stylesheets/annotation-overlay.css @@ -0,0 +1,25 @@ +.annotation-container { + z-index: 9999; + pointer-events: none; + position: absolute; + left: 0; + top: 0; + overflow: visible; +} + +.annotation-container.blended { + mix-blend-mode: multiply; +} + +.resizer { + cursor: col-resize; + touch-action: none; +} + +@media (any-pointer: coarse) { + .resizer { + stroke: transparent; + stroke-width: 20px; + margin: -10px; + } +}