Some test text!

Search
Hamburger Icon

Web / Guides / UI Import and Export

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:

Import a Modular UI

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.

modular-ui-import-error

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.

Adding a Function Map

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);
Importing with a Function Map
The function map should always be reimported along with its associated configuration.

Export a Modular UI

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();
Exporting Functions
Since functions can't be stored in JSON, only the function key will be exported. Read more about creating and maintaining your function map here.

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.

modular-ui-export-console

The function can then be copied and added to the function map when importing the configuration.

Modular UI Configuration Structure

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:

modular-ui-export-console

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

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. Refer to the list of item types here.

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

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

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.

modular-ui-flyout
{
  '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']
    }
  }
}
Default Navigation
Chevrons to denote that an item has children are added automatically. Back buttons are also added automatically to help with Flyout navigation.

Panels

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 .

Custom Panels
Currently, Custom Panels cannot be imported and exported, but they can still be added to a UI using the addPanel API

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 .

modular-ui-json-tab-panel
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);
});

Examples

Basic Modular UI Example

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.WebComponent(
  {
    path: '/path/to/your/webviewer',
    initialDoc: '/path/to/your/document.pdf',
    ui: 'beta' // enable Modular UI
  },
  viewerElement
).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);
});

Working with the Default UI

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.

Webviewer.WebComponent(
  {
    path: '/path/to/your/webviewer',
    initialDoc: '/path/to/your/document.pdf',
    ui: 'beta' // enable Modular UI
    enableFilePicker: true,
  },
  viewerElement
).then((instance) => {
  const configUI = {
    'modularComponents': {
      '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': [
          'divider-0.4011225832731946',
          'stylePanelToggle',
          'divider-0.5730925860609144',
          'undoButton',
          'redoButton',
          'eraserToolButton'
        ],
        'type': 'groupedItems',
        'grow': 0,
        'gap': 12,
        'alwaysVisible': false,
        'style': {}
      },
      'annotateGroupedItems': {
        'dataElement': 'annotateGroupedItems',
        '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',
        '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': {},
        'items': [
          'menu-toggle-button',
          'zoom-container',
          'default-ribbon-group',
          'searchPanelToggle',
          'notesPanelToggle',
          'page-controls-container'
        ]
      },
      'tools-header': {
        'dataElement': 'tools-header',
        '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': {
      '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': [
          'filePickerButton',
          'divider',
          'saveAsButton'
        ]
      }
    }
  };

  instance.UI.importModularComponents(configUI);
});

Have questions? Connect with our experts on Discord.