react-pdf-highlight-viewer 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,345 @@
1
+ # React PDF Highlighter
2
+
3
+ A lightweight, production-ready React library for rendering PDFs with text highlighting support. Built on top of `react-pdf` with a simple, intuitive API.
4
+
5
+ ## Features
6
+
7
+ ✨ **Simple API** - Just pass a PDF file and an array of highlights
8
+ 🎨 **Customizable Colors** - Set default or per-highlight colors
9
+ 🔤 **Case-Insensitive Matching** - Optional case-insensitive text matching
10
+ 📱 **Responsive** - Automatically adapts to container width
11
+ ⚡ **Performance Optimized** - Efficient DOM manipulation and memoization
12
+ 🔄 **Dynamic Updates** - Add/remove highlights on the fly
13
+ 📦 **Lightweight** - Minimal bundle size with peer dependencies
14
+ 🎯 **TypeScript Support** - Full type definitions included
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install react-pdf-highlight-viewer react react-dom react-pdf
20
+ ```
21
+
22
+ ### Peer Dependencies
23
+
24
+ This library requires the following peer dependencies:
25
+
26
+ - `react` ^19.2.0
27
+ - `react-dom` ^19.2.0
28
+ - `react-pdf` ^10.3.0
29
+
30
+ ## Quick Start
31
+
32
+ ```tsx
33
+ import { PdfHighlighter } from 'react-pdf-highlight-viewer';
34
+ import 'react-pdf-highlight-viewer/dist/react-pdf-highlight-viewer.css';
35
+
36
+ function App() {
37
+ const highlights = [
38
+ { pageNumber: 1, content: "important text" },
39
+ { pageNumber: 2, content: "another highlight", color: "#ff0000" }
40
+ ];
41
+
42
+ return (
43
+ <PdfHighlighter
44
+ file="https://example.com/document.pdf"
45
+ highlights={highlights}
46
+ />
47
+ );
48
+ }
49
+ ```
50
+
51
+ ## API Reference
52
+
53
+ ### `<PdfHighlighter />`
54
+
55
+ Main component for rendering PDFs with highlights.
56
+
57
+ #### Props
58
+
59
+ | Prop | Type | Default | Description |
60
+ |------|------|---------|-------------|
61
+ | `file` | `string \| File \| null` | **required** | PDF source (URL, data URI, or File object) |
62
+ | `highlights` | `Highlight[]` | `[]` | Array of text highlights to apply |
63
+ | `width` | `number` | `undefined` | Page width in pixels (responsive if not set) |
64
+ | `loading` | `React.ReactNode` | `<div>Loading PDF...</div>` | Custom loading component |
65
+ | `defaultHighlightColor` | `string` | `'#ffeb3b'` | Default highlight background color |
66
+ | `defaultCaseSensitive` | `boolean` | `true` | Default case sensitivity for all highlights |
67
+ | `pageProps` | `Omit<PageProps, ...>` | `undefined` | Additional props to pass to each Page component |
68
+ | `...documentProps` | `DocumentProps` | - | All other react-pdf Document props are supported |
69
+
70
+ ### `Highlight` Interface
71
+
72
+ ```typescript
73
+ interface Highlight {
74
+ pageNumber: number; // 1-indexed page number
75
+ content: string; // Exact text to highlight
76
+ color?: string; // Optional custom color (overrides default)
77
+ caseSensitive?: boolean; // Whether to match case-sensitively (default: true)
78
+ }
79
+ ```
80
+
81
+ ## Usage Examples
82
+
83
+ ### Basic Highlighting
84
+
85
+ ```tsx
86
+ import { PdfHighlighter } from 'npm-react-pdf-lib';
87
+ import 'npm-react-pdf-lib/dist/npm-react-pdf-lib.css';
88
+
89
+ function BasicExample() {
90
+ return (
91
+ <PdfHighlighter
92
+ file="/path/to/document.pdf"
93
+ highlights={[
94
+ { pageNumber: 1, content: "Chapter 1" }
95
+ ]}
96
+ />
97
+ );
98
+ }
99
+ ```
100
+
101
+ ### Custom Colors
102
+
103
+ ```tsx
104
+ function ColorExample() {
105
+ return (
106
+ <PdfHighlighter
107
+ file="/document.pdf"
108
+ defaultHighlightColor="#ffeb3b" // Yellow default
109
+ highlights={[
110
+ { pageNumber: 1, content: "Important", color: "#ff0000" }, // Red
111
+ { pageNumber: 1, content: "Note", color: "#00ff00" }, // Green
112
+ { pageNumber: 2, content: "Warning" } // Uses default yellow
113
+ ]}
114
+ />
115
+ );
116
+ }
117
+ ```
118
+
119
+ ### Case-Insensitive Matching
120
+
121
+ ```tsx
122
+ function CaseInsensitiveExample() {
123
+ return (
124
+ <PdfHighlighter
125
+ file="/document.pdf"
126
+ highlights={[
127
+ // Will match "Hello World", "HELLO WORLD", "hello world", etc.
128
+ { pageNumber: 1, content: "hello world", caseSensitive: false },
129
+
130
+ // Case-sensitive (default behavior)
131
+ { pageNumber: 1, content: "Exact Match" },
132
+
133
+ // Override default with custom color
134
+ { pageNumber: 2, content: "sample", caseSensitive: false, color: "#00ff00" }
135
+ ]}
136
+ />
137
+ );
138
+ }
139
+
140
+ // Set default case-insensitivity for all highlights
141
+ function GlobalCaseInsensitive() {
142
+ return (
143
+ <PdfHighlighter
144
+ file="/document.pdf"
145
+ defaultCaseSensitive={false} // All highlights are case-insensitive by default
146
+ highlights={[
147
+ { pageNumber: 1, content: "sample text" }, // Case-insensitive
148
+ { pageNumber: 2, content: "another example" }, // Case-insensitive
149
+ // Override to be case-sensitive for this one
150
+ { pageNumber: 3, content: "ExactCase", caseSensitive: true }
151
+ ]}
152
+ />
153
+ );
154
+ }
155
+ ```
156
+
157
+ ### Using React-PDF Props
158
+
159
+ The component extends `react-pdf`'s `DocumentProps`, so you can pass any Document or Page props:
160
+
161
+ ```tsx
162
+ function AdvancedExample() {
163
+ return (
164
+ <PdfHighlighter
165
+ file="/document.pdf"
166
+ highlights={[{ pageNumber: 1, content: "highlight me" }]}
167
+
168
+ // Document props (from react-pdf)
169
+ onLoadSuccess={(pdf) => console.log('Loaded', pdf.numPages, 'pages')}
170
+ onLoadError={(error) => console.error('Load error:', error)}
171
+ onSourceSuccess={() => console.log('Source loaded')}
172
+
173
+ // Page props (applied to all pages)
174
+ pageProps={{
175
+ scale: 1.5, // Zoom level
176
+ renderTextLayer: true, // Enable text selection
177
+ renderAnnotationLayer: false, // Disable annotations
178
+ renderMode: 'canvas' // Render mode: 'canvas' | 'svg'
179
+ }}
180
+ />
181
+ );
182
+ }
183
+ ```
184
+
185
+ ### Dynamic Highlights
186
+
187
+ ```tsx
188
+ function DynamicExample() {
189
+ const [highlights, setHighlights] = useState([]);
190
+
191
+ const addHighlight = (text) => {
192
+ setHighlights(prev => [...prev, {
193
+ pageNumber: 1,
194
+ content: text
195
+ }]);
196
+ };
197
+
198
+ const clearHighlights = () => setHighlights([]);
199
+
200
+ return (
201
+ <div>
202
+ <button onClick={() => addHighlight("search term")}>
203
+ Add Highlight
204
+ </button>
205
+ <button onClick={clearHighlights}>
206
+ Clear All
207
+ </button>
208
+ <PdfHighlighter
209
+ file="/document.pdf"
210
+ highlights={highlights}
211
+ />
212
+ </div>
213
+ );
214
+ }
215
+ ```
216
+
217
+ ### With File Upload
218
+
219
+ ```tsx
220
+ function FileUploadExample() {
221
+ const [file, setFile] = useState(null);
222
+
223
+ const handleFileChange = (e) => {
224
+ setFile(e.target.files[0]);
225
+ };
226
+
227
+ return (
228
+ <div>
229
+ <input type="file" accept=".pdf" onChange={handleFileChange} />
230
+ {file && (
231
+ <PdfHighlighter
232
+ file={file}
233
+ highlights={[{ pageNumber: 1, content: "example" }]}
234
+ />
235
+ )}
236
+ </div>
237
+ );
238
+ }
239
+ ```
240
+
241
+ ### Responsive Width
242
+
243
+ ```tsx
244
+ function ResponsiveExample() {
245
+ return (
246
+ <div style={{ maxWidth: '800px', margin: '0 auto' }}>
247
+ {/* PDF will adapt to container width */}
248
+ <PdfHighlighter
249
+ file="/document.pdf"
250
+ highlights={[]}
251
+ />
252
+ </div>
253
+ );
254
+ }
255
+ ```
256
+
257
+ ### Fixed Width
258
+
259
+ ```tsx
260
+ function FixedWidthExample() {
261
+ return (
262
+ <PdfHighlighter
263
+ file="/document.pdf"
264
+ width={600} // Fixed 600px width
265
+ highlights={[]}
266
+ />
267
+ );
268
+ }
269
+ ```
270
+
271
+ ## Important Notes
272
+
273
+ ### Text Matching
274
+
275
+ By default, the `content` field must **exactly match** the PDF's internal text representation:
276
+
277
+ - ✅ Correct: `"Hello World"` (if PDF has this exact text)
278
+ - ✅ Also works: `"hello world"` with `caseSensitive: false`
279
+ - ❌ Wrong: `"Hello World"` (extra space - whitespace must still match exactly)
280
+
281
+ **Tips**:
282
+ - Use `caseSensitive: false` to ignore case differences
283
+ - Copy text directly from the PDF viewer to ensure exact matching
284
+ - Whitespace must always match exactly, even with case-insensitive mode
285
+
286
+ ### Whitespace Issues
287
+
288
+ PDFs sometimes store text differently than it appears visually. If highlighting doesn't work:
289
+
290
+ 1. Open browser DevTools
291
+ 2. Inspect the PDF text layer
292
+ 3. Copy the exact text from the DOM
293
+ 4. Use that text in your `content` field
294
+
295
+ Example:
296
+ ```tsx
297
+ // Visual text: "purus aliquam"
298
+ // PDF internal: "purusaliquam" (no space!)
299
+
300
+ // Use the internal representation:
301
+ { pageNumber: 1, content: "purusaliquam" }
302
+ ```
303
+
304
+ ## Troubleshooting
305
+
306
+ ### Highlights Not Appearing
307
+
308
+ 1. **Check text matching**: Ensure `content` exactly matches PDF text
309
+ 2. **Verify page number**: Page numbers are 1-indexed (first page = 1)
310
+ 3. **Check console**: Look for errors or warnings
311
+ 4. **Inspect DOM**: Verify text exists in `.react-pdf__Page__textContent`
312
+
313
+ ### PDF Not Loading
314
+
315
+ 1. **CORS issues**: Ensure PDF URL allows cross-origin requests
316
+ 2. **File path**: Verify the file path or URL is correct
317
+ 3. **Worker error**: The library uses a CDN-hosted PDF.js worker by default
318
+
319
+ ### Performance Issues
320
+
321
+ For large PDFs with many highlights:
322
+
323
+ 1. Limit visible pages (implement pagination)
324
+ 2. Debounce highlight updates
325
+ 3. Use `React.memo` on parent components
326
+
327
+ ## Browser Support
328
+
329
+ - Chrome/Edge: ✅ Latest 2 versions
330
+ - Firefox: ✅ Latest 2 versions
331
+ - Safari: ✅ Latest 2 versions
332
+
333
+ ## License
334
+
335
+ MIT
336
+
337
+ ## Contributing
338
+
339
+ Contributions are welcome! Please open an issue or PR on GitHub.
340
+
341
+ ## Credits
342
+
343
+ Built with:
344
+ - [react-pdf](https://github.com/wojtekmaj/react-pdf) - PDF rendering
345
+ - [PDF.js](https://mozilla.github.io/pdf.js/) - PDF parsing (Mozilla)
@@ -0,0 +1,16 @@
1
+ import { default as React } from 'react';
2
+ import { PdfHighlighterProps } from '../types';
3
+ /**
4
+ * PdfHighlighter - A React component for rendering PDFs with text highlighting
5
+ *
6
+ * @example
7
+ * ```tsx
8
+ * <PdfHighlighter
9
+ * file="https://example.com/document.pdf"
10
+ * highlights={[
11
+ * { pageNumber: 1, content: "important text" }
12
+ * ]}
13
+ * />
14
+ * ```
15
+ */
16
+ export declare const PdfHighlighter: React.FC<PdfHighlighterProps>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ export * from './components/PdfHighlighter';
2
+ export type * from './types';
@@ -0,0 +1 @@
1
+ :root{--react-pdf-annotation-layer: 1;--annotation-unfocused-field-background: url("data:image/svg+xml;charset=UTF-8,<svg width='1px' height='1px' xmlns='http://www.w3.org/2000/svg'><rect width='100%' height='100%' style='fill:rgba(0, 54, 255, 0.13);'/></svg>");--input-focus-border-color: Highlight;--input-focus-outline: 1px solid Canvas;--input-unfocused-border-color: transparent;--input-disabled-border-color: transparent;--input-hover-border-color: black;--link-outline: none}@media screen and (forced-colors:active){:root{--input-focus-border-color: CanvasText;--input-unfocused-border-color: ActiveText;--input-disabled-border-color: GrayText;--input-hover-border-color: Highlight;--link-outline: 1.5px solid LinkText}.annotationLayer .textWidgetAnnotation :is(input,textarea):required,.annotationLayer .choiceWidgetAnnotation select:required,.annotationLayer .buttonWidgetAnnotation:is(.checkBox,.radioButton) input:required{outline:1.5px solid selectedItem}.annotationLayer .linkAnnotation:hover{-webkit-backdrop-filter:invert(100%);backdrop-filter:invert(100%)}}.annotationLayer{position:absolute;top:0;left:0;pointer-events:none;transform-origin:0 0;z-index:3}.annotationLayer[data-main-rotation="90"] .norotate{transform:rotate(270deg) translate(-100%)}.annotationLayer[data-main-rotation="180"] .norotate{transform:rotate(180deg) translate(-100%,-100%)}.annotationLayer[data-main-rotation="270"] .norotate{transform:rotate(90deg) translateY(-100%)}.annotationLayer canvas{position:absolute;width:100%;height:100%}.annotationLayer section{position:absolute;text-align:initial;pointer-events:auto;box-sizing:border-box;margin:0;transform-origin:0 0}.annotationLayer .linkAnnotation{outline:var(--link-outline)}.textLayer.selecting~.annotationLayer section{pointer-events:none}.annotationLayer :is(.linkAnnotation,.buttonWidgetAnnotation.pushButton)>a{position:absolute;font-size:1em;top:0;left:0;width:100%;height:100%}.annotationLayer :is(.linkAnnotation,.buttonWidgetAnnotation.pushButton)>a:hover{opacity:.2;background:#ff0;box-shadow:0 2px 10px #ff0}.annotationLayer .textAnnotation img{position:absolute;cursor:pointer;width:100%;height:100%;top:0;left:0}.annotationLayer .textWidgetAnnotation :is(input,textarea),.annotationLayer .choiceWidgetAnnotation select,.annotationLayer .buttonWidgetAnnotation:is(.checkBox,.radioButton) input{background-image:var(--annotation-unfocused-field-background);border:2px solid var(--input-unfocused-border-color);box-sizing:border-box;font:calc(9px * var(--total-scale-factor)) sans-serif;height:100%;margin:0;vertical-align:top;width:100%}.annotationLayer .textWidgetAnnotation :is(input,textarea):required,.annotationLayer .choiceWidgetAnnotation select:required,.annotationLayer .buttonWidgetAnnotation:is(.checkBox,.radioButton) input:required{outline:1.5px solid red}.annotationLayer .choiceWidgetAnnotation select option{padding:0}.annotationLayer .buttonWidgetAnnotation.radioButton input{border-radius:50%}.annotationLayer .textWidgetAnnotation textarea{resize:none}.annotationLayer .textWidgetAnnotation :is(input,textarea)[disabled],.annotationLayer .choiceWidgetAnnotation select[disabled],.annotationLayer .buttonWidgetAnnotation:is(.checkBox,.radioButton) input[disabled]{background:none;border:2px solid var(--input-disabled-border-color);cursor:not-allowed}.annotationLayer .textWidgetAnnotation :is(input,textarea):hover,.annotationLayer .choiceWidgetAnnotation select:hover,.annotationLayer .buttonWidgetAnnotation:is(.checkBox,.radioButton) input:hover{border:2px solid var(--input-hover-border-color)}.annotationLayer .textWidgetAnnotation :is(input,textarea):hover,.annotationLayer .choiceWidgetAnnotation select:hover,.annotationLayer .buttonWidgetAnnotation.checkBox input:hover{border-radius:2px}.annotationLayer .textWidgetAnnotation :is(input,textarea):focus,.annotationLayer .choiceWidgetAnnotation select:focus{background:none;border:2px solid var(--input-focus-border-color);border-radius:2px;outline:var(--input-focus-outline)}.annotationLayer .buttonWidgetAnnotation:is(.checkBox,.radioButton) :focus{background-image:none;background-color:transparent}.annotationLayer .buttonWidgetAnnotation.checkBox :focus{border:2px solid var(--input-focus-border-color);border-radius:2px;outline:var(--input-focus-outline)}.annotationLayer .buttonWidgetAnnotation.radioButton :focus{border:2px solid var(--input-focus-border-color);outline:var(--input-focus-outline)}.annotationLayer .buttonWidgetAnnotation.checkBox input:checked:before,.annotationLayer .buttonWidgetAnnotation.checkBox input:checked:after,.annotationLayer .buttonWidgetAnnotation.radioButton input:checked:before{background-color:CanvasText;content:"";display:block;position:absolute}.annotationLayer .buttonWidgetAnnotation.checkBox input:checked:before,.annotationLayer .buttonWidgetAnnotation.checkBox input:checked:after{height:80%;left:45%;width:1px}.annotationLayer .buttonWidgetAnnotation.checkBox input:checked:before{transform:rotate(45deg)}.annotationLayer .buttonWidgetAnnotation.checkBox input:checked:after{transform:rotate(-45deg)}.annotationLayer .buttonWidgetAnnotation.radioButton input:checked:before{border-radius:50%;height:50%;left:30%;top:20%;width:50%}.annotationLayer .textWidgetAnnotation input.comb{font-family:monospace;padding-left:2px;padding-right:0}.annotationLayer .textWidgetAnnotation input.comb:focus{width:103%}.annotationLayer .buttonWidgetAnnotation:is(.checkBox,.radioButton) input{appearance:none}.annotationLayer .popupTriggerArea{height:100%;width:100%}.annotationLayer .fileAttachmentAnnotation .popupTriggerArea{position:absolute}.annotationLayer .popupWrapper{position:absolute;font-size:calc(9px * var(--total-scale-factor));width:100%;min-width:calc(180px * var(--total-scale-factor));pointer-events:none}.annotationLayer .popup{position:absolute;max-width:calc(180px * var(--total-scale-factor));background-color:#ff9;box-shadow:0 calc(2px * var(--total-scale-factor)) calc(5px * var(--total-scale-factor)) #888;border-radius:calc(2px * var(--total-scale-factor));padding:calc(6px * var(--total-scale-factor));margin-left:calc(5px * var(--total-scale-factor));cursor:pointer;font:message-box;white-space:normal;word-wrap:break-word;pointer-events:auto}.annotationLayer .popup>*{font-size:calc(9px * var(--total-scale-factor))}.annotationLayer .popup h1{display:inline-block}.annotationLayer .popupDate{display:inline-block;margin-left:calc(5px * var(--total-scale-factor))}.annotationLayer .popupContent{border-top:1px solid rgba(51,51,51,1);margin-top:calc(2px * var(--total-scale-factor));padding-top:calc(2px * var(--total-scale-factor))}.annotationLayer .richText>*{white-space:pre-wrap;font-size:calc(9px * var(--total-scale-factor))}.annotationLayer .highlightAnnotation,.annotationLayer .underlineAnnotation,.annotationLayer .squigglyAnnotation,.annotationLayer .strikeoutAnnotation,.annotationLayer .freeTextAnnotation,.annotationLayer .lineAnnotation svg line,.annotationLayer .squareAnnotation svg rect,.annotationLayer .circleAnnotation svg ellipse,.annotationLayer .polylineAnnotation svg polyline,.annotationLayer .polygonAnnotation svg polygon,.annotationLayer .caretAnnotation,.annotationLayer .inkAnnotation svg polyline,.annotationLayer .stampAnnotation,.annotationLayer .fileAttachmentAnnotation{cursor:pointer}.annotationLayer section svg{position:absolute;width:100%;height:100%;top:0;left:0}.annotationLayer .annotationTextContent{position:absolute;width:100%;height:100%;opacity:0;color:transparent;-webkit-user-select:none;user-select:none;pointer-events:none}.annotationLayer .annotationTextContent span{width:100%;display:inline-block}:root{--react-pdf-text-layer: 1;--highlight-bg-color: rgba(180, 0, 170, 1);--highlight-selected-bg-color: rgba(0, 100, 0, 1)}@media screen and (forced-colors:active){:root{--highlight-bg-color: Highlight;--highlight-selected-bg-color: ButtonText}}[data-main-rotation="90"]{transform:rotate(90deg) translateY(-100%)}[data-main-rotation="180"]{transform:rotate(180deg) translate(-100%,-100%)}[data-main-rotation="270"]{transform:rotate(270deg) translate(-100%)}.textLayer{position:absolute;text-align:initial;inset:0;overflow:hidden;line-height:1;text-size-adjust:none;forced-color-adjust:none;transform-origin:0 0;z-index:2}.textLayer :is(span,br){color:transparent;position:absolute;white-space:pre;cursor:text;margin:0;transform-origin:0 0}.textLayer span.markedContent{top:0;height:0}.textLayer .highlight{margin:-1px;padding:1px;background-color:var(--highlight-bg-color);border-radius:4px}.textLayer .highlight.appended{position:initial}.textLayer .highlight.begin{border-radius:4px 0 0 4px}.textLayer .highlight.end{border-radius:0 4px 4px 0}.textLayer .highlight.middle{border-radius:0}.textLayer .highlight.selected{background-color:var(--highlight-selected-bg-color)}.textLayer br::selection{background:transparent}.textLayer .endOfContent{display:block;position:absolute;inset:100% 0 0;z-index:-1;cursor:default;-webkit-user-select:none;user-select:none}.textLayer.selecting .endOfContent{top:0}.hiddenCanvasElement{position:absolute;top:0;left:0;width:0;height:0;display:none}
@@ -0,0 +1,372 @@
1
+ import ae, { useState as te, useCallback as ne, useMemo as se, useRef as B, useEffect as Z } from "react";
2
+ import { pdfjs as Q, Document as ce, Page as le } from "react-pdf";
3
+ var W = { exports: {} }, Y = {};
4
+ var K;
5
+ function ue() {
6
+ if (K) return Y;
7
+ K = 1;
8
+ var c = /* @__PURE__ */ Symbol.for("react.transitional.element"), v = /* @__PURE__ */ Symbol.for("react.fragment");
9
+ function f(_, a, l) {
10
+ var u = null;
11
+ if (l !== void 0 && (u = "" + l), a.key !== void 0 && (u = "" + a.key), "key" in a) {
12
+ l = {};
13
+ for (var d in a)
14
+ d !== "key" && (l[d] = a[d]);
15
+ } else l = a;
16
+ return a = l.ref, {
17
+ $$typeof: c,
18
+ type: _,
19
+ key: u,
20
+ ref: a !== void 0 ? a : null,
21
+ props: l
22
+ };
23
+ }
24
+ return Y.Fragment = v, Y.jsx = f, Y.jsxs = f, Y;
25
+ }
26
+ var $ = {};
27
+ var ee;
28
+ function ie() {
29
+ return ee || (ee = 1, process.env.NODE_ENV !== "production" && (function() {
30
+ function c(e) {
31
+ if (e == null) return null;
32
+ if (typeof e == "function")
33
+ return e.$$typeof === O ? null : e.displayName || e.name || null;
34
+ if (typeof e == "string") return e;
35
+ switch (e) {
36
+ case i:
37
+ return "Fragment";
38
+ case z:
39
+ return "Profiler";
40
+ case T:
41
+ return "StrictMode";
42
+ case h:
43
+ return "Suspense";
44
+ case V:
45
+ return "SuspenseList";
46
+ case R:
47
+ return "Activity";
48
+ }
49
+ if (typeof e == "object")
50
+ switch (typeof e.tag == "number" && console.error(
51
+ "Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."
52
+ ), e.$$typeof) {
53
+ case I:
54
+ return "Portal";
55
+ case G:
56
+ return e.displayName || "Context";
57
+ case M:
58
+ return (e._context.displayName || "Context") + ".Consumer";
59
+ case J:
60
+ var r = e.render;
61
+ return e = e.displayName, e || (e = r.displayName || r.name || "", e = e !== "" ? "ForwardRef(" + e + ")" : "ForwardRef"), e;
62
+ case A:
63
+ return r = e.displayName || null, r !== null ? r : c(e.type) || "Memo";
64
+ case p:
65
+ r = e._payload, e = e._init;
66
+ try {
67
+ return c(e(r));
68
+ } catch {
69
+ }
70
+ }
71
+ return null;
72
+ }
73
+ function v(e) {
74
+ return "" + e;
75
+ }
76
+ function f(e) {
77
+ try {
78
+ v(e);
79
+ var r = !1;
80
+ } catch {
81
+ r = !0;
82
+ }
83
+ if (r) {
84
+ r = console;
85
+ var t = r.error, n = typeof Symbol == "function" && Symbol.toStringTag && e[Symbol.toStringTag] || e.constructor.name || "Object";
86
+ return t.call(
87
+ r,
88
+ "The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",
89
+ n
90
+ ), v(e);
91
+ }
92
+ }
93
+ function _(e) {
94
+ if (e === i) return "<>";
95
+ if (typeof e == "object" && e !== null && e.$$typeof === p)
96
+ return "<...>";
97
+ try {
98
+ var r = c(e);
99
+ return r ? "<" + r + ">" : "<...>";
100
+ } catch {
101
+ return "<...>";
102
+ }
103
+ }
104
+ function a() {
105
+ var e = j.A;
106
+ return e === null ? null : e.getOwner();
107
+ }
108
+ function l() {
109
+ return Error("react-stack-top-frame");
110
+ }
111
+ function u(e) {
112
+ if (w.call(e, "key")) {
113
+ var r = Object.getOwnPropertyDescriptor(e, "key").get;
114
+ if (r && r.isReactWarning) return !1;
115
+ }
116
+ return e.key !== void 0;
117
+ }
118
+ function d(e, r) {
119
+ function t() {
120
+ C || (C = !0, console.error(
121
+ "%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",
122
+ r
123
+ ));
124
+ }
125
+ t.isReactWarning = !0, Object.defineProperty(e, "key", {
126
+ get: t,
127
+ configurable: !0
128
+ });
129
+ }
130
+ function g() {
131
+ var e = c(this.type);
132
+ return N[e] || (N[e] = !0, console.error(
133
+ "Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release."
134
+ )), e = this.props.ref, e !== void 0 ? e : null;
135
+ }
136
+ function x(e, r, t, n, q, H) {
137
+ var o = t.ref;
138
+ return e = {
139
+ $$typeof: U,
140
+ type: e,
141
+ key: r,
142
+ props: t,
143
+ _owner: n
144
+ }, (o !== void 0 ? o : null) !== null ? Object.defineProperty(e, "ref", {
145
+ enumerable: !1,
146
+ get: g
147
+ }) : Object.defineProperty(e, "ref", { enumerable: !1, value: null }), e._store = {}, Object.defineProperty(e._store, "validated", {
148
+ configurable: !1,
149
+ enumerable: !1,
150
+ writable: !0,
151
+ value: 0
152
+ }), Object.defineProperty(e, "_debugInfo", {
153
+ configurable: !1,
154
+ enumerable: !1,
155
+ writable: !0,
156
+ value: null
157
+ }), Object.defineProperty(e, "_debugStack", {
158
+ configurable: !1,
159
+ enumerable: !1,
160
+ writable: !0,
161
+ value: q
162
+ }), Object.defineProperty(e, "_debugTask", {
163
+ configurable: !1,
164
+ enumerable: !1,
165
+ writable: !0,
166
+ value: H
167
+ }), Object.freeze && (Object.freeze(e.props), Object.freeze(e)), e;
168
+ }
169
+ function k(e, r, t, n, q, H) {
170
+ var o = r.children;
171
+ if (o !== void 0)
172
+ if (n)
173
+ if (D(o)) {
174
+ for (n = 0; n < o.length; n++)
175
+ E(o[n]);
176
+ Object.freeze && Object.freeze(o);
177
+ } else
178
+ console.error(
179
+ "React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead."
180
+ );
181
+ else E(o);
182
+ if (w.call(r, "key")) {
183
+ o = c(e);
184
+ var S = Object.keys(r).filter(function(oe) {
185
+ return oe !== "key";
186
+ });
187
+ n = 0 < S.length ? "{key: someKey, " + S.join(": ..., ") + ": ...}" : "{key: someKey}", b[o + n] || (S = 0 < S.length ? "{" + S.join(": ..., ") + ": ...}" : "{}", console.error(
188
+ `A props object containing a "key" prop is being spread into JSX:
189
+ let props = %s;
190
+ <%s {...props} />
191
+ React keys must be passed directly to JSX without using spread:
192
+ let props = %s;
193
+ <%s key={someKey} {...props} />`,
194
+ n,
195
+ o,
196
+ S,
197
+ o
198
+ ), b[o + n] = !0);
199
+ }
200
+ if (o = null, t !== void 0 && (f(t), o = "" + t), u(r) && (f(r.key), o = "" + r.key), "key" in r) {
201
+ t = {};
202
+ for (var X in r)
203
+ X !== "key" && (t[X] = r[X]);
204
+ } else t = r;
205
+ return o && d(
206
+ t,
207
+ typeof e == "function" ? e.displayName || e.name || "Unknown" : e
208
+ ), x(
209
+ e,
210
+ o,
211
+ t,
212
+ a(),
213
+ q,
214
+ H
215
+ );
216
+ }
217
+ function E(e) {
218
+ s(e) ? e._store && (e._store.validated = 1) : typeof e == "object" && e !== null && e.$$typeof === p && (e._payload.status === "fulfilled" ? s(e._payload.value) && e._payload.value._store && (e._payload.value._store.validated = 1) : e._store && (e._store.validated = 1));
219
+ }
220
+ function s(e) {
221
+ return typeof e == "object" && e !== null && e.$$typeof === U;
222
+ }
223
+ var m = ae, U = /* @__PURE__ */ Symbol.for("react.transitional.element"), I = /* @__PURE__ */ Symbol.for("react.portal"), i = /* @__PURE__ */ Symbol.for("react.fragment"), T = /* @__PURE__ */ Symbol.for("react.strict_mode"), z = /* @__PURE__ */ Symbol.for("react.profiler"), M = /* @__PURE__ */ Symbol.for("react.consumer"), G = /* @__PURE__ */ Symbol.for("react.context"), J = /* @__PURE__ */ Symbol.for("react.forward_ref"), h = /* @__PURE__ */ Symbol.for("react.suspense"), V = /* @__PURE__ */ Symbol.for("react.suspense_list"), A = /* @__PURE__ */ Symbol.for("react.memo"), p = /* @__PURE__ */ Symbol.for("react.lazy"), R = /* @__PURE__ */ Symbol.for("react.activity"), O = /* @__PURE__ */ Symbol.for("react.client.reference"), j = m.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, w = Object.prototype.hasOwnProperty, D = Array.isArray, y = console.createTask ? console.createTask : function() {
224
+ return null;
225
+ };
226
+ m = {
227
+ react_stack_bottom_frame: function(e) {
228
+ return e();
229
+ }
230
+ };
231
+ var C, N = {}, F = m.react_stack_bottom_frame.bind(
232
+ m,
233
+ l
234
+ )(), L = y(_(l)), b = {};
235
+ $.Fragment = i, $.jsx = function(e, r, t) {
236
+ var n = 1e4 > j.recentlyCreatedOwnerStacks++;
237
+ return k(
238
+ e,
239
+ r,
240
+ t,
241
+ !1,
242
+ n ? Error("react-stack-top-frame") : F,
243
+ n ? y(_(e)) : L
244
+ );
245
+ }, $.jsxs = function(e, r, t) {
246
+ var n = 1e4 > j.recentlyCreatedOwnerStacks++;
247
+ return k(
248
+ e,
249
+ r,
250
+ t,
251
+ !0,
252
+ n ? Error("react-stack-top-frame") : F,
253
+ n ? y(_(e)) : L
254
+ );
255
+ };
256
+ })()), $;
257
+ }
258
+ var re;
259
+ function fe() {
260
+ return re || (re = 1, process.env.NODE_ENV === "production" ? W.exports = ue() : W.exports = ie()), W.exports;
261
+ }
262
+ var P = fe();
263
+ Q.GlobalWorkerOptions.workerSrc = `https://unpkg.com/pdfjs-dist@${Q.version}/build/pdf.worker.min.mjs`;
264
+ const _e = ({
265
+ file: c,
266
+ highlights: v = [],
267
+ width: f,
268
+ loading: _,
269
+ defaultHighlightColor: a = "#ffeb3b",
270
+ defaultCaseSensitive: l = !0,
271
+ pageProps: u,
272
+ // Destructure all other DocumentProps to pass through
273
+ ...d
274
+ }) => {
275
+ const [g, x] = te(0), k = ne(({ numPages: s }) => {
276
+ x(s);
277
+ }, []), E = se(
278
+ () => Array.from({ length: g }, (s, m) => m + 1),
279
+ [g]
280
+ );
281
+ return /* @__PURE__ */ P.jsx("div", { className: "pdf-highlighter-container", "data-testid": "pdf-highlighter-container", children: /* @__PURE__ */ P.jsx(
282
+ ce,
283
+ {
284
+ file: c,
285
+ onLoadSuccess: k,
286
+ loading: _ || /* @__PURE__ */ P.jsx("div", { children: "Loading PDF..." }),
287
+ ...d,
288
+ children: E.map((s) => /* @__PURE__ */ P.jsx(
289
+ de,
290
+ {
291
+ pageNumber: s,
292
+ width: f,
293
+ highlights: v,
294
+ defaultColor: a,
295
+ defaultCaseSensitive: l,
296
+ pageProps: u
297
+ },
298
+ `page_${s}`
299
+ ))
300
+ }
301
+ ) });
302
+ }, de = ({
303
+ pageNumber: c,
304
+ width: v,
305
+ highlights: f,
306
+ defaultColor: _,
307
+ defaultCaseSensitive: a,
308
+ pageProps: l
309
+ }) => {
310
+ const u = B(null), [d, g] = te(!1), x = B(/* @__PURE__ */ new Map()), k = ne(() => {
311
+ setTimeout(() => {
312
+ g(!0);
313
+ }, 100);
314
+ }, []);
315
+ return Z(() => {
316
+ if (!d || !u.current) return;
317
+ const E = u.current.querySelector(".react-pdf__Page__textContent");
318
+ if (!E) return;
319
+ const s = Array.from(E.querySelectorAll('span[role="presentation"]'));
320
+ x.current.size === 0 && s.forEach((m) => {
321
+ x.current.set(m, m.textContent || "");
322
+ });
323
+ }, [d]), Z(() => {
324
+ if (!d || !u.current) return;
325
+ const E = u.current.querySelector(".react-pdf__Page__textContent");
326
+ if (!E) return;
327
+ const s = Array.from(E.querySelectorAll('span[role="presentation"]'));
328
+ if (s.length === 0 || (s.forEach((i) => {
329
+ const T = x.current.get(i);
330
+ T !== void 0 && (i.innerHTML = "", i.appendChild(document.createTextNode(T)));
331
+ }), !f || f.length === 0)) return;
332
+ const m = f.filter((i) => i.pageNumber === c);
333
+ if (m.length === 0) return;
334
+ const I = s.map((i) => i.textContent || "").join("");
335
+ m.forEach((i) => {
336
+ const { content: T, color: z, caseSensitive: M } = i;
337
+ if (!T) return;
338
+ const G = M !== void 0 ? M : a, J = z || _;
339
+ let h;
340
+ if (G)
341
+ h = I.indexOf(T);
342
+ else {
343
+ const p = I.toLowerCase(), R = T.toLowerCase();
344
+ h = p.indexOf(R);
345
+ }
346
+ if (h === -1) return;
347
+ const V = h + T.length;
348
+ let A = 0;
349
+ s.forEach((p) => {
350
+ const R = p.textContent || "", O = A, j = A + R.length, w = Math.max(h, O), D = Math.min(V, j);
351
+ if (w < D) {
352
+ const y = w - O, C = D - O, N = R.slice(0, y), F = R.slice(y, C), L = R.slice(C);
353
+ p.innerHTML = "", N && p.appendChild(document.createTextNode(N));
354
+ const b = document.createElement("mark");
355
+ b.style.backgroundColor = J, b.style.color = "#000", b.textContent = F, p.appendChild(b), L && p.appendChild(document.createTextNode(L));
356
+ }
357
+ A += R.length;
358
+ });
359
+ });
360
+ }, [d, f, c, _, a]), /* @__PURE__ */ P.jsx("div", { ref: u, "data-testid": `pdf-page-highlighter-${c}`, children: /* @__PURE__ */ P.jsx(
361
+ le,
362
+ {
363
+ pageNumber: c,
364
+ width: v,
365
+ onGetTextSuccess: k,
366
+ ...l
367
+ }
368
+ ) });
369
+ };
370
+ export {
371
+ _e as PdfHighlighter
372
+ };
@@ -0,0 +1,6 @@
1
+ (function(v,c){typeof exports=="object"&&typeof module<"u"?c(exports,require("react"),require("react-pdf")):typeof define=="function"&&define.amd?define(["exports","react","react-pdf"],c):(v=typeof globalThis<"u"?globalThis:v||self,c(v.ReactPdfHighlighter={},v.React,v.ReactPDF))})(this,(function(v,c,F){"use strict";var W={exports:{}},A={};var K;function re(){if(K)return A;K=1;var l=Symbol.for("react.transitional.element"),g=Symbol.for("react.fragment");function d(E,a,i){var u=null;if(i!==void 0&&(u=""+i),a.key!==void 0&&(u=""+a.key),"key"in a){i={};for(var m in a)m!=="key"&&(i[m]=a[m])}else i=a;return a=i.ref,{$$typeof:l,type:E,key:u,ref:a!==void 0?a:null,props:i}}return A.Fragment=g,A.jsx=d,A.jsxs=d,A}var w={};var ee;function ne(){return ee||(ee=1,process.env.NODE_ENV!=="production"&&(function(){function l(e){if(e==null)return null;if(typeof e=="function")return e.$$typeof===N?null:e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case f:return"Fragment";case X:return"Profiler";case T:return"StrictMode";case b:return"Suspense";case Z:return"SuspenseList";case h:return"Activity"}if(typeof e=="object")switch(typeof e.tag=="number"&&console.error("Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."),e.$$typeof){case U:return"Portal";case q:return e.displayName||"Context";case H:return(e._context.displayName||"Context")+".Consumer";case B:var t=e.render;return e=e.displayName,e||(e=t.displayName||t.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case C:return t=e.displayName||null,t!==null?t:l(e.type)||"Memo";case _:t=e._payload,e=e._init;try{return l(e(t))}catch{}}return null}function g(e){return""+e}function d(e){try{g(e);var t=!1}catch{t=!0}if(t){t=console;var r=t.error,n=typeof Symbol=="function"&&Symbol.toStringTag&&e[Symbol.toStringTag]||e.constructor.name||"Object";return r.call(t,"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",n),g(e)}}function E(e){if(e===f)return"<>";if(typeof e=="object"&&e!==null&&e.$$typeof===_)return"<...>";try{var t=l(e);return t?"<"+t+">":"<...>"}catch{return"<...>"}}function a(){var e=L.A;return e===null?null:e.getOwner()}function i(){return Error("react-stack-top-frame")}function u(e){if(Y.call(e,"key")){var t=Object.getOwnPropertyDescriptor(e,"key").get;if(t&&t.isReactWarning)return!1}return e.key!==void 0}function m(e,t){function r(){I||(I=!0,console.error("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",t))}r.isReactWarning=!0,Object.defineProperty(e,"key",{get:r,configurable:!0})}function S(){var e=l(this.type);return M[e]||(M[e]=!0,console.error("Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release.")),e=this.props.ref,e!==void 0?e:null}function x(e,t,r,n,J,Q){var o=r.ref;return e={$$typeof:V,type:e,key:t,props:r,_owner:n},(o!==void 0?o:null)!==null?Object.defineProperty(e,"ref",{enumerable:!1,get:S}):Object.defineProperty(e,"ref",{enumerable:!1,value:null}),e._store={},Object.defineProperty(e._store,"validated",{configurable:!1,enumerable:!1,writable:!0,value:0}),Object.defineProperty(e,"_debugInfo",{configurable:!1,enumerable:!1,writable:!0,value:null}),Object.defineProperty(e,"_debugStack",{configurable:!1,enumerable:!1,writable:!0,value:J}),Object.defineProperty(e,"_debugTask",{configurable:!1,enumerable:!1,writable:!0,value:Q}),Object.freeze&&(Object.freeze(e.props),Object.freeze(e)),e}function P(e,t,r,n,J,Q){var o=t.children;if(o!==void 0)if(n)if(z(o)){for(n=0;n<o.length;n++)R(o[n]);Object.freeze&&Object.freeze(o)}else console.error("React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.");else R(o);if(Y.call(t,"key")){o=l(e);var O=Object.keys(t).filter(function(ce){return ce!=="key"});n=0<O.length?"{key: someKey, "+O.join(": ..., ")+": ...}":"{key: someKey}",y[o+n]||(O=0<O.length?"{"+O.join(": ..., ")+": ...}":"{}",console.error(`A props object containing a "key" prop is being spread into JSX:
2
+ let props = %s;
3
+ <%s {...props} />
4
+ React keys must be passed directly to JSX without using spread:
5
+ let props = %s;
6
+ <%s key={someKey} {...props} />`,n,o,O,o),y[o+n]=!0)}if(o=null,r!==void 0&&(d(r),o=""+r),u(t)&&(d(t.key),o=""+t.key),"key"in t){r={};for(var $ in t)$!=="key"&&(r[$]=t[$])}else r=t;return o&&m(r,typeof e=="function"?e.displayName||e.name||"Unknown":e),x(e,o,r,a(),J,Q)}function R(e){s(e)?e._store&&(e._store.validated=1):typeof e=="object"&&e!==null&&e.$$typeof===_&&(e._payload.status==="fulfilled"?s(e._payload.value)&&e._payload.value._store&&(e._payload.value._store.validated=1):e._store&&(e._store.validated=1))}function s(e){return typeof e=="object"&&e!==null&&e.$$typeof===V}var p=c,V=Symbol.for("react.transitional.element"),U=Symbol.for("react.portal"),f=Symbol.for("react.fragment"),T=Symbol.for("react.strict_mode"),X=Symbol.for("react.profiler"),H=Symbol.for("react.consumer"),q=Symbol.for("react.context"),B=Symbol.for("react.forward_ref"),b=Symbol.for("react.suspense"),Z=Symbol.for("react.suspense_list"),C=Symbol.for("react.memo"),_=Symbol.for("react.lazy"),h=Symbol.for("react.activity"),N=Symbol.for("react.client.reference"),L=p.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,Y=Object.prototype.hasOwnProperty,z=Array.isArray,j=console.createTask?console.createTask:function(){return null};p={react_stack_bottom_frame:function(e){return e()}};var I,M={},G=p.react_stack_bottom_frame.bind(p,i)(),D=j(E(i)),y={};w.Fragment=f,w.jsx=function(e,t,r){var n=1e4>L.recentlyCreatedOwnerStacks++;return P(e,t,r,!1,n?Error("react-stack-top-frame"):G,n?j(E(e)):D)},w.jsxs=function(e,t,r){var n=1e4>L.recentlyCreatedOwnerStacks++;return P(e,t,r,!0,n?Error("react-stack-top-frame"):G,n?j(E(e)):D)}})()),w}var te;function oe(){return te||(te=1,process.env.NODE_ENV==="production"?W.exports=re():W.exports=ne()),W.exports}var k=oe();F.pdfjs.GlobalWorkerOptions.workerSrc=`https://unpkg.com/pdfjs-dist@${F.pdfjs.version}/build/pdf.worker.min.mjs`;const ae=({file:l,highlights:g=[],width:d,loading:E,defaultHighlightColor:a="#ffeb3b",defaultCaseSensitive:i=!0,pageProps:u,...m})=>{const[S,x]=c.useState(0),P=c.useCallback(({numPages:s})=>{x(s)},[]),R=c.useMemo(()=>Array.from({length:S},(s,p)=>p+1),[S]);return k.jsx("div",{className:"pdf-highlighter-container","data-testid":"pdf-highlighter-container",children:k.jsx(F.Document,{file:l,onLoadSuccess:P,loading:E||k.jsx("div",{children:"Loading PDF..."}),...m,children:R.map(s=>k.jsx(se,{pageNumber:s,width:d,highlights:g,defaultColor:a,defaultCaseSensitive:i,pageProps:u},`page_${s}`))})})},se=({pageNumber:l,width:g,highlights:d,defaultColor:E,defaultCaseSensitive:a,pageProps:i})=>{const u=c.useRef(null),[m,S]=c.useState(!1),x=c.useRef(new Map),P=c.useCallback(()=>{setTimeout(()=>{S(!0)},100)},[]);return c.useEffect(()=>{if(!m||!u.current)return;const R=u.current.querySelector(".react-pdf__Page__textContent");if(!R)return;const s=Array.from(R.querySelectorAll('span[role="presentation"]'));x.current.size===0&&s.forEach(p=>{x.current.set(p,p.textContent||"")})},[m]),c.useEffect(()=>{if(!m||!u.current)return;const R=u.current.querySelector(".react-pdf__Page__textContent");if(!R)return;const s=Array.from(R.querySelectorAll('span[role="presentation"]'));if(s.length===0||(s.forEach(f=>{const T=x.current.get(f);T!==void 0&&(f.innerHTML="",f.appendChild(document.createTextNode(T)))}),!d||d.length===0))return;const p=d.filter(f=>f.pageNumber===l);if(p.length===0)return;const U=s.map(f=>f.textContent||"").join("");p.forEach(f=>{const{content:T,color:X,caseSensitive:H}=f;if(!T)return;const q=H!==void 0?H:a,B=X||E;let b;if(q)b=U.indexOf(T);else{const _=U.toLowerCase(),h=T.toLowerCase();b=_.indexOf(h)}if(b===-1)return;const Z=b+T.length;let C=0;s.forEach(_=>{const h=_.textContent||"",N=C,L=C+h.length,Y=Math.max(b,N),z=Math.min(Z,L);if(Y<z){const j=Y-N,I=z-N,M=h.slice(0,j),G=h.slice(j,I),D=h.slice(I);_.innerHTML="",M&&_.appendChild(document.createTextNode(M));const y=document.createElement("mark");y.style.backgroundColor=B,y.style.color="#000",y.textContent=G,_.appendChild(y),D&&_.appendChild(document.createTextNode(D))}C+=h.length})})},[m,d,l,E,a]),k.jsx("div",{ref:u,"data-testid":`pdf-page-highlighter-${l}`,children:k.jsx(F.Page,{pageNumber:l,width:g,onGetTextSuccess:P,...i})})};v.PdfHighlighter=ae,Object.defineProperty(v,Symbol.toStringTag,{value:"Module"})}));
File without changes
@@ -0,0 +1,71 @@
1
+ import { DocumentProps, PageProps } from 'react-pdf';
2
+ /**
3
+ * Represents a text highlight in a PDF document
4
+ */
5
+ export interface Highlight {
6
+ /**
7
+ * The page number where the highlight should appear (1-indexed)
8
+ */
9
+ pageNumber: number;
10
+ /**
11
+ * The exact text content to highlight
12
+ * Note: Must match the PDF's internal text representation exactly (unless caseSensitive is false)
13
+ */
14
+ content: string;
15
+ /**
16
+ * Optional custom background color for this highlight
17
+ * @default '#ffeb3b' (yellow)
18
+ */
19
+ color?: string;
20
+ /**
21
+ * Whether to match text case-sensitively
22
+ * @default true
23
+ */
24
+ caseSensitive?: boolean;
25
+ }
26
+ /**
27
+ * Props for the PdfHighlighter component
28
+ * Extends react-pdf's DocumentProps to allow full customization
29
+ */
30
+ export interface PdfHighlighterProps extends Omit<DocumentProps, 'children'> {
31
+ /**
32
+ * Source of the PDF file
33
+ * Can be a URL string, base64 data URI, or File object
34
+ * @example "https://example.com/document.pdf"
35
+ * @example "data:application/pdf;base64,..."
36
+ */
37
+ file: string | File | null;
38
+ /**
39
+ * Array of text highlights to apply to the PDF
40
+ * @default []
41
+ */
42
+ highlights?: Highlight[];
43
+ /**
44
+ * Width of the PDF pages in pixels
45
+ * If not provided, pages will be responsive
46
+ * @default undefined (responsive)
47
+ */
48
+ width?: number;
49
+ /**
50
+ * Custom loading component to display while PDF is loading
51
+ * @default <div>Loading PDF...</div>
52
+ */
53
+ loading?: React.ReactNode;
54
+ /**
55
+ * Default highlight color for all highlights
56
+ * Individual highlights can override this with their own color
57
+ * @default '#ffeb3b' (yellow)
58
+ */
59
+ defaultHighlightColor?: string;
60
+ /**
61
+ * Default case sensitivity for all highlights
62
+ * Individual highlights can override this with their own caseSensitive property
63
+ * @default true
64
+ */
65
+ defaultCaseSensitive?: boolean;
66
+ /**
67
+ * Additional props to pass to each Page component
68
+ * Allows customization of page rendering (scale, renderMode, etc.)
69
+ */
70
+ pageProps?: Omit<PageProps, 'pageNumber' | 'width' | 'onGetTextSuccess'>;
71
+ }
package/dist/vite.svg ADDED
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
package/package.json ADDED
@@ -0,0 +1,84 @@
1
+ {
2
+ "name": "react-pdf-highlight-viewer",
3
+ "version": "1.0.0",
4
+ "description": "A lightweight React library for rendering PDFs with text highlighting support",
5
+ "keywords": [
6
+ "react",
7
+ "pdf",
8
+ "highlight",
9
+ "viewer",
10
+ "react-pdf",
11
+ "pdf-highlighter",
12
+ "text-highlight",
13
+ "document-viewer"
14
+ ],
15
+ "author": "Sivakkannan R",
16
+ "license": "MIT",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "https://github.com/sivakannan/react-pdf-highlight-viewer"
20
+ },
21
+ "bugs": {
22
+ "url": "https://github.com/sivakannan/react-pdf-highlight-viewer/issues"
23
+ },
24
+ "homepage": "https://github.com/sivakannan/react-pdf-highlight-viewer#readme",
25
+ "private": false,
26
+ "type": "module",
27
+ "main": "./dist/react-pdf-highlighter.umd.js",
28
+ "module": "./dist/react-pdf-highlighter.es.js",
29
+ "types": "./dist/index.d.ts",
30
+ "files": [
31
+ "dist",
32
+ "README.md"
33
+ ],
34
+ "exports": {
35
+ ".": {
36
+ "types": "./dist/index.d.ts",
37
+ "import": "./dist/react-pdf-highlighter.es.js",
38
+ "require": "./dist/react-pdf-highlighter.umd.js"
39
+ },
40
+ "./dist/npm-react-pdf-lib.css": "./dist/npm-react-pdf-lib.css"
41
+ },
42
+ "scripts": {
43
+ "dev": "vite",
44
+ "build": "tsc -b && vite build",
45
+ "lint": "eslint .",
46
+ "preview": "vite preview",
47
+ "test": "vitest run",
48
+ "test:watch": "vitest",
49
+ "test:coverage": "vitest run --coverage",
50
+ "test:ui": "vitest --ui",
51
+ "prepublishOnly": "npm run build"
52
+ },
53
+ "peerDependencies": {
54
+ "react": "^19.2.0",
55
+ "react-dom": "^19.2.0",
56
+ "react-pdf": "^10.3.0"
57
+ },
58
+ "dependencies": {
59
+ "prop-types": "^15.8.1"
60
+ },
61
+ "devDependencies": {
62
+ "@eslint/js": "^9.39.1",
63
+ "@testing-library/jest-dom": "^6.9.1",
64
+ "@testing-library/react": "^16.3.2",
65
+ "@testing-library/user-event": "^14.6.1",
66
+ "@types/node": "^24.10.1",
67
+ "@types/react": "^19.2.5",
68
+ "@types/react-dom": "^19.2.3",
69
+ "@vitejs/plugin-react": "^5.1.1",
70
+ "@vitest/coverage-v8": "^4.0.18",
71
+ "@vitest/ui": "^4.0.18",
72
+ "eslint": "^9.39.1",
73
+ "eslint-plugin-react-hooks": "^7.0.1",
74
+ "eslint-plugin-react-refresh": "^0.4.24",
75
+ "globals": "^16.5.0",
76
+ "happy-dom": "^20.5.3",
77
+ "jsdom": "^27.0.1",
78
+ "typescript": "~5.9.3",
79
+ "typescript-eslint": "^8.46.4",
80
+ "vite": "^7.2.4",
81
+ "vite-plugin-dts": "^4.5.4",
82
+ "vitest": "^4.0.18"
83
+ }
84
+ }