上下文表单由一个输入字段和一系列相关按钮组成。上下文表单可以在任何能够显示上下文工具栏的地方显示。此外,当注册一个包含launch配置的上下文表单时,会注册一个名为form:${name}的特殊上下文工具栏按钮,该按钮将启动那个特定的上下文表单。上下文表单是TinyMCE 4原始inlite主题中存在的“插入链接”表单的推广形式。

注册上下文表单

通过在注册表中调用addContextForm API来注册上下文表单。上下文表单的规范分为两部分:

启动

用于启动上下文表单的按钮字符串是form:${name},其中name是上下文表单的注册名称(例如form:link)。

只有当指定了上下文表单的launch设置时,这些按钮才会存在。

启动规范。这与启动此表单的按钮外观有关。

名称详细信息

launch

这是仅能出现在上下文工具栏中的启动按钮的规范。其类型将为contextformbutton或contextformtogglebutton,并将与下方定义完全相同,只是它不会有onAction。

从上下文工具栏启动上下文表单

如果已注册的上下文表单具有launch设置,则可以从上下文工具栏启动它。该条目的名称将为form:${name}(例如“form:link”)。当用户按下此按钮时,工具栏将变为指定的上下文表单。如果用户在通过上下文工具栏启动的上下文表单中按下Esc键,他们将返回到原始的上下文工具栏。

以编程方式启动上下文表单

有一个editor命令contexttoolbar-show,可以分发该命令以在当前选区显示上下文表单。该命令接受一个参数toolbarKey,用于指定要显示的已注册上下文表单或上下文工具栏的名称。

表单

这与表单本身有关。表单规范如下:

名称详细信息

launch

这是仅能出现在上下文工具栏中的启动按钮的规范。其类型将为contextformbutton或contextformtogglebutton,并将与下方定义完全相同,只是它不会有onAction。

label

这是将在表单中出现的标签。

placeholder

这指定了在输入字段有值之前出现在其中的占位符文本。

initValue

这是输入在表单中的初始值。

predicate

这控制上下文工具栏何时出现。

position

这控制上下文工具栏相对于当前光标出现的位置。

scope

这控制谓词(条件)是基于node的谓词还是基于editor的谓词。有关更多详细信息,请参见上下文工具栏优先级。

commands

这是要在上下文表单中显示的条目列表。它们可以是contextformbutton或contextformtogglebutton。

onSetup

(ContextFormApi) => (ContextFormApi) => void

可选默认值:() => () => {} - 当表单渲染时执行的函数。渲染完成后,它返回一个在上下文表单关闭时执行的函数。

上下文表单的定位

上下文表单有三种定位选项:selection、node或line。

  • selection位置将把上下文表单放置在当前选区的上方或下方,如果可能的话居中。

  • node位置将把上下文表单放置在节点(例如表格或图像)边界的上方或下方。

  • line位置将把上下文表单放置在当前选区的右侧(在从右至左的语言中为左侧)。

上下文表单按钮

与普通的上下文工具栏按钮不同,上下文表单按钮不是预先注册的。相反,你需要在commands部分完全定义每个按钮。

上下文表单按钮

上下文表单按钮的定义与普通工具栏按钮的定义非常相似。主要区别在于操作略有不同(因为它将与表单相关),并且类型是contextformbutton而不是button。以下是所有选项的完整列表:

type: 'contextformbutton';

名称值要求描述

primary

boolean

可选

这将在输入表单中按下< enter>时激活该按钮。

onAction

(formApi, togglebuttonApi) => void

必需

这决定了用户点击按钮时会发生什么。

active

boolean

可选

默认值:false

enabled

boolean

可选

默认值:true - 表示按钮状态。由按钮的 API 切换

tooltip

string

可选

按钮工具提示的文本。

text

string

可选

如果没有找到图标时要显示的文本。

icon

string

可选

要显示的图标名称。必须对应于以下位置的图标:在 图标包中,在 自定义图标包中,或使用 addIcon API添加的。

onSetup

(togglebuttonApi) => (togglebuttonApi) => void

可选

默认值:() => () => {} - 当按钮渲染时调用的函数。

其中 buttonApi 与常规工具栏按钮相同,FormApi 具有 (getValue: () => string, hide: () => void)

上下文表单切换按钮

上下文表单切换按钮与普通的工具栏切换按钮非常相似。主要区别在于操作略有不同(因为它将与表单相关),并且类型是 contextformtogglebutton,而不是 togglebutton。以下是完整的选项列表:

type: 'contextformtogglebutton';

名称值要求描述

primary

boolean

可选

这将在输入表单中按 <enter> 时激活按钮。

onAction

(formApi, togglebuttonApi) => void

必需

这决定了用户点击按钮时会发生什么。

active

boolean

可选

默认值:false

enabled

boolean

可选

默认值:true - 表示按钮状态。由按钮的 API 切换

tooltip

string

可选

按钮工具提示的文本。

text

string

可选

如果没有找到图标时要显示的文本。

icon

string

可选

要显示的图标名称。必须对应于以下位置的图标:在 图标包中,在 自定义图标包中,或使用 addIcon API添加的。

onSetup

(togglebuttonApi) => (togglebuttonApi) => void

可选

默认值:() => () => {} - 当按钮渲染时调用的函数。

其中 toggleButtonApi 与常规工具栏切换按钮相同。

formApi

contextformbutton 和 contextformtogglebutton 都会在其 onAction 回调中传入 formApi。formApi 有三个函数:

函数描述

hide

这将隐藏表单。默认情况下,没有按钮会隐藏表单。开发者有责任在需要上下文表单在操作后关闭的按钮的 onAction 处理程序中隐藏表单。

back

这将恢复上一个工具栏。这在您想在工具栏中按返回时返回到上一个工具栏的情况下很有用。

getValue

这将检索输入字段中当前键入的值。

配置示例

此示例展示了链接插件如何添加标准的链接上下文表单。每当选择任何内容时,上下文表单都会显示。

HTML

<textarea id="context-form">
  <p>点击下面的示例链接将显示新配置的上下文表单。</p>

  <a href="https://www.tiny.cloud/docs/tinymce/8/">TinyMCE 文档</a>

  <p>点击文本不应调用上下文表单。</p>
</textarea>

JS

tinymce.init({
  selector: 'textarea#context-form',
  height: 300,
  setup: (editor) => {
    const isAnchorElement = (node) => {
      return node.nodeName.toLowerCase() === 'a' && node.href;
    }

    const getAnchorElement = () => {
      const node = editor.selection.getNode();
      return isAnchorElement(node) ? node : null;
    };

    editor.ui.registry.addContextForm('link-form', {
      launch: {
        type: 'contextformtogglebutton',
        icon: 'link'
      },
      label: 'Link',
      predicate: isAnchorElement,
      placeholder: 'https://www.example.com',
      initValue: () => {
        const elm = getAnchorElement();
        return !!elm ? elm.href : '';
      },
      commands: [
        {
          type: 'contextformtogglebutton',
          icon: 'link',
          tooltip: 'Link',
          primary: true,
          onSetup: (buttonApi) => {
            buttonApi.setActive(!!getAnchorElement());
            const nodeChangeHandler = () => {
              buttonApi.setActive(!editor.readonly && !!getAnchorElement());
            };
            editor.on('nodechange', nodeChangeHandler);
            return () => editor.off('nodechange', nodeChangeHandler)
          },
          onAction: (formApi) => {
            const value = formApi.getValue();
            console.log('Save link clicked with value: ' + value);
            formApi.hide();
          }
        },
        {
          type: 'contextformtogglebutton',
          icon: 'unlink',
          tooltip: 'Remove link',
          active: false,
          onAction: (formApi) => {
            console.log('Remove link clicked');
            formApi.hide();
          }
        }
      ]
    });
  },
  content_style: 'body { font-family:Helvetica,Arial,sans-serif; font-size:16px }'
});

解决上下文工具栏与上下文表单冲突的问题

在某些情况下,自定义上下文工具栏或自定义上下文表单可能会与以下内容发生冲突:

  • 编辑器提供的上下文工具栏或上下文表单,

  • 由 快速工具栏(quickbars)插件提供的上下文工具栏或上下文表单,

  • 其他自定义上下文工具栏或自定义上下文表单。

确定上下文工具栏和上下文表单的显示优先级

有三个设置决定了上下文工具栏和上下文表单的优先级:scope、predicate 和 position。

  • scope - 将上下文工具栏或表单设置为:特定类型的内容(node)专用,或通用(全局)工具栏或表单(editor)。

  • predicate - 谓词是上下文工具栏或表单应该显示的条件。此设置是一个函数,用于确定上下文菜单或表单是否适用于当前选择或光标位置。此函数应返回一个布尔值。

  • position - 设置上下文工具栏或表单相对于当前上下文(selection、node 或 line)的渲染位置。

通常:

  • 上下文表单的优先级高于上下文工具栏。

  • 具有 scope: 'node' 的上下文表单的优先级高于 scope: 'editor'。

  • 对于一个选择或光标位置,只能显示一个上下文表单,它们不能与其他上下文表单或上下文工具栏拼接。

  • 具有 position: 'selection' 的上下文工具栏的优先级高于 position: 'node',而 position: 'line' 的优先级最低。

  • 当有多个上下文工具栏需要显示时,编辑器会将上下文工具栏拼接在一起:

    • 匹配的谓词,

    • 相同的 scope 和 position 值。

      拼接的工具栏可能包含重复的工具栏项。

  • 如果对于选择或光标位置没有找到匹配的上下文工具栏或上下文表单,那么编辑器将递归地在当前节点的父节点上搜索匹配项,直到到达编辑器内容的根节点。

上下文工具栏和上下文表单的优先级说明

以下说明可用于解决上下文工具栏和上下文表单的行为问题。

编辑器将使用以下过程来确定将显示哪个上下文工具栏或上下文表单:

  1. 查找所有具有以下条件的上下文表单:

    • scope: 'node'

    • 匹配当前选择或光标位置的谓词(条件)。

      如果有任何匹配的上下文表单,找到的第一个表单将在编辑器中显示,并且过程结束。

  2. 查找所有具有以下条件的上下文表单:

    • scope: 'editor'

    • 匹配当前选择或光标位置的谓词。

      如果有任何匹配的上下文表单,找到的第一个表单将在编辑器中显示,并且过程结束。

  3. 查找所有具有以下条件的上下文工具栏:

    • 任何 scope

    • 匹配当前选择或光标位置的谓词。

      如果有任何匹配的上下文工具栏,编辑器将根据 position 值对上下文工具栏进行优先级排序。

  • 所有 position: 'selection' 或 position: 'node' 的 context toolbars 将被拼接,拼接后的 toolbar 将被显示,过程结束。

  • 否则,所有 position: line 的 context toolbars 将被拼接,拼接后的 toolbar 将被显示,过程结束。

  • 查找所有同时满足以下条件的 context forms:

    • 任何 scope

    • 谓词匹配当前节点、选区或光标位置的父节点。

      如果找到任何 context forms,第一个找到的将在编辑器中显示,过程结束。

  • 查找所有同时满足以下条件的 context toolbars:

    • 任何 scope

    • 谓词匹配当前节点、选区或光标位置的父节点。

      如果有任何匹配的 context toolbars,编辑器将根据 position 值优先显示 context toolbars。

    • 如果有 position: 'selection' 的 context toolbars,它们将被拼接,拼接后的 toolbar 将被显示,过程结束。

    • 如果有 position: 'node' 的 context toolbars,它们将被拼接,拼接后的 toolbar 将被显示,过程结束。

    • 否则,所有 position: line 的 context toolbars 将被拼接,拼接后的 toolbar 将被显示,过程结束。

  • 对于 DOM 中每个连续的父节点,重复步骤 4 和 5,以查找具有 scope: node 的 context toolbars 和 context forms,直到:

    • 找到并显示匹配的 context form 或 context toolbars,

    • 到达编辑器的根节点。