diff --git a/.github/workflows/node.yml b/.github/workflows/node.yml index 2ab5d5f..a83e4e3 100644 --- a/.github/workflows/node.yml +++ b/.github/workflows/node.yml @@ -46,7 +46,7 @@ jobs: env: CYPRESS_INSTALL_BINARY: 0 PUPPETEER_SKIP_DOWNLOAD: true - run: npm ci + run: npm i - name: Lint run: npm run lint diff --git a/README.md b/README.md index b39df9a..e983b62 100644 --- a/README.md +++ b/README.md @@ -13,18 +13,19 @@ A Vue 2 component for rendering PDFs with draggable and resizable element overla ### Props -| Prop | Type | Default | -|------|------|---------| -| `width` | String | `'100%'` | -| `height` | String | `'100%'` | -| `initFiles` | Array | `[]` | -| `initFileNames` | Array | `[]` | -| `initialScale` | Number | `1` | -| `showPageFooter` | Boolean | `true` | -| `hideSelectionUI` | Boolean | `false` | -| `showSelectionHandles` | Boolean | `true` | -| `showElementActions` | Boolean | `true` | -| `pageCountFormat` | String | `'{currentPage} of {totalPages}'` | +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `width` | String | `'100%'` | Container width | +| `height` | String | `'100%'` | Container height | +| `initFiles` | Array | `[]` | PDF files to load | +| `initFileNames` | Array | `[]` | Names for the PDF files | +| `initialScale` | Number | `1` | Initial zoom scale | +| `showPageFooter` | Boolean | `true` | Show page footer with document name and page number | +| `hideSelectionUI` | Boolean | `false` | Hide selection handles and actions UI | +| `showSelectionHandles` | Boolean | `true` | Show resize/move handles on selected elements | +| `showElementActions` | Boolean | `true` | Show action buttons on selected elements | +| `pageCountFormat` | String | `'{currentPage} of {totalPages}'` | Format string for page counter | +| `autoFitZoom` | Boolean | `false` | Automatically adjust zoom to fit viewport on window resize | ### Events @@ -35,3 +36,4 @@ A Vue 2 component for rendering PDFs with draggable and resizable element overla - `element-{type}` - Custom element rendering (e.g., `element-signature`) - `custom` - Fallback for elements without specific type - `actions` - Custom action buttons + diff --git a/package.json b/package.json index 4d85f97..45ce890 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@libresign/pdf-elements", "description": "PDF viewer with draggable and resizable element overlays for Vue 2", - "version": "0.1.2", + "version": "0.2.0", "author": "LibreCode ", "private": false, "main": "dist/pdf-elements.umd.js", diff --git a/src/components/PDFElements.vue b/src/components/PDFElements.vue index 09fcf5b..cceea4f 100644 --- a/src/components/PDFElements.vue +++ b/src/components/PDFElements.vue @@ -179,6 +179,10 @@ export default { type: String, default: '{currentPage} of {totalPages}', }, + autoFitZoom: { + type: Boolean, + default: false, + }, }, data() { return { @@ -224,6 +228,9 @@ export default { window.addEventListener('resize', this.onViewportScroll) this.$el?.addEventListener('scroll', this.onViewportScroll, { passive: true }) this.$el?.addEventListener('wheel', this.boundHandleWheel, { passive: false }) + if (this.autoFitZoom) { + window.addEventListener('resize', this.adjustZoomToFit) + } }, beforeUnmount() { if (this.zoomRafId) { @@ -241,6 +248,9 @@ export default { window.removeEventListener('scroll', this.onViewportScroll) window.removeEventListener('resize', this.onViewportScroll) this.$el?.removeEventListener('scroll', this.onViewportScroll) + if (this.autoFitZoom) { + window.removeEventListener('resize', this.adjustZoomToFit) + } if (this.viewportRafId) { window.cancelAnimationFrame(this.viewportRafId) this.viewportRafId = 0 @@ -804,6 +814,29 @@ export default { const pagesScale = doc.pagesScale[pageIndex] || 1 return pageRef.getCanvasMeasurement().canvasHeight / pagesScale }, + calculateOptimalScale(maxPageWidth) { + const containerWidth = this.$el?.clientWidth || 0 + if (!containerWidth || !maxPageWidth) return 1 + + const availableWidth = containerWidth - 40 + return Math.max(0.1, Math.min(2, availableWidth / maxPageWidth)) + }, + adjustZoomToFit() { + if (!this.autoFitZoom || !this.pdfDocuments.length) return + + const canvases = this.$el?.querySelectorAll('canvas') + if (!canvases?.length) return + + const maxCanvasWidth = Math.max(...Array.from(canvases).map(canvas => + canvas.width / (this.scale || 1), + )) + + const optimalScale = this.calculateOptimalScale(maxCanvasWidth) + if (Math.abs(optimalScale - this.scale) > 0.01) { + this.scale = optimalScale + this.visualScale = optimalScale + } + }, }, }