Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions playground/annotations/bulk-delete-annotations/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { baseOptions } from "../../shared/base-options";

// Bulk delete ALL annotations
window.NutrientViewer.load({
...baseOptions,
theme: window.NutrientViewer.Theme.DARK,
}).then((instance) => {
const items = instance.toolbarItems;

const deleteButton = {
type: "custom",
id: "delete-annotations",
title: "Delete", // or "Remove"
onPress: async () => {
try {
const pagesAnnotations = await Promise.all(
Array.from({ length: instance.totalPageCount }).map((_, pageIndex) =>
instance.getAnnotations(pageIndex),
),
);

const annotationIds = pagesAnnotations.flatMap((pageAnnotations) =>
pageAnnotations.map((annotation) => annotation.id).toArray(),
);

if (annotationIds.length === 0) {
console.info("No annotations to delete.");
return;
}

await instance.delete(annotationIds);
console.info(`Deleted ${annotationIds.length} annotations.`);
} catch (e) {
console.error("Failed to delete annotations:", e);
}
},
};

instance.setToolbarItems([...items, deleteButton]);
});
6 changes: 6 additions & 0 deletions playground/annotations/bulk-delete-annotations/playground.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
category: annotations
title: Bulk Delete Annotations
description: Example to bulk delete annotations from the entire document. After the viewer loads, it gathers annotations from every page, flattens their IDs, and deletes them in a single operation..
keywords: [annoatations, delete]
---
104 changes: 104 additions & 0 deletions playground/annotations/custom-ink-color-buttons/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import type { Color, ToolbarItem } from "@nutrient-sdk/viewer";
import { baseOptions } from "../../shared/base-options";

// Initialize window.NutrientViewer with custom configuration
window.NutrientViewer.load({
...baseOptions,
theme: window.NutrientViewer.Theme.DARK,
}).then((instance) => {
// SVG template for pen icon - used for both red and blue pens
const PEN_ICON_SVG = `
<svg xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
fill="currentColor"
class="bi bi-pen"
viewBox="0 0 16 16">
<path d="m13.498.795.149-.149a1.207 1.207 0 1 1 1.707 1.708l-.149.148a1.5 1.5 0 0 1-.059 2.059L4.854 14.854a.5.5 0 0 1-.233.131l-4 1a.5.5 0 0 1-.606-.606l1-4a.5.5 0 0 1 .131-.232l9.642-9.642a.5.5 0 0 0-.642.056L6.854 4.854a.5.5 0 1 1-.708-.708L9.44.854A1.5 1.5 0 0 1 11.5.796a1.5 1.5 0 0 1 1.998-.001zm-.644.766a.5.5 0 0 0-.707 0L1.95 11.756l-.764 3.057 3.057-.764L14.44 3.854a.5.5 0 0 0 0-.708l-1.585-1.585z"/>
</svg>`;

// Creates a pen icon DOM node with specified color
function createPenIconNode(color: string): HTMLDivElement {
const node = document.createElement("div");
node.innerHTML = PEN_ICON_SVG.replace("currentColor", color);
node.querySelector("path")?.setAttribute("fill", color);
const svg = node.querySelector("svg");
if (svg) {
svg.style.color = color;
}
return node;
}

// Creates a custom toolbar item
function createInkToolbarItem(
title: string,
node: HTMLDivElement,
color: Color,
): ToolbarItem {
return {
type: "custom",
title,
node,
onPress: async () => {
const { interactionMode } = instance.viewState;

if (interactionMode === "INK" || interactionMode === null) {
// Disable current interaction mode
instance.setViewState((viewState) =>
viewState.set("interactionMode", null),
);

// Update ink preset with selected color
instance.setAnnotationPresets((presets) => ({
...presets,
ink: {
...presets.ink,
strokeColor: color,
},
}));

// Enable ink mode with new color
instance.setCurrentAnnotationPreset("ink");
instance.setViewState((viewState) =>
viewState.set(
"interactionMode",
window.NutrientViewer.InteractionMode.INK,
),
);
} else {
// Disable interaction mode
instance.setViewState((viewState) =>
viewState.set("interactionMode", null),
);
}
},
};
}

// Create pen icon nodes for each color
const redPenNode = createPenIconNode("red");
const bluePenNode = createPenIconNode("blue");

// Create toolbar items for red and blue ink
const redInkTool = createInkToolbarItem(
"Red Ink",
redPenNode,
window.NutrientViewer.Color.RED,
);
const blueInkTool = createInkToolbarItem(
"Blue Ink",
bluePenNode,
window.NutrientViewer.Color.BLUE,
);

// Add custom tools to toolbar next to the default ink tool
instance.setToolbarItems((items) =>
items.reduce<ToolbarItem[]>(
(toolbar, item) =>
item.type === "ink"
? [...toolbar, item, redInkTool, blueInkTool]
: [...toolbar, item],
[],
),
);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
category: annotations
title: Custom Ink Color Buttons
description: Adds custom toolbar buttons for red and blue ink annotations. When clicked, these buttons update the ink preset color and activate ink drawing mode, allowing quick color switching without navigating through menus.
keywords: [annotations, ink, toolbar, custom, color]
---
124 changes: 124 additions & 0 deletions playground/annotations/custom-stamp-toolbar-items/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import type { Instance, ToolbarItem } from "@nutrient-sdk/viewer";
import { baseOptions } from "../../shared/base-options";

let selectedStamp: string | null = null;
let instance: Instance | null = null;

function updateCursor(inst: Instance) {
const contentDoc = inst.contentDocument;
if (!("host" in contentDoc)) return;

const container = contentDoc.host as HTMLElement;
if (selectedStamp) {
container.style.cursor = "move";
} else {
container.style.cursor = "default";
}
}

// Function to apply stamp to the currently visible page
async function applyStampToCurrentPage(
inst: Instance,
stampType: string,
top = 50,
left = 50,
width = 150,
height = 50,
) {
// Get the current page index from the view state
const currentPageIndex = inst.viewState.currentPageIndex;

const stamp = new window.NutrientViewer.Annotations.StampAnnotation({
pageIndex: currentPageIndex,
stampType: stampType,
boundingBox: new window.NutrientViewer.Geometry.Rect({
left: left - width / 2,
top: top - height / 2,
width: width,
height: height,
}),
});

// Create the stamp annotation
await inst.create(stamp);
}

// Function to remove stamp annotations from the current page
async function removeStampFromCurrentPage() {
if (!instance) return;

const currentPageIndex = instance.viewState.currentPageIndex;

// Get all annotations on the current page
const annotations = await instance.getAnnotations(currentPageIndex);

// Filter for stamp annotations only
const stampAnnotations = annotations.filter(
(annotation) =>
annotation instanceof window.NutrientViewer.Annotations.StampAnnotation,
);
await instance.delete(stampAnnotations);
}

const customStampItems: ToolbarItem[] = [
{
type: "custom",
id: "no-stamp",
title: "No Stamp",
dropdownGroup: "custom-stamps",
onPress: () => removeStampFromCurrentPage(),
},
{
type: "custom",
id: "stamp-approved",
title: "Approved Stamp",
dropdownGroup: "custom-stamps",
onPress: () => {
selectedStamp = "Approved";
if (instance) updateCursor(instance);
},
},
{
type: "custom",
id: "stamp-rejected",
title: "Rejected Stamp",
dropdownGroup: "custom-stamps",
onPress: () => {
selectedStamp = "Rejected";
if (instance) updateCursor(instance);
},
},
{
type: "custom",
id: "stamp-draft",
title: "Draft Stamp",
dropdownGroup: "custom-stamps",
onPress: () => {
selectedStamp = "Draft";
if (instance) updateCursor(instance);
},
},
];

window.NutrientViewer.load({
...baseOptions,
theme: window.NutrientViewer.Theme.DARK,
toolbarItems: [
...window.NutrientViewer.defaultToolbarItems,
...customStampItems,
],
}).then((_instance) => {
instance = _instance;
instance.addEventListener("page.press", async (event) => {
if (selectedStamp) {
await applyStampToCurrentPage(
_instance,
selectedStamp,
event.point.y,
event.point.x,
);
selectedStamp = null; // Reset after applying
updateCursor(_instance);
}
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
category: annotations
title: Custom Stamp Toolbar Items
description: Adds custom toolbar dropdown items for stamp annotations (Approved, Rejected, Draft). Select a stamp type, then click anywhere on the page to place it. Also includes a "No Stamp" option to remove all stamps from the current page.
keywords: [annotations, stamp, toolbar, dropdown, custom]
---
Loading