Some test text!
Web / Guides / UI Import and Export
With the WebViewer Modular UI, it is possible to import and export an entire UI configuration using JSON.
This provides a simple and efficient way to customize the Webviewer UI whether starting from the out of the box UI or assembling something new using modular components. A configuration can be saved as a separate file or worked on as an object to make customization and editing easier with all the different components available in one place.
A configuration can contain:
To use a Modular UI configuration, it needs to be imported into WebViewer. The configuration can be imported using the UI.importModularComponents
method. The method takes a configuration JSON object as a parameter along with an optional function map.
instance.UI.importModularComponents(configUI);
The import method will validate the configuration to ensure that it is correctly formatted and that all Modular Components, Modular Headers, Flyouts, and Panels have their necessary properties. If any errors are found, an error message will be printed in the console and the import operation will be aborted.
In the example above, there is an error thrown which shows that a component menu-toggle-button
has an invalid key type
and explains which type
properties are valid.
Functions cannot be imported and exported via JSON but a function map can be used to define functions like onClick
on components such as Custom Buttons. The function map is kept separately from the Modular UI configuration. It needs to contain all functions that are used by custom components as they cannot be exported along with the other components in a JSON format.
The function map should contain one object with keys to denote the name of the function and the value as the function itself. As shown below:
const functionMap = {
'alertClick': () => alert('Alert triggered!'),
'flyoutSecondButtonOnClick': () => {
console.log('Second Item clicked!');
},
};
You can then refer to functions in the function map by their key in your modularComponents. For example, the alertClick
function key can be added to a Custom Button as shown below:
{
"modularComponents": {
"myButton": {
"type": "customButton",
"dataElement": "myButton",
"label": "My Button",
"onClick": "alertClick"
},
"flyoutFirstButton": {
"type": "customButton",
"dataElement": "flyoutFirstButton",
"label": "Flyout First Button",
"children": ["flyoutSecondButton"]
},
"flyoutSecondButton": {
"type": "customButton",
"dataElement": "flyoutSecondButton",
"label": "Flyout Second Button",
"onClick": "flyoutSecondButtonOnClick"
}
}
}
The function map can be added by importing it along with the UI configuration.
instance.UI.importModularComponents(configUI, functionMap);
A Modular UI configuration can be exported from WebViewer using the UI.exportModularComponents
method. The method will return a JSON object containing all the Modular Components, Headers, Flyouts, and Panels that have been added to the UI.
instance.UI.exportModularComponents();
When exporting a Modular UI configuration, a JSON object will be returned. The JSON object can then be copied or saved to a file for viewing and editing.
Components are validated upon export and must include a data element to be exported successfully. Any validation warnings will be printed to the console.
If there are any functions that are referred to by components in the Modular UI and they are not included in a function map, they will be printed in a console warning.
The function can then be copied and added to the function map when importing the configuration.
A Modular UI configuration is a JSON object that contains all the components that are used to build a UI. The configuration can contain the following properties:
The above properties can be reordered in any way that is convenient for the user. Each property can contain multiple objects with each object representing a component that is used in the UI.
To define each component and its properties within the top-level objects of the configuration structure, they should be given an object key which should also be the dataElement
of the component. The dataElement
is a unique identifier used to refer to the component in the UI.
{
"modularComponents": {
"myButton": {
dataElement: "myButton",
}
}
}
Modular Components can be added to a UI configuration by adding a modularComponents
property with all components as child objects. Modular Components refer to any items or containers that can be added to a Modular Header or a Flyout. A valid Modular Component in the configuration needs to include a dataElement
property (or object key) that is unique to the component.
Modular components also need to contain a type
property so that WebViewer knows which kind of component it needs to create. The type
property can be any of the item types normally added to a Modular Header or Flyout. More detail in the list of item types - api definitions.
To add components to the UI, their object keys (dataElement
) need to be included in the items
property of a Modular Header or Flyout. Since Flyouts support a nested structure, items
can also be included in the children
property to create a nested Flyout.
Modular Components should be added to the configuration as shown below. In this example, a Button component is added to the configuration with a dataElement
of myButton
and a label
of My Button
. It also calls an onClick
function of alertClick
when clicked. The logic for alertClick
is stored in the function map and only referenced in the configuration JSON.
{
"modularComponents": {
"myButton": {
"type": "customButton",
"dataElement": "myButton",
"label": "My Button",
"onClick": "alertClick"
}
}
}
For more information on items which can be used in Modular Components, refer to the Items and Containers documentation.
Modular Headers can be easily created by adding a modularHeaders
property with each header listed as a child object to your UI configuration.
Each header can have an items
property which should contain an array of dataElements of the components to be added to the header. The items contained in a header all need to be valid and included in the modularComponents
section of the modular UI configuration in order to be used.
There also needs to be a placement
property that specifies where the header should be placed in the UI. The placement
property can be any of the following values: top
, left
, right
, or bottom
.
Modular headers can also include any of the container properties used to customize how they appear in your UI.
Modular Headers should be added to the configuration as shown below. In this example, a Modular Header is added to the configuration JSON with a dataElement
of myHeader
and a placement
of top
. The items
property contains an array with the dataElement
of the Button component that was added earlier.
{
"modularHeaders": {
"myHeader": {
"dataElement": "myHeader",
"placement": "top",
"items": ["flyoutToggle", "myButton", "searchPanelToggle"]
}
}
}
For more information on Modular Headers, refer to the Modular Headers documentation.
Flyouts are menus which are toggled by a component (ie. a Toggle Element Button ) and can be added to the JSON by adding a flyouts
property.
Flyouts can contain items similarly to headers but in the case of a Flyout, the items can also be used to create submenus when they have a children
property which contains other items. The items
property should contain an array of dataElements
of the components to be added to the first level of the Flyout.
The example below shows how to create a simple Modular UI configuration which includes a header that contains a Toggle Element Button to open a Flyout. The Flyout contains a Custom Button which has a children
property that contains another Custom Button which is opened as a submenu.
{
"modularComponents": {
"flyoutToggle": {
"type": "toggleButton",
"img": "ic-hamburger-menu",
"dataElement": "flyoutToggle",
"toggleElement": "myFlyout"
},
"flyoutFirstButton": {
"type": "customButton",
"dataElement": "flyoutFirstButton",
"label": "Flyout First Button",
"children": ["flyoutSecondButton"]
},
"flyoutSecondButton": {
"type": "customButton",
"dataElement": "flyoutSecondButton",
"label": "Flyout Second Button",
"onClick": "flyoutSecondButtonOnClick"
},
},
"modularHeaders": {
"myHeader": {
"dataElement": "myHeader",
"placement": "top",
"items": ["flyoutToggle"]
}
},
"flyouts": {
"myFlyout": {
"dataElement": "myFlyout",
"items": ["flyoutFirstButton"]
}
}
}
Panels can be included by adding a panels
property with each panel listed as a child object in the configuration.
Panels need to include a location
property that specifies where the panel should be placed in the UI. The location
property can be either left
or right
.
They must also include a render
property that specifies which Panel to render. The render
property can include a prebuilt panel name or it can be tabPanel
which allows you to create a Tabbed Panel .
The following example shows how to add a panel that is rendered as a Search Panel to a Modular UI configuration.
{
"panels": {
"myPanel": {
"dataElement": "myPanel",
"location": "left",
"render": "searchPanel"
}
}
}
The next example shows how to create a Modular UI configuration that includes a Tabbed Panel similar to the one in the Default UI .
Webviewer.WebComponent(
{
path: '/path/to/your/webviewer',
initialDoc: '/path/to/your/document.pdf',
ui: 'beta' // enable Modular UI
},
viewerElement
).then((instance) => {
const configUI = {
"modularComponents": {
"left-panel-toggle": {
"dataElement": "left-panel-toggle",
"title": "Left Panel",
"type": "toggleButton",
"img": "icon-header-sidebar-line",
"toggleElement": "customLeftPanel"
}
},
"modularHeaders": {
"default-top-header": {
"dataElement": "default-top-header",
"placement": "top",
"items": [
"left-panel-toggle"
]
}
},
"panels": {
"customLeftPanel": {
"render": "tabPanel",
"dataElement": "customLeftPanel",
"panelsList": [
{
"render": "thumbnailPanel"
},
{
"render": "outlinesPanel"
},
{
"render": "bookmarkPanel"
},
{
"render": "layersPanel"
},
{
"render": "signaturePanel"
},
{
"render": "fileAttachmentPanel"
},
{
"render": "portfolioPanel"
}
],
"location": "left"
},
"thumbnailPanel": {
"dataElement": "thumbnailPanel",
"render": "thumbnailsPanel",
"location": "left"
},
"outlinesPanel": {
"dataElement": "outlinesPanel",
"render": "outlinesPanel",
"location": "left"
},
"bookmarkPanel": {
"dataElement": "bookmarkPanel",
"render": "bookmarksPanel",
"location": "left"
},
"layersPanel": {
"dataElement": "layersPanel",
"render": "layersPanel",
"location": "left"
},
"fileAttachmentPanel": {
"dataElement": "fileAttachmentPanel",
"render": "fileAttachmentPanel",
"location": "left"
},
"signaturePanel": {
"dataElement": "signaturePanel",
"render": "signaturePanel",
"location": "left"
},
"portfolioPanel": {
"dataElement": "portfolioPanel",
"render": "portfolioPanel",
"location": "left"
}
}
};
instance.UI.importModularComponents(configUI);
});
The following example shows how to create a basic Modular UI configuration. It includes a Custom Button, a Custom Header, a Panel, and a Flyout with a submenu. The configuration JSON object is then imported into WebViewer along with a function map that contains functions for the Custom Buttons.
WebViewer(
{
path: '/path/to/your/webviewer',
initialDoc: '/path/to/your/document.pdf',
},
// your viewer element
document.getElementById('viewer')
).then((instance) => {
const configUI = {
"modularComponents": {
"myButton": {
"type": "customButton",
"dataElement": "myButton",
"label": "My Button",
"onClick": "alertClick"
},
"flyoutToggle": {
"type": "toggleButton",
"img": "ic-hamburger-menu",
"dataElement": "flyoutToggle",
"toggleElement": "myFlyout"
},
"flyoutFirstButton": {
"type": "customButton",
"dataElement": "flyoutFirstButton",
"label": "Flyout First Button",
"children": [
"flyoutSecondButton"
]
},
"flyoutSecondButton": {
"type": "customButton",
"dataElement": "flyoutSecondButton",
"label": "Flyout Second Button",
"onClick": "flyoutSecondButtonOnClick"
},
"searchPanelToggle": {
"type": "toggleButton",
"img": "icon-header-search",
"dataElement": "searchPanelToggle",
"toggleElement": "myPanel"
}
},
"modularHeaders": {
"myHeader": {
"dataElement": "myHeader",
"placement": "top",
"items": [
"flyoutToggle",
"myButton",
"searchPanelToggle"
]
}
},
"panels": {
"myPanel": {
"dataElement": "myPanel",
"location": "left",
"render": "searchPanel"
}
},
"flyouts": {
"myFlyout": {
"dataElement": "myFlyout",
"items": [
"flyoutFirstButton"
]
}
}
};
const functionMap = {
'alertClick': () => alert('Alert triggered!'),
'flyoutSecondButtonOnClick': () => {
console.log('Second Item clicked!');
},
};
instance.UI.importModularComponents(configUI, functionMap);
});
The following example was build using an export of the Default UI as a base.
It has been customized by editing the default ribbon to only contain the View and Annotate ribbon items, and adding the page navigation buttons to the top header.
It also has some reordered tools in the Annotate ribbon group which will now open on the left side of the UI instead of at the top underneath the default header. The Style Panel’s location has changed to open on the right instead of the left as well.
Finally, the main menu flyout was also customized to include only the Open File and Save As buttons.
There is no need to include a function map in this example as no custom functions are used in the configuration.
See the comments in the code snippet below to understand how the configuration was built.
WebViewer(
{
path: '/path/to/your/webviewer',
initialDoc: '/path/to/your/document.pdf',
// add Open File button to the Main Menu
enableFilePicker: true,
},
// your viewer element
document.getElementById('viewer')
).then((instance) => {
const configUI = {
"modularComponents": {
// only include the buttons, grouped items, dividers,
// ribbon items, and ribbon groups that are needed here
"page-controls-container": {
"dataElement": "page-controls-container",
"type": "pageControls"
},
"filePickerButton": {
"dataElement": "filePickerButton",
"type": "presetButton"
},
"saveAsButton": {
"dataElement": "saveAsButton",
"type": "presetButton"
},
"menu-toggle-button": {
"dataElement": "menu-toggle-button",
"img": "ic-hamburger-menu",
"title": "component.menuOverlay",
"toggleElement": "MainMenuFlyout",
"type": "toggleButton"
},
"zoom-container": {
"dataElement": "zoom-container",
"type": "zoom"
},
"highlightToolButton": {
"dataElement": "highlightToolButton",
"type": "toolButton",
"toolName": "AnnotationCreateTextHighlight"
},
"underlineToolButton": {
"dataElement": "underlineToolButton",
"type": "toolButton",
"toolName": "AnnotationCreateTextUnderline"
},
"strikeoutToolButton": {
"dataElement": "strikeoutToolButton",
"type": "toolButton",
"toolName": "AnnotationCreateTextStrikeout"
},
"squigglyToolButton": {
"dataElement": "squigglyToolButton",
"type": "toolButton",
"toolName": "AnnotationCreateTextSquiggly"
},
"freeTextToolButton": {
"dataElement": "freeTextToolButton",
"type": "toolButton",
"toolName": "AnnotationCreateFreeText"
},
"rectangleToolButton": {
"dataElement": "rectangleToolButton",
"type": "toolButton",
"toolName": "AnnotationCreateRectangle"
},
"markInsertTextToolButton": {
"dataElement": "markInsertTextToolButton",
"type": "toolButton",
"toolName": "AnnotationCreateMarkInsertText"
},
"markReplaceTextToolButton": {
"dataElement": "markReplaceTextToolButton",
"type": "toolButton",
"toolName": "AnnotationCreateMarkReplaceText"
},
"freeHandToolButton": {
"dataElement": "freeHandToolButton",
"type": "toolButton",
"toolName": "AnnotationCreateFreeHand"
},
"freeHandHighlightToolButton": {
"dataElement": "freeHandHighlightToolButton",
"type": "toolButton",
"toolName": "AnnotationCreateFreeHandHighlight"
},
"stickyToolButton": {
"dataElement": "stickyToolButton",
"type": "toolButton",
"toolName": "AnnotationCreateSticky"
},
"divider-0.4011225832731946": {
"dataElement": "divider-0.4011225832731946",
"type": "divider"
},
"stylePanelToggle": {
"dataElement": "stylePanelToggle",
"title": "action.style",
"type": "toggleButton",
"img": "icon-style-panel-toggle",
"toggleElement": "stylePanel"
},
"divider-0.5730925860609144": {
"dataElement": "divider-0.5730925860609144",
"type": "divider"
},
"undoButton": {
"dataElement": "undoButton",
"type": "presetButton",
"buttonType": "undoButton"
},
"redoButton": {
"dataElement": "redoButton",
"type": "presetButton",
"buttonType": "redoButton"
},
"eraserToolButton": {
"dataElement": "eraserToolButton",
"type": "toolButton",
"toolName": "AnnotationEraserTool"
},
"defaultAnnotationUtilities": {
"dataElement": "defaultAnnotationUtilities",
// items here are defined above
"items": [
"divider-0.4011225832731946",
"stylePanelToggle",
"divider-0.5730925860609144",
"undoButton",
"redoButton",
"eraserToolButton"
],
"type": "groupedItems",
"grow": 0,
"gap": 12,
"alwaysVisible": false,
"style": {}
},
"annotateGroupedItems": {
"dataElement": "annotateGroupedItems",
// items here are defined above
"items": [
"underlineToolButton",
"highlightToolButton",
"rectangleToolButton",
"freeTextToolButton",
"freeHandToolButton",
"freeHandHighlightToolButton",
"stickyToolButton",
"squigglyToolButton",
"strikeoutToolButton",
"markInsertTextToolButton",
"markReplaceTextToolButton",
"defaultAnnotationUtilities"
],
"type": "groupedItems",
"justifyContent": "center",
"grow": 0,
"gap": 12,
"alwaysVisible": false,
"style": {}
},
"toolbarGroup-View": {
"dataElement": "toolbarGroup-View",
"title": "View",
"type": "ribbonItem",
"label": "View",
"groupedItems": [],
"toolbarGroup": "toolbarGroup-View"
},
"toolbarGroup-Annotate": {
"dataElement": "toolbarGroup-Annotate",
"title": "Annotate",
"type": "ribbonItem",
"label": "Annotate",
"groupedItems": [
"annotateGroupedItems"
],
"toolbarGroup": "toolbarGroup-Annotate"
},
"default-ribbon-group": {
"dataElement": "default-ribbon-group",
// include all the tool groups that you want in the ribbon here
"items": [
"toolbarGroup-View",
"toolbarGroup-Annotate"
],
"type": "ribbonGroup",
"justifyContent": "center",
"grow": 2,
"gap": 12,
"alwaysVisible": false,
"style": {}
},
"searchPanelToggle": {
"dataElement": "searchPanelToggle",
"title": "component.searchPanel",
"type": "toggleButton",
"img": "icon-header-search",
"toggleElement": "searchPanel"
},
"notesPanelToggle": {
"dataElement": "notesPanelToggle",
"title": "component.notesPanel",
"type": "toggleButton",
"img": "icon-header-chat-line",
"toggleElement": "notesPanel"
}
},
"modularHeaders": {
"default-top-header": {
"dataElement": "default-top-header",
"placement": "top",
"grow": 0,
"gap": 12,
"position": "start",
"float": false,
"stroke": true,
"dimension": {
"paddingTop": 8,
"paddingBottom": 8,
"borderWidth": 1
},
"style": {},
// include all items that you want in the top header here
"items": [
"menu-toggle-button",
"zoom-container",
"default-ribbon-group",
"searchPanelToggle",
"notesPanelToggle",
"page-controls-container"
]
},
"tools-header": {
"dataElement": "tools-header",
// choose where you want to place the secondary header here
"placement": "left",
"justifyContent": "start",
"grow": 0,
"gap": 12,
"position": "end",
"float": false,
"stroke": true,
"dimension": {
"paddingTop": 8,
"paddingBottom": 8,
"borderWidth": 1
},
"style": {},
"items": [
"annotateGroupedItems"
]
}
},
"panels": {
// placing all the panels on the right side
// since the left side is already occupied by the tools header
"stylePanel": {
"dataElement": "stylePanel",
"render": "stylePanel",
"location": "right"
},
"notesPanel": {
"dataElement": "notesPanel",
"render": "notesPanel",
"location": "right"
},
"searchPanel": {
"dataElement": "searchPanel",
"render": "searchPanel",
"location": "right"
}
},
"flyouts": {
"MainMenuFlyout": {
"dataElement": "MainMenuFlyout",
"items": [
// include the buttons that are needed in the Main Menu here
"filePickerButton",
"divider",
"saveAsButton"
]
}
}
};
instance.UI.importModularComponents(configUI);
});
Trial setup questions? Ask experts on Discord
Need other help? Contact Support
Pricing or product questions? Contact Sales