Створення власних елементів.

Вивчення DXPR BuilderРозширенняРозробка на платформі

Кожен елемент DXPR Builder це об’єкт JavaScript, доданий до window.dxprBuilder.dxpr_elements. Редактор перетворює кожен об’єкт на клас елемента з уже приєднаними вкладками 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: клієнтські шаблони twig.js, скомпільовані з dxpr_builder/templates/.
  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 classes і параметри розміщення);
    • додає успадковані параметри до params визначення;
    • конвертує кожен параметр через make_param_type(), який зіставляє param.type із зареєстрованими типами параметрів;
    • змішує властивості визначення в прототип класу.

Визначення тому мусить бути на масиві після виконання dxpr_elements.js і перед виконанням dxpr_builder.js. Усі шість файлів завантажуються з атрибутом defer, тож порядок тегів скриптів у документі це порядок виконання.

Визначення елемента

Це елемент Alert із dxpr_builder/dxpr_elements.js (джерело: 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 Машинне ім’я. Воно пишеться у збережений HTML як data-azb="az_alert" і використовується для пошуку класу, коли сторінка розбирається знову. Зберігайте префікс 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.

Глядачі, які не отримують редактор, отримують цей HTML без його атрибутів data-az*: DxprBuilderService::parseDocumentForCleanup() здирає їх, крім фіксованого списку, який він тримає на анімованих елементах і на вбудованих динамічних елементах на кшталт Carousel. Якщо елемент потребує JavaScript або CSS для відвідувачів, покладіть атрибут data-dxpr-builder-libraries="<key>" на його корінь: parseDocumentForTemplateLibrary() приєднує dxpr_builder/elements.<key> для кожного ключа без слеша. Ключі це бібліотеки elements.* у dxpr_builder.libraries.yml; ключ зі слешом шанується лише коли він є library зареєстрованого набору піктограм.

Мінімальний робочий приклад

Приклад додає елемент «Notice», який рендерить абзац із вибраним кольором. Він будує свій DOM напряму, бо скомпільовані шаблони twig.js у dxpr_templates.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-хуків, які додають службові класи та набори піктограм.
  • Архітектура для того, як редактор і форматувальник поля пасують одне одному.