react-pdf-highlighter-plus 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.
Files changed (99) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +411 -0
  3. package/dist/esm/components/AreaHighlight.d.ts +82 -0
  4. package/dist/esm/components/AreaHighlight.js +109 -0
  5. package/dist/esm/components/AreaHighlight.js.map +1 -0
  6. package/dist/esm/components/DrawingCanvas.d.ts +48 -0
  7. package/dist/esm/components/DrawingCanvas.js +277 -0
  8. package/dist/esm/components/DrawingCanvas.js.map +1 -0
  9. package/dist/esm/components/DrawingHighlight.d.ts +70 -0
  10. package/dist/esm/components/DrawingHighlight.js +164 -0
  11. package/dist/esm/components/DrawingHighlight.js.map +1 -0
  12. package/dist/esm/components/FreetextHighlight.d.ts +112 -0
  13. package/dist/esm/components/FreetextHighlight.js +193 -0
  14. package/dist/esm/components/FreetextHighlight.js.map +1 -0
  15. package/dist/esm/components/HighlightLayer.d.ts +49 -0
  16. package/dist/esm/components/HighlightLayer.js +37 -0
  17. package/dist/esm/components/HighlightLayer.js.map +1 -0
  18. package/dist/esm/components/ImageHighlight.d.ts +63 -0
  19. package/dist/esm/components/ImageHighlight.js +65 -0
  20. package/dist/esm/components/ImageHighlight.js.map +1 -0
  21. package/dist/esm/components/MonitoredHighlightContainer.d.ts +37 -0
  22. package/dist/esm/components/MonitoredHighlightContainer.js +42 -0
  23. package/dist/esm/components/MonitoredHighlightContainer.js.map +1 -0
  24. package/dist/esm/components/MouseMonitor.d.ts +34 -0
  25. package/dist/esm/components/MouseMonitor.js +30 -0
  26. package/dist/esm/components/MouseMonitor.js.map +1 -0
  27. package/dist/esm/components/MouseSelection.d.ts +66 -0
  28. package/dist/esm/components/MouseSelection.js +122 -0
  29. package/dist/esm/components/MouseSelection.js.map +1 -0
  30. package/dist/esm/components/PdfHighlighter.d.ts +184 -0
  31. package/dist/esm/components/PdfHighlighter.js +410 -0
  32. package/dist/esm/components/PdfHighlighter.js.map +1 -0
  33. package/dist/esm/components/PdfLoader.d.ts +55 -0
  34. package/dist/esm/components/PdfLoader.js +57 -0
  35. package/dist/esm/components/PdfLoader.js.map +1 -0
  36. package/dist/esm/components/ShapeCanvas.d.ts +51 -0
  37. package/dist/esm/components/ShapeCanvas.js +205 -0
  38. package/dist/esm/components/ShapeCanvas.js.map +1 -0
  39. package/dist/esm/components/ShapeHighlight.d.ts +107 -0
  40. package/dist/esm/components/ShapeHighlight.js +140 -0
  41. package/dist/esm/components/ShapeHighlight.js.map +1 -0
  42. package/dist/esm/components/SignaturePad.d.ts +40 -0
  43. package/dist/esm/components/SignaturePad.js +138 -0
  44. package/dist/esm/components/SignaturePad.js.map +1 -0
  45. package/dist/esm/components/TextHighlight.d.ts +93 -0
  46. package/dist/esm/components/TextHighlight.js +115 -0
  47. package/dist/esm/components/TextHighlight.js.map +1 -0
  48. package/dist/esm/components/TipContainer.d.ts +27 -0
  49. package/dist/esm/components/TipContainer.js +58 -0
  50. package/dist/esm/components/TipContainer.js.map +1 -0
  51. package/dist/esm/contexts/HighlightContext.d.ts +44 -0
  52. package/dist/esm/contexts/HighlightContext.js +16 -0
  53. package/dist/esm/contexts/HighlightContext.js.map +1 -0
  54. package/dist/esm/contexts/PdfHighlighterContext.d.ts +89 -0
  55. package/dist/esm/contexts/PdfHighlighterContext.js +16 -0
  56. package/dist/esm/contexts/PdfHighlighterContext.js.map +1 -0
  57. package/dist/esm/index.d.ts +19 -0
  58. package/dist/esm/index.js +19 -0
  59. package/dist/esm/index.js.map +1 -0
  60. package/dist/esm/lib/coordinates.d.ts +16 -0
  61. package/dist/esm/lib/coordinates.js +69 -0
  62. package/dist/esm/lib/coordinates.js.map +1 -0
  63. package/dist/esm/lib/export-pdf.d.ts +81 -0
  64. package/dist/esm/lib/export-pdf.js +511 -0
  65. package/dist/esm/lib/export-pdf.js.map +1 -0
  66. package/dist/esm/lib/get-bounding-rect.d.ts +3 -0
  67. package/dist/esm/lib/get-bounding-rect.js +35 -0
  68. package/dist/esm/lib/get-bounding-rect.js.map +1 -0
  69. package/dist/esm/lib/get-client-rects.d.ts +3 -0
  70. package/dist/esm/lib/get-client-rects.js +43 -0
  71. package/dist/esm/lib/get-client-rects.js.map +1 -0
  72. package/dist/esm/lib/group-highlights-by-page.d.ts +6 -0
  73. package/dist/esm/lib/group-highlights-by-page.js +23 -0
  74. package/dist/esm/lib/group-highlights-by-page.js.map +1 -0
  75. package/dist/esm/lib/optimize-client-rects.d.ts +3 -0
  76. package/dist/esm/lib/optimize-client-rects.js +65 -0
  77. package/dist/esm/lib/optimize-client-rects.js.map +1 -0
  78. package/dist/esm/lib/pdfjs-dom.d.ts +9 -0
  79. package/dist/esm/lib/pdfjs-dom.js +55 -0
  80. package/dist/esm/lib/pdfjs-dom.js.map +1 -0
  81. package/dist/esm/lib/screenshot.d.ts +4 -0
  82. package/dist/esm/lib/screenshot.js +24 -0
  83. package/dist/esm/lib/screenshot.js.map +1 -0
  84. package/dist/esm/style/AreaHighlight.css +134 -0
  85. package/dist/esm/style/DrawingCanvas.css +62 -0
  86. package/dist/esm/style/DrawingHighlight.css +184 -0
  87. package/dist/esm/style/FreetextHighlight.css +249 -0
  88. package/dist/esm/style/ImageHighlight.css +97 -0
  89. package/dist/esm/style/MouseSelection.css +15 -0
  90. package/dist/esm/style/PdfHighlighter.css +77 -0
  91. package/dist/esm/style/ShapeCanvas.css +47 -0
  92. package/dist/esm/style/ShapeHighlight.css +182 -0
  93. package/dist/esm/style/SignaturePad.css +83 -0
  94. package/dist/esm/style/TextHighlight.css +199 -0
  95. package/dist/esm/style/pdf_viewer.css +41 -0
  96. package/dist/esm/types.d.ts +213 -0
  97. package/dist/esm/types.js +2 -0
  98. package/dist/esm/types.js.map +1 -0
  99. package/package.json +91 -0
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Daniel Arnould
4
+ Copyright (c) 2017 Artem Tyurin
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,411 @@
1
+ # react-pdf-highlighter-plus
2
+
3
+ <p align="center">
4
+ <a href="https://github.com/QuocVietHa08/react-pdf-highlighter-plus/stargazers">
5
+ <img src="https://img.shields.io/github/stars/QuocVietHa08/react-pdf-highlighter-plus?style=social" alt="GitHub stars">
6
+ </a>
7
+ <a href="https://github.com/QuocVietHa08/react-pdf-highlighter-plus/actions/workflows/node.js.yml">
8
+ <img src="https://github.com/QuocVietHa08/react-pdf-highlighter-plus/actions/workflows/node.js.yml/badge.svg" alt="Node.js CI">
9
+ </a>
10
+ <a href="https://badge.fury.io/js/react-pdf-highlighter-extended">
11
+ <img src="https://badge.fury.io/js/react-pdf-highlighter-extended.svg" alt="npm version">
12
+ </a>
13
+ <a href="https://www.npmjs.com/package/react-pdf-highlighter-extended">
14
+ <img src="https://img.shields.io/npm/dm/react-pdf-highlighter-extended.svg" alt="npm downloads">
15
+ </a>
16
+ </p>
17
+
18
+ <p align="center">
19
+ <strong>A powerful React library for annotating PDF documents</strong>
20
+ </p>
21
+
22
+ <p align="center">
23
+ Text highlights • Area highlights • Freetext notes • Images & signatures • Freehand drawing • PDF export
24
+ </p>
25
+
26
+ ---
27
+
28
+ ## Overview
29
+
30
+ `react-pdf-highlighter-plus` provides a highly customizable annotation experience for PDF documents in React applications. Built on [PDF.js](https://github.com/mozilla/pdf.js), it stores highlight positions in viewport-independent coordinates, making them portable across different screen sizes.
31
+
32
+ ## Features
33
+
34
+ | Feature | Description |
35
+ |---------|-------------|
36
+ | **Text Highlights** | Select and highlight text passages |
37
+ | **Area Highlights** | Draw rectangular regions on PDFs |
38
+ | **Freetext Notes** | Draggable, editable sticky notes with custom styling |
39
+ | **Images & Signatures** | Upload images or draw signatures directly on PDFs |
40
+ | **Freehand Drawing** | Draw freehand annotations with customizable stroke |
41
+ | **PDF Export** | Export annotated PDF with all highlights embedded |
42
+ | **Zoom Support** | Full zoom functionality with position-independent data |
43
+ | **Fully Customizable** | Exposed styling on all components |
44
+
45
+ ## Quick Links
46
+
47
+ | Resource | Link |
48
+ |----------|------|
49
+ | Live Demo | [View Demo](https://quocvietha08.github.io/react-pdf-highlighter-plus/example-app/) |
50
+ | Documentation | [API Docs](https://quocvietha08.github.io/react-pdf-highlighter-plus/docs/) |
51
+ | NPM Package | [npm](https://www.npmjs.com/package/react-pdf-highlighter-extended) |
52
+
53
+ ---
54
+
55
+ ## Installation (Coming soon)
56
+
57
+ ```bash
58
+ npm install react-pdf-highlighter-extended
59
+ ```
60
+
61
+ ---
62
+
63
+ ## Quick Start
64
+
65
+ ### Basic Setup
66
+
67
+ ```tsx
68
+ import {
69
+ PdfLoader,
70
+ PdfHighlighter,
71
+ TextHighlight,
72
+ AreaHighlight,
73
+ useHighlightContainerContext,
74
+ } from "react-pdf-highlighter-extended";
75
+
76
+ function App() {
77
+ const [highlights, setHighlights] = useState([]);
78
+
79
+ return (
80
+ <PdfLoader document="https://example.com/document.pdf">
81
+ {(pdfDocument) => (
82
+ <PdfHighlighter
83
+ pdfDocument={pdfDocument}
84
+ highlights={highlights}
85
+ enableAreaSelection={(e) => e.altKey}
86
+ >
87
+ <HighlightContainer />
88
+ </PdfHighlighter>
89
+ )}
90
+ </PdfLoader>
91
+ );
92
+ }
93
+
94
+ function HighlightContainer() {
95
+ const { highlight, isScrolledTo } = useHighlightContainerContext();
96
+
97
+ return highlight.type === "text" ? (
98
+ <TextHighlight highlight={highlight} isScrolledTo={isScrolledTo} />
99
+ ) : (
100
+ <AreaHighlight highlight={highlight} isScrolledTo={isScrolledTo} />
101
+ );
102
+ }
103
+ ```
104
+
105
+ ---
106
+
107
+ ## Highlight Types
108
+
109
+ ### 1. Text Highlights
110
+
111
+ Select text in the PDF to create highlights.
112
+
113
+ ```tsx
114
+ <TextHighlight
115
+ highlight={highlight}
116
+ isScrolledTo={isScrolledTo}
117
+ style={{ background: "rgba(255, 226, 143, 1)" }}
118
+ />
119
+ ```
120
+
121
+ ### 2. Area Highlights
122
+
123
+ Hold `Alt` and drag to create rectangular highlights.
124
+
125
+ ```tsx
126
+ <PdfHighlighter
127
+ enableAreaSelection={(event) => event.altKey}
128
+ // ...
129
+ >
130
+ ```
131
+
132
+ ### 3. Freetext Notes
133
+
134
+ Create draggable, editable text annotations with customizable styling.
135
+
136
+ ```tsx
137
+ import { FreetextHighlight } from "react-pdf-highlighter-extended";
138
+
139
+ <PdfHighlighter
140
+ enableFreetextCreation={() => freetextMode}
141
+ onFreetextClick={(position) => {
142
+ addHighlight({ type: "freetext", position, content: { text: "Note" } });
143
+ }}
144
+ >
145
+
146
+ // In your highlight container:
147
+ <FreetextHighlight
148
+ highlight={highlight}
149
+ onChange={handlePositionChange}
150
+ onTextChange={handleTextChange}
151
+ onStyleChange={handleStyleChange}
152
+ color="#333333"
153
+ backgroundColor="#ffffc8"
154
+ fontSize="14px"
155
+ />
156
+ ```
157
+
158
+ **Features:**
159
+ - Drag to reposition
160
+ - Click to edit text
161
+ - Built-in style panel (colors, font size, font family)
162
+ - Toolbar appears on hover
163
+
164
+ [Full Documentation →](docs/freetext-highlights.md)
165
+
166
+ ### 4. Images & Signatures
167
+
168
+ Upload images or draw signatures and place them on PDFs.
169
+
170
+ ```tsx
171
+ import { ImageHighlight, SignaturePad } from "react-pdf-highlighter-extended";
172
+
173
+ // Signature pad modal
174
+ <SignaturePad
175
+ isOpen={isOpen}
176
+ onComplete={(dataUrl) => setPendingImage(dataUrl)}
177
+ onClose={() => setIsOpen(false)}
178
+ />
179
+
180
+ // In your highlight container:
181
+ <ImageHighlight
182
+ highlight={highlight}
183
+ onChange={handlePositionChange}
184
+ onEditStart={() => toggleEditInProgress(true)}
185
+ onEditEnd={() => toggleEditInProgress(false)}
186
+ />
187
+ ```
188
+
189
+ **Features:**
190
+ - Upload any image format
191
+ - Draw signatures with mouse or touch
192
+ - Drag to reposition
193
+ - Resize while maintaining aspect ratio
194
+ - Toolbar appears on hover
195
+
196
+ [Full Documentation →](docs/image-signature-highlights.md)
197
+
198
+ ### 5. Freehand Drawing
199
+
200
+ Draw freehand annotations directly on PDFs.
201
+
202
+ ```tsx
203
+ import { DrawingHighlight } from "react-pdf-highlighter-extended";
204
+
205
+ <PdfHighlighter
206
+ enableDrawingCreation={() => drawingMode}
207
+ onDrawingComplete={(position, dataUrl) => {
208
+ addHighlight({ type: "drawing", position, content: { image: dataUrl } });
209
+ }}
210
+ drawingConfig={{
211
+ strokeColor: "#ff0000",
212
+ strokeWidth: 2,
213
+ }}
214
+ >
215
+
216
+ // In your highlight container:
217
+ <DrawingHighlight
218
+ highlight={highlight}
219
+ onChange={handlePositionChange}
220
+ />
221
+ ```
222
+
223
+ **Features:**
224
+ - Freehand drawing with mouse or touch
225
+ - Customizable stroke color and width
226
+ - Stored as PNG for PDF export compatibility
227
+ - Drag to reposition
228
+
229
+ [Full Documentation →](docs/drawing-highlights.md)
230
+
231
+ ---
232
+
233
+ ## PDF Export
234
+
235
+ Export your annotated PDF with all highlights embedded.
236
+
237
+ ```tsx
238
+ import { exportPdf } from "react-pdf-highlighter-extended";
239
+
240
+ const handleExport = async () => {
241
+ const pdfBytes = await exportPdf(pdfUrl, highlights, {
242
+ textHighlightColor: "rgba(255, 226, 143, 0.5)",
243
+ areaHighlightColor: "rgba(255, 226, 143, 0.5)",
244
+ onProgress: (current, total) => console.log(`${current}/${total} pages`),
245
+ });
246
+
247
+ // Download the file
248
+ const blob = new Blob([pdfBytes], { type: "application/pdf" });
249
+ const url = URL.createObjectURL(blob);
250
+ const a = document.createElement("a");
251
+ a.href = url;
252
+ a.download = "annotated.pdf";
253
+ a.click();
254
+ URL.revokeObjectURL(url);
255
+ };
256
+ ```
257
+
258
+ **Supported highlight types:**
259
+ - Text highlights (colored rectangles)
260
+ - Area highlights (colored rectangles)
261
+ - Freetext notes (background + wrapped text)
262
+ - Images & signatures (embedded PNG/JPG)
263
+ - Freehand drawings (embedded PNG)
264
+
265
+ [Full Documentation →](docs/pdf-export.md)
266
+
267
+ ---
268
+
269
+ ## Component Architecture
270
+
271
+ ```
272
+ ┌─────────────────────────────────────────────────────┐
273
+ │ PdfLoader │
274
+ │ Loads PDF document via PDF.js │
275
+ │ │
276
+ │ ┌───────────────────────────────────────────────┐ │
277
+ │ │ PdfHighlighter │ │
278
+ │ │ Manages viewer, events, coordinate systems │ │
279
+ │ │ │ │
280
+ │ │ ┌─────────────────────────────────────────┐ │ │
281
+ │ │ │ User-defined HighlightContainer │ │ │
282
+ │ │ │ Renders highlights using context hooks │ │ │
283
+ │ │ │ │ │ │
284
+ │ │ │ • TextHighlight │ │ │
285
+ │ │ │ • AreaHighlight │ │ │
286
+ │ │ │ • FreetextHighlight │ │ │
287
+ │ │ │ • ImageHighlight │ │ │
288
+ │ │ │ • DrawingHighlight │ │ │
289
+ │ │ └─────────────────────────────────────────┘ │ │
290
+ │ └───────────────────────────────────────────────┘ │
291
+ └─────────────────────────────────────────────────────┘
292
+ ```
293
+
294
+ ### Context Hooks
295
+
296
+ | Hook | Purpose |
297
+ |------|---------|
298
+ | `usePdfHighlighterContext()` | Viewer utilities: `scrollToHighlight`, `setTip`, `getCurrentSelection` |
299
+ | `useHighlightContainerContext()` | Per-highlight utilities: `highlight`, `viewportToScaled`, `screenshot` |
300
+
301
+ ---
302
+
303
+ ## Coordinate Systems
304
+
305
+ The library uses two coordinate systems:
306
+
307
+ | System | Description | Use Case |
308
+ |--------|-------------|----------|
309
+ | **Viewport** | Pixel coordinates relative to current zoom | Rendering on screen |
310
+ | **Scaled** | Normalized (0-1) coordinates relative to page | Storage & retrieval |
311
+
312
+ ```tsx
313
+ // Converting between systems
314
+ const { viewportToScaled } = useHighlightContainerContext();
315
+
316
+ // Save position (viewport → scaled)
317
+ const scaledPosition = viewportToScaled(boundingRect);
318
+
319
+ // Highlights are automatically converted to viewport when rendering
320
+ ```
321
+
322
+ ---
323
+
324
+ ## Customization
325
+
326
+ ### Custom Highlight Interface
327
+
328
+ ```tsx
329
+ interface MyHighlight extends Highlight {
330
+ category: string;
331
+ comment?: string;
332
+ author?: string;
333
+ }
334
+
335
+ // Use the generic type
336
+ const { highlight } = useHighlightContainerContext<MyHighlight>();
337
+ ```
338
+
339
+ ### Custom Styling
340
+
341
+ ```tsx
342
+ // Via props
343
+ <TextHighlight
344
+ highlight={highlight}
345
+ style={{ background: categoryColors[highlight.category] }}
346
+ />
347
+
348
+ // Via CSS classes
349
+ .TextHighlight { }
350
+ .AreaHighlight { }
351
+ .FreetextHighlight { }
352
+ .ImageHighlight { }
353
+ .DrawingHighlight { }
354
+ ```
355
+
356
+ ### Tips and Popups
357
+
358
+ ```tsx
359
+ import { MonitoredHighlightContainer } from "react-pdf-highlighter-extended";
360
+
361
+ <MonitoredHighlightContainer
362
+ highlightTip={{
363
+ position: highlight.position,
364
+ content: <MyPopup highlight={highlight} />,
365
+ }}
366
+ >
367
+ <TextHighlight highlight={highlight} />
368
+ </MonitoredHighlightContainer>
369
+ ```
370
+
371
+ ---
372
+
373
+ ## Running Locally
374
+
375
+ ```bash
376
+ git clone https://github.com/QuocVietHa08/react-pdf-highlighter-plus.git
377
+ cd react-pdf-highlighter-plus
378
+ npm install
379
+ npm run dev
380
+ ```
381
+
382
+ ---
383
+
384
+ ## API Reference
385
+
386
+ See the full [API Reference](docs/api-reference.md) for detailed documentation on all components and types.
387
+
388
+ ---
389
+
390
+ ## Contributing
391
+
392
+ Contributions are welcome! Please:
393
+
394
+ 1. Fork the repository
395
+ 2. Create a feature branch
396
+ 3. Make your changes
397
+ 4. Submit a pull request
398
+
399
+ For bugs, please [open an issue](https://github.com/QuocVietHa08/react-pdf-highlighter-plus/issues) with clear reproduction steps.
400
+
401
+ ---
402
+
403
+ ## License
404
+
405
+ MIT
406
+
407
+ ---
408
+
409
+ ## Credits
410
+
411
+ Originally forked from [`react-pdf-highlighter`](https://github.com/agentcooper/react-pdf-highlighter) with significant architectural changes including context-based APIs, zoom support, freetext/image/drawing highlights, and PDF export functionality.
@@ -0,0 +1,82 @@
1
+ import React, { CSSProperties, MouseEvent, ReactNode } from "react";
2
+ import "../style/AreaHighlight.css";
3
+ import type { LTWHP, ViewportHighlight } from "../types";
4
+ /**
5
+ * Style options for area highlight appearance.
6
+ */
7
+ export interface AreaHighlightStyle {
8
+ highlightColor?: string;
9
+ }
10
+ /**
11
+ * The props type for {@link AreaHighlight}.
12
+ *
13
+ * @category Component Properties
14
+ */
15
+ export interface AreaHighlightProps {
16
+ /**
17
+ * The highlight to be rendered as an {@link AreaHighlight}.
18
+ */
19
+ highlight: ViewportHighlight;
20
+ /**
21
+ * A callback triggered whenever the highlight area is either finished
22
+ * being moved or resized.
23
+ *
24
+ * @param rect - The updated highlight area.
25
+ */
26
+ onChange?(rect: LTWHP): void;
27
+ /**
28
+ * Has the highlight been auto-scrolled into view? By default, this will render the highlight red.
29
+ */
30
+ isScrolledTo?: boolean;
31
+ /**
32
+ * react-rnd bounds on the highlight area. This is useful for preventing the user
33
+ * moving the highlight off the viewer/page. See [react-rnd docs](https://github.com/bokuweb/react-rnd).
34
+ */
35
+ bounds?: string | Element;
36
+ /**
37
+ * A callback triggered whenever a context menu is opened on the highlight area.
38
+ *
39
+ * @param event - The mouse event associated with the context menu.
40
+ */
41
+ onContextMenu?(event: MouseEvent<HTMLDivElement>): void;
42
+ /**
43
+ * Event called whenever the user tries to move or resize an {@link AreaHighlight}.
44
+ */
45
+ onEditStart?(): void;
46
+ /**
47
+ * Custom styling to be applied to the {@link AreaHighlight} component.
48
+ */
49
+ style?: CSSProperties;
50
+ /**
51
+ * Background color for the highlight.
52
+ * Default: "rgba(255, 226, 143, 1)" (yellow)
53
+ */
54
+ highlightColor?: string;
55
+ /**
56
+ * Callback triggered when the style changes.
57
+ */
58
+ onStyleChange?(style: AreaHighlightStyle): void;
59
+ /**
60
+ * Callback triggered when the delete button is clicked.
61
+ */
62
+ onDelete?(): void;
63
+ /**
64
+ * Custom style icon. Replaces the default palette icon.
65
+ */
66
+ styleIcon?: ReactNode;
67
+ /**
68
+ * Custom delete icon. Replaces the default trash icon.
69
+ */
70
+ deleteIcon?: ReactNode;
71
+ /**
72
+ * Custom color presets for the style panel.
73
+ * Default: ["rgba(255, 226, 143, 1)", "#ffcdd2", "#c8e6c9", "#bbdefb", "#e1bee7"]
74
+ */
75
+ colorPresets?: string[];
76
+ }
77
+ /**
78
+ * Renders a resizeable and interactive rectangular area for a highlight.
79
+ *
80
+ * @category Component
81
+ */
82
+ export declare const AreaHighlight: ({ highlight, onChange, isScrolledTo, bounds, onContextMenu, onEditStart, style, highlightColor, onStyleChange, onDelete, styleIcon, deleteIcon, colorPresets, }: AreaHighlightProps) => React.JSX.Element;
@@ -0,0 +1,109 @@
1
+ import React, { useState, useRef, useEffect, } from "react";
2
+ import { getPageFromElement } from "../lib/pdfjs-dom";
3
+ import "../style/AreaHighlight.css";
4
+ import { Rnd } from "react-rnd";
5
+ // Default icons
6
+ const DefaultStyleIcon = () => (React.createElement("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "currentColor" },
7
+ React.createElement("path", { d: "M12 3c-4.97 0-9 4.03-9 9s4.03 9 9 9c.83 0 1.5-.67 1.5-1.5 0-.39-.15-.74-.39-1.01-.23-.26-.38-.61-.38-.99 0-.83.67-1.5 1.5-1.5H16c2.76 0 5-2.24 5-5 0-4.42-4.03-8-9-8zm-5.5 9c-.83 0-1.5-.67-1.5-1.5S5.67 9 6.5 9 8 9.67 8 10.5 7.33 12 6.5 12zm3-4C8.67 8 8 7.33 8 6.5S8.67 5 9.5 5s1.5.67 1.5 1.5S10.33 8 9.5 8zm5 0c-.83 0-1.5-.67-1.5-1.5S13.67 5 14.5 5s1.5.67 1.5 1.5S15.33 8 14.5 8zm3 4c-.83 0-1.5-.67-1.5-1.5S16.67 9 17.5 9s1.5.67 1.5 1.5-.67 1.5-1.5 1.5z" })));
8
+ const DefaultDeleteIcon = () => (React.createElement("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "currentColor" },
9
+ React.createElement("path", { d: "M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z" })));
10
+ // Default color presets
11
+ const DEFAULT_COLOR_PRESETS = [
12
+ "rgba(255, 226, 143, 1)", // Yellow (default)
13
+ "#ffcdd2", // Light red
14
+ "#c8e6c9", // Light green
15
+ "#bbdefb", // Light blue
16
+ "#e1bee7", // Light purple
17
+ ];
18
+ /**
19
+ * Renders a resizeable and interactive rectangular area for a highlight.
20
+ *
21
+ * @category Component
22
+ */
23
+ export const AreaHighlight = ({ highlight, onChange, isScrolledTo, bounds, onContextMenu, onEditStart, style, highlightColor = "rgba(255, 226, 143, 1)", onStyleChange, onDelete, styleIcon, deleteIcon, colorPresets = DEFAULT_COLOR_PRESETS, }) => {
24
+ const [isStylePanelOpen, setIsStylePanelOpen] = useState(false);
25
+ const [isHovered, setIsHovered] = useState(false);
26
+ const stylePanelRef = useRef(null);
27
+ // Close style panel when clicking outside
28
+ useEffect(() => {
29
+ if (!isStylePanelOpen)
30
+ return;
31
+ const handleClickOutside = (e) => {
32
+ if (stylePanelRef.current &&
33
+ !stylePanelRef.current.contains(e.target)) {
34
+ setIsStylePanelOpen(false);
35
+ }
36
+ };
37
+ // Delay adding listener to avoid immediate close
38
+ const timeoutId = setTimeout(() => {
39
+ document.addEventListener("mousedown", handleClickOutside);
40
+ }, 0);
41
+ return () => {
42
+ clearTimeout(timeoutId);
43
+ document.removeEventListener("mousedown", handleClickOutside);
44
+ };
45
+ }, [isStylePanelOpen]);
46
+ const highlightClass = isScrolledTo ? "AreaHighlight--scrolledTo" : "";
47
+ // Generate key based on position. This forces a remount (and a defaultpos update)
48
+ // whenever highlight position changes (e.g., when updated, scale changes, etc.)
49
+ // We don't use position as state because when updating Rnd this would happen and cause flickering:
50
+ // User moves Rnd -> Rnd records new pos -> Rnd jumps back -> highlight updates -> Rnd re-renders at new pos
51
+ const key = `${highlight.position.boundingRect.width}${highlight.position.boundingRect.height}${highlight.position.boundingRect.left}${highlight.position.boundingRect.top}`;
52
+ // Merge custom style with highlight color
53
+ const mergedStyle = {
54
+ ...style,
55
+ backgroundColor: highlightColor,
56
+ };
57
+ return (React.createElement("div", { className: `AreaHighlight ${highlightClass}`, onContextMenu: onContextMenu },
58
+ (onStyleChange || onDelete) && (React.createElement("div", { className: "AreaHighlight__toolbar-wrapper", style: {
59
+ position: "absolute",
60
+ left: highlight.position.boundingRect.left,
61
+ top: highlight.position.boundingRect.top - 28,
62
+ paddingBottom: 12,
63
+ }, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false) },
64
+ React.createElement("div", { className: `AreaHighlight__toolbar ${isHovered || isStylePanelOpen ? "AreaHighlight__toolbar--visible" : ""}` },
65
+ onStyleChange && (React.createElement("button", { className: "AreaHighlight__style-button", onClick: (e) => {
66
+ e.stopPropagation();
67
+ setIsStylePanelOpen(!isStylePanelOpen);
68
+ }, title: "Change color", type: "button" }, styleIcon || React.createElement(DefaultStyleIcon, null))),
69
+ onDelete && (React.createElement("button", { className: "AreaHighlight__delete-button", onClick: (e) => {
70
+ e.stopPropagation();
71
+ onDelete();
72
+ }, title: "Delete", type: "button" }, deleteIcon || React.createElement(DefaultDeleteIcon, null)))),
73
+ isStylePanelOpen && onStyleChange && (React.createElement("div", { className: "AreaHighlight__style-panel", ref: stylePanelRef, onClick: (e) => e.stopPropagation() },
74
+ React.createElement("div", { className: "AreaHighlight__style-row" },
75
+ React.createElement("label", null, "Color"),
76
+ React.createElement("div", { className: "AreaHighlight__color-options" },
77
+ React.createElement("div", { className: "AreaHighlight__color-presets" }, colorPresets.map((c) => (React.createElement("button", { key: c, type: "button", className: `AreaHighlight__color-preset ${highlightColor === c ? "active" : ""}`, style: { backgroundColor: c }, onClick: () => onStyleChange({ highlightColor: c }), title: c })))),
78
+ React.createElement("input", { type: "color", value: highlightColor, onChange: (e) => {
79
+ onStyleChange({ highlightColor: e.target.value });
80
+ } }))))))),
81
+ React.createElement(Rnd, { onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), className: "AreaHighlight__part", onDragStop: (_, data) => {
82
+ const boundingRect = {
83
+ ...highlight.position.boundingRect,
84
+ top: data.y,
85
+ left: data.x,
86
+ };
87
+ onChange && onChange(boundingRect);
88
+ }, onResizeStop: (_mouseEvent, _direction, ref, _delta, position) => {
89
+ const boundingRect = {
90
+ top: position.y,
91
+ left: position.x,
92
+ width: ref.offsetWidth,
93
+ height: ref.offsetHeight,
94
+ pageNumber: getPageFromElement(ref)?.number || -1,
95
+ };
96
+ onChange && onChange(boundingRect);
97
+ }, onDragStart: onEditStart, onResizeStart: onEditStart, default: {
98
+ x: highlight.position.boundingRect.left,
99
+ y: highlight.position.boundingRect.top,
100
+ width: highlight.position.boundingRect.width,
101
+ height: highlight.position.boundingRect.height,
102
+ }, key: key, bounds: bounds,
103
+ // Prevevent any event clicks as clicking is already used for movement
104
+ onClick: (event) => {
105
+ event.stopPropagation();
106
+ event.preventDefault();
107
+ }, style: mergedStyle })));
108
+ };
109
+ //# sourceMappingURL=AreaHighlight.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AreaHighlight.js","sourceRoot":"","sources":["../../../src/components/AreaHighlight.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ import React from "react";
2
+ import { DrawingStroke, ScaledPosition } from "../types";
3
+ import "../style/DrawingCanvas.css";
4
+ import type { PDFViewer as TPDFViewer } from "pdfjs-dist/web/pdf_viewer.mjs";
5
+ /**
6
+ * The props type for {@link DrawingCanvas}.
7
+ *
8
+ * @category Component Properties
9
+ */
10
+ export interface DrawingCanvasProps {
11
+ /**
12
+ * Whether drawing mode is active.
13
+ */
14
+ isActive: boolean;
15
+ /**
16
+ * Stroke color for drawing.
17
+ * @default "#000000"
18
+ */
19
+ strokeColor?: string;
20
+ /**
21
+ * Stroke width for drawing.
22
+ * @default 3
23
+ */
24
+ strokeWidth?: number;
25
+ /**
26
+ * The PDF viewer instance.
27
+ */
28
+ viewer: InstanceType<typeof TPDFViewer>;
29
+ /**
30
+ * Callback when drawing is complete.
31
+ *
32
+ * @param dataUrl - The drawing as a PNG data URL.
33
+ * @param position - Scaled position of the drawing on the page.
34
+ * @param strokes - The stroke data for later editing.
35
+ */
36
+ onComplete: (dataUrl: string, position: ScaledPosition, strokes: DrawingStroke[]) => void;
37
+ /**
38
+ * Callback when drawing is cancelled.
39
+ */
40
+ onCancel: () => void;
41
+ }
42
+ /**
43
+ * A transparent overlay canvas for freehand drawing on PDF pages.
44
+ * Supports mouse and touch input.
45
+ *
46
+ * @category Component
47
+ */
48
+ export declare const DrawingCanvas: ({ isActive, strokeColor, strokeWidth, viewer, onComplete, onCancel, }: DrawingCanvasProps) => React.JSX.Element | null;