Skip to content

Commit 8137994

Browse files
authored
Refactor Document Editor Examples (#168)
1 parent b9549f3 commit 8137994

File tree

4 files changed

+266
-0
lines changed

4 files changed

+266
-0
lines changed
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import type {
2+
DocumentEditorToolbarItem,
3+
DocumentEditorUIHandler,
4+
DocumentOperations,
5+
Instance,
6+
} from "@nutrient-sdk/viewer";
7+
import { baseOptions } from "../../shared/base-options";
8+
9+
// Copy and paste pages in document editor using custom toolbar buttons
10+
11+
let _instance: Instance | null = null;
12+
let copiedPagesBuffer: ArrayBuffer | null = null;
13+
let copiedPages: number[] = [];
14+
15+
const copyButton: DocumentEditorToolbarItem = {
16+
type: "custom",
17+
id: "copy-pages",
18+
title: "Copy Pages",
19+
onPress: async (_event: Event, handler?: DocumentEditorUIHandler) => {
20+
if (!handler || !_instance) return;
21+
22+
const selectedPages = handler.getSelectedPageIndexes();
23+
if (selectedPages.length === 0) {
24+
alert("Please select pages to copy!");
25+
return;
26+
}
27+
28+
const operations: DocumentOperations.DocumentOperationsUnion[] = [
29+
{ type: "keepPages", pageIndexes: selectedPages },
30+
];
31+
32+
copiedPagesBuffer = await _instance.exportPDFWithOperations(operations);
33+
copiedPages = selectedPages;
34+
},
35+
};
36+
37+
const pasteButton: DocumentEditorToolbarItem = {
38+
type: "custom",
39+
id: "paste-pages",
40+
title: "Paste Pages",
41+
onPress: async (_event: Event, handler?: DocumentEditorUIHandler) => {
42+
if (!handler || !_instance || !copiedPagesBuffer) {
43+
alert("You haven't copied any pages yet!");
44+
return;
45+
}
46+
47+
const selectedPages = handler.getSelectedPageIndexes();
48+
if (selectedPages.length > 1) {
49+
alert("Please select only one page to paste after.");
50+
return;
51+
}
52+
53+
const afterPageIndex =
54+
selectedPages.length === 1
55+
? selectedPages[0]
56+
: _instance.totalPageCount - 1;
57+
58+
const blob = new File([copiedPagesBuffer], "copied-pages.pdf", {
59+
type: "application/pdf",
60+
});
61+
62+
handler.setOperations(
63+
(operations) =>
64+
operations.push({
65+
type: "importDocument",
66+
afterPageIndex,
67+
treatImportedDocumentAsOnePage: false,
68+
document: blob,
69+
}),
70+
true,
71+
);
72+
73+
copiedPages = [];
74+
copiedPagesBuffer = null;
75+
},
76+
};
77+
78+
window.NutrientViewer.load({
79+
...baseOptions,
80+
theme: window.NutrientViewer.Theme.DARK,
81+
documentEditorToolbarItems: [
82+
...window.NutrientViewer.defaultDocumentEditorToolbarItems,
83+
copyButton,
84+
pasteButton,
85+
],
86+
}).then((instance: Instance) => {
87+
_instance = instance;
88+
});
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
category: document-editor
3+
title: Custom Copy and Paste Buttons
4+
description: Add custom copy and paste buttons to the document editor toolbar that allow users to copy selected pages and paste them elsewhere in the document.
5+
keywords: [document editor, copy pages, paste pages, custom toolbar, toolbar items, exportPDFWithOperations, importDocument]
6+
---
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
import type {
2+
DocumentEditorFooterItem,
3+
Instance,
4+
ViewState,
5+
} from "@nutrient-sdk/viewer";
6+
import { baseOptions } from "../../shared/base-options";
7+
8+
// Drag and drop PDFs in document editor footer to import
9+
10+
let _instance: Instance | null = null;
11+
let fileArray: File | null = null;
12+
13+
window.NutrientViewer.load({
14+
...baseOptions,
15+
theme: window.NutrientViewer.Theme.DARK,
16+
}).then((instance: Instance) => {
17+
_instance = instance;
18+
19+
// Add a custom document editor footer item
20+
instance.setDocumentEditorFooterItems((items: DocumentEditorFooterItem[]) => {
21+
// Create a custom node for drag and drop
22+
const dropZoneNode = document.createElement("div");
23+
dropZoneNode.innerHTML = "Drop PDF here";
24+
25+
// Find the index of the cancel button
26+
const cancelIndex = items.findIndex((item) => item.type === "cancel");
27+
28+
const customItem: DocumentEditorFooterItem = {
29+
type: "custom",
30+
id: "pdf-drop-zone",
31+
node: dropZoneNode,
32+
onPress: (
33+
_event: Event,
34+
documentEditorUIHandler?: {
35+
setOperations: (
36+
callback: (operations: any) => any,
37+
clearPagesSelection?: boolean,
38+
) => void | Promise<void>;
39+
getSelectedPageIndexes: () => number[];
40+
},
41+
) => {
42+
if (fileArray && documentEditorUIHandler) {
43+
documentEditorUIHandler.setOperations(
44+
(operations: any) =>
45+
operations.push({
46+
type: "importDocument",
47+
beforePageIndex: 0,
48+
treatImportedDocumentAsOnePage: false,
49+
document: fileArray,
50+
}),
51+
true,
52+
);
53+
fileArray = null;
54+
} else {
55+
alert("Drag a file to import");
56+
}
57+
},
58+
};
59+
60+
// Remove the spacer divs that might be taking up space
61+
const filteredItems = items.filter((item) => {
62+
if (item.type === "custom" && !item.id && item.node) {
63+
const element = item.node as HTMLElement;
64+
return !(
65+
element.className && element.className.includes("BaselineUI-Box")
66+
);
67+
}
68+
return true;
69+
});
70+
71+
// Insert the custom item after the cancel button
72+
if (cancelIndex !== -1) {
73+
filteredItems.splice(cancelIndex + 1, 0, customItem);
74+
} else {
75+
filteredItems.unshift(customItem);
76+
}
77+
78+
return filteredItems;
79+
});
80+
81+
instance.addEventListener(
82+
"viewState.change",
83+
(viewState: ViewState, _previousViewState: ViewState) => {
84+
if (
85+
viewState.get("interactionMode") ===
86+
window.NutrientViewer.InteractionMode.DOCUMENT_EDITOR
87+
) {
88+
setTimeout(() => {
89+
if (!instance.contentDocument) return;
90+
91+
const dropPdfHereNode = instance.contentDocument.querySelector(
92+
"#pdf-drop-zone",
93+
) as HTMLElement | null;
94+
95+
if (dropPdfHereNode) {
96+
// Apply styling to the drop zone
97+
dropPdfHereNode.style.padding = "8px 16px";
98+
dropPdfHereNode.style.margin = "0 10px";
99+
dropPdfHereNode.style.border =
100+
"2px dashed var(--bui-color-text-primary)";
101+
dropPdfHereNode.style.background =
102+
"var(--bui-color-background-secondary-subtle)";
103+
dropPdfHereNode.style.color = "var(--bui-color-text-primary)";
104+
dropPdfHereNode.style.borderRadius = "4px";
105+
dropPdfHereNode.style.flexGrow = "10";
106+
dropPdfHereNode.style.justifyContent = "center";
107+
handleDragAndDrop(dropPdfHereNode);
108+
}
109+
}, 100);
110+
}
111+
},
112+
);
113+
});
114+
115+
// Function to handle file processing
116+
function handleFiles(files: FileList): void {
117+
console.log(files);
118+
119+
// Find and click the button element
120+
if (_instance && _instance.contentDocument) {
121+
const dropZoneElement = _instance.contentDocument.getElementById(
122+
"pdf-drop-zone",
123+
) as HTMLElement | null;
124+
125+
if (dropZoneElement) {
126+
Array.from(files).forEach((file: File) => {
127+
fileArray = file;
128+
dropZoneElement.click();
129+
});
130+
}
131+
}
132+
}
133+
134+
// Function to add drag and drop capabilities to nodes
135+
function handleDragAndDrop(dropZone: HTMLElement): void {
136+
// Prevent default behavior on dragover and dragenter to allow drop
137+
dropZone.addEventListener("dragover", (event: DragEvent) => {
138+
event.preventDefault();
139+
dropZone.style.border =
140+
"2px dashed var(--bui-color-support-success-medium)";
141+
});
142+
143+
dropZone.addEventListener("dragenter", (event: DragEvent) => {
144+
event.preventDefault();
145+
dropZone.style.border =
146+
"2px dashed var(--bui-color-support-success-medium)";
147+
});
148+
149+
dropZone.addEventListener("dragleave", (event: DragEvent) => {
150+
event.preventDefault();
151+
// Reset styling when no longer dragging over
152+
dropZone.style.border = "2px dashed var(--bui-color-text-primary)";
153+
});
154+
155+
// Handle the drop event
156+
dropZone.addEventListener("drop", (event: DragEvent) => {
157+
event.preventDefault();
158+
dropZone.style.border = "2px dashed var(--bui-color-text-primary)";
159+
160+
// Access the dropped files from the DataTransfer object
161+
const files = event.dataTransfer?.files;
162+
if (files) {
163+
handleFiles(files);
164+
}
165+
});
166+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
category: document-editor
3+
title: Drag and Drop Files to Import
4+
description: Add a custom drag-and-drop zone to the document editor footer that allows users to import PDF files by dragging them into the editor interface.
5+
keywords: [document editor, drag and drop, import document, footer items, custom toolbar, file upload, UI customization]
6+
---

0 commit comments

Comments
 (0)