From 2a68b1ab6a4f58a78519f2fdd0112a6978d218fc Mon Sep 17 00:00:00 2001 From: Rahul Soni Date: Mon, 22 Dec 2025 18:03:54 +0530 Subject: [PATCH 1/2] feat: add grid overlay example --- playground/form-creator/grid-overlay/index.ts | 64 +++++++++++++++++++ .../form-creator/grid-overlay/playground.mdx | 6 ++ .../form-creator/grid-overlay/styles.css | 13 ++++ 3 files changed, 83 insertions(+) create mode 100644 playground/form-creator/grid-overlay/index.ts create mode 100644 playground/form-creator/grid-overlay/playground.mdx create mode 100644 playground/form-creator/grid-overlay/styles.css diff --git a/playground/form-creator/grid-overlay/index.ts b/playground/form-creator/grid-overlay/index.ts new file mode 100644 index 0000000..281e007 --- /dev/null +++ b/playground/form-creator/grid-overlay/index.ts @@ -0,0 +1,64 @@ +import type { Instance } from "@nutrient-sdk/viewer"; +import { baseOptions } from "../../shared/base-options"; + +// Display grid overlay when focusing on annotations in Form Creator mode + +window.NutrientViewer.load({ + ...baseOptions, + theme: window.NutrientViewer.Theme.DARK, + toolbarItems: [ + ...window.NutrientViewer.defaultToolbarItems, + { type: "form-creator" }, + ], +}).then(async (instance: Instance) => { + const pageIndex = instance.viewState.currentPageIndex; + const pageInfo = await instance.pageInfoForIndex(pageIndex); + if (!pageInfo) return; + + const { width, height } = pageInfo; + + instance.addEventListener("annotations.focus", () => { + attachGridOverlayToggle(instance, width, height, pageIndex); + }); + + instance.addEventListener("page.press", () => { + setTimeout(() => { + try { + instance.removeCustomOverlayItem("grid-overlay"); + } catch (_) { + // Ignore if overlay doesn't exist + } + }, 50); + }); +}); + +function attachGridOverlayToggle( + instance: Instance, + width: number, + height: number, + pageIndex: number, +) { + try { + instance.removeCustomOverlayItem("grid-overlay"); + } catch (_) { + // Ignore if overlay doesn't exist + } + + const grid = document.createElement("div"); + grid.style.width = width + "px"; + grid.style.height = height + "px"; + grid.style.pointerEvents = "none"; + grid.style.backgroundImage = `linear-gradient(to right, rgba(18,0,18,0.1) 1px, transparent 0.1px), + linear-gradient(to bottom, rgba(18,0,18,0.1) 1px, transparent 0.1px)`; + grid.style.backgroundSize = "25px 25px"; + grid.style.zIndex = "-1"; + + const overlayItem = new window.NutrientViewer.CustomOverlayItem({ + id: "grid-overlay", + node: grid, + pageIndex, + position: new window.NutrientViewer.Geometry.Point({ x: 0, y: 0 }), + }); + + instance.setCustomOverlayItem(overlayItem); +} diff --git a/playground/form-creator/grid-overlay/playground.mdx b/playground/form-creator/grid-overlay/playground.mdx new file mode 100644 index 0000000..1639759 --- /dev/null +++ b/playground/form-creator/grid-overlay/playground.mdx @@ -0,0 +1,6 @@ +--- +category: form-creator +title: Grid Overlay for Form Creator +description: Display a grid overlay when focusing on annotations in Form Creator mode to help with precise widget placement. +keywords: [form-creator, grid, overlay, alignment, CustomOverlayItem] +--- diff --git a/playground/form-creator/grid-overlay/styles.css b/playground/form-creator/grid-overlay/styles.css new file mode 100644 index 0000000..2f98820 --- /dev/null +++ b/playground/form-creator/grid-overlay/styles.css @@ -0,0 +1,13 @@ +.grid-overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + pointer-events: none; + z-index: 9999; + background-image: + linear-gradient(0deg, black 1px, transparent 1px), + linear-gradient(90deg, black 1px, transparent 1px); + background-size: 50px 50px; +} From 8e20fe7cf572f20bed373ef7993e5d07e0e71431 Mon Sep 17 00:00:00 2001 From: Rahul Soni Date: Mon, 22 Dec 2025 18:04:07 +0530 Subject: [PATCH 2/2] feat: add custom menu example --- playground/form-creator/widget-menu/index.ts | 101 ++++++++++++++++++ .../form-creator/widget-menu/playground.mdx | 6 ++ .../form-creator/widget-menu/styles.css | 4 + 3 files changed, 111 insertions(+) create mode 100644 playground/form-creator/widget-menu/index.ts create mode 100644 playground/form-creator/widget-menu/playground.mdx create mode 100644 playground/form-creator/widget-menu/styles.css diff --git a/playground/form-creator/widget-menu/index.ts b/playground/form-creator/widget-menu/index.ts new file mode 100644 index 0000000..2c5670b --- /dev/null +++ b/playground/form-creator/widget-menu/index.ts @@ -0,0 +1,101 @@ +import type { Instance, ViewState } from "@nutrient-sdk/viewer"; +import { baseOptions } from "../../shared/base-options"; + +// Custom floating menu for Form Creator with quick access to all widget types + +window.NutrientViewer.load({ + ...baseOptions, + theme: window.NutrientViewer.Theme.DARK, + toolbarItems: [ + ...window.NutrientViewer.defaultToolbarItems, + { type: "form-creator" }, + ], +}).then((instance: Instance) => { + attachFormCreatorListener(instance); +}); + +function attachFormCreatorListener(instance: Instance) { + const doc = instance.contentDocument; + const button = doc.querySelector( + ".NutrientViewer-Toolbar-Button-Form-Creator", + ); + if (!button) return; + + const OVERLAY_ID = "form-creator-floating"; + + function renderFloating() { + const old = document.getElementById(OVERLAY_ID); + if (old) old.remove(); + + if (!button || button.getAttribute("aria-pressed") !== "true") return; + + const overlay = document.createElement("div"); + overlay.id = OVERLAY_ID; + overlay.style.cssText = + "position:fixed;display:flex;flex-wrap:wrap;gap:4px;" + + "background:rgba(0,0,0,0.7);padding:8px;border-radius:4px;" + + "font-family:sans-serif;color:white;z-index:10000;"; + + const modes = [ + { + label: "Button", + mode: window.NutrientViewer.InteractionMode.BUTTON_WIDGET, + }, + { + label: "Text", + mode: window.NutrientViewer.InteractionMode.TEXT_WIDGET, + }, + { + label: "Checkbox", + mode: window.NutrientViewer.InteractionMode.CHECKBOX_WIDGET, + }, + { + label: "Radio", + mode: window.NutrientViewer.InteractionMode.RADIO_BUTTON_WIDGET, + }, + { + label: "Combo", + mode: window.NutrientViewer.InteractionMode.COMBO_BOX_WIDGET, + }, + { + label: "List", + mode: window.NutrientViewer.InteractionMode.LIST_BOX_WIDGET, + }, + { + label: "Signature", + mode: window.NutrientViewer.InteractionMode.SIGNATURE_WIDGET, + }, + { + label: "Date", + mode: window.NutrientViewer.InteractionMode.DATE_WIDGET, + }, + ]; + + modes.forEach((item) => { + const btn = document.createElement("button"); + btn.textContent = item.label; + btn.style.padding = "4px 8px"; + btn.addEventListener("click", () => { + instance.setViewState((vs: ViewState) => + vs.set("interactionMode", item.mode), + ); + }); + overlay.appendChild(btn); + }); + + const toolbar = doc.querySelector(".NutrientViewer-Toolbar"); + if (!toolbar) return; + const r = toolbar.getBoundingClientRect(); + overlay.style.top = r.bottom + window.scrollY + "px"; + overlay.style.left = r.left + window.scrollX + "px"; + + document.body.appendChild(overlay); + } + + button.addEventListener("click", () => { + setTimeout(renderFloating, 0); + }); + instance.addEventListener("viewState.currentPageIndex.change", () => { + setTimeout(renderFloating, 0); + }); +} diff --git a/playground/form-creator/widget-menu/playground.mdx b/playground/form-creator/widget-menu/playground.mdx new file mode 100644 index 0000000..a6cdade --- /dev/null +++ b/playground/form-creator/widget-menu/playground.mdx @@ -0,0 +1,6 @@ +--- +category: form-creator +title: Custom Form Creator Menu +description: Create a floating menu with quick access buttons for all Form Creator widget types (Button, Text, Checkbox, Radio, Combo, List, Signature, Date). +keywords: [form-creator, widget, toolbar, floating menu, InteractionMode] +--- diff --git a/playground/form-creator/widget-menu/styles.css b/playground/form-creator/widget-menu/styles.css new file mode 100644 index 0000000..75698ab --- /dev/null +++ b/playground/form-creator/widget-menu/styles.css @@ -0,0 +1,4 @@ +/* Add your CSS here */ +.PSPDFKit-Form-Creator-Toolbar { + display: none !important; +}