# # UI fragments

Fragment 是一段 UI 代码段，用来在 UI template 中插入、删除和修改组件。其适用于在基于内置模板的基础上进行少量的 UI 自定义。

如果需要大量的自定义布局和设备自适应，请参考 Appearance 和 layout template 中描述的方法。

## # 简单示例

以下的代码是使用 fragment 配置从 mobile 和 desktop/tablet 布局中移除 `comment-tab` 组件。点击 `run` 运行示例，您可以使用 Chrome DevTool 的 device mode 来模拟 mobile/tablet 的效果。

    <script>
        var CustomAppearance= UIExtension.appearances.AdaptiveAppearance.extend({
            getDefaultFragments: function() {
                var isMobile = PDFViewCtrl.DeviceInfo.isMobile;
                if(isMobile) {
                    // mobile 设备端的 Fragment 配资。
                    return [{
                        target: 'comment-tab',
                        action: 'remove'
                    },{
                        target: 'comment-tab-li',
                        action: 'remove'
                    }, {
                        target: 'comment-tab-body',
                        action: 'remove'
                    }];
                } else {
                    // desktop/tablet 设备端的 Fragment 配资。
                    return [{
                        target: 'comment-tab',
                        action: 'remove'
                    }, {
                        target: 'fv--comment-tab-paddle',
                        action: 'remove'
                    }, {
                        target: 'hand-tool',
                        config: {
                            callback: {
                                around: function(callback, args) {
                                    try{
                                        console.info('before callback');
                                        var ret;
                                        if(callback instanceof UIExtension.Controller) {
                                            ret = callback.handle(...args);
                                        } else {
                                            ret = callback.apply(this, args);
                                        }
                                        console.info('after callback');
                                        return ret;
                                    }catch(e) {
                                        console.error(e, 'an error occurred');
                                    } finally {
                                        console.info('');
                                    }
                                }
                            }
                        },
                    }];
                }
            }
        });
        var libPath = window.top.location.origin + '/lib';

        var pdfui = new UIExtension.PDFUI({
                viewerOptions: {
                    libPath: libPath,
                    jr: {
                        licenseSN: licenseSN,
                        licenseKey: licenseKey
                    }
                },
                renderTo: document.body,
                // 根据设备类型提供不同的 appearance。
                appearance: CustomAppearance,
                addons: []
        });
    </script>

    {
        "iframeOptions": {
            "style": "height: 500px",
            "injectCss": [
                "/lib/UIExtension.vw.css"
            ]
        }
    }

## # Fragment 配置参数描述

- `target`: 控件的名称， 每个控件的名称都是唯一的。
- `action`: 表示这个片段的作用方式，默认为 `UIExtension.UIConsts.FRAGMENT_ACTION.EXT`。具体说明如下：
  - `UIExtension.UIConsts.FRAGMENT_ACTION.EXT`: 扩展目标控件。
  - `UIExtension.UIConsts.FRAGMENT_ACTION.BEFORE`: 在目标控件前插入控件。
  - `UIExtension.UIConsts.FRAGMENT_ACTION.AFTER`: 在目标控件后插入控件。
  - `UIExtension.UIConsts.FRAGMENT_ACTION.APPEND`: 将控件插入到目标控件，要求目标控件必须是容器类型的。
  - `UIExtension.UIConsts.FRAGMENT_ACTION.FILL`: 将目标控件的子空间全部清空并用新控件覆盖，要求目标控件必须是容器类型的。
  - `UIExtension.UIConsts.FRAGMENT_ACTION.REPLACE`: 将目标控件替换为新控件。
  - `UIExtension.UIConsts.FRAGMENT_ACTION.REMOVE`: 删除目标控件。
- `template`: 控件模版。其内容为 XML 格式，action 为 BEFORE/AFTER/APPEND/FILL/REPLACE。
- `config`: 控件配置对象，action 为 REMOVE 时无效。

  - `config.target`: 模板中控件的名称，只有当 action 为 BEFORE/AFTER/APPEND/FILL/REPLACE 时需要用到。

  - `config.attrs`: 设置控件的 html 属性。

  - `config.callback`: 控件的业务逻辑实现， 有以下三种写法：

    - function: 控件的事件会调用到这个函数，而且会覆盖内置回调。支持 function 的基础组件只有 (xbutton、dropdown-button 和context-menu-item) 。如果要基于内置回调添加功能，可以用第二种方法。

    - controller class: Controller 类可以监听组件生命周期以及处理更多的组件事件：

          {
              target: 'hand-tool',
              config: {
                  callback: class extends UIExtension.Controller {
                      mounted() {
                          super.mounted();
                          this.component.element.addEventListener('hover', e => {
                              console.info('mouse over', this.component)
                          })
                      }
                      handle() {
                          console.info('hand-tool clicked')
                      }
                  }
              }
          }

    - decorator object: 包含了一系列用于拦截 controller handle 方法执行的函数钩子，包括 before, after, thrown 以及 around。

          {
              target: 'hand-tool',
              config: {
                  callback: {
                      before: function() {
                          // 在controller handle 方法调用之前执行的函数， 它可以接收到 handle 函数的所有参数。
                      },
                      after: function(returnValue) {
                          // 在 controller handle 方法调用之后执行的函数，它可以接收到 handle 函数返回值以及调用参数。
                      },
                      thrown: function(error) {
                          // 在 controller handle 方法抛出异常之后执行的函数，它可以接收到异常对象以及调用参数。
                      },
                      around: function(callback, args) {
                          // 接收 controller handle 方法引用以及调用参数，在 around 回调中，可以对 handle 方法运行前后以及在捕获异常的代码块中执行代码。并且还可以决定是否执行 handle 方法。

                          try{
                              console.info('before callback');
                              var ret;
                              if(callback instanceof UIExtension.Controller) {
                                  ret = callback.handle(...args);
                              } else {
                                  ret = callback.apply(this, args);
                              }
                              console.info('after callback');
                              return ret;
                          }catch(e) {
                              console.error(e, 'an error occurred');
                          } finally {
                              console.info('');
                          }
                      }
                  }
              }
          }

## # 注意事项

> 建议 fragment 仅用于对 UI 进行微调。如果对内置布局修改比较大，请参阅 Appearance 和 layout template 小节中描述的方法。