Documentation Index
Fetch the complete documentation index at: https://docs.joyfill.io/llms.txt
Use this file to discover all available pages before exploring further.
Overview
The JoyDoc SDK provides comprehensive image upload functionality through image fields, supporting both single and multiple image uploads with flexible handling options.
Architecture Overview
When users interact with image fields, the SDK triggers upload events that your application must handle by implementing upload handlers.
Flow:
- User selects image field → SDK creates upload event
- Your upload handler receives the event
- You handle the image processing and pass the image url back to SDK
- SDK updates the form with the provided URLs
Upload Methods
JoyDoc provides two methods for handling image uploads:
onFileUploadAsync (configured in field settings)
onUploadAsync (configured in JoyDoc component props)
Basic Implementation Examples
onFieldUploadAsync
You can also configure onFileUploadAsync via field settings as documented in the Joyfill Customize Settings guide:
const fieldSettings = {
field: {
onFileUploadAsync: async (params, fileUploads) => {
console.log(
"onFileUploadAsync via field settings: ",
params,
fileUploads
);
const uploadPromises = fileUploads.map(async (file) => {
const dataUri = await getDataUriForFileUpload(file);
return uploadFileAsync(params.fieldIdentifier, dataUri);
});
const results = await Promise.all(uploadPromises);
return results;
},
},
};
// Use with JoyDoc component
<JoyDoc
doc={document}
fieldSettings={fieldSettings}
onChange={handleChange}
mode="edit"
width={816}
height={600}
/>;
onUploadAsync
Configure upload handling at the document level:
<JoyDoc
doc={document}
onUploadAsync={async (params, fileUploads) => {
console.log("onUploadAsync: ", params, fileUploads);
const resultPromises = await fileUploads.map(async (fileUpload) => {
const dataUri = await getDataUriForFileUpload(fileUpload);
return uploadFileAsync(params.fieldIdentifier, dataUri);
});
return Promise.all(resultPromises)
.then((responses) => {
// Normalize response for different file types
const finalResponse = Array.isArray(responses[0])
? responses[0]
: responses;
return finalResponse;
})
.catch((error) => {
console.error("Upload error:", error);
if (error) return;
});
}}
// ... other props
/>;
Parameters
Both methods receive the same parameters:
- params: Object containing field/document metadata
params.fileId: The file ID
params.pageId: The page ID
params.fieldId: The field ID
params.fieldPositionId: The field position ID
params.fieldIdentifier: The field identifier
params.documentId: The document ID
params.documentIdentifier: The document identifier
- fileUploads: Array of File objects to be uploaded
Return Value
Both methods should return an array of upload results. Each result should contain:
_id: Unique identifier for the uploaded file
url: URL where the file can be accessed
fileName: Original filename
fileSize: Size of the file in bytes
Complete Working Examples
Example 1: onFileUploadAsync
Here’s a complete working example using onFileUploadAsync:
import React, { useState } from 'react';
import { JoyDoc, getDefaultDocument } from '@joyfill/components';
function OnFileUploadAsyncExample() {
const [document, setDocument] = useState(() => {
const doc = getDefaultDocument();
const fields = [
{
_id: 'profileImage',
identifier: 'profileImage',
type: 'image',
title: 'Profile Image',
value: [],
file: doc.files[0]._id,
multi: false
}
];
doc.fields = fields;
doc.files[0].pages[0].fieldPositions.push({
_id: 'profileImage-position',
field: 'profileImage',
x: 0,
y: 0,
width: 1,
height: 1,
displayType: 'original'
});
return doc;
});
const handleChange = (changelogs, updatedDoc) => {
setDocument(updatedDoc);
};
const fieldSettings = {
field: {
systemFilePicker: true,
onFileUploadAsync: async (params, fileUploads) => {
console.log('onFileUploadAsync triggered:', params, fileUploads);
const uploadPromises = fileUploads.map(async (file) => {
const dataUri = await getDataUriForFileUpload(file);
return uploadFileAsync(params.fieldIdentifier, dataUri);
});
const results = await Promise.all(uploadPromises);
console.log('Upload completed:', results);
return results;
}
}
};
return (
<div>
<h1>onFileUploadAsync Example</h1>
<JoyDoc
doc={document}
fieldSettings={fieldSettings}
onChange={handleChange}
mode="edit"
width={816}
height={600}
/>
</div>
);
}
export default OnFileUploadAsyncExample;
Example 2: onUploadAsync
Here’s a complete working example using onUploadAsync:
import React, { useState } from "react";
import { JoyDoc, getDefaultDocument } from "@joyfill/components";
function OnUploadAsyncExample() {
const [document, setDocument] = useState(() => {
const doc = getDefaultDocument();
const fields = [
{
_id: "galleryImages",
identifier: "galleryImages",
type: "image",
title: "Image Gallery",
value: [],
file: doc.files[0]._id,
multi: true,
},
];
doc.fields = fields;
doc.files[0].pages[0].fieldPositions.push({
_id: "galleryImages-position",
field: "galleryImages",
x: 0,
y: 0,
width: 1,
height: 1,
displayType: "original",
});
return doc;
});
const handleChange = (changelogs, updatedDoc) => {
setDocument(updatedDoc);
};
const handleUpload = async (params, fileUploads) => {
console.log("onUploadAsync triggered:", params, fileUploads);
const resultPromises = await fileUploads.map(async (fileUpload) => {
const dataUri = await getDataUriForFileUpload(fileUpload);
return uploadFileAsync(params.fieldIdentifier, dataUri);
});
return Promise.all(resultPromises)
.then((responses) => {
// Normalize response for different file types
const finalResponse = Array.isArray(responses[0])
? responses[0]
: responses;
console.log("Upload completed:", finalResponse);
return finalResponse;
})
.catch((error) => {
console.error("Upload error:", error);
if (error) return;
});
};
return (
<div>
<h1>onUploadAsync Example</h1>
<JoyDoc
doc={document}
onUploadAsync={handleUpload}
onChange={handleChange}
mode="edit"
width={816}
height={600}
/>
</div>
);
}
export default OnUploadAsyncExample;
Setup steps
Step 1: Create Joyfill Account
- To begin working with Joyfill, go to Joyfill’s Platform and create an account (jump to step 2 if you already have an account).
- By creating an account you will add yourself as the first user to your newly created Joyfill Organization and be placed inside the Joyfill Manager.
Step 2: Generate Your userAccessToken
- Once you’re inside the Joyfill Manager you will want to select from the top navigation bar Settings & Users -> Manager Users -> and click “Access Tokens” button next to your user.
- Once the modal appears select “Add Access Token”. Copy and securely store your access token for later use.
Step3 Api Url
- The Api url is
http://api-joy.joyfill.io
Helper Functions
These utility functions are used in both upload methods:
getDataUriForFileUpload
Converts a File object to a data URI string:
const getDataUriForFileUpload = async (fileUpload) => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(fileUpload);
reader.onloadend = async () => {
resolve(reader.result);
};
reader.onerror = () => {
reject(new Error('Failed to read file'));
};
});
};
uploadFileAsync
Uploads a data URI to the server:
const uploadFileAsync = async (docIdentifier, dataUri) => {
const response = await fetch(`${apiUrl}/v1/documents/${docIdentifier}/files/datauri`, {
method: 'POST',
mode: 'cors',
headers: getHeaders(),
body: JSON.stringify({ file: dataUri })
});
const data = await response.json();
return data;
};
Returns the necessary headers for API requests:
const getHeaders = () => {
return {
Authorization: `Bearer ${userAccessToken}`,
'Content-Type': 'application/json'
};
};