Creare elementi personalizzati.

Learn DXPR BuilderExtendArchitectuur en API’s

Ogni elemento di DXPR Builder è un oggetto JavaScript inserito in window.dxprBuilder.dxpr_elements. L’editor trasforma ogni oggetto in una classe di elemento con le schede Style e Animation già collegate. Questa pagina mostra come sono definiti gli elementi inclusi in dxpr_builder/dxpr_elements.js, come un modulo aggiunge la sua definizione all’editor e come appare l’HTML risultante. Non esistono modelli Twig lato server per elemento; gli elementi sono renderizzati nel browser.

Come l’editor registra gli elementi

L’editor carica sei script in quest’ordine, definiti in dxpr_builder_library_info_build() in dxpr_builder.module come libreria dxpr_builder/editor.builder:

  1. dxpr_templates.js: modelli twig.js lato client compilati da dxpr_builder/templates/.
  2. dxpr_global.js: helper condivisi e integrazione CKEditor 5.
  3. dxpr_events.js
  4. dxpr_param_types.js: inserisce un oggetto per tipo di input in window.dxprBuilder.dxpr_param_types.
  5. dxpr_elements.js: crea l’array window.dxprBuilder.dxpr_elements e inserisce una definizione per elemento.
  6. dxpr_builder.js: l’editor. Quando questo file è eseguito chiama create_dxpr_elements(), che cicla su window.dxprBuilder.dxpr_elements e per ogni definizione:
    • chiama register_animated_element(base, is_container, Class), così la classe estende AnimatedElement (scheda Style, scheda Animation, HTML ID, HTML classes e parametri di posizionamento);
    • accoda i parametri ereditati ai params della definizione;
    • converte ogni parametro tramite make_param_type(), che confronta param.type con i tipi di parametro registrati;
    • mescola le proprietà della definizione nel prototipo della classe.

Una definizione deve quindi stare sull’array dopo che dxpr_elements.js è girato e prima che dxpr_builder.js giri. Tutti e sei i file si caricano con l’attributo defer, quindi l’ordine dei tag script nel documento è l’ordine di esecuzione.

La definizione di elemento

Questo è l’elemento Alert da dxpr_builder/dxpr_elements.js (sorgente: dxpr_builder/build/dxpr-elements/elements/az_alert.js), ridotto alle proprietà necessarie a ogni elemento. L’originale imposta anche el_class, un handler onCancel e un 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"));
  },
});
Proprietà Scopo
base Nome macchina. È scritto nell’HTML salvato come data-azb="az_alert" ed è usato per trovare la classe quando la pagina è analizzata di nuovo. Mantieni il prefisso az_.
name, nameEn Etichetta nel selettore di elementi e sua forma non tradotta.
icon Classi CSS per l’icona del selettore. Le icone incluse sono definite in dxpr_builder/sass/dxpr-builder-backend/components/_custom-icons.scss; un elemento personalizzato richiede il proprio CSS per la sua classe icona.
category Scheda del selettore. Gli elementi di layout usano Drupal.t("Layout"); blocchi e viste usano "CMS". Omettila per la scheda di contenuto predefinita.
is_container true quando l’elemento contiene altri elementi.
largeSettingsWindow Apre la finestra delle impostazioni nella dimensione maggiore.
hidden Nasconde l’elemento dal selettore mantenendolo renderizzabile.
params Impostazioni mostrate nella finestra. Vedi sotto.
show_settings_on_create Apre la finestra delle impostazioni appena l’elemento è rilasciato.
has_content L’elemento ha un’area di contenuto modificabile (dom_content_element).
render(...args) Costruisce this.dom_element da this.attrs, poi chiama il render base.
save_history() Inserisce una voce di annullamento con un’etichetta.
parse_attrs_from_dom(element) Opzionale. Rilegge i valori dei parametri dal markup salvato, usato insieme a skip_data_azb e skip_params da elementi come Image che memorizzano i valori negli attributi <img> invece che in data-azat-*.

Parametri

Ogni voce in params ha:

Chiave Significato
type Uno dei tipi di parametro registrati (sezione successiva).
heading Etichetta del campo.
param_name Chiave in this.attrs e, nell’HTML salvato, l’attributo data-azat-<param_name>. content è speciale: è il corpo dell’elemento, non un attributo.
value Valore predefinito. Per dropdown è un oggetto di opzioni con callback getText, getValue e getDefault.
description Testo di aiuto sotto il campo.
tab Raggruppa il campo in un accordion con nome, ad esempio Drupal.t("SEO").
can_be_empty Memorizza una stringa vuota invece di rimuovere l’attributo.
safe Salta la codifica URL del valore memorizzato.

I tipi di parametro sono i valori type inseriti in window.dxprBuilder.dxpr_param_types in 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 e video. Un tipo non registrato ripiega a un semplice input di testo (BaseParamType).

Non riutilizzare un param_name ereditato da ogni elemento, come color, background, el_class, hash o i nomi di animazione an_*. La scheda Style possiede già color, quindi un secondo parametro con quel nome non è mai scritto in data-azat-color.

Cosa viene salvato

update_data() in dxpr_builder.js scrive l’elemento in HTML:

  • data-azb="<base>" sull’elemento root;
  • data-azat-<param_name>="<value>" per ogni parametro il cui valore differisce dal predefinito, codificato URL a meno che il parametro imposti safe: true;
  • i parametri della scheda Style come style inline e classi, mai come attributi data-azat-*;
  • data-azcnt="true" sull’elemento di contenuto quando has_content è impostato.

I visitatori senza editor ricevono questo HTML senza i suoi attributi data-az*: DxprBuilderService::parseDocumentForCleanup() li rimuove, tranne un elenco fisso mantenuto sugli elementi animati e sugli elementi dinamici integrati come Carousel. Se l’elemento richiede JavaScript o CSS per i visitatori, metti un attributo data-dxpr-builder-libraries="<key>" sulla sua root: parseDocumentForTemplateLibrary() collega dxpr_builder/elements.<key> per ogni chiave senza slash. Le chiavi sono le librerie elements.* in dxpr_builder.libraries.yml; una chiave con slash è onorata solo quando è la library di un set di icone registrato.

Esempio minimo funzionante

L’esempio aggiunge un elemento "Notice" che renderizza un paragrafo con un colore scelto. Costruisce il suo DOM direttamente perché i modelli twig.js compilati in dxpr_templates.js fanno parte del bundle dell’editor e non possono essere estesi da un altro modulo.

  1. Crea 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. Inserisci il file nella libreria dell’editor tra dxpr_elements.js (peso -4) e dxpr_builder.js (peso -3) in 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 e defer corrispondono alle voci che il modulo definisce per i suoi file, così il file mantiene il suo posto nella sequenza sia che gli asset dell’editor vengano dalla CDN o da una build locale.

  3. Svuota la cache e apri una pagina builder:

    drush cr
    
  4. Aggiungi l’elemento dal selettore, impostane testo e colore, e salva. Il campo memorizza:

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

    Un visitatore riceve lo stesso markup senza gli attributi data-az*. Il paragrafo mantiene il suo colore inline, quindi nessuna libreria per visitatori serve per questo elemento.

Limiti da conoscere

  • I profili non elencano elementi personalizzati. La checklist degli elementi in un profilo builder viene da un elenco fisso in src/Form/DxprBuilderProfileForm.php e src/Service/Handler/ProfileHandler.php. Gli elementi fuori da quell’elenco non sono mai nascosti da un profilo.
  • Le icone per il selettore richiedono CSS dal tuo modulo; le classi az-custom-icon incluse nel builder coprono solo gli elementi inclusi.
  • DxprBuilderService::getDxprElementsFolders() e il suo hook_dxpr_builder_elements_folders_alter() restituiscono un elenco di cartelle elements nei temi, ma niente nell’editor legge quell’elenco. Non è una route di registrazione.
  • Le schede Style e Animation sono aggiunte per te da register_animated_element(); non ridefinire quei parametri.

Prossimi passi

  • Riferimento API hook per gli hook PHP che aggiungono classi di utilità e set di icone.
  • Architettura per come l’editor e il formatter di campo stanno insieme.