@servicetitan/dte-pdf-editor
v1.14.0
Published
A React component library for creating interactive PDF editors with drag-and-drop field placement, data model integration, e-signature support, and fillable form fields.
Keywords
Readme
@servicetitan/dte-pdf-editor
A React component library for creating interactive PDF editors with drag-and-drop field placement, data model integration, e-signature support, and fillable form fields.
Features
- 📄 PDF Rendering: Display and interact with PDF documents using
react-pdfandpdfjs-dist - 🎯 Drag & Drop Field Placement: Intuitively place fields on PDF pages by dragging from a sidebar
- 📊 Data Model Integration: Connect fields to structured data models using JSON Schema
- ✍️ E-Signature Support: Add signature, initials, date signed, and full name fields
- 📝 Fillable Fields: Support for text, date, checkbox, and radio button inputs
- 👥 Multi-Recipient Support: Assign fields to different recipients with color-coded visualization
- ⚙️ Field Configuration: Configure field properties including position, size, label, and recipient assignment
- 👁️ View Mode: Display PDFs with filled data in a read-only or interactive view mode
Installation
npm install @servicetitan/dte-pdf-editorPeer Dependencies
This package requires the following peer dependencies:
npm install @servicetitan/anvil2@^1.46.9 react@~18.3.1 react-dom@~18.3.1Quick Start
import { PdfEditor, PdfField, PdfView, SchemaObject } from '@servicetitan/dte-pdf-editor';
import { useState } from 'react';
const pdfUrl = 'https://example.com/document.pdf';
function App() {
const [fields, setFields] = useState<PdfField[]>([]);
const [data, setData] = useState<Record<string, any>>({});
const recipients = [
{ id: 1, name: 'technician', displayName: 'Technician' },
{ id: 2, name: 'customer', displayName: 'Customer' },
];
const dataModel: SchemaObject = {
type: 'object',
properties: {
CustomerName: {
type: 'string',
title: 'Customer Name',
options: { placeholder: 'Enter customer name' },
},
},
};
return (
<PdfEditor
pdfUrl={pdfUrl}
fields={fields}
onFieldsChange={setFields}
dataModel={dataModel}
recipients={recipients}
/>
);
}Components
PdfEditor
The main component for editing PDF documents and placing fields.
Props
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| pdfUrl | string | Yes | URL or path to the PDF file |
| fields | PdfField[] | No | Array of fields placed on the PDF |
| onFieldsChange | (fields: PdfField[]) => void | Yes | Callback when fields are added, modified, or deleted |
| dataModel | SchemaObject | No | JSON Schema object defining available data model fields |
| recipients | RecipientInfo[] | No | Array of recipients that can be assigned to fields |
| loading | boolean | No | Whether the PDF is currently loading |
| loadingPlaceholder | ReactNode | No | Custom component to display while PDF is loading |
| errorPlaceholder | ReactNode | No | Custom component to display if PDF fails to load |
Example
<PdfEditor
pdfUrl="https://example.com/document.pdf"
fields={fields}
onFieldsChange={setFields}
dataModel={dataModel}
recipients={recipients}
loading={!pdfUrl}
loadingPlaceholder={<Spinner />}
errorPlaceholder={<Alert status="danger" title="Failed to load PDF" />}
/>PdfView
Component for displaying PDFs with filled data in view mode.
Props
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| pdfUrl | string | Yes | URL or path to the PDF file |
| fields | PdfField[] | Yes | Array of fields to display on the PDF |
| data | DataModelValues | No | Data object containing values for data model fields |
| recipients | RecipientInfo[] | No | Array of recipients for field assignment |
| fillingBy | string[] | No | Array of recipient names that are allowed to fill fields |
| onDataChange | (changedData: { [path: string]: string \| boolean }) => void | No | Callback when fillable field data changes |
| loading | boolean | No | Whether the PDF is currently loading |
| loadingPlaceholder | ReactNode | No | Custom component to display while PDF is loading |
| errorPlaceholder | ReactNode | No | Custom component to display if PDF fails to load |
Example
<PdfView
pdfUrl="https://example.com/document.pdf"
fields={fields}
data={data}
recipients={recipients}
fillingBy={['technician']}
onDataChange={(changedData) => {
setData({ ...data, ...changedData });
}}
/>Types
PdfField
Represents a field placed on a PDF document.
interface PdfField {
id: string;
type: FieldTypeEnum;
subType?: FillableFieldType | ESignFieldType;
x: number;
y: number;
page: number;
label: string;
width: number;
height: number;
required?: boolean;
path?: string;
recipient?: string;
}FieldTypeEnum
Enumeration of field types:
dataModel: Field connected to a data model schemaeSign: E-signature fieldfillable: User-fillable form field
ESignFieldType
Types of e-signature fields:
signature: Signature fieldinitials: Initials fielddateSigned: Date signed fieldfullName: Full name field
FillableFieldType
Types of fillable fields:
text: Text inputdate: Date pickercheckbox: Checkboxradio: Radio button
SchemaObject
JSON Schema object defining the data model structure. Supports nested objects, arrays, and various string subtypes (string, text, html, image).
interface SchemaObject {
type: 'object';
properties: Record<string, SchemaNode>;
title?: string;
options?: SchemaFieldBaseOptions;
}RecipientInfo
Information about a recipient that can be assigned to fields.
interface RecipientInfo {
id: number;
name: string;
displayName: string;
}Usage Examples
Basic Editor with Data Model
import { PdfEditor, PdfField, SchemaObject } from '@servicetitan/dte-pdf-editor';
import { useState } from 'react';
const dataModel: SchemaObject = {
type: 'object',
properties: {
JobInformation: {
type: 'object',
properties: {
JobNumber: {
type: 'string',
title: 'Job Number',
options: { placeholder: 'Enter job number' },
},
JobName: {
type: 'string',
title: 'Job Name',
options: { placeholder: 'Enter job name' },
},
},
},
},
};
function MyEditor() {
const [fields, setFields] = useState<PdfField[]>([]);
return (
<PdfEditor
pdfUrl="https://example.com/document.pdf"
fields={fields}
onFieldsChange={setFields}
dataModel={dataModel}
/>
);
}Editor with Recipients
const recipients = [
{ id: 1, name: 'technician', displayName: 'Technician' },
{ id: 2, name: 'customer', displayName: 'Customer' },
{ id: 3, name: 'accountant', displayName: 'Accountant' },
];
<PdfEditor
pdfUrl={pdfUrl}
fields={fields}
onFieldsChange={setFields}
recipients={recipients}
/>View Mode with Data
const [data, setData] = useState({
'JobInformation.JobNumber': 'JOB-12345',
'JobInformation.JobName': 'HVAC Installation',
});
<PdfView
pdfUrl={pdfUrl}
fields={fields}
data={data}
recipients={recipients}
fillingBy={['technician']}
onDataChange={(changedData) => {
setData({ ...data, ...changedData });
}}
/>Toggle Between Edit and View Modes
function PdfEditorPage() {
const [fields, setFields] = useState<PdfField[]>([]);
const [isView, setIsView] = useState(false);
const [data, setData] = useState<Record<string, any>>({});
return (
<>
<Button onClick={() => setIsView(!isView)}>
{isView ? 'Edit' : 'View'}
</Button>
{!isView ? (
<PdfEditor
pdfUrl={pdfUrl}
fields={fields}
onFieldsChange={setFields}
dataModel={dataModel}
recipients={recipients}
/>
) : (
<PdfView
pdfUrl={pdfUrl}
fields={fields}
data={data}
recipients={recipients}
fillingBy={['technician']}
onDataChange={(changedData) => {
setData({ ...data, ...changedData });
}}
/>
)}
</>
);
}Field Operations
Adding Fields
Fields are added by dragging from the sidebar onto the PDF canvas. The sidebar displays:
- Data Model Fields: Grouped by schema structure
- E-Sign Fields: Signature, initials, date signed, full name
- Fillable Fields: Text, date, checkbox, radio
Configuring Fields
Click on a field to open the configuration panel where you can:
- Change the label
- Adjust position and size
- Assign a recipient
- Set required status
- Update the data model path
Moving and Resizing
- Move: Click and drag a field to reposition it
- Resize: Use the resize handles on field corners
Deleting Fields
Select a field and use the delete button in the configuration panel, or press the delete key.
Styling
The package includes default styles. To customize, you can override CSS classes:
.dte-pdf-editor: Main editor container.dte-pdf-view-container: View mode container.dte-pdf-wrapper: PDF document wrapper.dte-pdf-field-overlay: Field overlay container.dte-pdf-editor-sidebar-container: Sidebar container
Browser Support
This package requires modern browsers with support for:
- ES6+
- Canvas API
- Drag and Drop API
License
Copyright © ServiceTitan
