# # 自定义UI

从 7.0 版本开始，Foxit PDF SDK for Web 提供了包含功能模块的内置 UI 框架，该 UI 框架是通过使用 Foxit PDF SDK for Web 来实现的，并且封装在 UIExtension.js 中。此外，自定义 UI 也非常简单。Foxit PDF SDK for Web 为开发人员提供了一组丰富的 API，用来自定义 web viewer 的外观和风格。

UIExtension 的界面由两部分组成：template 和 fragments。Template 相当于是对 HTML 的扩展，在模板中的控件需要通过标签来声明。Template 用来自定义无需交互的 UI 布局 (css 样式，图标，文本等) 。Fragments 是一组 UI snippet，用来自定义模板中的控件的配置项和交互逻辑。每一个 snippet 都有一个操作类型 "aciton"，其用来指定 snippet 的作用方式 (append, prepend, before, after, ext, replace, insert, 和 remove，默认是 ext)。通过这些作用方式，您可以向 template 中插入、删除、替换和修改控件。

## # 使用 template 自定义UI布局

Template 主要用来自定义控件的 UI 布局。以下示例将介绍 template 的使用方法。所有的示例代码都是基于集成章节的 集成功能完整的webViewer 小节.

### # 创建一个简单的 template

一个最简单的模板，如下所示：

    var pdfui = new UIExtension.PDFUI({
        viewerOptions: {
            libPath: './lib', // the library path of web sdk.
            jr: {
                licenseSN: licenseSN,
                licenseKey: licenseKey
            }
        },
        renderTo: '#pdf-ui', // the div (id="pdf-ui").
        appearance: UIExtension.appearances.Appearance.extend({
            getLayoutTemplate: function() {
                return [
                '<webpdf>',
                '    <viewer></viewer>',
                '</webpdf>'
                ].join('');
            }
        })
    });

- `<webpdf>` 标签会监听文档打开和关闭事件，触发 Appearance 对象的 enableAll 和 disableAll 方法来启用/禁用界面上相关的组件。
- `<viewer>` 标签是用于渲染 PDF 内容的地方。每个 template 都必须含有一个 `<viewer>` 标签。该标签可以放置在任何位置，请参考后面的示例代码。

刷新浏览器 (http://127.0.0.1:8080/index.html
(opens new window))，然后您可以看到一个简单的不带任何 UI 组件的 web PDF viewer。

### # 添加一个工具栏

使用 `<toolbar>` 标签添加一个新的工具栏按钮。

    var pdfui = new UIExtension.PDFUI({
        viewerOptions: {
            libPath: './lib', // the library path of web sdk.
            jr: {
                licenseSN: licenseSN,
                licenseKey: licenseKey
            }
        },
        renderTo: '#pdf-ui', // the div (id="pdf-ui").
        appearance: UIExtension.appearances.Appearance.extend({
            getLayoutTemplate: function() {
                return [
                    '<webpdf>',
                    '    <toolbar>',
                    '        <open-file-dropdown></open-file-dropdown>',
                    '    </toolbar>',
                    '    <viewer></viewer>',
                    '</webpdf>'
                ].join('')
            }
        })
    });

刷新浏览器 (http://127.0.0.1:8080/index.html
(opens new window)), 然后您可以看到新加的工具栏按钮。

### # 添加一个标签页

使用`<tabs>` 和 `<tab>` 标签来添加一个新的标签页。

    var CustomAppearance = UIExtension.appearances.Appearance.extend({
        getLayoutTemplate: function() {
            return [
                '<webpdf>',
                '    <toolbar>',
                '        <gtab group="custom-tabs" text="Home" body="home-tab-body">',
                '        </gtab>',
                '        <gtab group="custom-tabs" text="Comment" body="comment-tab-body">',
                '        </gtab>',
                '    </toolbar>',
                '   <div class="tab-bodies">',
                '       <div name="home-tab-body">',
                '            <open-file-dropdown></open-file-dropdown>',
                '       </div>',
                '       <div name="comment-tab-body" class="flex-row">',
                '           <create-strikeout-button></create-strikeout-button>',
                '           <create-underline-button></create-underline-button>',
                '           <create-squiggly-button></create-squiggly-button>',
                '           <create-replace-button></create-replace-button>',
                '           <create-caret-button></create-caret-button>',
                '           <create-note-button></create-note-button>',
                '       </div>',
                '   </div>',
                '    <viewer></viewer>',
                '</webpdf>'
            ].join('')
        }
    })
    var pdfui = new UIExtension.PDFUI({
        viewerOptions: {
            libPath: './lib', // the library path of web sdk.
            jr: {
                licenseSN: licenseSN,
                licenseKey: licenseKey
            }
        },
        renderTo: '#pdf-ui', // the div (id="pdf-ui").
        appearance: CustomAppearance
    });

刷新浏览器 (http://127.0.0.1:8080/index.html
(opens new window)), 然后您可以看到新添加的标签页。

### # 添加一个侧边栏

使用 `<sidebar>` 标签来添加一个新的侧边栏按钮。

    var CustomAppearance = UIExtension.appearances.Appearance.extend({
        getLayoutTemplate: function() {
            return [
                '<webpdf>',
                '    <toolbar>',
                '        <gtab group="custom-tabs" text="Home" body="home-tab-body">',
                '        </gtab>',
                '        <gtab group="custom-tabs" text="Comment" body="comment-tab-body">',
                '        </gtab>',
                '    </toolbar>',
                '   <div class="tab-bodies">',
                '       <div name="home-tab-body">',
                '            <open-file-dropdown></open-file-dropdown>',
                '       </div>',
                '       <div name="comment-tab-body" class="flex-row">',
                '           <create-strikeout-button></create-strikeout-button>',
                '           <create-underline-button></create-underline-button>',
                '           <create-squiggly-button></create-squiggly-button>',
                '           <create-replace-button></create-replace-button>',
                '           <create-caret-button></create-caret-button>',
                '           <create-note-button></create-note-button>',
                '       </div>',
                '   </div>',
                '    <div class="flex-row">',
                '        <sidebar>',
                '            <bookmark-sidebar-panel></bookmark-sidebar-panel>',
                '        </sidebar>',
                '        <viewer></viewer>',
                '    </div>',
                '</webpdf>'
            ].join('')
        }
    })
    var pdfui = new UIExtension.PDFUI({
        viewerOptions: {
            libPath: './lib', // the library path of web sdk.
            jr: {
                licenseSN: licenseSN,
                licenseKey: licenseKey
            }
        },
        renderTo: '#pdf-ui', // the div (id="pdf-ui").
        appearance: CustomAppearance
    });

刷新浏览器 (http://127.0.0.1:8080/index.html
(opens new window)), 然后您可以看到新添加的书签侧边栏。

### # 内置布局模板

内置布局模板位于 \examples\UIExtension\layout_templates 目录下。桌面端请参考 built-in-pc-layout-template.tpl 文件，移动端请参考 built-in-mobile-layout-template.tpl文件。您可以根据需要直接修改模板来自定义 UI。

在 \examples\UIExtension\custom_appearance 目录下，Foxit PDF SDK for Web 提供了两个自定义模板示例。 " adaptive-to-the-device.html" 能自适应设备 (桌面端和移动端) ，" not-adaptive-to-the-device.html" 不能自适应设备，仅支持桌面端布局。

## # 使用 fragments 自定义 UI

Fragments 是一组 UI snippet，用来自定义模板中的控件的配置项和交互逻辑。

### # 创建一个下拉菜单

下面的示例代码创建一个包含两个 drop-down 按钮的下拉菜单, 然后将其加入到 "home-tab-group-hand" 组件的子组件列表的末尾。

如何获取目标组件的名称 (只针对 widget 部件)，您可以在浏览器中右键点击该组件，选择 "审查"， 然后在对应的 `<a>` 标签中找到 "component-name" 属性的值。对于容器组件，比如`target:"home-tab-group-hand"`, 您可以在浏览器中右键点击其中一个子组件，选择 "审查"，然后在相关的 `<div>` 标签中找到 "component-name" 属性的值。

    const customModule = UIExtension.PDFUI.module('custom', []);
    customModule.controller('CustomController', {
        mounted: function () {
            console.info(this.component, 'mounted');
        },
        handle: function (selectedFile) {
            alert(selectedFile.name);
        }
    });
    var CustomController = customModule.getControllerClass('CustomController');

    var CustomAppearance = UIExtension.appearances.RibbonAppearance.extend({
        getDefaultFragments: function() {
            return [{
                // Add a component to the end of the list of children of a specified target component.
                action: UIExtension.UIConsts.FRAGMENT_ACTION.APPEND,
                // Specify the name of the target component that the new components defined in the above template will be appended to. All the target names of fragments are defined in the layout template.
                target: 'home-tab-group-hand',
                // Define the properties of the added component, such as icon, text, and css style.
                template: [
                    '<dropdown icon-class="fv__icon-toolbar-stamp">',
                    '    <dropdown-button name="show-hello-button" icon-class="fv__icon-toolbar-hand">say hello</dropdown-button>',
                    '    <dropdown-button name="select-pdf-file-button" accept=".pdf" file-selector icon-class="fv__icon-toolbar-open">open</dropdown-button>',
                    '</dropdown>'
                ].join(''),
                // Define the interaction logic of the added component.
                config: [{
                    // specify the component in the above template that the configuration will be applied to.
                    // For example, the configuration will be applied to the component with the name of "show-hello-button".
                    target: 'show-hello-button',
                    callback: function () {
                        alert('hello');
                    }
                },
                {
                    // The configuration will be applied to the component with the name of "select-pdf-file-button" which is defined in the above template of fragments.
                    target: 'select-pdf-file-button',
                    // Extend Controller, and implement the handle function.
                    callback: CustomController
                }]
            }]
        }
    })

    var pdfui = new UIExtension.PDFUI({
        viewerOptions: {
            libPath: './lib', // the library path of web sdk.
            jr: {
                licenseSN: licenseSN,
                licenseKey: licenseKey
            }
        },
        renderTo: '#pdf-ui', // the div (id="pdf-ui").
        appearance: CustomAppearance
    });

刷新浏览器 (http://127.0.0.1:8080/index.html
(opens new window)), 然后您可以看到新添加的下拉菜单。

### # 删除已有工具栏按钮

通过 fragment 删除一个工具栏按钮是非常简单的。比如，删除手型工具，您只需要向 fragment 中添加一个新的对象。基于以上示例，添加如下的代码：

    {
        target: 'hand-tool',
        action: UIExtension.UIConsts.FRAGMENT_ACTION.REMOVE
    }

刷新浏览器 (http://127.0.0.1:8080/index.html
(opens new window)), 然后您可以看到手型工具已被移除。如下所示：

### # 修改已有工具栏按钮

通过 fragment 修改一个工具栏按钮也是非常简单的。类似 删除已有工具栏控件 章节，您只需要向 fragment 中添加一个新的对象。

#### # 修改按钮的图标

例如，修改手型工具的图标，只需要添加如下的代码

    {
        target: 'hand-tool',
        config: {
            iconCls: 'fv__icon-toolbar-note' // your custom icon.
        }
    }

刷新浏览器 (http://127.0.0.1:8080/index.html
(opens new window)), 然后您可以看到手型工具的图标已被修改。

#### # 修改按钮的工具提示信息

例如，修改手型工具的工具提示信息，只需要添加如下的代码：

    {
        target: 'hand-tool',
        config: {
            tooltip: {
                title: 'your custom tooltip'
            }
        }
    }

刷新浏览器 (http://127.0.0.1:8080/index.html
(opens new window)), 然后您可以看到手型工具的工具提示信息已被修改为 "your custom tooltip"。

#### # 修改按钮的事件

有两种方法用来修改按钮的事件：

1. 覆盖内置的事件。比如：

    {
        target: 'hand-tool',
        config: {
            callback: function() {
                alert('your click event handler');
            }
        }
    }

2. 基于原有内置事件的逻辑基础上，添加自定义行为。可以使用如下的方式进行配置：

    {
        target: 'hand-tool',
        config: {
            callback: {
                before: function (handleMethodArguments ) {
                    console.info('called before handle callback with arguments: ', handleMethodArguments);
                },
                after: function (value, handleMethodArguments ) {
                    console.info('called after handle callback with returning value and arguments: ', value, handleMethodArguments);
                }
            }
        }

#### # 其它示例

有关更多示例代码，请参阅 \examples\UIExtension\fragment_usage 目录下的示例。

## # 模块化

为了区分内置组件和用户自定义组件以避免冲突，UIExtension 提供了模块化功能，可以将组件分别注册在不同的模块中。然后，在 template 中声明组件时加上模块名前缀。

### # 创建自定义模块

如果您需要定义和使用和内置组件同名的自定义组件，您可以首先创建一个自定义模块，然后在该模块中注册自定义组件。

例如，在内置组件中已经存在一个名为 "dropdown" 的组件，如果您需要定义一个同名的自定义组件，您可以参考以下的示例:

创建一个新的模块 "my-widgets"，然后在该模块中注册一个自定义组件：

    // Create a new module. Please note that the second parameter must be an array if you create a new module.
    var module = UIExtension.modular.module('my-widgets', []);

    function UserDefinedDropdownComponent() {
        UIExtension.Component.apply(this, arguments);
    }
    UserDefinedDropdownComponent.getName = function() {
       return 'dropdown'; // Declare the tag name of the component. There is already an existing component with the same name of 'dropdown' in the built-in component.
    }
    UserDefinedDropdownComponent.prototype.constructor = UIExtension.Component;
    UserDefinedDropdownComponent.prototype.render = function() {
    UIExtension.Component.prototype.render.call(this);
           this.element.innerText = 'User-defined dropdown component';
    }
    module.registerComponent(UserDefinedDropdownComponent);

然后，内置 dropdown 组件和自定义 dropdown 组件可以使用如下的方式进行区别：

    <!-- built-in dropdown -->
    <dropdown></dropdown>
    <!-- user-defined dropdown -->
    <my-widgets:dropdown></my-widgets:dropdown>

例如，使用自定义 dropdown 组件：

    var pdfui = new UIExtension.PDFUI({
        // Omit other parameters.
        appearance: UIExtension.appearances.RibbonAppearance.extend({
            getDefautFragments: function() {
                return [{
                    action: UIExtension.UIConsts.FRAGMENT_ACTION.APPEND,
                    target: 'home-tab-group-hand',
                    template: '<my-widgets:dropdown></my-widgets:dropdown>' // use a colon to separate the module name and component name in the template.
                }];
            }
        })
    });