# # 自定义表单属性对话框

本章节将介绍如何替换默认表单属性对话框，并使用内置表单属性编辑组件构建新的属性对话框。我们还将展示如何利用 PDFFormProperty 创建一个新的表单属性编辑组件。

注意：

> 如果您的应用是基于 PDFViewCtrl 实现，而不是基于 UIExtension 实现，则本章内容不适用。

## # 替换默认表单属性对话框

在某些情况下，默认的表单属性对话框可能无法满足您的需求。例如，您可能需要添加自定义表单属性或修改现有属性的配置方式。

要替换默认表单属性对话框，可以通过以下两种方法实现。

### # 1. 使用自定义的对话框组件替换

这种替换方式适用于对话框组件是基于 UIExtension 框架实现的。您可以通过重载 `doShown` 方法来替换默认表单属性对话框。

注意：

> 对话框的 `name` 属性名称必须设定为 `fv--form-designer-widget-properties-dialog`，否则不会被识别。

#### # 步骤

- 创建自定义对话框: 根据您的需求，设计并实现一个自定义对话框组件。以下是一个简单的示例。

      class CustomDialogComponent extends UIExtension.SeniorComponentFactory.createSuperClass({
          template: `
              <layer @var.dialog="$component">
                  <layer-header
                      @draggable="{type: 'parent'}"
                      title="Widget Properties Dialog"
                  ></layer-header>
                  <div>
                      Field Name: @{dialog.properties.fieldName}
                  </div>
              </layer>
          `
      }) {
          static getName() {
              return 'widget-properties-dialog'
          }
          properties = {
              fieldName: ''
          }
          doShown() {
              this.pdfUI.getAllActivatedElements().then(([widgetComponent]) => {
                  if(widgetComponent.annot?.getType() === 'widget') {
                      this.properties.fieldName = widgetComponent.annot.getField().getName();
                  } else {
                      this.properties.fieldName = 'No Widget Selected';
                  }
                  this.digest();
              })
              super.doShown();
          }
      }

- 注册对话框到自定义模块中：

      const customFormDesignerModule = UIExtension.modular.module('custom-form-designer', []);
      customFormDesignerModule.registerComponent(CustomDialogComponent);

- 通过 UI Fragment 配置替换内置的对话框组件

      new UIExtension.PDFUI({
          appearance: UIExtension.appearances.adaptive.extend({
              getDefaultFragments() {
                  return [{
                      target: 'fv--form-designer-widget-properties-dialog',
                      action: 'replace',
                      template: `<custom-form-designer:widget-properties-dialog name="fv--form-designer-widget-properties-dialog"></custom-form-designer:widget-properties-dialog>`
                  }];
              }
          }),
          // ...其他配置
      })

#### # 完整示例

以下是完整的示例，请自行运行并打开一个带表单的 PDF 文档以查看效果。

    <script>
        const libPath = window.top.location.origin + '/lib';
        const FRAGMENT_ACTION = UIExtension.UIConsts.FRAGMENT_ACTION;

        const customFormDesignerModule = UIExtension.modular.module('custom-form-designer', []);

        customFormDesignerModule.registerComponent(
            class CustomDialogComponent extends UIExtension.SeniorComponentFactory.createSuperClass({
                template: `
                    <layer @var.dialog="$component">
                        <layer-header
                            @draggable="{type: 'parent'}"
                            title="Widget Properties Dialog"
                        ></layer-header>
                        <div>
                            Field Name: @{dialog.properties.fieldName}
                        </div>
                    </layer>
                `
            }) {
                static getName() {
                    return 'widget-properties-dialog'
                }
                properties = {
                    fieldName: ''
                }
                doShown() {
                    this.pdfUI.getAllActivatedElements().then(([widgetComponent]) => {
                        if(widgetComponent.annot?.getType() === 'widget') {
                            this.properties.fieldName = widgetComponent.annot.getField().getName();
                        } else {
                            this.properties.fieldName = 'No Widget Selected';
                        }
                        this.digest();
                    })
                    super.doShown();
                }
            }
        );

        const pdfui = new UIExtension.PDFUI({
            viewerOptions: {
                libPath: libPath,
                jr: {
                    licenseSN: licenseSN,
                    licenseKey: licenseKey
                }
            },
            renderTo: document.body,
            appearance: UIExtension.appearances.adaptive.extend({
                getDefaultFragments() {
                    return [{
                        target: 'fv--form-designer-widget-properties-dialog',
                        action: 'replace',
                        template: `<custom-form-designer:widget-properties-dialog
                                        name="fv--form-designer-widget-properties-dialog"
                                    ></custom-form-designer:widget-properties-dialog>`
                    }];
                }
            }),
            addons: libPath + '/uix-addons/allInOne.js'
        });
    </script>

    {
        "iframeOptions": {
            "style": "height: 500px"
        }
    }

### # 2. 替换表单右键菜单项

如果属性对话框不是通过 UIExtension 框架或者 layer 组件实现，但仍需要通过右键菜单触发显示，可以通过以下步骤来替换右键菜单。

#### # 步骤

- 创建自定义表单属性对话框：根据您的需求，设计并实现一个自定义对话框组件。以下仅以一个简单的原生 `dialog` 实现的对话框为例。

  定义对话框的 HTML 结构：

      <dialog id="form-properties-dialog">
          <label>
              <span>field name:</span>
              <input type="text" name="fieldName">
          </label>
          <button type="button" id="close-form-properties-dialog-button">Close</button>
      <dialog>

  定义对话框的 JavaScript 实现类：

      class CustomFormPropertiesDialog {
          constructor(dialogId, pdfUI) {
              this.dialogId = dialogId;
              this.pdfUI = pdfUI;
              this.dialogElement = document.getElementById(this.dialogId);
              this.fieldNameElement = this.dialogElement.querySelector('input[name="fieldName"]');
              this.closeDialogBtn = this.dialogElement.querySelector('#close-form-properties-dialog-button');
              this.closeDialogBtn.addEventListener('click', () => {
                  this.dialogElement.close();
              })
          }
          show() {
              this.pdfUI.getAllActivatedElements().then(([widgetComponent]) => {
                  let fieldName;
                  if(widgetComponent.annot?.getType() === 'widget') {
                      fieldName = widgetComponent.annot.getField().getName();
                  } else {
                      fieldName = 'No Widget Selected';
                  }
                  this.fieldNameElement.value = fieldName;
              })
              this.dialogElement.showModal();
          }
      }

- 创建一个 Controller，用于处理右键菜单项的点击事件：

      const customFormDesignerModule = UIExtension.modular.module('custom-form-designer', []);
      customFormDesignerModule.registerController(
          class ShowCustomFormPropertiesDialogController extends UIExtension.Controller {
              static getName() {
                  return 'ShowCustomFormPropertiesDialogController'
              }
              mounted() {
                  super.mounted();
                  this.dialog = new CustomFormPropertiesDialog('form-properties-dialog', this.pdfUI);
              }
              handle() {
                  this.dialog.show();
              }
          }
      )

- 替换右键菜单项：

      new UIExtension.PDFUI({
          appearance: UIExtension.appearances.adaptive.extend({
              getDefaultFragments() {
                  return [{
                      target: 'fv--ui-show-widget-properties-contextmenu-item',
                      action: 'replace',
                      template: `<contextmenu-item @controller="custom-form-designer:ShowCustomFormPropertiesDialogController">Show properties</contextmenu-item>`
                  }];
              }
          }),
          // ...其他配置
      })

#### # 完整示例

以下是完整的示例，请自行运行并打开一个带表单的 PDF 文档以查看效果。

    <html>
    <dialog id="form-properties-dialog">
        <label>
            <span>field name:</span>
            <input type="text" name="fieldName">
        </label>
        <button type="button" id="close-form-properties-dialog-button">Close</button>
    </dialog>
    </html>
    <script>
        class CustomFormPropertiesDialog {
            constructor(dialogId, pdfUI) {
                this.dialogId = dialogId;
                this.pdfUI = pdfUI;
                this.dialogElement = document.getElementById(this.dialogId);
                this.fieldNameElement = this.dialogElement.querySelector('input[name="fieldName"]');
                this.closeDialogBtn = this.dialogElement.querySelector('#close-form-properties-dialog-button');
                this.closeDialogBtn.addEventListener('click', () => {
                    this.dialogElement.close();
                })
            }
            show() {
                this.pdfUI.getAllActivatedElements().then(([widgetComponent]) => {
                    let fieldName;
                    if(widgetComponent.annot?.getType() === 'widget') {
                        fieldName = widgetComponent.annot.getField().getName();
                    } else {
                        fieldName = 'No Widget Selected';
                    }
                    this.fieldNameElement.value = fieldName;
                })
                this.dialogElement.showModal();
            }
        }
        const libPath = window.top.location.origin + '/lib';
        const FRAGMENT_ACTION = UIExtension.UIConsts.FRAGMENT_ACTION;

        const customFormDesignerModule = UIExtension.modular.module('custom-form-designer', []);
        customFormDesignerModule.registerController(
            class ShowCustomFormPropertiesDialogController extends UIExtension.Controller {
                static getName() {
                    return 'ShowCustomFormPropertiesDialogController'
                }
                mounted() {
                    super.mounted();
                    this.dialog = new CustomFormPropertiesDialog('form-properties-dialog', this.pdfUI);
                }
                handle() {
                    this.dialog.show();
                }
            }
        )

        const pdfui = new UIExtension.PDFUI({
            viewerOptions: {
                libPath: libPath,
                jr: {
                    licenseSN: licenseSN,
                    licenseKey: licenseKey
                }
            },
            renderTo: document.body,
            appearance: UIExtension.appearances.adaptive.extend({
                getDefaultFragments() {
                    return [{
                        target: 'fv--ui-show-widget-properties-contextmenu-item',
                        action: 'replace',
                        template: `<contextmenu-item
                                        @controller="custom-form-designer:ShowCustomFormPropertiesDialogController"
                                   >Show properties</contextmenu-item>`
                    }];
                }
            }),
            addons: libPath + '/uix-addons/allInOne.js'
        });
    </script>

    {
        "iframeOptions": {
            "style": "height: 500px"
        }
    }

## # 自定义表单属性对话框布局

在表单属性对话框中，我们针对不同的属性类型提供了细粒度的属性编辑组件（详见 内置表单属性编辑组件）。通过这些组件，您可以根据需求复用属性编辑组件，并自定义表单属性对话框的布局。