@signiphi/pdf-signer 0.1.1
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/INSTALLING_LOCALLY.md +184 -0
- package/LICENSE +2 -0
- package/README.md +1093 -0
- package/assets/viewer.html +314 -0
- package/dist/__tests__/helpers/fixtures.d.ts +43 -0
- package/dist/__tests__/helpers/fixtures.d.ts.map +1 -0
- package/dist/__tests__/helpers/mocks.d.ts +333 -0
- package/dist/__tests__/helpers/mocks.d.ts.map +1 -0
- package/dist/__tests__/setup.d.ts +6 -0
- package/dist/__tests__/setup.d.ts.map +1 -0
- package/dist/components/AttachmentUpload.d.ts +17 -0
- package/dist/components/AttachmentUpload.d.ts.map +1 -0
- package/dist/components/EditableFieldsPanel.d.ts +30 -0
- package/dist/components/EditableFieldsPanel.d.ts.map +1 -0
- package/dist/components/ErrorBoundary.d.ts +67 -0
- package/dist/components/ErrorBoundary.d.ts.map +1 -0
- package/dist/components/FormFieldsView.d.ts +42 -0
- package/dist/components/FormFieldsView.d.ts.map +1 -0
- package/dist/components/PdfViewerStyled.d.ts +16 -0
- package/dist/components/PdfViewerStyled.d.ts.map +1 -0
- package/dist/components/PoweredBySigniphi.d.ts +11 -0
- package/dist/components/PoweredBySigniphi.d.ts.map +1 -0
- package/dist/components/SignatureCanvas.d.ts +12 -0
- package/dist/components/SignatureCanvas.d.ts.map +1 -0
- package/dist/components/SignatureInitialsBox.d.ts +23 -0
- package/dist/components/SignatureInitialsBox.d.ts.map +1 -0
- package/dist/components/SignatureModal.d.ts +22 -0
- package/dist/components/SignatureModal.d.ts.map +1 -0
- package/dist/components/SigningInstructions.d.ts +12 -0
- package/dist/components/SigningInstructions.d.ts.map +1 -0
- package/dist/components/SubmissionForm.d.ts +52 -0
- package/dist/components/SubmissionForm.d.ts.map +1 -0
- package/dist/components/ViewToggleToolbar.d.ts +30 -0
- package/dist/components/ViewToggleToolbar.d.ts.map +1 -0
- package/dist/components/form-fields/CheckboxRenderer.d.ts +10 -0
- package/dist/components/form-fields/CheckboxRenderer.d.ts.map +1 -0
- package/dist/components/form-fields/DateFieldRenderer.d.ts +14 -0
- package/dist/components/form-fields/DateFieldRenderer.d.ts.map +1 -0
- package/dist/components/form-fields/DropdownRenderer.d.ts +14 -0
- package/dist/components/form-fields/DropdownRenderer.d.ts.map +1 -0
- package/dist/components/form-fields/FormFieldRenderer.d.ts +20 -0
- package/dist/components/form-fields/FormFieldRenderer.d.ts.map +1 -0
- package/dist/components/form-fields/InitialsFieldRenderer.d.ts +14 -0
- package/dist/components/form-fields/InitialsFieldRenderer.d.ts.map +1 -0
- package/dist/components/form-fields/RadioGroupRenderer.d.ts +10 -0
- package/dist/components/form-fields/RadioGroupRenderer.d.ts.map +1 -0
- package/dist/components/form-fields/SignatureFieldRenderer.d.ts +16 -0
- package/dist/components/form-fields/SignatureFieldRenderer.d.ts.map +1 -0
- package/dist/components/form-fields/TextFieldRenderer.d.ts +14 -0
- package/dist/components/form-fields/TextFieldRenderer.d.ts.map +1 -0
- package/dist/components/form-fields/TextLabelRenderer.d.ts +14 -0
- package/dist/components/form-fields/TextLabelRenderer.d.ts.map +1 -0
- package/dist/components/form-fields/index.d.ts +14 -0
- package/dist/components/form-fields/index.d.ts.map +1 -0
- package/dist/components/index.d.ts +14 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +6297 -0
- package/dist/components/index.js.map +1 -0
- package/dist/components/index.mjs +6248 -0
- package/dist/components/index.mjs.map +1 -0
- package/dist/core/PdfViewerCore.d.ts +19 -0
- package/dist/core/PdfViewerCore.d.ts.map +1 -0
- package/dist/core/SignatureCaptureCore.d.ts +37 -0
- package/dist/core/SignatureCaptureCore.d.ts.map +1 -0
- package/dist/core/index.d.ts +3 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +907 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/index.mjs +884 -0
- package/dist/core/index.mjs.map +1 -0
- package/dist/hooks/index.d.ts +8 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +2167 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/index.mjs +2139 -0
- package/dist/hooks/index.mjs.map +1 -0
- package/dist/hooks/useAttachments.d.ts +25 -0
- package/dist/hooks/useAttachments.d.ts.map +1 -0
- package/dist/hooks/useFieldFiltering.d.ts +29 -0
- package/dist/hooks/useFieldFiltering.d.ts.map +1 -0
- package/dist/hooks/useFormFields.d.ts +23 -0
- package/dist/hooks/useFormFields.d.ts.map +1 -0
- package/dist/hooks/useMultiSignerContext.d.ts +25 -0
- package/dist/hooks/useMultiSignerContext.d.ts.map +1 -0
- package/dist/hooks/usePdfViewer.d.ts +52 -0
- package/dist/hooks/usePdfViewer.d.ts.map +1 -0
- package/dist/hooks/useSignatureCapture.d.ts +17 -0
- package/dist/hooks/useSignatureCapture.d.ts.map +1 -0
- package/dist/hooks/useSignatures.d.ts +25 -0
- package/dist/hooks/useSignatures.d.ts.map +1 -0
- package/dist/index.css +4929 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7220 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +7093 -0
- package/dist/index.mjs.map +1 -0
- package/dist/integrations/index.d.ts +6 -0
- package/dist/integrations/index.d.ts.map +1 -0
- package/dist/integrations/index.js +242 -0
- package/dist/integrations/index.js.map +1 -0
- package/dist/integrations/index.mjs +218 -0
- package/dist/integrations/index.mjs.map +1 -0
- package/dist/integrations/next-config.d.ts +46 -0
- package/dist/integrations/next-config.d.ts.map +1 -0
- package/dist/integrations/vite-plugin.d.ts +48 -0
- package/dist/integrations/vite-plugin.d.ts.map +1 -0
- package/dist/lib/index.d.ts +3 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/ui/alert.d.ts +9 -0
- package/dist/lib/ui/alert.d.ts.map +1 -0
- package/dist/lib/ui/button.d.ts +12 -0
- package/dist/lib/ui/button.d.ts.map +1 -0
- package/dist/lib/ui/calendar.d.ts +10 -0
- package/dist/lib/ui/calendar.d.ts.map +1 -0
- package/dist/lib/ui/card.d.ts +9 -0
- package/dist/lib/ui/card.d.ts.map +1 -0
- package/dist/lib/ui/checkbox.d.ts +5 -0
- package/dist/lib/ui/checkbox.d.ts.map +1 -0
- package/dist/lib/ui/dialog.d.ts +20 -0
- package/dist/lib/ui/dialog.d.ts.map +1 -0
- package/dist/lib/ui/index.d.ts +12 -0
- package/dist/lib/ui/index.d.ts.map +1 -0
- package/dist/lib/ui/input.d.ts +6 -0
- package/dist/lib/ui/input.d.ts.map +1 -0
- package/dist/lib/ui/label.d.ts +6 -0
- package/dist/lib/ui/label.d.ts.map +1 -0
- package/dist/lib/ui/popover.d.ts +7 -0
- package/dist/lib/ui/popover.d.ts.map +1 -0
- package/dist/lib/ui/radio-group.d.ts +6 -0
- package/dist/lib/ui/radio-group.d.ts.map +1 -0
- package/dist/lib/ui/select.d.ts +14 -0
- package/dist/lib/ui/select.d.ts.map +1 -0
- package/dist/lib/utils.d.ts +7 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/styles/index.css +5004 -0
- package/dist/types/index.d.ts +265 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +26 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/index.mjs +23 -0
- package/dist/types/index.mjs.map +1 -0
- package/dist/utils/attachment-validators.d.ts +118 -0
- package/dist/utils/attachment-validators.d.ts.map +1 -0
- package/dist/utils/audit-trail.d.ts +27 -0
- package/dist/utils/audit-trail.d.ts.map +1 -0
- package/dist/utils/date-validation.d.ts +30 -0
- package/dist/utils/date-validation.d.ts.map +1 -0
- package/dist/utils/errors.d.ts +106 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/field-extraction.d.ts +27 -0
- package/dist/utils/field-extraction.d.ts.map +1 -0
- package/dist/utils/field-visibility.d.ts +104 -0
- package/dist/utils/field-visibility.d.ts.map +1 -0
- package/dist/utils/index.d.ts +17 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +2501 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/index.mjs +2404 -0
- package/dist/utils/index.mjs.map +1 -0
- package/dist/utils/logger.d.ts +16 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/pdf-field-type-helpers.d.ts +78 -0
- package/dist/utils/pdf-field-type-helpers.d.ts.map +1 -0
- package/dist/utils/pdf-helpers.d.ts +38 -0
- package/dist/utils/pdf-helpers.d.ts.map +1 -0
- package/dist/utils/pdf-lib-loader.d.ts +45 -0
- package/dist/utils/pdf-lib-loader.d.ts.map +1 -0
- package/dist/utils/pdf-manipulation.d.ts +93 -0
- package/dist/utils/pdf-manipulation.d.ts.map +1 -0
- package/dist/utils/pdf-validators.d.ts +149 -0
- package/dist/utils/pdf-validators.d.ts.map +1 -0
- package/dist/utils/pdf-viewer-filter.d.ts +35 -0
- package/dist/utils/pdf-viewer-filter.d.ts.map +1 -0
- package/dist/utils/pdf-widget-helpers.d.ts +98 -0
- package/dist/utils/pdf-widget-helpers.d.ts.map +1 -0
- package/dist/utils/pdfjs-config.d.ts +56 -0
- package/dist/utils/pdfjs-config.d.ts.map +1 -0
- package/dist/utils/pdfjs-version-check.d.ts +28 -0
- package/dist/utils/pdfjs-version-check.d.ts.map +1 -0
- package/dist/utils/performance-monitor.d.ts +172 -0
- package/dist/utils/performance-monitor.d.ts.map +1 -0
- package/dist/utils/tracking.d.ts +89 -0
- package/dist/utils/tracking.d.ts.map +1 -0
- package/package.json +180 -0
- package/scripts/analyze-bundle.js +271 -0
- package/scripts/copy-utils.js +227 -0
- package/scripts/copy-utils.test.js +164 -0
- package/scripts/postinstall.js +109 -0
- package/scripts/setup.js +108 -0
- package/src/styles/index.css +139 -0
package/README.md
ADDED
|
@@ -0,0 +1,1093 @@
|
|
|
1
|
+
# @signiphi/pdf-signer
|
|
2
|
+
|
|
3
|
+
Flexible React components for PDF viewing, form filling, and signature capture. Built with React, TypeScript, and PDF.js.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 📄 **PDF Viewing** - Render PDFs with PDF.js viewer
|
|
8
|
+
- ✍️ **Signature Capture** - Draw or upload signatures
|
|
9
|
+
- 📝 **Form Filling** - Fill PDF form fields
|
|
10
|
+
- 🎨 **Flexible Styling** - Use headless components or pre-styled components
|
|
11
|
+
- 📱 **Responsive** - Works on desktop and mobile devices
|
|
12
|
+
- 🔧 **TypeScript** - Fully typed API
|
|
13
|
+
- 🌳 **Tree-shakeable** - Import only what you need
|
|
14
|
+
- ⚡ **Performance Optimized** - Lazy loading and bundle size optimization
|
|
15
|
+
- 📊 **Performance Monitoring** - Built-in performance tracking utilities
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @signiphi/pdf-signer
|
|
21
|
+
# or
|
|
22
|
+
pnpm add @signiphi/pdf-signer
|
|
23
|
+
# or
|
|
24
|
+
yarn add @signiphi/pdf-signer
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Peer Dependencies
|
|
28
|
+
|
|
29
|
+
This package requires the following peer dependencies:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install react react-dom @radix-ui/react-dialog @radix-ui/react-label @radix-ui/react-select @radix-ui/react-checkbox @radix-ui/react-radio-group @radix-ui/react-tabs lucide-react
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
If using styled components, you'll also need Tailwind CSS:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm install -D tailwindcss
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### PDF.js Setup
|
|
42
|
+
|
|
43
|
+
**The package works out of the box!** PDF.js viewer files are automatically copied to your `public/pdfjs/` directory when you install the package.
|
|
44
|
+
|
|
45
|
+
#### Automatic Setup (Default)
|
|
46
|
+
|
|
47
|
+
After installation, PDF.js viewer files are automatically set up:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npm install @signiphi/pdf-signer
|
|
51
|
+
# PDF.js files automatically copied to public/pdfjs/
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**What happens automatically:**
|
|
55
|
+
- ✅ Viewer files copied to `public/pdfjs/` via postinstall script
|
|
56
|
+
- ✅ Worker loaded from `node_modules` (bundled by your build tool)
|
|
57
|
+
- ✅ No manual configuration needed
|
|
58
|
+
- ✅ Works offline
|
|
59
|
+
- ✅ No CORS issues
|
|
60
|
+
|
|
61
|
+
**Default configuration:**
|
|
62
|
+
```typescript
|
|
63
|
+
{
|
|
64
|
+
viewerBasePath: '/pdfjs', // Viewer files in public/pdfjs/
|
|
65
|
+
workerSrc: 'node_modules/pdfjs-dist/build/pdf.worker.mjs' // Worker from node_modules
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
#### Manual Setup (If Postinstall Fails)
|
|
70
|
+
|
|
71
|
+
If the automatic setup doesn't work (e.g., in some CI/CD environments or local file installations), run:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
npx signiphi-setup
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Note for local installations:** When installing from a local directory using `file:` protocol (e.g., `npm install file:../pdf-signer`), npm may not run the postinstall script automatically. You must run `npx signiphi-setup` manually after installation. See [INSTALLING_LOCALLY.md](./INSTALLING_LOCALLY.md) for details.
|
|
78
|
+
|
|
79
|
+
Or with a custom directory:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
npx signiphi-setup public/my-custom-path
|
|
83
|
+
npx signiphi-setup --force # Force reinstall
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Framework Compatibility
|
|
87
|
+
|
|
88
|
+
### ✅ Supported Frameworks
|
|
89
|
+
|
|
90
|
+
This package works with all modern React frameworks:
|
|
91
|
+
- ✅ **Vite** - Works out of the box
|
|
92
|
+
- ✅ **Create React App (CRA)** - Works out of the box
|
|
93
|
+
- ✅ **Next.js** - Requires client-side component (see below)
|
|
94
|
+
- ✅ **Remix** - Use with `ClientOnly` wrapper
|
|
95
|
+
- ✅ **Gatsby** - Works out of the box
|
|
96
|
+
- ✅ **Any React 18+ project**
|
|
97
|
+
|
|
98
|
+
### Next.js Usage
|
|
99
|
+
|
|
100
|
+
This package uses browser APIs (`window`, `iframe`, `document`) and **must run client-side only**.
|
|
101
|
+
|
|
102
|
+
#### App Router (Next.js 13+)
|
|
103
|
+
|
|
104
|
+
Add `'use client'` directive to components using the PDF viewer:
|
|
105
|
+
|
|
106
|
+
```tsx
|
|
107
|
+
'use client';
|
|
108
|
+
|
|
109
|
+
import { PdfViewerStyled } from '@signiphi/pdf-signer';
|
|
110
|
+
import '@signiphi/pdf-signer/styles';
|
|
111
|
+
|
|
112
|
+
export default function PdfPage() {
|
|
113
|
+
return (
|
|
114
|
+
<PdfViewerStyled
|
|
115
|
+
pdfUrl="/document.pdf"
|
|
116
|
+
onLoad={() => console.log('PDF loaded')}
|
|
117
|
+
/>
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
#### Pages Router (Next.js 12 and below)
|
|
123
|
+
|
|
124
|
+
Use dynamic import with `ssr: false`:
|
|
125
|
+
|
|
126
|
+
```tsx
|
|
127
|
+
import dynamic from 'next/dynamic';
|
|
128
|
+
|
|
129
|
+
const PdfViewerStyled = dynamic(
|
|
130
|
+
() => import('@signiphi/pdf-signer').then(mod => mod.PdfViewerStyled),
|
|
131
|
+
{ ssr: false }
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
export default function PdfPage() {
|
|
135
|
+
return <PdfViewerStyled pdfUrl="/document.pdf" />;
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
#### Next.js Configuration (Optional)
|
|
140
|
+
|
|
141
|
+
For automatic PDF.js file copying during build:
|
|
142
|
+
|
|
143
|
+
```javascript
|
|
144
|
+
// next.config.js
|
|
145
|
+
const { withSigniphiPdfJs } = require('@signiphi/pdf-signer/next');
|
|
146
|
+
|
|
147
|
+
module.exports = withSigniphiPdfJs({
|
|
148
|
+
// your next.js config
|
|
149
|
+
});
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Framework Plugins (Optional)
|
|
153
|
+
|
|
154
|
+
For additional build-time reliability:
|
|
155
|
+
|
|
156
|
+
**Vite:**
|
|
157
|
+
```typescript
|
|
158
|
+
// vite.config.ts
|
|
159
|
+
import { defineConfig } from 'vite';
|
|
160
|
+
import { signiphiPdfJs } from '@signiphi/pdf-signer/vite';
|
|
161
|
+
|
|
162
|
+
export default defineConfig({
|
|
163
|
+
plugins: [signiphiPdfJs()],
|
|
164
|
+
});
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**Next.js:**
|
|
168
|
+
```javascript
|
|
169
|
+
// next.config.js
|
|
170
|
+
const { withSigniphiPdfJs } = require('@signiphi/pdf-signer/next');
|
|
171
|
+
|
|
172
|
+
module.exports = withSigniphiPdfJs({
|
|
173
|
+
// your next.js config
|
|
174
|
+
});
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
#### Custom Configuration (Advanced)
|
|
178
|
+
|
|
179
|
+
If you need to use a different path:
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
import { setPdfJsConfig } from '@signiphi/pdf-signer';
|
|
183
|
+
|
|
184
|
+
// Custom path
|
|
185
|
+
setPdfJsConfig({
|
|
186
|
+
viewerBasePath: '/custom/path/pdfjs',
|
|
187
|
+
workerSrc: '/custom/path/pdfjs/build/pdf.worker.mjs'
|
|
188
|
+
});
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
**Troubleshooting:**
|
|
192
|
+
|
|
193
|
+
If PDF.js files weren't copied automatically:
|
|
194
|
+
|
|
195
|
+
1. **Check if postinstall ran:**
|
|
196
|
+
```bash
|
|
197
|
+
# Look for this message during install:
|
|
198
|
+
# ✓ @signiphi/pdf-signer: PDF.js files copied to /path/to/public/pdfjs
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
2. **Verify pdfjs-dist is installed:**
|
|
202
|
+
```bash
|
|
203
|
+
npm list pdfjs-dist
|
|
204
|
+
# Should show: pdfjs-dist@5.3.93
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
3. **Run manual setup:**
|
|
208
|
+
```bash
|
|
209
|
+
npx signiphi-setup
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
4. **Check that `public/pdfjs/` directory exists:**
|
|
213
|
+
```bash
|
|
214
|
+
ls public/pdfjs/
|
|
215
|
+
# Should show: build/, web/, .version
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
5. **If using CI/CD with `--ignore-scripts`:**
|
|
219
|
+
```bash
|
|
220
|
+
# Postinstall won't run, use manual setup:
|
|
221
|
+
npx signiphi-setup
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
**Note:** CDN loading is not recommended due to CORS issues. The package uses local files by default.
|
|
225
|
+
|
|
226
|
+
## Quick Start
|
|
227
|
+
|
|
228
|
+
### Using Styled Components (Easiest)
|
|
229
|
+
|
|
230
|
+
```tsx
|
|
231
|
+
import { PdfViewer, SignatureCanvas } from '@signiphi/pdf-signer';
|
|
232
|
+
import { useRef } from 'react';
|
|
233
|
+
import type { PdfViewerRef, SignatureCanvasRef } from '@signiphi/pdf-signer';
|
|
234
|
+
|
|
235
|
+
function App() {
|
|
236
|
+
const pdfViewerRef = useRef<PdfViewerRef>(null);
|
|
237
|
+
const signatureRef = useRef<SignatureCanvasRef>(null);
|
|
238
|
+
|
|
239
|
+
const handleLoadPdf = async () => {
|
|
240
|
+
await pdfViewerRef.current?.loadPdf('/path/to/document.pdf');
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
const handleSaveSignature = (dataUrl: string) => {
|
|
244
|
+
console.log('Signature saved:', dataUrl);
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
return (
|
|
248
|
+
<div>
|
|
249
|
+
<PdfViewer
|
|
250
|
+
ref={pdfViewerRef}
|
|
251
|
+
onLoad={() => console.log('PDF loaded')}
|
|
252
|
+
onError={(error) => console.error('Error:', error)}
|
|
253
|
+
/>
|
|
254
|
+
|
|
255
|
+
<SignatureCanvas
|
|
256
|
+
ref={signatureRef}
|
|
257
|
+
onSignature={handleSaveSignature}
|
|
258
|
+
/>
|
|
259
|
+
</div>
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### Using Hooks
|
|
265
|
+
|
|
266
|
+
```tsx
|
|
267
|
+
import { usePdfViewer, useSignatureCapture } from '@signiphi/pdf-signer';
|
|
268
|
+
|
|
269
|
+
function App() {
|
|
270
|
+
const {
|
|
271
|
+
viewerRef,
|
|
272
|
+
isLoading,
|
|
273
|
+
error,
|
|
274
|
+
loadPdf,
|
|
275
|
+
getFormFieldValues,
|
|
276
|
+
} = usePdfViewer();
|
|
277
|
+
|
|
278
|
+
const {
|
|
279
|
+
canvasRef,
|
|
280
|
+
signatureDataUrl,
|
|
281
|
+
saveSignature,
|
|
282
|
+
clear,
|
|
283
|
+
} = useSignatureCapture();
|
|
284
|
+
|
|
285
|
+
const handleSubmit = async () => {
|
|
286
|
+
const formValues = await getFormFieldValues();
|
|
287
|
+
const signature = saveSignature();
|
|
288
|
+
|
|
289
|
+
console.log('Form values:', formValues);
|
|
290
|
+
console.log('Signature:', signature);
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
return (
|
|
294
|
+
<div>
|
|
295
|
+
<PdfViewer ref={viewerRef} />
|
|
296
|
+
<SignatureCanvas ref={canvasRef} />
|
|
297
|
+
<button onClick={handleSubmit}>Submit</button>
|
|
298
|
+
</div>
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### Using Headless Components (Maximum Flexibility)
|
|
304
|
+
|
|
305
|
+
```tsx
|
|
306
|
+
import { PdfViewerCore, SignatureCaptureCore } from '@signiphi/pdf-signer/headless';
|
|
307
|
+
import type { PdfViewerRef, SignatureCanvasRef } from '@signiphi/pdf-signer';
|
|
308
|
+
|
|
309
|
+
function CustomPdfViewer() {
|
|
310
|
+
const viewerRef = useRef<PdfViewerRef>(null);
|
|
311
|
+
|
|
312
|
+
return (
|
|
313
|
+
<PdfViewerCore
|
|
314
|
+
ref={viewerRef}
|
|
315
|
+
onLoad={() => console.log('Loaded')}
|
|
316
|
+
onError={(err) => console.error(err)}
|
|
317
|
+
>
|
|
318
|
+
{({ iframeRef, handleIframeLoad }) => (
|
|
319
|
+
<div className="my-custom-viewer">
|
|
320
|
+
<iframe
|
|
321
|
+
ref={iframeRef}
|
|
322
|
+
onLoad={handleIframeLoad}
|
|
323
|
+
className="w-full h-screen"
|
|
324
|
+
/>
|
|
325
|
+
</div>
|
|
326
|
+
)}
|
|
327
|
+
</PdfViewerCore>
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
## Error Handling
|
|
333
|
+
|
|
334
|
+
The package includes comprehensive error handling with custom error classes and an ErrorBoundary component.
|
|
335
|
+
|
|
336
|
+
### ErrorBoundary Component
|
|
337
|
+
|
|
338
|
+
Wrap your PDF components in an ErrorBoundary to catch and handle errors gracefully:
|
|
339
|
+
|
|
340
|
+
```tsx
|
|
341
|
+
import { ErrorBoundary, PdfViewerStyled } from '@signiphi/pdf-signer';
|
|
342
|
+
|
|
343
|
+
function App() {
|
|
344
|
+
return (
|
|
345
|
+
<ErrorBoundary
|
|
346
|
+
fallback={({ error, resetErrorBoundary }) => (
|
|
347
|
+
<div>
|
|
348
|
+
<h2>Something went wrong</h2>
|
|
349
|
+
<p>{error.message}</p>
|
|
350
|
+
<button onClick={resetErrorBoundary}>Try Again</button>
|
|
351
|
+
</div>
|
|
352
|
+
)}
|
|
353
|
+
onError={(error, errorInfo) => {
|
|
354
|
+
// Log to your error tracking service
|
|
355
|
+
console.error('PDF Error:', error, errorInfo);
|
|
356
|
+
}}
|
|
357
|
+
>
|
|
358
|
+
<PdfViewerStyled pdfUrl="/document.pdf" formFields={fields} />
|
|
359
|
+
</ErrorBoundary>
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### Custom Error Classes
|
|
365
|
+
|
|
366
|
+
The package provides specific error types for better error handling:
|
|
367
|
+
|
|
368
|
+
```typescript
|
|
369
|
+
import {
|
|
370
|
+
PdfValidationError,
|
|
371
|
+
PdfProcessingError,
|
|
372
|
+
FormFieldError,
|
|
373
|
+
AttachmentValidationError,
|
|
374
|
+
isPdfValidationError,
|
|
375
|
+
getErrorMessage,
|
|
376
|
+
} from '@signiphi/pdf-signer/utils';
|
|
377
|
+
|
|
378
|
+
try {
|
|
379
|
+
// PDF operations
|
|
380
|
+
await loadPdf(url);
|
|
381
|
+
} catch (err) {
|
|
382
|
+
if (isPdfValidationError(err)) {
|
|
383
|
+
console.error('PDF validation failed:', err.message);
|
|
384
|
+
} else if (isFormFieldError(err)) {
|
|
385
|
+
console.error('Field error:', err.fieldName, err.message);
|
|
386
|
+
} else {
|
|
387
|
+
console.error('Unknown error:', getErrorMessage(err));
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
## Validation
|
|
393
|
+
|
|
394
|
+
### PDF Validation
|
|
395
|
+
|
|
396
|
+
Validate PDF files before loading:
|
|
397
|
+
|
|
398
|
+
```typescript
|
|
399
|
+
import { validatePdfBytes, validatePdfUrl } from '@signiphi/pdf-signer/utils';
|
|
400
|
+
|
|
401
|
+
// Validate PDF URL format
|
|
402
|
+
const urlResult = validatePdfUrl('/documents/contract.pdf');
|
|
403
|
+
if (!urlResult.valid) {
|
|
404
|
+
console.error(urlResult.error);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// Validate PDF bytes (after fetching)
|
|
408
|
+
const pdfBytes = await fetch(url).then(r => r.arrayBuffer());
|
|
409
|
+
const bytesResult = validatePdfBytes(new Uint8Array(pdfBytes));
|
|
410
|
+
if (!bytesResult.valid) {
|
|
411
|
+
console.error(bytesResult.error);
|
|
412
|
+
}
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
### Form Field Validation
|
|
416
|
+
|
|
417
|
+
Validate form fields and field values:
|
|
418
|
+
|
|
419
|
+
```typescript
|
|
420
|
+
import {
|
|
421
|
+
validateFormField,
|
|
422
|
+
validateFieldValues,
|
|
423
|
+
validateSignatures,
|
|
424
|
+
} from '@signiphi/pdf-signer/utils';
|
|
425
|
+
|
|
426
|
+
// Validate field definition
|
|
427
|
+
const fieldResult = validateFormField({
|
|
428
|
+
id: 'field1',
|
|
429
|
+
name: 'firstName',
|
|
430
|
+
type: FormFieldType.TEXT,
|
|
431
|
+
label: 'First Name',
|
|
432
|
+
position: { x: 100, y: 200, width: 150, height: 30, page: 1 },
|
|
433
|
+
required: true,
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
if (!fieldResult.valid) {
|
|
437
|
+
console.error('Field errors:', fieldResult.errors);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// Validate field values
|
|
441
|
+
const valuesResult = validateFieldValues({
|
|
442
|
+
field1: 'value1',
|
|
443
|
+
field2: 'value2',
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
// Validate signatures
|
|
447
|
+
const sigsResult = validateSignatures({
|
|
448
|
+
sig1: 'data:image/png;base64,...',
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
if (!sigsResult.valid) {
|
|
452
|
+
console.error('Signature errors:', sigsResult.errors);
|
|
453
|
+
}
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
### Attachment Validation
|
|
457
|
+
|
|
458
|
+
Validate file uploads with custom constraints:
|
|
459
|
+
|
|
460
|
+
```typescript
|
|
461
|
+
import {
|
|
462
|
+
validateFile,
|
|
463
|
+
validateFiles,
|
|
464
|
+
validateFileOrThrow,
|
|
465
|
+
formatFileSize,
|
|
466
|
+
DEFAULT_ATTACHMENT_CONSTRAINTS,
|
|
467
|
+
} from '@signiphi/pdf-signer/utils';
|
|
468
|
+
|
|
469
|
+
// Validate single file
|
|
470
|
+
const result = validateFile(file, {
|
|
471
|
+
maxFileSize: 10 * 1024 * 1024, // 10MB
|
|
472
|
+
maxTotalSize: 50 * 1024 * 1024, // 50MB
|
|
473
|
+
maxFiles: 10,
|
|
474
|
+
allowedTypes: ['image/*', 'application/pdf'],
|
|
475
|
+
allowedExtensions: ['.pdf', '.jpg', '.png'],
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
if (!result.valid) {
|
|
479
|
+
result.errors.forEach(error => console.error(error));
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// Throw on validation failure
|
|
483
|
+
try {
|
|
484
|
+
validateFileOrThrow(file, constraints);
|
|
485
|
+
// File is valid, proceed with upload
|
|
486
|
+
} catch (err) {
|
|
487
|
+
if (isAttachmentValidationError(err)) {
|
|
488
|
+
alert(err.message);
|
|
489
|
+
console.log('File:', err.fileName);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// Validate multiple files with existing attachments
|
|
494
|
+
const filesResult = validateFiles(newFiles, existingAttachments, constraints);
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
## TypeScript Support
|
|
498
|
+
|
|
499
|
+
The package is built with TypeScript and provides full type safety.
|
|
500
|
+
|
|
501
|
+
### Strict Type Checking
|
|
502
|
+
|
|
503
|
+
The package is built with strict TypeScript settings enabled:
|
|
504
|
+
- `strict: true`
|
|
505
|
+
- `noImplicitReturns: true`
|
|
506
|
+
- `noUncheckedIndexedAccess: true`
|
|
507
|
+
|
|
508
|
+
This ensures maximum type safety and helps catch errors at compile time.
|
|
509
|
+
|
|
510
|
+
### Type Imports
|
|
511
|
+
|
|
512
|
+
Import types for use in your code:
|
|
513
|
+
|
|
514
|
+
```typescript
|
|
515
|
+
import type {
|
|
516
|
+
FormField,
|
|
517
|
+
FormFieldType,
|
|
518
|
+
FormFieldPosition,
|
|
519
|
+
MultiSignerContext,
|
|
520
|
+
Attachment,
|
|
521
|
+
AttachmentConstraints,
|
|
522
|
+
EsignFormField,
|
|
523
|
+
Signer,
|
|
524
|
+
SubmissionData,
|
|
525
|
+
} from '@signiphi/pdf-signer/types';
|
|
526
|
+
|
|
527
|
+
// Define form fields with full type safety
|
|
528
|
+
const fields: FormField[] = [
|
|
529
|
+
{
|
|
530
|
+
id: 'field1',
|
|
531
|
+
name: 'firstName',
|
|
532
|
+
type: FormFieldType.TEXT,
|
|
533
|
+
label: 'First Name',
|
|
534
|
+
position: { x: 100, y: 200, width: 150, height: 30, page: 1 },
|
|
535
|
+
required: true,
|
|
536
|
+
}
|
|
537
|
+
];
|
|
538
|
+
|
|
539
|
+
// Multi-signer context
|
|
540
|
+
const context: MultiSignerContext = {
|
|
541
|
+
isMultiSigner: true,
|
|
542
|
+
currentSignerEmail: 'user@example.com',
|
|
543
|
+
isPrimarySigner: true,
|
|
544
|
+
isFinalSigner: false,
|
|
545
|
+
};
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
### Type Guards
|
|
549
|
+
|
|
550
|
+
Use type guards to safely handle errors:
|
|
551
|
+
|
|
552
|
+
```typescript
|
|
553
|
+
import {
|
|
554
|
+
isPdfValidationError,
|
|
555
|
+
isPdfProcessingError,
|
|
556
|
+
isFormFieldError,
|
|
557
|
+
isAttachmentValidationError,
|
|
558
|
+
} from '@signiphi/pdf-signer/utils';
|
|
559
|
+
|
|
560
|
+
try {
|
|
561
|
+
// Operations
|
|
562
|
+
} catch (err) {
|
|
563
|
+
if (isPdfValidationError(err)) {
|
|
564
|
+
// TypeScript knows err is PdfValidationError here
|
|
565
|
+
console.error('PDF error:', err.message, err.details);
|
|
566
|
+
} else if (isFormFieldError(err)) {
|
|
567
|
+
// TypeScript knows err is FormFieldError here
|
|
568
|
+
console.error('Field error:', err.fieldName, err.message);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
## API Reference
|
|
574
|
+
|
|
575
|
+
### Components
|
|
576
|
+
|
|
577
|
+
#### `<PdfViewer />`
|
|
578
|
+
|
|
579
|
+
Pre-styled PDF viewer component.
|
|
580
|
+
|
|
581
|
+
**Props:**
|
|
582
|
+
- `className?: string` - Additional CSS classes
|
|
583
|
+
- `onLoad?: () => void` - Callback when PDF loads
|
|
584
|
+
- `onError?: (error: string) => void` - Callback on error
|
|
585
|
+
|
|
586
|
+
**Ref Methods:**
|
|
587
|
+
- `loadPdf(url: string): Promise<void>` - Load a PDF from URL
|
|
588
|
+
- `getFormFieldValues(): Promise<Record<string, string>>` - Get form field values
|
|
589
|
+
- `getAllFieldNames(): Promise<string[]>` - Get all field names
|
|
590
|
+
- `saveDocument(): Promise<Uint8Array | null>` - Save modified PDF
|
|
591
|
+
|
|
592
|
+
#### `<SignatureCanvas />`
|
|
593
|
+
|
|
594
|
+
Pre-styled signature canvas component.
|
|
595
|
+
|
|
596
|
+
**Props:**
|
|
597
|
+
- `width?: number` - Canvas width (default: 400)
|
|
598
|
+
- `height?: number` - Canvas height (default: 200)
|
|
599
|
+
- `onSignature?: (dataUrl: string) => void` - Callback when signature is saved
|
|
600
|
+
- `onCancel?: () => void` - Callback when cancelled
|
|
601
|
+
- `title?: string` - Placeholder text
|
|
602
|
+
- `className?: string` - Additional CSS classes
|
|
603
|
+
- `showActions?: boolean` - Show action buttons (default: true)
|
|
604
|
+
|
|
605
|
+
**Ref Methods:**
|
|
606
|
+
- `clear(): void` - Clear the canvas
|
|
607
|
+
- `getSignatureDataUrl(): string | null` - Get signature as data URL
|
|
608
|
+
- `isEmpty(): boolean` - Check if canvas is empty
|
|
609
|
+
|
|
610
|
+
### Hooks
|
|
611
|
+
|
|
612
|
+
#### `usePdfViewer()`
|
|
613
|
+
|
|
614
|
+
Hook for managing PDF viewer state.
|
|
615
|
+
|
|
616
|
+
**Returns:**
|
|
617
|
+
```typescript
|
|
618
|
+
{
|
|
619
|
+
viewerRef: RefObject<PdfViewerRef>;
|
|
620
|
+
isLoading: boolean;
|
|
621
|
+
error: string | null;
|
|
622
|
+
isLoaded: boolean;
|
|
623
|
+
loadPdf: (url: string) => Promise<void>;
|
|
624
|
+
handleLoad: () => void;
|
|
625
|
+
handleError: (error: string) => void;
|
|
626
|
+
getFormFieldValues: () => Promise<Record<string, string>>;
|
|
627
|
+
getAllFieldNames: () => Promise<string[]>;
|
|
628
|
+
saveDocument: () => Promise<Uint8Array | null>;
|
|
629
|
+
}
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
#### `useFormFields(fields: FormField[])`
|
|
633
|
+
|
|
634
|
+
Hook for managing form field state and validation.
|
|
635
|
+
|
|
636
|
+
**Returns:**
|
|
637
|
+
```typescript
|
|
638
|
+
{
|
|
639
|
+
fieldValues: Record<string, string>;
|
|
640
|
+
errors: ValidationError[];
|
|
641
|
+
hasErrors: boolean;
|
|
642
|
+
updateField: (fieldId: string, value: string) => void;
|
|
643
|
+
updateMultipleFields: (values: Record<string, string>) => void;
|
|
644
|
+
validateFields: () => boolean;
|
|
645
|
+
resetFields: () => void;
|
|
646
|
+
getFieldValue: (fieldId: string) => string;
|
|
647
|
+
}
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
#### `useSignatureCapture()`
|
|
651
|
+
|
|
652
|
+
Hook for managing signature capture state.
|
|
653
|
+
|
|
654
|
+
**Returns:**
|
|
655
|
+
```typescript
|
|
656
|
+
{
|
|
657
|
+
canvasRef: RefObject<SignatureCanvasRef>;
|
|
658
|
+
signatureDataUrl: string | null;
|
|
659
|
+
uploadedImage: string | null;
|
|
660
|
+
uploadError: string | null;
|
|
661
|
+
clear: () => void;
|
|
662
|
+
saveSignature: () => string | null;
|
|
663
|
+
isEmpty: () => boolean;
|
|
664
|
+
handleFileUpload: (file: File) => void;
|
|
665
|
+
clearUpload: () => void;
|
|
666
|
+
reset: () => void;
|
|
667
|
+
}
|
|
668
|
+
```
|
|
669
|
+
|
|
670
|
+
### Configuration API
|
|
671
|
+
|
|
672
|
+
#### `setPdfJsConfig(config)`
|
|
673
|
+
|
|
674
|
+
Configure PDF.js paths globally.
|
|
675
|
+
|
|
676
|
+
```typescript
|
|
677
|
+
import { setPdfJsConfig } from '@signiphi/pdf-signer';
|
|
678
|
+
|
|
679
|
+
setPdfJsConfig({
|
|
680
|
+
viewerBasePath: '/pdfjs', // Path to PDF.js viewer
|
|
681
|
+
workerSrc: '/pdfjs/build/pdf.worker.mjs' // Path to worker (optional, auto-calculated)
|
|
682
|
+
});
|
|
683
|
+
```
|
|
684
|
+
|
|
685
|
+
#### `getPdfJsConfig()`
|
|
686
|
+
|
|
687
|
+
Get current PDF.js configuration.
|
|
688
|
+
|
|
689
|
+
```typescript
|
|
690
|
+
import { getPdfJsConfig } from '@signiphi/pdf-signer';
|
|
691
|
+
|
|
692
|
+
const config = getPdfJsConfig();
|
|
693
|
+
console.log(config.viewerBasePath); // Current viewer path
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
#### `resetPdfJsConfig()`
|
|
697
|
+
|
|
698
|
+
Reset configuration to CDN defaults.
|
|
699
|
+
|
|
700
|
+
```typescript
|
|
701
|
+
import { resetPdfJsConfig } from '@signiphi/pdf-signer';
|
|
702
|
+
|
|
703
|
+
resetPdfJsConfig(); // Resets to CDN
|
|
704
|
+
```
|
|
705
|
+
|
|
706
|
+
### Types
|
|
707
|
+
|
|
708
|
+
```typescript
|
|
709
|
+
import type {
|
|
710
|
+
FormField,
|
|
711
|
+
FormFieldType,
|
|
712
|
+
FormFieldPosition,
|
|
713
|
+
PdfViewerRef,
|
|
714
|
+
SignatureCanvasRef,
|
|
715
|
+
PdfPage,
|
|
716
|
+
Submission,
|
|
717
|
+
ValidationError,
|
|
718
|
+
PdfJsConfig,
|
|
719
|
+
} from '@signiphi/pdf-signer';
|
|
720
|
+
```
|
|
721
|
+
|
|
722
|
+
## Tailwind Configuration
|
|
723
|
+
|
|
724
|
+
The package uses **Tailwind CSS v4** with CSS-based configuration.
|
|
725
|
+
|
|
726
|
+
### Option 1: Using Vite (Recommended)
|
|
727
|
+
|
|
728
|
+
If using Vite, install the Tailwind Vite plugin:
|
|
729
|
+
|
|
730
|
+
```bash
|
|
731
|
+
npm install -D @tailwindcss/vite tailwindcss
|
|
732
|
+
```
|
|
733
|
+
|
|
734
|
+
Update your `vite.config.ts`:
|
|
735
|
+
|
|
736
|
+
```ts
|
|
737
|
+
import { defineConfig } from 'vite';
|
|
738
|
+
import react from '@vitejs/plugin-react';
|
|
739
|
+
import tailwindcss from '@tailwindcss/vite';
|
|
740
|
+
|
|
741
|
+
export default defineConfig({
|
|
742
|
+
plugins: [react(), tailwindcss()],
|
|
743
|
+
});
|
|
744
|
+
```
|
|
745
|
+
|
|
746
|
+
### Option 2: Using PostCSS
|
|
747
|
+
|
|
748
|
+
Install dependencies:
|
|
749
|
+
|
|
750
|
+
```bash
|
|
751
|
+
npm install -D @tailwindcss/postcss tailwindcss
|
|
752
|
+
```
|
|
753
|
+
|
|
754
|
+
Create/update `postcss.config.js`:
|
|
755
|
+
|
|
756
|
+
```js
|
|
757
|
+
module.exports = {
|
|
758
|
+
plugins: {
|
|
759
|
+
'@tailwindcss/postcss': {},
|
|
760
|
+
},
|
|
761
|
+
};
|
|
762
|
+
```
|
|
763
|
+
|
|
764
|
+
### Import Styles
|
|
765
|
+
|
|
766
|
+
In your main entry file:
|
|
767
|
+
|
|
768
|
+
```tsx
|
|
769
|
+
import '@signiphi/pdf-signer/styles';
|
|
770
|
+
```
|
|
771
|
+
|
|
772
|
+
That's it! Tailwind v4 uses CSS-based configuration, so no `tailwind.config.js` file is needed.
|
|
773
|
+
|
|
774
|
+
## Design System
|
|
775
|
+
|
|
776
|
+
The package uses the **DockMaster Design System** for consistent branding and styling.
|
|
777
|
+
|
|
778
|
+
### Colors
|
|
779
|
+
|
|
780
|
+
The package uses OKLCH color space with the following palette:
|
|
781
|
+
|
|
782
|
+
- **Primary Blue**: `#0099CD` - Buttons, links, interactive elements
|
|
783
|
+
- **Light Blue**: `#BDD9E8` - Borders, secondary backgrounds
|
|
784
|
+
- **Dark Blue**: `#161848` - Text, foreground elements
|
|
785
|
+
- **Yellow**: `#FFC844` - Accent color, highlights
|
|
786
|
+
- **Greys**: Various shades for borders, backgrounds, and muted content
|
|
787
|
+
|
|
788
|
+
**Dark mode** is fully supported with adjusted colors for optimal readability.
|
|
789
|
+
|
|
790
|
+
### Typography
|
|
791
|
+
|
|
792
|
+
#### Body Font
|
|
793
|
+
Uses system font stack for optimal performance and native feel.
|
|
794
|
+
|
|
795
|
+
#### Signature Font
|
|
796
|
+
**Dancing Script** (Google Fonts) is used for signature and initials fields.
|
|
797
|
+
|
|
798
|
+
✅ **Automatically included** - The font is imported when you import the package styles, no additional setup needed!
|
|
799
|
+
|
|
800
|
+
### Border Radius
|
|
801
|
+
|
|
802
|
+
Default radius: `0.625rem` (10px)
|
|
803
|
+
|
|
804
|
+
### Responsive Design
|
|
805
|
+
|
|
806
|
+
All components are fully responsive with breakpoints:
|
|
807
|
+
- **Mobile**: < 640px
|
|
808
|
+
- **Tablet**: 640px - 1024px
|
|
809
|
+
- **Desktop**: 1024px+
|
|
810
|
+
|
|
811
|
+
Components adapt their layout, spacing, and font sizes for optimal viewing on all devices.
|
|
812
|
+
|
|
813
|
+
### Dark Mode
|
|
814
|
+
|
|
815
|
+
Enable dark mode by adding the `dark` class to your root element:
|
|
816
|
+
|
|
817
|
+
```tsx
|
|
818
|
+
<html className="dark">
|
|
819
|
+
{/* Your app */}
|
|
820
|
+
</html>
|
|
821
|
+
```
|
|
822
|
+
|
|
823
|
+
Or use a theme provider like `next-themes`:
|
|
824
|
+
|
|
825
|
+
```tsx
|
|
826
|
+
import { ThemeProvider } from 'next-themes';
|
|
827
|
+
|
|
828
|
+
function App() {
|
|
829
|
+
return (
|
|
830
|
+
<ThemeProvider attribute="class">
|
|
831
|
+
<SubmissionForm {...props} />
|
|
832
|
+
</ThemeProvider>
|
|
833
|
+
);
|
|
834
|
+
}
|
|
835
|
+
```
|
|
836
|
+
|
|
837
|
+
For more details, see **[DESIGN_SYSTEM.md](./DESIGN_SYSTEM.md)**.
|
|
838
|
+
|
|
839
|
+
## Examples
|
|
840
|
+
|
|
841
|
+
See the `examples/demo` directory for a complete working example.
|
|
842
|
+
|
|
843
|
+
## Multi-Signer Support
|
|
844
|
+
|
|
845
|
+
The package supports multi-signer workflows where multiple people sign the same document in sequence. Each signer only sees and fills their assigned fields.
|
|
846
|
+
|
|
847
|
+
### Quick Start
|
|
848
|
+
|
|
849
|
+
```tsx
|
|
850
|
+
import { SubmissionForm, type Signer } from '@signiphi/pdf-signer';
|
|
851
|
+
|
|
852
|
+
function DocumentSigningPage() {
|
|
853
|
+
// Backend provides current signer information
|
|
854
|
+
const currentSigner: Signer = {
|
|
855
|
+
id: "1",
|
|
856
|
+
email: "user@example.com",
|
|
857
|
+
name: "John Doe",
|
|
858
|
+
signOrder: 1, // 1 = primary, 2+ = secondary
|
|
859
|
+
status: "pending"
|
|
860
|
+
};
|
|
861
|
+
|
|
862
|
+
return (
|
|
863
|
+
<SubmissionForm
|
|
864
|
+
pdfUrl="/contract.pdf"
|
|
865
|
+
// Multi-signer props
|
|
866
|
+
currentSigner={currentSigner}
|
|
867
|
+
isMultipleSignature={true}
|
|
868
|
+
totalSigners={3}
|
|
869
|
+
// Callback
|
|
870
|
+
onSubmit={async (data) => {
|
|
871
|
+
await submitToBackend(data);
|
|
872
|
+
}}
|
|
873
|
+
/>
|
|
874
|
+
);
|
|
875
|
+
}
|
|
876
|
+
```
|
|
877
|
+
|
|
878
|
+
### Key Features
|
|
879
|
+
|
|
880
|
+
- **Field Filtering**: Each signer only sees fields assigned to them
|
|
881
|
+
- **Partial Flattening**: Each signer flattens only their fields, leaving others editable
|
|
882
|
+
- **Role-Based Visibility**: Primary, secondary, and final signer roles with different field access
|
|
883
|
+
- **PDF Viewer Filtering**: Modified PDF shown to each signer (fields removed), original preserved
|
|
884
|
+
|
|
885
|
+
### Signer Roles
|
|
886
|
+
|
|
887
|
+
#### Primary Signer (`signOrder === 1`)
|
|
888
|
+
- Usually the document creator or main party
|
|
889
|
+
- Sees: Own assigned fields + "recipients" group fields
|
|
890
|
+
|
|
891
|
+
#### Secondary Signer (`signOrder > 1`, not final)
|
|
892
|
+
- Middle signers in the chain
|
|
893
|
+
- Sees: Only their own assigned fields
|
|
894
|
+
|
|
895
|
+
#### Final Signer (`signOrder === totalSigners`)
|
|
896
|
+
- Last person in the signing chain
|
|
897
|
+
- Sees: Own assigned fields + "signers" group fields + unassigned fields
|
|
898
|
+
|
|
899
|
+
### Field Assignment Patterns
|
|
900
|
+
|
|
901
|
+
```typescript
|
|
902
|
+
// Direct assignment to specific signer
|
|
903
|
+
const field = {
|
|
904
|
+
id: 'buyer_signature',
|
|
905
|
+
assignedSignerEmail: 'buyer@example.com',
|
|
906
|
+
// ...
|
|
907
|
+
};
|
|
908
|
+
|
|
909
|
+
// Recipients group (all primaries)
|
|
910
|
+
const field = {
|
|
911
|
+
id: 'company_name',
|
|
912
|
+
assignedSignerEmail: 'recipients',
|
|
913
|
+
// ...
|
|
914
|
+
};
|
|
915
|
+
|
|
916
|
+
// Signers group (final signer only)
|
|
917
|
+
const field = {
|
|
918
|
+
id: 'completion_date',
|
|
919
|
+
assignedSignerEmail: 'signers',
|
|
920
|
+
// ...
|
|
921
|
+
};
|
|
922
|
+
```
|
|
923
|
+
|
|
924
|
+
### Complete Guide
|
|
925
|
+
|
|
926
|
+
For detailed information including:
|
|
927
|
+
- Visibility rules matrix
|
|
928
|
+
- Backend integration examples
|
|
929
|
+
- Testing strategies
|
|
930
|
+
- Advanced topics
|
|
931
|
+
|
|
932
|
+
See **[MULTI_SIGNER.md](./MULTI_SIGNER.md)** for the complete guide.
|
|
933
|
+
|
|
934
|
+
## Testing
|
|
935
|
+
|
|
936
|
+
The package includes comprehensive test coverage:
|
|
937
|
+
|
|
938
|
+
### Test Coverage
|
|
939
|
+
|
|
940
|
+
- **Utility Tests** (~182 tests) - Validation, PDF manipulation, field visibility, error handling
|
|
941
|
+
- **Hook Tests** (~150 tests) - All React hooks with full integration testing
|
|
942
|
+
- `useSignatures` - Signature state management
|
|
943
|
+
- `useAttachments` - File attachment handling
|
|
944
|
+
- `useFormFields` - Form field state and validation
|
|
945
|
+
- `useMultiSignerContext` - Multi-signer context logic
|
|
946
|
+
- `useFieldFiltering` - Field visibility filtering
|
|
947
|
+
- `usePdfViewer` - PDF viewer operations
|
|
948
|
+
|
|
949
|
+
### Running Tests
|
|
950
|
+
|
|
951
|
+
```bash
|
|
952
|
+
# Run all tests
|
|
953
|
+
pnpm test
|
|
954
|
+
|
|
955
|
+
# Run with coverage
|
|
956
|
+
pnpm test:coverage
|
|
957
|
+
|
|
958
|
+
# Run in watch mode
|
|
959
|
+
pnpm test:watch
|
|
960
|
+
```
|
|
961
|
+
|
|
962
|
+
### Test Environment
|
|
963
|
+
|
|
964
|
+
Tests use Vitest with **happy-dom** for React component testing. The test suite includes:
|
|
965
|
+
- Unit tests for utilities and hooks
|
|
966
|
+
- Integration tests for multi-signer workflows
|
|
967
|
+
- Mocked PDF.js and pdf-lib for fast, reliable testing
|
|
968
|
+
- Comprehensive edge case coverage
|
|
969
|
+
|
|
970
|
+
**Why happy-dom?** We use happy-dom instead of jsdom for better Windows compatibility, faster execution, and lighter memory footprint while maintaining full DOM API support.
|
|
971
|
+
|
|
972
|
+
## Browser Support
|
|
973
|
+
|
|
974
|
+
- Chrome/Edge 90+
|
|
975
|
+
- Firefox 88+
|
|
976
|
+
- Safari 14+
|
|
977
|
+
- Mobile browsers with touch support
|
|
978
|
+
|
|
979
|
+
## Performance & Bundle Size
|
|
980
|
+
|
|
981
|
+
### Optimized Bundle Sizes
|
|
982
|
+
|
|
983
|
+
The package is optimized for minimal bundle size impact:
|
|
984
|
+
|
|
985
|
+
| Entry Point | Raw Size | Gzipped | Use Case |
|
|
986
|
+
|-------------|----------|---------|----------|
|
|
987
|
+
| Main (`@signiphi/pdf-signer`) | 193 KB | 38 KB | Full package |
|
|
988
|
+
| Headless (`/headless`) | 21 KB | 5 KB | Core functionality only |
|
|
989
|
+
| Components (`/components`) | 167 KB | 33 KB | UI components only |
|
|
990
|
+
| Hooks (`/hooks`) | 71 KB | 14 KB | React hooks only |
|
|
991
|
+
| Utils (`/utils`) | 73 KB | 15 KB | Utilities only |
|
|
992
|
+
|
|
993
|
+
### Lazy Loading
|
|
994
|
+
|
|
995
|
+
The package uses lazy loading for heavy dependencies to minimize initial bundle size:
|
|
996
|
+
|
|
997
|
+
```typescript
|
|
998
|
+
// pdf-lib (~150 KB) is only loaded when actually needed
|
|
999
|
+
import { fillPdfWithSignatures } from '@signiphi/pdf-signer';
|
|
1000
|
+
|
|
1001
|
+
// pdf-lib loads automatically on first use
|
|
1002
|
+
await fillPdfWithSignatures(pdfBytes, signatures, formValues);
|
|
1003
|
+
```
|
|
1004
|
+
|
|
1005
|
+
**Benefits:**
|
|
1006
|
+
- ✅ View-only users never load pdf-lib (~150 KB savings)
|
|
1007
|
+
- ✅ Form filling users only pay the cost when needed
|
|
1008
|
+
- ✅ Automatic code splitting
|
|
1009
|
+
|
|
1010
|
+
### Tree-Shaking
|
|
1011
|
+
|
|
1012
|
+
Import only what you need using specific entry points:
|
|
1013
|
+
|
|
1014
|
+
```typescript
|
|
1015
|
+
// Import just the headless core (no UI)
|
|
1016
|
+
import { PdfViewerCore } from '@signiphi/pdf-signer/headless';
|
|
1017
|
+
|
|
1018
|
+
// Import just hooks (no components)
|
|
1019
|
+
import { usePdfViewer } from '@signiphi/pdf-signer/hooks';
|
|
1020
|
+
|
|
1021
|
+
// Import just utilities (no React code)
|
|
1022
|
+
import { validatePdfBytes } from '@signiphi/pdf-signer/utils';
|
|
1023
|
+
|
|
1024
|
+
// Import just components (no hooks)
|
|
1025
|
+
import { SignatureCanvas } from '@signiphi/pdf-signer/components';
|
|
1026
|
+
```
|
|
1027
|
+
|
|
1028
|
+
### Performance Monitoring
|
|
1029
|
+
|
|
1030
|
+
Built-in performance monitoring utilities for tracking critical operations:
|
|
1031
|
+
|
|
1032
|
+
```typescript
|
|
1033
|
+
import { createPerformanceMonitor } from '@signiphi/pdf-signer';
|
|
1034
|
+
|
|
1035
|
+
// Create a monitor
|
|
1036
|
+
const monitor = createPerformanceMonitor();
|
|
1037
|
+
|
|
1038
|
+
// Track PDF loading
|
|
1039
|
+
const endTimer = monitor.startTimer('pdf-load');
|
|
1040
|
+
await loadPdf(url);
|
|
1041
|
+
endTimer();
|
|
1042
|
+
|
|
1043
|
+
// Get metrics
|
|
1044
|
+
const metrics = monitor.getMetrics();
|
|
1045
|
+
console.log(`PDF load avg: ${metrics['pdf-load'].avg}ms`);
|
|
1046
|
+
```
|
|
1047
|
+
|
|
1048
|
+
**Track:**
|
|
1049
|
+
- PDF loading time
|
|
1050
|
+
- Form field updates
|
|
1051
|
+
- Signature capture performance
|
|
1052
|
+
- Custom operations
|
|
1053
|
+
|
|
1054
|
+
### Bundle Analysis
|
|
1055
|
+
|
|
1056
|
+
Analyze your bundle size:
|
|
1057
|
+
|
|
1058
|
+
```bash
|
|
1059
|
+
# Analyze bundle sizes
|
|
1060
|
+
pnpm analyze
|
|
1061
|
+
|
|
1062
|
+
# Check against budgets
|
|
1063
|
+
pnpm analyze:check
|
|
1064
|
+
|
|
1065
|
+
# Build and analyze
|
|
1066
|
+
pnpm build:analyze
|
|
1067
|
+
```
|
|
1068
|
+
|
|
1069
|
+
### Production Builds
|
|
1070
|
+
|
|
1071
|
+
For production, set `NODE_ENV=production` to enable minification:
|
|
1072
|
+
|
|
1073
|
+
```bash
|
|
1074
|
+
NODE_ENV=production pnpm build
|
|
1075
|
+
```
|
|
1076
|
+
|
|
1077
|
+
This reduces bundle size by ~20-30% through minification.
|
|
1078
|
+
|
|
1079
|
+
## Contributing
|
|
1080
|
+
|
|
1081
|
+
Contributions are welcome! Please open an issue or pull request.
|
|
1082
|
+
|
|
1083
|
+
## Documentation
|
|
1084
|
+
|
|
1085
|
+
- **[API.md](./API.md)** - Comprehensive API reference for all components, hooks, and utilities
|
|
1086
|
+
- **[TROUBLESHOOTING.md](./TROUBLESHOOTING.md)** - Common issues and solutions
|
|
1087
|
+
- **[MULTI_SIGNER.md](./MULTI_SIGNER.md)** - Complete multi-signer implementation guide
|
|
1088
|
+
- **[DESIGN_SYSTEM.md](./DESIGN_SYSTEM.md)** - Design system and styling guide
|
|
1089
|
+
|
|
1090
|
+
## Support
|
|
1091
|
+
|
|
1092
|
+
For issues and questions, please open a GitHub issue or refer to the TROUBLESHOOTING.md guide.
|
|
1093
|
+
|