// ES6 Compliant Syntax
// Copilot name: GitHub Copilot, version: 1.0.0, model: GPT-4, version: 2024-06, date: 2025-10-23
// File: showcase-demos/edit-table-of-contents/ui-elements.js
// Class with static UI elements and related functions for the table of contents demo
class UIElements {
// The list of registered panels in the webviewer
static viewerPanels = null;
// The tab panel, representing the webviewer left panel
static tabPanel = {
handle: null,
dataElement: 'tabPanel'
};
// The table of contents sub-panel to be registered
static tableOfContentsPanel = {
handle: null,
dataElement: 'tableOfContentsPanel',
render: null,
};
static outlineName = 'My First Outline';
static pageNumber = 1;
static xCoordinate = 0;
static yCoordinate = 0;
// Customize the webviewer left panel
static customizeUI = (instance) => {
const { UI } = instance;
// Close the tab panel (if it's open) for refreshment.
UI.closeElements([UIElements.tabPanel.dataElement]);
// Get the list of registered panels in the webviewer
UIElements.viewerPanels = UI.getPanels();
// Find the Tab Panel to modify. The table of contents sub-panel will be added to this Tab panel.
UIElements.tabPanel.handle = UIElements.viewerPanels.find((panel) => panel.dataElement === UIElements.tabPanel.dataElement);
// Register the table of contents sub-panel
UIElements.RegisterTableOfContentsPanel(instance);
// Add the new table of contents sub-panel to list of sub-panels under the Tab Panel
UIElements.tableOfContentsPanel.handle = { render: UIElements.tableOfContentsPanel.dataElement };
UIElements.tabPanel.handle.panelsList = [UIElements.tableOfContentsPanel.handle, ...UIElements.tabPanel.handle.panelsList];
UI.openElements([UIElements.tabPanel.dataElement]);
};
// Register the table of contents sub-panel
static RegisterTableOfContentsPanel = (instance) => {
UIElements.tableOfContentsPanel.render = UIElements.createTableOfContentsPanelElements(instance);
instance.UI.addPanel({
dataElement: UIElements.tableOfContentsPanel.dataElement,
location: 'left',
icon: '<svg xmlns="http://www.w3.org/2000/svg" width="18px" height="18px" viewBox="0 0 103.19 122.88"><path d="M17.16 0h82.72a3.32 3.32 0 013.31 3.31v92.32c-.15 2.58-3.48 2.64-7.08 2.48H15.94c-4.98 0-9.05 4.07-9.05 9.05s4.07 9.05 9.05 9.05h80.17v-9.63h7.08v12.24c0 2.23-1.82 4.05-4.05 4.05H16.29C7.33 122.88 0 115.55 0 106.59V17.16C0 7.72 7.72 0 17.16 0zm3.19 13.4h2.86c1.46 0 2.66.97 2.66 2.15v67.47c0 1.18-1.2 2.15-2.66 2.15h-2.86c-1.46 0-2.66-.97-2.66-2.15V15.55c.01-1.19 1.2-2.15 2.66-2.15z" fill-rule="evenodd" clip-rule="evenodd"/></svg>',
title: 'Table of Contents',
render: () => UIElements.tableOfContentsPanel.render,
});
};
// Create the table of contents panel elements.
static createTableOfContentsPanelElements = (instance) => {
let panelDiv = document.createElement('div');
panelDiv.id = 'tableOfContentsPanel';
let paragraph = document.createTextNode('Add or edit the existing document outline or table of contents to create accessible navigation of your documents.');
panelDiv.appendChild(paragraph);
let dividerDiv = document.createElement('div');
dividerDiv.style.borderTop = '1px solid #ccc';
dividerDiv.style.margin = '10px 0';
panelDiv.appendChild(dividerDiv);
// Outline details
let outlineDetails = document.createElement("h3");
outlineDetails.textContent = "Outline Details";
panelDiv.appendChild(outlineDetails);
// Outline name label
let outlineTitle = document.createTextNode('Name:');
panelDiv.appendChild(outlineTitle);
// Outline name input field
const inputName = document.createElement('input');
inputName.id = 'inputName';
inputName.type = 'text';
inputName.value = UIElements.outlineName;
inputName.addEventListener("input", () => UIElements.outlineName = inputName.value);
inputName.addEventListener("keydown", () => UIElements.outlineName = inputName.value);
panelDiv.appendChild(document.createElement('p'));
panelDiv.appendChild(inputName);
// Outline destination page label
let outlineDestination = document.createTextNode('Destination page:');
panelDiv.appendChild(document.createElement('p'));
panelDiv.appendChild(outlineDestination);
// Outline destination page input field
const pageCount = instance.Core.documentViewer.getPageCount();
const inputDestination = document.createElement('input');
inputDestination.id = 'inputDestination';
inputDestination.type = 'number';
inputDestination.min = '1';
inputDestination.max = pageCount.toString();
inputDestination.step = '1';
inputDestination.value = UIElements.pageNumber.toString();
inputDestination.addEventListener("input", () => {
UIElements.inputFieldLimitations(inputDestination);
UIElements.pageNumber = parseInt(inputDestination.value);
});
inputDestination.addEventListener("keydown", () => {
UIElements.inputFieldLimitations(inputDestination);
UIElements.pageNumber = parseInt(inputDestination.value);
});
panelDiv.appendChild(document.createElement('p'));
panelDiv.appendChild(inputDestination);
// Full page checkbox
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.checked = true;
checkbox.onchange = () => {
const inputX = UIElements.tableOfContentsPanel.render.querySelector('#inputX');
const inputY = UIElements.tableOfContentsPanel.render.querySelector('#inputY');
if (checkbox.checked) {
setDefaultCoordinates(instance);
inputX.value = UIElements.xCoordinate.toString();
inputY.value = UIElements.yCoordinate.toString();
}
inputX.disabled = checkbox.checked;
inputY.disabled = checkbox.checked;
};
// Full page checkbox label
const label = document.createElement('label');
label.textContent = 'Full page';
panelDiv.appendChild(document.createElement('p'));
panelDiv.appendChild(checkbox);
panelDiv.appendChild(label);
// X and Y coordinate input fields
UIElements.createCoordinateInput(panelDiv, 'X');
UIElements.createCoordinateInput(panelDiv, 'Y');
// Add Outline button
let button = document.createElement("button");
button.textContent = 'Add New Outline';
button.style.backgroundColor = 'blue';
button.style.color = 'white';
button.style.border = 'none';
button.style.padding = '10px 15px';
button.style.borderRadius = '12px';
button.onmouseover = () => button.style.opacity = '0.8';
button.onmouseout = () => button.style.opacity = '1.0';
button.style.cursor = 'pointer';
button.onclick = () => {
addOutline(instance);
button.style.opacity = '1.0';
};
panelDiv.appendChild(document.createElement('p'));
panelDiv.appendChild(button);
return panelDiv;
};
// Create coordinate input fields
static createCoordinateInput(panelDiv, axis) {
// Coordinate title field
let title = document.createTextNode(`${axis} Coordinate:`);
title.id = `title${axis.toUpperCase()}`;
panelDiv.appendChild(document.createElement('p'));
panelDiv.appendChild(title);
// Coordinate input field
const input = document.createElement('input');
input.id = `input${axis.toUpperCase()}`;
input.type = 'number';
input.min = '0';
input.disabled = true;
input.value = axis === 'X' ? UIElements.xCoordinate.toString() : UIElements.yCoordinate.toString();
input.addEventListener("input", () => {
UIElements.inputFieldLimitations(input);
axis === 'X' ? UIElements.xCoordinate = parseInt(input.value) : UIElements.yCoordinate = parseInt(input.value);
});
input.addEventListener("keydown", () => {
UIElements.inputFieldLimitations(input);
axis === 'X' ? UIElements.xCoordinate = parseInt(input.value) : UIElements.yCoordinate = parseInt(input.value);
});
panelDiv.appendChild(document.createElement('p'));
panelDiv.appendChild(input);
}
// Limitations for input fields.
// Ensures values are non-negative and within min/max range.
// Used for page number and coordinate input fields.
static inputFieldLimitations = (element) => {
element.value = Math.abs(parseInt(element.value));
const min = parseInt(element.min);
const max = parseInt(element.max);
if (element.value > max)
element.value = max;
else if (element.value < min)
element.value = min;
};
}