PDF source adapter
retab-ui/pdf-sourceFreeComponent
Bridges the document-source model to the PDF viewer: pdfAnchorToTarget and renderPdfSourceOverlay for PDF bbox anchors.
Install it
npx shadcn@latest add https://ui.retab.com/r/pdf-source.jsonSource
1"use client";23import * as React from "react";45import type {6 Source,7 SourceAnchor,8 SourceLocation,9} from "@/lib/document-source";10import {11 PdfHighlight,12 type PageOverlayProps,13 type PdfViewerHandle,14} from "@/components/ui/pdf-viewer";1516export interface SourceTarget {17 scrollTo?: (source: Source, options: { behavior: ScrollBehavior }) => void;18}1920/**21 * Turn a source anchor into a PDF target, or `undefined` when the anchor isn't22 * a page region (a spreadsheet cell, a text span, …). PDF/image bbox anchors are23 * normalized [0, 1]; the viewer wants percentages, so scale by 100. An image24 * lands on the single page 1.25 */26export function pdfAnchorToTarget(27 anchor: SourceAnchor,28): SourceLocation | undefined {29 if (anchor.kind === "pdf_bbox") {30 if (!isPositiveInteger(anchor.page) || !isValidNormalizedBox(anchor)) {31 return undefined;32 }33 return {34 page: anchor.page,35 area: {36 left: anchor.left * 100,37 top: anchor.top * 100,38 width: anchor.width * 100,39 height: anchor.height * 100,40 },41 };42 }43 if (anchor.kind === "image_bbox") {44 if (anchor.page != null && !isPositiveInteger(anchor.page)) {45 return undefined;46 }47 if (!isValidNormalizedBox(anchor)) return undefined;48 return {49 page: 1,50 area: {51 left: anchor.left * 100,52 top: anchor.top * 100,53 width: anchor.width * 100,54 height: anchor.height * 100,55 },56 };57 }58 return undefined;59}6061function isPositiveInteger(value: number): boolean {62 return Number.isInteger(value) && value >= 1;63}6465function isValidNormalizedBox({66 left,67 top,68 width,69 height,70}: {71 left: number;72 top: number;73 width: number;74 height: number;75}): boolean {76 return (77 Number.isFinite(left) &&78 Number.isFinite(top) &&79 Number.isFinite(width) &&80 Number.isFinite(height) &&81 left >= 0 &&82 top >= 0 &&83 width > 0 &&84 height > 0 &&85 left + width <= 1 &&86 top + height <= 187 );88}8990/**91 * A stable source target over a `PdfViewer` ref. The ref is read lazily, so this92 * is just the bridge from source anchors to the viewer's imperative handle.93 */94export function usePdfSourceTarget(95 viewerRef: React.RefObject<PdfViewerHandle | null>,96): SourceTarget {97 return React.useMemo<SourceTarget>(98 () => ({99 scrollTo: (source: Source, options) => {100 const target = pdfAnchorToTarget(source.anchor);101 if (target) {102// … truncated
What it pulls in
Other registry items
Files it writes
More from retab-ui
All 130 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Alertalert | retab-ui | Components | Free | 1 dep | |
| Alert Dialogalert-dialog | retab-ui | Components | Free | 1 dep | |
| Aspect Ratioaspect-ratio | retab-ui | Components | Free | no deps | |
| Attachment Sidebarattachment-sidebar | retab-ui | Components | Free | 1 dep | |
| Autocompleteautocomplete | retab-ui | Components | Free | 2 deps | |
| Calendarcalendar | retab-ui | Components | Free | 2 deps | |
| Code Viewercode-viewer | retab-ui | Components | Free | 2 deps · 32 files | |
| Commandcommand | retab-ui | Components | Free | 2 deps |
