Used to create a custom extension from scratch.
The name should consist of a description and a type, where type can be "Source", "Loader", "Validator", "Transform", "Store", or "View". createFilePondExtensionSet uses the type to auto sort the extensions set.
Options are the default options available for configuring this extension, these options are passed to didSetProps when updated and are available on props.
And then there’s the extension function body which receives the extension state and methods to interact with the FilePond instance.
const MyExtension = createExtension(
'NameType',
{
// options
},
(state, pond) => {
// the extension state
const { props, didSetProps } = state;
// the internal api
const {
on,
insertEntries,
removeEntries,
updateEntry,
replaceEntry,
pushTask,
abortTasks,
setEntryExtensionStatus,
getEntryExtensionStatus,
} = pond;
// called when props are updated
didSetProps((props) => {
//
});
// a task that needs to run on an entry, these are queued, so only run if other tasks finish successfully
async function taskCustomJob(entry) {
//
}
// called when an entry is updated
function handleUpdateEntry(entry) {
//
}
// called when the entry list is updated
function handleUpdateEntries(entries) {
//
}
// when the entry list is updated we call handleUpdateEntries
const unsubUpdateEntryList = on('updateEntries', debounce(handleUpdateEntries));
// when an entry is updated we call handleUpdateEntry
const unsubUpdateEntry = on('updateEntry', handleUpdateEntry);
return {
destroy: () => {
unsubUpdateEntry();
unsubUpdateEntryList();
},
};
}
);
Creating a PDF Preview extension
This extension will use PDF.js to load a PDF and create a poster image of its first page. The poster image can then be displayed by the EntryListView extension.
import { defineFilePond } from 'filepond';
import { locale } from 'filepond/locales/en-gb.js';
import { createExtension } from 'filepond/extensions/index.js';
import { createFilePondEntryList, appendEntryImageView } from 'filepond/templates/index.js';
// So we can see the image preview
const template = createFilePondEntryList();
appendEntryImageView(template);
// The PDF Preview Loader extension
const PDFPreviewLoader = createExtension(
'PDFPreviewLoader',
{},
(
{ props, extensionName },
{
on,
updateEntry,
pushTask,
setEntryExtensionState,
getEntryExtensionState,
setEntryExtensionStatus,
getEntryExtensionStatus,
}
) => {
// very basic pdf detection
function isPDF(file) {
return file.type === 'application/pdf';
}
// this task will generate the PDF
async function taskGeneratePDFPreview(entry) {
// let's start the loading indicator
setEntryExtensionStatus(entry, {
type: 'system',
code: 'LOAD_BUSY',
progress: Infinity,
});
// try to create a screenshot of the first page
try {
// dynamically load pdfjs and set its worker url
const cdn = 'https://cdn.jsdelivr.net/npm/pdfjs-dist';
const { getDocument, GlobalWorkerOptions } = await import(/* @vite-ignore */ cdn);
GlobalWorkerOptions.workerSrc = cdn + '/build/pdf.worker.min.mjs';
// let's convert the pdf to a png
const objectURL = URL.createObjectURL(entry.file);
const pdf = await getDocument(objectURL).promise;
// get first page
const page = await pdf.getPage(1);
// get a scaled viewport for the pdf
const viewport = page.getViewport({ scale: 1 });
// create the target canvas to draw on
const canvas = document.createElement('canvas');
canvas.width = viewport.width;
canvas.height = viewport.height;
// ask pdfjs to draw to the canvas
await page.render({
canvasContext: canvas.getContext('2d'),
transform: null,
viewport: viewport,
}).promise;
// we turn the canvas into a blob
const blob = await new Promise((resolve) => canvas.toBlob(resolve));
// release url
URL.revokeObjectURL(objectURL);
// update state of the entry so the poster can be displayed
updateEntry(entry, {
extension: {
// So we know which file was used as source
[extensionName]: {
posterSrc: entry.file,
},
// the poster the EntryListView will use
EntryListView: {
poster: blob,
},
},
});
} catch (error) {
setEntryExtensionStatus(entry, {
type: 'error',
code: 'LOAD_ERROR',
values: { error },
});
// so scheduler aborts rest of tasks
throw error;
}
// done!
setEntryExtensionStatus(entry, {
type: 'success',
code: 'LOAD_COMPLETE',
});
}
// this will be called everytime the entry is updated
function handleUpdateEntry(entry) {
// get extension entry props to help determine what next step to take
const status = getEntryExtensionStatus(entry);
const { posterSrc } = getEntryExtensionState(entry);
// is in error state
const hasFailed = status?.type === 'error';
// already has generated preview
const hasPoster = posterSrc === entry.src;
// failed to generate preview, not ready yet, or already generated the preview
if (hasFailed || !(entry.src instanceof File) || !isPDF(entry.src) || hasPoster) {
return;
}
// let's queue our preview generation task
pushTask(entry.id, taskGeneratePDFPreview);
}
const unsubUpdateEntry = on('updateEntry', handleUpdateEntry);
return {
destroy: () => {
unsubUpdateEntry();
},
};
}
);
// Create FilePond
const elements = defineFilePond({
locale,
extensions: [PDFPreviewLoader],
EntryListView: {
template,
},
});