概述

本指南全面介绍如何创建、构建和部署自定义图标包,包括常见问题的解决方案。详细说明了两种方法:

  • 手动方法,用于添加少量自定义图标

  • 使用 Icon Pack Template 工具 从 SVG 文件夹中轻松生成所需的输出文件。

图标在 TinyMCE 中的工作原理

TinyMCE 图标包是一个 JavaScript 结构,包含 SVGs 字符串。图标包通常保存在单独的文件中,但如有需要也可以内联。图标包可用于添加一个或多个自定义图标,或替换部分或全部默认的 TinyMCE 图标。

图标包仅需要包含自定义图标;对于自定义图标包中缺失的图标,默认的 TinyMCE 图标将作为备选方案使用。

图标包结构和加载

理解图标包加载:

TinyMCE 从路径 TINYMCE_BASE/icons/${iconPackName}/icons.js 加载图标包,其中:

  • TINYMCE_BASE 是 TinyMCE 的根目录(包含 tinymce.min.js 的目录)。

  • ${iconPackName} 是图标包的名称。

注册图标包

使用 IconManager 向 TinyMCE 注册图标包。无论是手动创建图标包还是使用 Icon Pack Template 工具,用于设置图标包的 JavaScript 如下所示:

tinymce.IconManager.add('custom-pack-name', {
  icons: {
    'bold': '<svg width="24" height="24">...</svg>',
    'italic': '<svg width="24" height="24">...</svg>',
    'custom-icon': '<svg width="24" height="24">...</svg>',
    // ... more icons
  }
});

手动创建图标包

当仅添加少量图标时,手动配置新图标包可能比使用 Icon Pack Template 工具 更简单。具体操作如下:

  1. 收集新图标的 SVG 文件。提取其内容字符串,例如 <svg width="24" height="24">...</svg>

  2. 调用 tinymce.IconManager.add()

  3. 将图标包名称指定为第一个参数

  4. 第二个参数是一个对象,其键为 icons,并包含一个嵌套对象,该嵌套对象包含新图标及其名称和 SVG 字符串作为键和值。

然后可以在 TinyMCE 中按名称使用这些新图标。

图标覆盖:如果自定义图标与默认的 TinyMCE 图标名称相同,它们将覆盖默认图标。

此 JavaScript 可以作为单独的图标文件包含,或在应用程序的 tinymce.init() 调用旁边内联。配置 TinyMCE 加载图标包,并在必要时使用 icons 和 icons_url 选项指定从何处加载。

命名图标包文件:当从单独的文件加载图标包时,最安全的做法是将文件命名为 icons.js。这样可以确保如果将文件放在默认位置,TinyMCE 将识别该文件为图标包。

示例:内联使用自定义图标包

tinymce.IconManager.add('customIcons', { // icon pack name
  icons: { // list icons as name-SVG pairs
    'my-custom-icon': '<svg width="24" height="24">...</svg>',
  }
});

tinymce.init({
  selector: '#editor',
  icons: 'customIcons', // tell {productname} to load the custom icon pack
  toolbar: 'myCustomButton',
  setup: function(editor) {
    editor.ui.registry.addToolbarButton('myCustomButton', {
      icon: 'my-custom-icon' // use the custom icon for a custom toolbar button
    });
  }
});

使用 Icon Pack Template 工具

对于大型自定义图标包,请使用 Icon Pack Template 工具,该工具可以从一个 SVG 文件夹生成所需的输出文件。这种方法在创建包含大量图标的图标包或自动化构建过程时非常理想。

有关分步说明,请参阅:使用 Icon Pack Template 工具。

部署图标包文件

图标包文件可以通过以下两种方式提供:

  • 与 TinyMCE 一起提供

  • 独立于 TinyMCE 提供

使用 icons 和 icons_url 选项适当配置 TinyMCE。

默认图标包位置:

icons 选项仅能识别位于 TINYMCE_BASE/icons/${iconPackName}/icons.js 的图标文件;其中:

  • TINYMCE_BASE 是 TinyMCE 的根目录(包含 tinymce.min.js 的目录)。

与 TinyMCE 一起部署图标包

初始化时,TinyMCE 将尝试加载通过 icons 选项指定的任何图标包。图标包中的图标将与 TinyMCE 的默认图标 合并,图标包中的图标将覆盖具有相同标识符的默认图标。

对于自定义图标包:

tinymce.init({
  selector: 'textarea',
  icons: 'my_icon_pack'  // TINYMCE_BASE/icons/my_icon_pack/icons.js
});

分开部署图标包和 TinyMCE

初始化时,TinyMCE 将尝试从 icons_url 指定的位置加载通过 icons 选项指定的任何图标包。图标包中的图标将与 TinyMCE 的默认图标 合并,图标包中的图标将覆盖具有相同标识符的默认图标。

理解 icons_url 和 icons 的关系:

使用 icons_url 时,还必须指定 icons 选项:

  • icons_url - 告诉 TinyMCE 图标包文件在哪里。

  • icons - 告诉 TinyMCE 从加载的文件中使用哪个图标包。

示例:

tinymce.init({
  selector: 'textarea',
  icons_url: 'dist/icons/my_icon_pack/icons.js', // 在哪里找到它
  icons: 'my_icon_pack',  // 使用哪个包
  // ... 其他选项
});

使用

要从单独的位置使用 TinyMCE 图标包:

  1. 确保图标包在指定的 URL 可用。

  2. 向 tinymce.init 添加 icons_url 选项。

  3. 使用 icons 设置指定图标包名称。

对于自定义图标包:

tinymce.init({
  selector: 'textarea',  // 根据您的 HTML 更改此值
  icons_url: 'dist/icons/my_icon_pack/icons.js', // 自定义图标包的路径
  icons: 'my_icon_pack'  // 使用自定义图标包
});

常见的开发场景

使用自定义图标包进行本地开发

在本地使用自定义图标包开发时:

tinymce.init({
  selector: 'textarea',
  icons_url: 'dist/icons/my_icon_pack/icons.js', // 自定义图标包的路径
  icons: 'my_icon_pack',  // 使用自定义图标包
  toolbar: 'myButton',
  setup: (editor) => {
    editor.ui.registry.addButton('myButton', {
      icon: 'bold',    // 使用您的自定义粗体图标
      onAction: (_) => {
        editor.insertContent('&nbsp;<strong>自定义图标被点击!</strong>&nbsp;');
      }
    });
  }
});

CDN 部署

部署到 CDN 或单独的服务器时:

tinymce.init({
  selector: 'textarea',
  icons_url: 'https://cdn.example.com/icons/my_icon_pack/icons.js', // CDN URL
  icons: 'my_icon_pack',  // 使用自定义图标包
  // ... 其他选项
});

交互演示

通过交互演示查看自定义图标包的实际效果:

HTML

<div style="display: flex; gap: 20px; width: 100%; flex-wrap: wrap;">
  <div style="flex: 1; min-width: 300px;">
    <h2>默认 TinyMCE 图标</h2>
    <textarea id="default-editor" style="width: 100%;">
      <h2>默认图标编辑器</h2>
      <p>此编辑器使用<strong>默认 TinyMCE 图标</strong>。</p>
      <p>注意工具栏按钮的标准外观。</p>
      <ul>
        <li>粗体、斜体、下划线按钮</li>
        <li>链接和图像按钮</li>
        <li>列表和代码按钮</li>
      </ul>
    </textarea>
  </div>
  <div style="flex: 1; min-width: 300px;">
    <h2>自定义图标包</h2>
    <textarea id="custom-editor" style="width: 100%;">
      <h2>自定义图标编辑器</h2>
      <p>此编辑器使用通过我们的图标包系统创建的<strong>自定义图标包</strong>。</p>
      
      <h3>工作原理:</h3>
      <p><strong>演示设置:</strong>对于这个实时演示,自定义图标是通过 JavaScript 文件中直接使用 <code>tinymce.IconManager.add()</code> 定义的。</p>
      
      <p><strong>生产环境设置:</strong>在正常使用中,您需要:</p>
      <ul>
        <li>使用模板构建您的图标包(请参阅我们的指南)</li>
        <li>将生成的 <code>icons.js</code> 文件放在 <code>dist/icons/your-pack-name/</code></li>
        <li>使用 <code>icons_url: 'path/to/icons.js'</code> 来加载它</li>
      </ul>
      
      <p>编辑器配置为 <code>icons: 'my-icon-pack'</code> 以使用自定义图标。<strong>尝试自定义按钮:</strong>在上方的工具栏中查找自定义的粗体、斜体和音频按钮。</p>
    </textarea>
  </div>
</div>

<div style="margin-top: 20px; padding: 15px; background: #f8f9fa; border-radius: 4px; font-size: 14px; color: #666;">
  <strong>图标来源:</strong>此演示中的自定义图标来自 <a href="https://freesvgicons.com/" target="_blank" rel="noopener">FreeSVGIcons.com</a> - 一个拥有 250k+ 开源 SVG 图标的集合。
</div>

JS

// 加载自定义图标包(用于实时演示兼容)
tinymce.IconManager.add('my-icon-pack', {
  icons: {
      'audio': '<svg width="24" height="24"><path d="M10.8 19q.9 0 1.5-.7t.7-1.5V13h3v-2h-4v3.9l-.6-.3-.6-.1q-1 0-1.7.7t-.6 1.6q0 .9.7 1.5t1.6.7ZM6 22q-.8 0-1.4-.6T4 20V4q0-.8.6-1.4T6 2h8l6 6v12q0 .8-.6 1.4T18 22H6Zm7-13h5l-5-5v5Z"/></svg>',
      'bold': '<svg width="24" height="24"><path fill-rule="evenodd" d="M3.6 2.3a1.4 1.4 0 0 0-1.4 1.3v16.8c0 .7.7 1.4 1.4 1.4h16.8a1.4 1.4 0 0 0 1.4-1.4V3.6a1.4 1.4 0 0 0-1.4-1.3H3.6Zm6 4a1.4 1.4 0 0 0-1.3 1.3v9.3c0 .7.6 1.4 1.3 1.4H12v-.8.8a2.5 2.5 0 0 0 .2 0 4.6 4.6 0 0 0 1.6-.5c.5-.2 1-.5 1.3-1 .4-.5.7-1.2.7-2 0-.9-.3-1.6-.7-2a3.2 3.2 0 0 0-.8-.9 2.8 2.8 0 0 0 .4-.5c.4-.5.5-1.1.5-1.9s-.1-1.4-.5-1.9a3 3 0 0 0-1.1-1 3.9 3.9 0 0 0-1.6-.3H9.6Zm.1 6.5H12a3.2 3.2 0 0 1 1.2.2l.7.6c.2.2.4.6.4 1.1s-.2 1-.4 1.2a1.8 1.8 0 0 1-.7.6 3.2 3.2 0 0 1-1.1.2H9.7v-4Zm2.3 4Zm0-5.6H9.7V7.8H12l1 .2.5.5c.1.2.3.5.3 1s-.2.8-.3 1a1.4 1.4 0 0 1-.6.5 2.3 2.3 0 0 1-.9.2Z" clip-rule="evenodd"/></svg>',
      'italic': '<svg width="24" height="24"><path fill-rule="evenodd" d="M3.6 2.3a1.4 1.4 0 0 0-1.4 1.3v16.8c0 .7.7 1.4 1.4 1.4h16.8a1.4 1.4 0 0 0 1.4-1.4V3.6a1.4 1.4 0 0 0-1.4-1.3H3.6ZM16 6.8h-1.5L11 17.1h1a.8.8 0 0 1 0 1.6H8a.8.8 0 0 1 0-1.6h1.5L13 6.8h-1a.8.8 0 0 1 0-1.5h4a.8.8 0 0 1 0 1.5Z" clip-rule="evenodd"/></svg>',
  }
});

// 默认图标编辑器
tinymce.init({
selector: '#default-editor',
icons: 'material', // 使用 material 图标包
plugins: 'lists link image code',
toolbar: 'undo redo | bold italic underline | bullist numlist | link image code',
height: 500,
// license_key: 'gpl'
});

// 自定义图标编辑器
tinymce.init({
selector: '#custom-editor',
icons: 'my-icon-pack',
plugins: 'lists link image code',
toolbar: 'undo redo | myButton1 myButton2 myButton3 | bullist numlist | link image code',
height: 500,
setup: (editor) => {
  editor.ui.registry.addButton('myButton1', {
    icon: 'bold',    // 由 'bold.svg' 创建的 'bold' 图标
    onAction: (_) => {
      editor.insertContent('&nbsp;<strong>这是我的自定义 bold 图标按钮!</strong>&nbsp;');
    }
  });
  editor.ui.registry.addButton('myButton2', {
    icon: 'italic',    // 由 'italic.svg' 创建的 'italic' 图标
    onAction: (_) => {
      editor.insertContent('&nbsp;<strong>这是我的自定义 italic 图标按钮!</strong>&nbsp;');
    }
  });
  editor.ui.registry.addButton('myButton3', {
    icon: 'audio',    // 由 'audio.svg' 创建的 'audio' 图标
    onAction: (_) => {
      editor.insertContent('&nbsp;<strong>这是我的自定义 audio 图标按钮!</strong>&nbsp;');
    }
  });
}
});