Tworzenie własnych elementów.

Poznaj DXPR BuilderRozszerzeniaTwórz na tej bazie

Każdy element kreatora DXPR to obiekt JavaScript wepchnięty na window.dxprBuilder.dxpr_elements. Edytor zamienia każdy obiekt w klasę elementu z dołączonymi kartami Style i Animation. Ta strona pokazuje, jak zdefiniowane są dostarczone elementy w dxpr_builder/dxpr_elements.js, jak moduł dodaje własną definicję do edytora i jak wygląda wynikowy HTML. Nie ma szablonów Twig po stronie serwera dla elementu; elementy renderują się w przeglądarce.

Jak edytor rejestruje elementy

Edytor wczytuje sześć skryptów w tej kolejności, zdefiniowanych w dxpr_builder_library_info_build() w dxpr_builder.module jako biblioteka dxpr_builder/editor.builder:

  1. dxpr_templates.js: szablony twig.js po stronie klienta skompilowane z dxpr_builder/templates/.
  2. dxpr_global.js: współdzielone helpery i integracja CKEditor 5.
  3. dxpr_events.js
  4. dxpr_param_types.js: wpycha jeden obiekt na typ wejścia na window.dxprBuilder.dxpr_param_types.
  5. dxpr_elements.js: tworzy tablicę window.dxprBuilder.dxpr_elements i wpycha jedną definicję na element.
  6. dxpr_builder.js: edytor. Gdy ten plik się wykona, wywołuje create_dxpr_elements(), które przechodzi pętlą window.dxprBuilder.dxpr_elements i dla każdej definicji:
    • wywołuje register_animated_element(base, is_container, Class), więc klasa rozszerza AnimatedElement (karta Style, karta Animation, HTML ID, klasy HTML i parametry umiejscowienia);
    • dołącza odziedziczone parametry do params definicji;
    • konwertuje każdy parametr przez make_param_type(), które dopasowuje param.type do zarejestrowanych typów parametrów;
    • wmiesza właściwości definicji do prototypu klasy.

Definicja musi więc znaleźć się na tablicy po wykonaniu dxpr_elements.js i przed wykonaniem dxpr_builder.js. Wszystkie sześć plików ładuje się z atrybutem defer, więc kolejność dokumentów tagów script jest kolejnością wykonania.

Definicja elementu

Tak wygląda element Alert z dxpr_builder/dxpr_elements.js (źródło: dxpr_builder/build/dxpr-elements/elements/az_alert.js), przycięty do właściwości potrzebnych każdemu elementowi. Oryginał ustawia też el_class, handler onCancel i listener 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"));
  },
});
Właściwość Przeznaczenie
base Nazwa maszynowa. Zapisywana jest do zapisanego HTML jako data-azb="az_alert" i służy do znalezienia klasy, gdy strona jest ponownie parsowana. Zachowaj prefiks az_.
name, nameEn Etykieta w wybieraku elementów i jej nieprzetłumaczona forma.
icon Klasy CSS ikony wybieraka. Dostarczone ikony zdefiniowane są w dxpr_builder/sass/dxpr-builder-backend/components/_custom-icons.scss; własny element potrzebuje własnego CSS dla klasy ikony.
category Karta wybieraka. Elementy układu używają Drupal.t("Layout"); bloki i widoki używają "CMS". Pomiń dla domyślnej karty treści.
is_container true, gdy element zawiera inne elementy.
largeSettingsWindow Otwiera okno ustawień w większym rozmiarze.
hidden Ukrywa element przed wybierakiem, zachowując jego renderowalność.
params Ustawienia pokazywane w oknie. Zobacz poniżej.
show_settings_on_create Otwiera okno ustawień, gdy tylko element zostanie upuszczony.
has_content Element ma edytowalny obszar treści (dom_content_element).
render(...args) Buduje this.dom_element z this.attrs, potem wywołuje bazowe render.
save_history() Wpycha wpis cofania z etykietą.
parse_attrs_from_dom(element) Opcjonalne. Odczytuje wartości parametrów z powrotem z zapisanego HTML, używane wraz z skip_data_azb i skip_params przez elementy takie jak Image, które przechowują wartości w atrybutach <img> zamiast data-azat-*.

Parametry

Każdy wpis w params ma:

Klucz Znaczenie
type Jeden z zarejestrowanych typów parametrów (następna sekcja).
heading Etykieta pola.
param_name Klucz w this.attrs i, w zapisanym HTML, atrybut data-azat-<param_name>. content jest specjalne: to treść elementu, nie atrybut.
value Wartość domyślna. Dla dropdown to obiekt opcji z callbackami getText, getValue i getDefault.
description Tekst pomocy pod polem.
tab Grupuje pole w nazwany akordeon, na przykład Drupal.t("SEO").
can_be_empty Przechowaj pusty ciąg zamiast porzucać atrybut.
safe Pomiń kodowanie URL przechowywanej wartości.

Typy parametrów to wartości type wepchnięte na window.dxprBuilder.dxpr_param_types w 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 i video. Typ, którego nie zarejestrujesz, wraca do zwykłego wejścia tekstowego (BaseParamType).

Nie używaj ponownie param_name, które dziedziczy każdy element, takiego jak color, background, el_class, hash albo nazwy animacji an_*. Karta Style już posiada color, więc drugi parametr o tej nazwie nigdy nie jest zapisywany do data-azat-color.

Co się zapisuje

update_data() w dxpr_builder.js zapisuje element do HTML:

  • data-azb="<base>" na elemencie głównym;
  • data-azat-<param_name>="<value>" dla każdego parametru, którego wartość różni się od domyślnej, kodowane URL, chyba że parametr ustawia safe: true;
  • parametry karty Style jako osadzone style i klasy, nigdy jako atrybuty data-azat-*;
  • data-azcnt="true" na elemencie treści, gdy ustawiono has_content.

Odwiedzający bez edytora otrzymują ten HTML bez jego atrybutów data-az*: DxprBuilderService::parseDocumentForCleanup() usuwa je, poza stałą listą zachowywaną na elementach animowanych i na wbudowanych elementach dynamicznych takich jak Carousel. Jeśli element potrzebuje JavaScript albo CSS dla odwiedzających, umieść atrybut data-dxpr-builder-libraries="<key>" na jego elemencie głównym: parseDocumentForTemplateLibrary() dołącza dxpr_builder/elements.<key> dla każdego klucza bez ukośnika. Klucze to biblioteki elements.* w dxpr_builder.libraries.yml; klucz z ukośnikiem honorowany jest tylko, gdy jest library zarejestrowanego zestawu ikon.

Minimalny działający przykład

Przykład dodaje element „Notice”, który renderuje akapit z wybranym kolorem. Buduje swój DOM bezpośrednio, ponieważ skompilowane szablony twig.js w dxpr_templates.js są częścią pakietu edytora i nie mogą być rozszerzane przez inny moduł.

  1. Utwórz 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. Wstaw plik do biblioteki edytora między dxpr_elements.js (waga -4) a dxpr_builder.js (waga -3) w 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 i defer odpowiadają wpisom definiowanym przez moduł dla własnych plików, więc plik zachowuje swoje miejsce w sekwencji niezależnie od tego, czy pliki edytora pochodzą z CDN, czy z lokalnej kompilacji.

  3. Wyczyść pamięć podręczną i otwórz stronę kreatora:

    drush cr
    
  4. Dodaj element z wybieraka, ustaw jego tekst i kolor, i zapisz. Pole przechowuje:

    <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>
    

    Odwiedzający dostaje ten sam HTML bez atrybutów data-az*. Akapit zachowuje swój osadzony kolor, więc ten element nie potrzebuje biblioteki dla odwiedzających.

Ograniczenia, o których warto wiedzieć

  • Profile nie wymieniają własnych elementów. Lista kontrolna elementów w profilu kreatora pochodzi ze stałej listy w src/Form/DxprBuilderProfileForm.php i src/Service/Handler/ProfileHandler.php. Elementy spoza tej listy nigdy nie są ukrywane przez profil.
  • Ikony dla wybieraka potrzebują CSS z twojego modułu; klasy az-custom-icon dostarczane przez kreator obejmują tylko dostarczone elementy.
  • DxprBuilderService::getDxprElementsFolders() i jego hook_dxpr_builder_elements_folders_alter() zwracają listę katalogów elements w motywach, ale nic w edytorze nie odczytuje tej listy. To nie jest trasa rejestracji.
  • Karty Style i Animation dodawane są za ciebie przez register_animated_element(); nie redefiniuj tych parametrów.

Co dalej?

  • Hooki API dla hooków PHP dodających klasy narzędziowe i zestawy ikon.
  • Architektura dla dopasowania edytora i formatera pola do siebie.