Skip to content

Commit af90cf0

Browse files
committed
chore: rebase dependabot eslint-js branch on main
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
2 parents a6c7a8b + 641414c commit af90cf0

7 files changed

Lines changed: 202 additions & 92 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ A Vue 3 component for rendering PDFs with draggable and resizable element overla
99

1010
**[Demo](https://libresign.github.io/pdf-elements/)** · [Examples](examples/)
1111

12+
<img width="754" height="607" alt="image" src="https://github.com/user-attachments/assets/65009896-21ab-4ec5-9548-2707f8d16cdf" />
13+
1214
## Development
1315

1416
- `npm run dev` - Run the demo with Vite

eslint.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const vue = require('eslint-plugin-vue')
77
const tsPlugin = require('@typescript-eslint/eslint-plugin')
88
const tsParser = require('@typescript-eslint/parser')
99
const vueParser = require('vue-eslint-parser')
10-
const vitest = require('eslint-plugin-vitest')
10+
const vitest = require('@vitest/eslint-plugin')
1111

1212
const vitestGlobals = {
1313
afterAll: 'readonly',

package.json

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
11
{
22
"name": "@libresign/pdf-elements",
33
"description": "PDF viewer with draggable and resizable element overlays for Vue 3",
4-
"version": "1.0.0",
4+
"version": "1.1.3",
55
"author": "LibreCode <contact@librecode.coop>",
66
"private": false,
7-
"main": "dist/index.js",
8-
"module": "dist/index.js",
7+
"main": "dist/index.mjs",
8+
"module": "dist/index.mjs",
99
"types": "dist/index.d.ts",
1010
"exports": {
1111
".": {
12-
"import": "./dist/index.js",
13-
"types": "./dist/index.d.ts"
14-
}
12+
"types": "./dist/index.d.ts",
13+
"import": "./dist/index.mjs",
14+
"default": "./dist/index.mjs"
15+
},
16+
"./dist/index.css": "./dist/index.css",
17+
"./src/components/PDFElements.vue": "./src/components/PDFElements.vue",
18+
"./src/utils/asyncReader": "./src/utils/asyncReader.ts"
1519
},
1620
"repository": {
1721
"type": "git",
@@ -47,19 +51,19 @@
4751
},
4852
"devDependencies": {
4953
"@nextcloud/browserslist-config": "^3.1.2",
50-
"@eslint/js": "^10.0.1",
51-
"@typescript-eslint/eslint-plugin": "^8.56.0",
52-
"@typescript-eslint/parser": "^8.56.0",
54+
"@eslint/js": "^10.0.1",
55+
"@typescript-eslint/eslint-plugin": "^8.58.0",
56+
"@typescript-eslint/parser": "^8.58.0",
5357
"@vitejs/plugin-vue": "^6.0.4",
54-
"eslint": "^9.0.0",
58+
"eslint": "^10.1.0",
5559
"eslint-plugin-vue": "^10.8.0",
56-
"eslint-plugin-vitest": "^0.5.4",
57-
"globals": "^15.9.0",
60+
"@vitest/eslint-plugin": "^1.6.14",
61+
"globals": "^17.4.0",
5862
"happy-dom": "^20.7.0",
5963
"postcss": "^8.5.6",
6064
"rollup-plugin-visualizer": "^7.0.0",
61-
"typescript": "^5.9.3",
62-
"vite": "^7.3.1",
65+
"typescript": "^6.0.2",
66+
"vite": "^8.0.2",
6367
"vite-plugin-dts": "^4.5.4",
6468
"vitest": "^4.0.18",
6569
"@vue/test-utils": "^2.4.6",
@@ -69,11 +73,6 @@
6973
"browserslist": [
7074
"extends @nextcloud/browserslist-config"
7175
],
72-
"overrides": {
73-
"eslint-plugin-vitest": {
74-
"@typescript-eslint/utils": "^8.56.0"
75-
}
76-
},
7776
"files": [
7877
"dist",
7978
"src",

src/components/DraggableElement.vue

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -371,15 +371,13 @@ export default defineComponent({
371371
}
372372
373373
const minSize = 16 / (this.pagesScale || 1)
374-
let newWidth = this.startWidth
375-
let newHeight = this.startHeight
376374
let newLeft = this.startLeft
377375
let newTop = this.startTop
378376
379377
const widthChange = this.direction.includes('right') ? deltaX : this.direction.includes('left') ? -deltaX : 0
380-
newWidth = this.startWidth + widthChange
378+
let newWidth = this.startWidth + widthChange
381379
if (newWidth < minSize) newWidth = minSize
382-
newHeight = newWidth / this.aspectRatio
380+
let newHeight = newWidth / this.aspectRatio
383381
384382
if (this.direction.includes('left')) {
385383
newLeft = this.startLeft + (this.startWidth - newWidth)

src/components/PDFElements.vue

Lines changed: 117 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,19 @@ SPDX-License-Identifier: AGPL-3.0-or-later
2323
/>
2424
<div
2525
class="overlay"
26+
:role="isAddingMode ? 'button' : undefined"
27+
:tabindex="isAddingMode ? 0 : -1"
28+
:aria-label="getOverlayAriaLabel(docIndex, pIndex)"
2629
@mousemove="handleMouseMove"
2730
@touchmove="handleMouseMove"
2831
@click="handleOverlayClick(docIndex, pIndex, $event)"
2932
@touchend="handleOverlayClick(docIndex, pIndex, $event)"
33+
@keydown="handleOverlayKeyDown(docIndex, pIndex, $event)"
3034
>
3135
<div
3236
v-if="isAddingMode && previewPageDocIndex === docIndex && previewPageIndex === pIndex && previewElement && previewVisible"
3337
class="preview-element"
38+
aria-hidden="true"
3439
:style="{
3540
left: `${previewPosition.x * previewScale.x}px`,
3641
top: `${previewPosition.y * previewScale.y}px`,
@@ -217,6 +222,10 @@ export default defineComponent({
217222
type: Boolean,
218223
default: false,
219224
},
225+
pageAriaLabel: {
226+
type: Function as PropType<(info: { docIndex: number; docName: string; totalDocs: number; pageNumber: number; totalPages: number; isAddingMode: boolean }) => string>,
227+
default: null,
228+
},
220229
pdfjsOptions: {
221230
type: Object as PropType<Record<string, unknown>>,
222231
default: () => ({}),
@@ -532,16 +541,77 @@ export default defineComponent({
532541
},
533542
getPointerPosition(event) {
534543
if (event?.type?.includes?.('touch')) {
544+
const touch = event.touches?.[0] || event.changedTouches?.[0]
535545
return {
536-
x: event.touches?.[0]?.clientX,
537-
y: event.touches?.[0]?.clientY,
546+
x: touch?.clientX,
547+
y: touch?.clientY,
538548
}
539549
}
540550
return {
541551
x: event?.clientX,
542552
y: event?.clientY,
543553
}
544554
},
555+
updatePreviewFromClientPoint(cursorX, cursorY) {
556+
let target = null
557+
558+
if (this.lastHoverRect &&
559+
cursorX >= this.lastHoverRect.left && cursorX <= this.lastHoverRect.right &&
560+
cursorY >= this.lastHoverRect.top && cursorY <= this.lastHoverRect.bottom) {
561+
target = {
562+
docIndex: this.previewPageDocIndex,
563+
pageIndex: this.previewPageIndex,
564+
rect: this.lastHoverRect,
565+
}
566+
} else {
567+
const rectEntries = this.getPageBoundsList().length
568+
? this.getPageBoundsList()
569+
: Object.values(this.getPageBoundsMap())
570+
for (let i = 0; i < rectEntries.length; i++) {
571+
const entry = rectEntries[i]
572+
const rect = entry.rect
573+
if (cursorX >= rect.left && cursorX <= rect.right &&
574+
cursorY >= rect.top && cursorY <= rect.bottom) {
575+
target = entry
576+
break
577+
}
578+
}
579+
}
580+
581+
if (!target) {
582+
this.previewVisible = false
583+
this.previewScale = { x: 1, y: 1 }
584+
this.lastHoverRect = null
585+
return false
586+
}
587+
588+
this.previewPageDocIndex = target.docIndex
589+
this.previewPageIndex = target.pageIndex
590+
this.lastHoverRect = target.rect
591+
const canvasEl = this.getPageCanvasElement(target.docIndex, target.pageIndex)
592+
const pagesScale = this.pdfDocuments[target.docIndex]?.pagesScale?.[target.pageIndex] || 1
593+
const renderWidth = canvasEl?.width || target.rect.width
594+
const renderHeight = canvasEl?.height || target.rect.height
595+
const layoutScaleX = renderWidth ? target.rect.width / renderWidth : 1
596+
const layoutScaleY = renderHeight ? target.rect.height / renderHeight : 1
597+
const relX = (cursorX - target.rect.left) / layoutScaleX / pagesScale
598+
const relY = (cursorY - target.rect.top) / layoutScaleY / pagesScale
599+
600+
const pageWidth = renderWidth / pagesScale
601+
const pageHeight = renderHeight / pagesScale
602+
this.previewScale.x = pagesScale
603+
this.previewScale.y = pagesScale
604+
let x = relX - this.previewElement.width / 2
605+
let y = relY - this.previewElement.height / 2
606+
607+
x = Math.max(0, Math.min(x, pageWidth - this.previewElement.width))
608+
y = Math.max(0, Math.min(y, pageHeight - this.previewElement.height))
609+
610+
this.previewPosition.x = x
611+
this.previewPosition.y = y
612+
this.previewVisible = true
613+
return true
614+
},
545615
546616
getDisplayedPageScale(docIndex, pageIndex) {
547617
void this.pageBoundsVersion
@@ -619,65 +689,7 @@ export default defineComponent({
619689
const pending = this.pendingHoverClientPos
620690
if (!pending) return
621691
622-
const cursorX = pending.x
623-
const cursorY = pending.y
624-
let target = null
625-
626-
if (this.lastHoverRect &&
627-
cursorX >= this.lastHoverRect.left && cursorX <= this.lastHoverRect.right &&
628-
cursorY >= this.lastHoverRect.top && cursorY <= this.lastHoverRect.bottom) {
629-
target = {
630-
docIndex: this.previewPageDocIndex,
631-
pageIndex: this.previewPageIndex,
632-
rect: this.lastHoverRect,
633-
}
634-
} else {
635-
const rectEntries = this.getPageBoundsList().length
636-
? this.getPageBoundsList()
637-
: Object.values(this.getPageBoundsMap())
638-
for (let i = 0; i < rectEntries.length; i++) {
639-
const entry = rectEntries[i]
640-
const rect = entry.rect
641-
if (cursorX >= rect.left && cursorX <= rect.right &&
642-
cursorY >= rect.top && cursorY <= rect.bottom) {
643-
target = entry
644-
break
645-
}
646-
}
647-
}
648-
649-
if (!target) {
650-
this.previewVisible = false
651-
this.previewScale = { x: 1, y: 1 }
652-
this.lastHoverRect = null
653-
return
654-
}
655-
656-
this.previewPageDocIndex = target.docIndex
657-
this.previewPageIndex = target.pageIndex
658-
this.lastHoverRect = target.rect
659-
const canvasEl = this.getPageCanvasElement(target.docIndex, target.pageIndex)
660-
const pagesScale = this.pdfDocuments[target.docIndex]?.pagesScale?.[target.pageIndex] || 1
661-
const renderWidth = canvasEl?.width || target.rect.width
662-
const renderHeight = canvasEl?.height || target.rect.height
663-
const layoutScaleX = renderWidth ? target.rect.width / renderWidth : 1
664-
const layoutScaleY = renderHeight ? target.rect.height / renderHeight : 1
665-
const relX = (cursorX - target.rect.left) / layoutScaleX / pagesScale
666-
const relY = (cursorY - target.rect.top) / layoutScaleY / pagesScale
667-
668-
const pageWidth = renderWidth / pagesScale
669-
const pageHeight = renderHeight / pagesScale
670-
this.previewScale.x = pagesScale
671-
this.previewScale.y = pagesScale
672-
let x = relX - this.previewElement.width / 2
673-
let y = relY - this.previewElement.height / 2
674-
675-
x = Math.max(0, Math.min(x, pageWidth - this.previewElement.width))
676-
y = Math.max(0, Math.min(y, pageHeight - this.previewElement.height))
677-
678-
this.previewPosition.x = x
679-
this.previewPosition.y = y
680-
this.previewVisible = true
692+
this.updatePreviewFromClientPoint(pending.x, pending.y)
681693
})
682694
},
683695
handleOverlayClick(docIndex, pageIndex, event) {
@@ -730,6 +742,42 @@ export default defineComponent({
730742
}
731743
},
732744
745+
getOverlayAriaLabel(docIndex, pageIndex) {
746+
const doc = this.pdfDocuments?.[docIndex]
747+
const docName = doc?.name ?? `Document ${docIndex + 1}`
748+
const totalDocs = this.pdfDocuments?.length ?? 1
749+
const totalPages = doc?.numPages ?? 0
750+
const pageNumber = pageIndex + 1
751+
if (this.pageAriaLabel) {
752+
return this.pageAriaLabel({ docIndex, docName, totalDocs, pageNumber, totalPages, isAddingMode: this.isAddingMode })
753+
}
754+
const docPrefix = totalDocs > 1 ? `Document ${docIndex + 1} of ${totalDocs} (${docName}), ` : ''
755+
if (this.isAddingMode) {
756+
return `${docPrefix}Page ${pageNumber} of ${totalPages}. Press Enter or Space to place here.`
757+
}
758+
return `${docPrefix}Page ${pageNumber} of ${totalPages}.`
759+
},
760+
761+
handleOverlayKeyDown(docIndex, pageIndex, event) {
762+
if (!this.isAddingMode || !this.previewElement) return
763+
if (event.key !== 'Enter' && event.key !== ' ') return
764+
event.preventDefault()
765+
766+
const pageWidth = this.getPageWidth(docIndex, pageIndex)
767+
const pageHeight = this.getPageHeight(docIndex, pageIndex)
768+
const scale = this.getDisplayedPageScale(docIndex, pageIndex) || 1
769+
770+
this.previewPageDocIndex = docIndex
771+
this.previewPageIndex = pageIndex
772+
this.previewScale = { x: scale, y: scale }
773+
this.previewPosition = {
774+
x: Math.round((pageWidth - this.previewElement.width) / 2),
775+
y: Math.round((pageHeight - this.previewElement.height) / 2),
776+
}
777+
this.previewVisible = true
778+
this.finishAdding()
779+
},
780+
733781
handleWheel(event) {
734782
if (!event.ctrlKey) return
735783
event.preventDefault()
@@ -755,8 +803,14 @@ export default defineComponent({
755803
this.cachePageBounds()
756804
},
757805
758-
finishAdding() {
806+
finishAdding(event) {
759807
if (!this.isAddingMode || !this.previewElement) return
808+
if (!this.previewVisible && event) {
809+
const { x, y } = this.getPointerPosition(event)
810+
if (Number.isFinite(x) && Number.isFinite(y)) {
811+
this.updatePreviewFromClientPoint(x, y)
812+
}
813+
}
760814
if (!this.previewVisible) return
761815
762816
const objectToAdd = {
@@ -1205,7 +1259,7 @@ export default defineComponent({
12051259
.flatMap(doc => doc.pageWidths || [])
12061260
.filter(width => width > 0)
12071261
1208-
let maxCanvasWidth = 0
1262+
let maxCanvasWidth
12091263
if (widths.length) {
12101264
maxCanvasWidth = Math.max(...widths)
12111265
} else {

0 commit comments

Comments
 (0)