Créer des éléments personnalisés.
Chaque élément DXPR Builder est un objet JavaScript empilé sur window.dxprBuilder.dxpr_elements. L’éditeur transforme chaque objet en classe d’élément avec les onglets Style et Animation déjà attachés. Cette page montre comment les éléments livrés sont définis dans dxpr_builder/dxpr_elements.js, comment un module ajoute sa propre définition à l’éditeur, et à quoi ressemble le HTML résultant. Il n’y a pas de modèles Twig côté serveur par élément ; les éléments sont rendus dans le navigateur.
Comment l’éditeur enregistre les éléments
L’éditeur charge six scripts dans cet ordre, définis dans dxpr_builder_library_info_build() dans dxpr_builder.module comme la bibliothèque dxpr_builder/editor.builder :
dxpr_templates.js: modèles twig.js côté client compilés depuisdxpr_builder/templates/.dxpr_global.js: utilitaires partagés et l’intégration CKEditor 5.dxpr_events.jsdxpr_param_types.js: empile un objet par type d’entrée surwindow.dxprBuilder.dxpr_param_types.dxpr_elements.js: crée le tableauwindow.dxprBuilder.dxpr_elementset empile une définition par élément.dxpr_builder.js: l’éditeur. Quand ce fichier s’exécute, il appellecreate_dxpr_elements(), qui boucle surwindow.dxprBuilder.dxpr_elementset pour chaque définition :- appelle
register_animated_element(base, is_container, Class), pour que la classe étendeAnimatedElement(onglet Style, onglet Animation, HTML ID, HTML classes et paramètres de placement) ; - ajoute les paramètres hérités aux
paramsde la définition ; - convertit chaque paramètre via
make_param_type(), qui fait correspondreparam.typeaux types de paramètres enregistrés ; - mélange les propriétés de la définition dans le prototype de la classe.
- appelle
Une définition doit donc être sur le tableau après l’exécution de dxpr_elements.js et avant celle de dxpr_builder.js. Les six fichiers se chargent avec l’attribut defer, donc l’ordre des balises de script dans le document est l’ordre d’exécution.
La définition d’élément
Voici l’élément Alert de dxpr_builder/dxpr_elements.js (source : dxpr_builder/build/dxpr-elements/elements/az_alert.js), réduit aux propriétés requises par chaque élément. L’original définit aussi el_class, un gestionnaire onCancel et un écouteur 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"));
},
});
| Propriété | Rôle |
|---|---|
base |
Nom machine. Il est écrit dans le HTML enregistré comme data-azb="az_alert" et sert à retrouver la classe quand la page est de nouveau analysée. Gardez le préfixe az_. |
name, nameEn |
Libellé dans le sélecteur d’éléments et sa forme non traduite. |
icon |
Classes CSS pour l’icône du sélecteur. Les icônes livrées sont définies dans dxpr_builder/sass/dxpr-builder-backend/components/_custom-icons.scss ; un élément personnalisé a besoin de son propre CSS pour sa classe d’icône. |
category |
Onglet du sélecteur. Les éléments de mise en page utilisent Drupal.t("Layout") ; blocs et vues utilisent "CMS". Omettez-le pour l’onglet de contenu par défaut. |
is_container |
true quand l’élément contient d’autres éléments. |
largeSettingsWindow |
Ouvre le dialogue de réglages en grande taille. |
hidden |
Masque l’élément du sélecteur tout en le gardant rendable. |
params |
Réglages montrés dans le dialogue. Voir ci-dessous. |
show_settings_on_create |
Ouvre le dialogue de réglages dès que l’élément est déposé. |
has_content |
L’élément a une zone de contenu modifiable (dom_content_element). |
render(...args) |
Construit this.dom_element depuis this.attrs, puis appelle le render de base. |
save_history() |
Empile une entrée d’annulation avec un libellé. |
parse_attrs_from_dom(element) |
Optionnel. Relit les valeurs de paramètres depuis le balisage enregistré, utilisé avec skip_data_azb et skip_params par des éléments comme Image qui stockent les valeurs dans les attributs <img> au lieu de data-azat-*. |
Paramètres
Chaque entrée de params a :
| Clé | Sens |
|---|---|
type |
L’un des types de paramètres enregistrés (section suivante). |
heading |
Libellé du champ. |
param_name |
Clé dans this.attrs et, dans le HTML enregistré, l’attribut data-azat-<param_name>. content est spécial : c’est le corps de l’élément, pas un attribut. |
value |
Valeur par défaut. Pour dropdown c’est un objet d’options avec les callbacks getText, getValue et getDefault. |
description |
Texte d’aide sous le champ. |
tab |
Regroupe le champ dans un accordéon nommé, par exemple Drupal.t("SEO"). |
can_be_empty |
Stocke une chaîne vide au lieu de retirer l’attribut. |
safe |
Ignore l’encodage URL de la valeur stockée. |
Les types de paramètres sont les valeurs type empilées sur window.dxprBuilder.dxpr_param_types dans 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 et video. Un type non enregistré retombe sur un champ de texte brut (BaseParamType).
Ne réutilisez pas un param_name hérité par chaque élément, comme color, background, el_class, hash ou les noms d’animation an_*. L’onglet Style possède déjà color, donc un second paramètre avec ce nom n’est jamais écrit dans data-azat-color.
Ce qui est enregistré
update_data() dans dxpr_builder.js écrit l’élément en HTML :
data-azb="<base>"sur l’élément racine ;data-azat-<param_name>="<value>"pour chaque paramètre dont la valeur diffère de son défaut, encodé en URL sauf si le paramètre définitsafe: true;- les paramètres de l’onglet Style comme
styleen ligne et classes, jamais comme attributsdata-azat-*; data-azcnt="true"sur l’élément de contenu quandhas_contentest défini.
Les visiteurs sans éditeur reçoivent ce HTML sans ses attributs data-az* : DxprBuilderService::parseDocumentForCleanup() les retire, sauf une liste fixe conservée sur les éléments animés et sur les éléments dynamiques intégrés comme Carousel. Si l’élément a besoin de JavaScript ou de CSS pour les visiteurs, posez un attribut data-dxpr-builder-libraries="<key>" sur sa racine : parseDocumentForTemplateLibrary() attache dxpr_builder/elements.<key> pour chaque clé sans barre oblique. Les clés sont les bibliothèques elements.* dans dxpr_builder.libraries.yml ; une clé avec une barre oblique n’est honorée que si elle est la library d’un jeu d’icônes enregistré.
Exemple minimal fonctionnel
L’exemple ajoute un élément « Notice » qui rend un paragraphe avec une couleur choisie. Il construit son DOM directement car les modèles twig.js compilés dans dxpr_templates.js font partie du paquet de l’éditeur et ne peuvent pas être étendus par un autre module.
-
Créez
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")); }, }); -
Insérez le fichier dans la bibliothèque de l’éditeur entre
dxpr_elements.js(poids-4) etdxpr_builder.js(poids-3) dansmymodule.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: FALSEetdefercorrespondent aux entrées que le module définit pour ses propres fichiers, pour que le fichier garde sa place dans la séquence, que les ressources de l’éditeur viennent du CDN ou d’un build local. -
Videz le cache et ouvrez une page du builder :
drush cr -
Ajoutez l’élément depuis le sélecteur, définissez son texte et sa couleur, et enregistrez. Le champ stocke :
<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 visiteur reçoit le même balisage sans les attributs
data-az*. Le paragraphe garde sa couleur en ligne, donc aucune bibliothèque visiteur n’est requise pour cet élément.
Limites à connaître
- Les profils ne listent pas les éléments personnalisés. La liste d’éléments d’un profil du builder vient d’une liste fixe dans
src/Form/DxprBuilderProfileForm.phpetsrc/Service/Handler/ProfileHandler.php. Les éléments hors de cette liste ne sont jamais masqués par un profil. - Les icônes pour le sélecteur ont besoin de CSS depuis votre module ; les classes
az-custom-iconlivrées par le builder ne couvrent que les éléments livrés. DxprBuilderService::getDxprElementsFolders()et sonhook_dxpr_builder_elements_folders_alter()renvoient une liste de dossierselementsdans les thèmes, mais rien dans l’éditeur ne lit cette liste. Ce n’est pas une voie d’enregistrement.- Les onglets Style et Animation sont ajoutés pour vous par
register_animated_element(); ne redéfinissez pas ces paramètres.
Et ensuite ?
- API des hooks pour les hooks PHP qui ajoutent des classes utilitaires et des jeux d’icônes.
- Architecture pour l’articulation entre l’éditeur et le formateur de champ.