创建自定义元素.

Learn DXPR BuilderExtendBouw erop voort

每个 DXPR Builder 元素是推入 window.dxprBuilder.dxpr_elements 的 JavaScript 对象。编辑器将每个对象变成元素类,Style 和 Animation 选项卡已挂好。本页展示自带元素在 dxpr_builder/dxpr_elements.js 中如何定义、模块如何向编辑器添加自己的定义,以及生成的 HTML 什么样。元素没有服务器端 Twig 模板;元素在浏览器中渲染。

编辑器如何注册元素

编辑器按以下顺序加载六个脚本,由 dxpr_builder_library_info_build()(dxpr_builder.module 中的)定义为 dxpr_builder/editor.builder 库:

  1. dxpr_templates.js:从 dxpr_builder/templates/ 编译的客户端 twig.js 模板。
  2. dxpr_global.js:共享辅助函数和 CKEditor 5 集成。
  3. dxpr_events.js
  4. dxpr_param_types.js:向 window.dxprBuilder.dxpr_param_types 推入每种输入类型一个对象。
  5. dxpr_elements.js:创建 window.dxprBuilder.dxpr_elements 数组并推入每个元素一个定义。
  6. dxpr_builder.js:编辑器。该文件执行时调用 create_dxpr_elements(),遍历 window.dxprBuilder.dxpr_elements 并对每个定义:
    • 调用 register_animated_element(base, is_container, Class),让类继承 AnimatedElement(Style 选项卡、Animation 选项卡、HTML ID、HTML 类和位置参数);
    • 将继承的参数追加到定义的 params;
    • 通过 make_param_type() 转换每个参数,它将 param.type 与已注册的参数类型匹配;
    • 将定义的属性混入类原型。

因此定义必须在 dxpr_elements.js 运行后、dxpr_builder.js 运行前进入数组。六个文件都带 defer 属性加载,因此 script 标签的文档顺序即执行顺序。

元素定义

这是 dxpr_builder/dxpr_elements.js 中的 Alert 元素(源码:dxpr_builder/build/dxpr-elements/elements/az_alert.js),精简到每个元素都需要的属性。原件还设置了 el_class、onCancel 处理器和 focusout 监听器。

window.dxprBuilder.dxpr_elements.push({
  base: "az_alert",
  name: Drupal.t("Alert"),
  nameEn: "Alert",
  icon: "az-custom-icon az-alert-icon",
  largeSettingsWindow: true,
  hidden: false,
  params: [
    {
      type: "textarea",
      heading: Drupal.t("Message"),
      param_name: "content",
      value: Drupal.t(
        "This is a placeholder text. Your actual content will go here. Edit this to include your own information.",
      ),
    },
    {
      type: "dropdown",
      heading: Drupal.t("Type"),
      param_name: "type",
      description: Drupal.t("Select message type."),
      getText: (opt) => opt.text,
      getValue: (opt) => opt.value,
      getDefault: (param) => param.value["alert-success"].value,
      value: {
        "alert-success": { text: Drupal.t("Success"), value: "alert-success" },
        "alert-info": { text: Drupal.t("Info"), value: "alert-info" },
        "alert-warning": { text: Drupal.t("Warning"), value: "alert-warning" },
        "alert-danger": { text: Drupal.t("Danger"), value: "alert-danger" },
      },
      passthrough: true,
    },
  ],
  show_settings_on_create: true,
  has_content: true,
  render(...args) {
    const state = {
      class: `az-element az-alert alert ${this.attrs.type} ${this.get_el_classes()} ${this.get_content_classes()}`,
      style: this.attrs.style,
      message: this.attrs.content,
    };
    this.dom_element = this.baseclass.prototype.render_template(
      "elements/alert/alert",
      state,
    );
    this.baseclass.prototype.render.apply(this, args);
  },
  save_history() {
    const container = this.get_my_container();
    container.save_container_history(Drupal.t("Edit alert"));
  },
});
属性 用途
base 机器名。它以 data-azb="az_alert" 写入已保存 HTML,页面重新解析时用于找到类。保留 az_ 前缀。
name、nameEn 元素选择器中的标签及其未翻译形式。
icon 选择器图标的 CSS 类。自带图标定义在 dxpr_builder/sass/dxpr-builder-backend/components/_custom-icons.scss 中;自定义元素需要为自己的图标类写 CSS。
category 选择器选项卡。布局元素用 Drupal.t("Layout");区块和视图用 "CMS"。默认内容选项卡请省略。
is_container true(元素容纳其他元素时)。
largeSettingsWindow 以较大尺寸打开设置对话框。
hidden 在选择器中隐藏元素,但保持可渲染。
params 对话框中显示的设置。见下文。
show_settings_on_create 元素放下后立即打开设置对话框。
has_content 元素有可编辑内容区(dom_content_element)。
render(...args) 构建 this.dom_element(从 this.attrs),再调用基础 render。
save_history() 推入带标签的撤销条目。
parse_attrs_from_dom(element) 可选。从已保存标记中读回参数值,与 skip_data_azb 和 skip_params 配合,供 Image 等将值存放在 <img> 属性而不是 data-azat-* 中的元素使用。

参数

每个 params 条目有:

键 含义
type 已注册的参数类型之一(下一节)。
heading 字段标签。
param_name 键在 this.attrs 中,在已保存 HTML 中是 data-azat-<param_name> 属性。content 特殊:它是元素正文,不是属性。
value 默认值。对 dropdown 是带 getText、getValue 和 getDefault 回调的选项对象。
description 字段下的帮助文本。
tab 将字段分组到命名折叠中,例如 Drupal.t("SEO")。
can_be_empty 存放空字符串,而不是丢弃属性。
safe 跳过存放值的 URL 编码。

参数类型是推入的 type 值,推到 window.dxprBuilder.dxpr_param_types(dxpr_builder/dxpr_param_types.js 中):boxmodel、checkbox、checkboxes、colorpicker、datetime、dropdown、dxb_slider、html、icon、image、images、link、links、numericfield、row_layout、saved_datetime、social_links、textarea、textfield 和 video。未注册的类型回退到普通文本输入框(BaseParamType)。

不要重复使用每个元素继承的 param_name,例如 color、background、el_class、hash 或 an_* 动画名。Style 选项卡已拥有 color,因此同名的第二个参数从不写入 data-azat-color。

保存的内容

update_data()(dxpr_builder.js 中的)将元素写为 HTML:

  • data-azb="<base>"(根元素上);
  • data-azat-<param_name>="<value>" 写入每个值与默认值不同的参数,URL 编码,除非参数设置了 safe: true;
  • Style 选项卡的参数写为内联 style 和类,从不写为 data-azat-* 属性;
  • data-azcnt="true"(设置了 has_content 时,在内容元素上)。

没有编辑器的访客收到去掉 data-az* 属性的 HTML:DxprBuilderService::parseDocumentForCleanup() 剥离它们,只在动画元素和 Carousel 等内置动态元素上保留固定列表。如果元素需要访客端 JavaScript 或 CSS,请在根元素上放 data-dxpr-builder-libraries="<key>" 属性:parseDocumentForTemplateLibrary() 为每个不带斜杠的键附加 dxpr_builder/elements.<key>。键是 elements.* 库(dxpr_builder.libraries.yml 中的);带斜杠的键只在它是已注册图标集的 library 时有效。

最小可用示例

示例添加渲染带颜色段落的“Notice”元素。它直接构建 DOM,因为 dxpr_templates.js 中编译的 twig.js 模板是编辑器包的一部分,别的模块无法扩展。

  1. 创建 mymodule/js/notice-element.js:

    window.dxprBuilder.dxpr_elements.push({
      base: "az_notice",
      name: Drupal.t("Notice"),
      nameEn: "Notice",
      icon: "az-custom-icon az-alert-icon",
      params: [
        {
          type: "textfield",
          heading: Drupal.t("Text"),
          param_name: "text",
          value: Drupal.t("Notice text"),
        },
        {
          type: "colorpicker",
          heading: Drupal.t("Colour"),
          param_name: "notice_color",
          value: "#0d6efd",
        },
      ],
      show_settings_on_create: true,
      render(...args) {
        const el = document.createElement("div");
        el.className = `az-element az-notice ${this.get_el_classes()}`;
        if (this.attrs.style) {
          el.setAttribute("style", this.attrs.style);
        }
        const p = document.createElement("p");
        p.style.color = this.attrs.notice_color;
        p.textContent = this.attrs.text;
        el.appendChild(p);
        this.dom_element = el;
        this.baseclass.prototype.render.apply(this, args);
      },
      save_history() {
        this.get_my_container().save_container_history(Drupal.t("Edit notice"));
      },
    });
    
  2. 将文件插入 dxpr_elements.js(权重 -4)与 dxpr_builder.js(权重 -3)之间的编辑器库(在 mymodule.module 中):

    /**
     * Implements hook_library_info_alter().
     */
    function mymodule_library_info_alter(array &$libraries, string $extension): void {
      if ($extension === 'dxpr_builder' && isset($libraries['editor.builder'])) {
        $path = '/' . \Drupal::service('extension.list.module')->getPath('mymodule');
        $libraries['editor.builder']['js'][$path . '/js/notice-element.js'] = [
          'weight' => -3.5,
          'preprocess' => FALSE,
          'attributes' => ['defer' => TRUE],
        ];
      }
    }
    

    preprocess: FALSE 和 defer 与模块为自己文件定义的条目一致,因此无论编辑器资源来自 CDN 还是本地构建,文件在序列中的位置不变。

  3. 清除缓存并打开构建器页面:

    drush cr
    
  4. 从选择器添加元素,设置文本和颜色并保存。字段存放:

    <div class="az-element az-notice" id="..." data-azb="az_notice"
         data-azat-text="Hello" data-azat-notice_color="%23198754">
      <p style="color: #198754;">Hello</p>
    </div>
    

    访客得到去掉 data-az* 属性的同样标记。段落保留内联颜色,因此该元素不需要访客库。

要知道的限制

  • 档案不列出自定义元素。 构建器档案中的元素清单来自 src/Form/DxprBuilderProfileForm.php 和 src/Service/Handler/ProfileHandler.php 中的固定列表。该列表外的元素从不受档案隐藏。
  • 选择器的图标需要模块的 CSS;构建器自带的 az-custom-icon 类只覆盖自带元素。
  • DxprBuilderService::getDxprElementsFolders() 及其 hook_dxpr_builder_elements_folders_alter() 返回主题中 elements 文件夹的列表,但编辑器中没有任何东西读该列表。它不是注册路线。
  • Style 和 Animation 选项卡由 register_animated_element() 为您添加;不要重定义这些参数。

接下来做什么?

  • Hooks API:添加实用类和图标集的 PHP 钩子。
  • 架构:编辑器与字段格式化器的配合。