Skip to content

Commit 985f29e

Browse files
committed
fix(mobile): harden touch placement and pdf-elements runtime behavior
Signed-off-by: Phillip <phillipxh@gmail.com>
1 parent f6ccad9 commit 985f29e

2 files changed

Lines changed: 60 additions & 13 deletions

File tree

src/components/PdfEditor/PdfEditor.vue

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -342,15 +342,20 @@ function handleDocumentTouchEnd(event: Event) {
342342
// Work around mobile tap placement timing in pdf-elements: touchend has no
343343
// touches[0], so preview may never become visible on first tap.
344344
if (instance.isAddingMode && instance.previewElement && !instance.previewVisible && instance.handleMouseMove) {
345+
touchEvent.preventDefault?.()
346+
touchEvent.stopImmediatePropagation?.()
347+
345348
instance.handleMouseMove({
346349
type: 'touchmove',
347350
touches: [{ clientX: touchPoint.clientX, clientY: touchPoint.clientY }],
348351
})
349352
requestAnimationFrame(() => {
350-
if (instance.isAddingMode) {
351-
instance.finishAdding?.()
352-
}
353-
scheduleSignerAddedCheck()
353+
requestAnimationFrame(() => {
354+
if (instance.isAddingMode) {
355+
instance.finishAdding?.()
356+
}
357+
scheduleSignerAddedCheck()
358+
})
354359
})
355360
return
356361
}
@@ -492,21 +497,32 @@ defineExpose({
492497
}
493498
494499
.action-btn {
495-
border: none;
496-
background: transparent;
497-
color: #ffffff;
500+
border: 1px solid #cbd5e1;
501+
background: #f8fafc;
502+
color: #0f172a;
498503
padding: 4px;
499504
min-height: 0;
500505
min-width: 0;
501506
border-radius: 4px;
507+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
502508
cursor: pointer;
503509
display: inline-flex;
504510
align-items: center;
505511
justify-content: center;
506512
transition: background 120ms ease;
507513
508514
&:hover {
509-
background: rgba(255, 255, 255, 0.1);
515+
background: #e2e8f0;
516+
}
517+
518+
:deep(svg),
519+
:deep(.icon-vue),
520+
:deep(.material-design-icon),
521+
:deep([class*='icon']) {
522+
color: currentColor;
523+
fill: currentColor;
524+
stroke: currentColor;
525+
opacity: 1;
510526
}
511527
}
512528

vite.config.mjs

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,53 @@
66
import { createAppConfig } from '@nextcloud/vite-config'
77
import { resolve } from 'node:path'
88

9-
const patchPdfElementsTouchmovePassive = {
10-
name: 'patch-pdf-elements-touchmove-passive',
9+
const patchPdfElementsRuntimeFixes = {
10+
name: 'patch-pdf-elements-runtime-fixes',
1111
enforce: 'pre',
1212
transform(code, id) {
1313
if (!id.includes('/@libresign/pdf-elements/')) {
1414
return null
1515
}
16-
if (!id.endsWith('/dist/index.mjs') && !id.endsWith('/src/components/DraggableElement.vue')) {
16+
if (!id.endsWith('/dist/index.mjs')
17+
&& !id.endsWith('/src/components/DraggableElement.vue')
18+
&& !id.endsWith('/src/components/PDFElements.vue')) {
1719
return null
1820
}
1921

20-
const replaced = code.replace(
22+
let replaced = code
23+
24+
// Drag/resize listeners must be non-passive because handleMove calls preventDefault.
25+
replaced = replaced.replace(
2126
/window\.addEventListener\((['"])touchmove\1,\s*this\.boundHandleMove\)/g,
2227
'window.addEventListener($1touchmove$1, this.boundHandleMove, { passive: false })',
2328
)
2429

30+
// Adding-mode touchmove also needs to be non-passive.
31+
replaced = replaced.replace(
32+
/document\.addEventListener\((['"])touchmove\1,\s*this\.handleMouseMove,\s*\{\s*passive:\s*(?:!0|true)\s*\}\)/g,
33+
'document.addEventListener($1touchmove$1, this.handleMouseMove, { passive: false })',
34+
)
35+
36+
// Guard against race where add mode ends while RAF callback is still queued.
37+
replaced = replaced.replace(
38+
/const s = this\.pendingHoverClientPos;\s*if \(!s\) return;/g,
39+
'if (!this.isAddingMode || !this.previewElement) { this.pendingHoverClientPos = null; return; } const s = this.pendingHoverClientPos; if (!s) return;',
40+
)
41+
42+
// Defensive access to preview element dimensions in async mobile flow.
43+
replaced = replaced.replace(/this\.previewElement\.width/g, '(this.previewElement?.width || 0)')
44+
replaced = replaced.replace(/this\.previewElement\.height/g, '(this.previewElement?.height || 0)')
45+
46+
// Keep toolbar above by default, but place below when signature is near top.
47+
replaced = replaced.replace(
48+
/const e = this\.pagesScale \|\| 1, t = this\.mode === "drag", i = this\.mode === "resize", s = t \? this\.offsetX : 0, n = t \? this\.offsetY : 0, a = i \? this\.resizeOffsetX : 0, o = i \? this\.resizeOffsetY : 0, r = i \? this\.resizeOffsetW : 0, h = this\.object\.x \+ s \+ a, l = this\.object\.y \+ n \+ o, u = this\.object\.width \+ r, d = l - 60, g = d < 0 \? l \+ 8 : d;/g,
49+
'const e = this.pagesScale || 1, t = this.mode === "drag", i = this.mode === "resize", s = t ? this.offsetX : 0, n = t ? this.offsetY : 0, a = i ? this.resizeOffsetX : 0, o = i ? this.resizeOffsetY : 0, r = i ? this.resizeOffsetW : 0, h = i ? this.resizeOffsetH : 0, l = this.object.x + s + a, u = this.object.y + n + o, c = this.object.width + r, d = this.object.height + h, g = u * e < 72, f = g ? u + d : u, b = g ? "translate(-50%, 8px)" : "translate(-50%, calc(-100% - 8px))";',
50+
)
51+
replaced = replaced.replace(
52+
/left: `\$\{\(h \+ u \/ 2\) \* e\}px`,\s*top: `\$\{g \* e\}px`,\s*transform: "translateX\(-50%\)"/g,
53+
'left: `${(l + c / 2) * e}px`, top: `${f * e}px`, transform: b',
54+
)
55+
2556
return replaced === code ? null : { code: replaced, map: null }
2657
},
2758
}
@@ -45,7 +76,7 @@ export default createAppConfig({
4576
},
4677
},
4778
plugins: [
48-
patchPdfElementsTouchmovePassive,
79+
patchPdfElementsRuntimeFixes,
4980
{
5081
name: 'vue-devtools',
5182
config(_, { mode }) {

0 commit comments

Comments
 (0)