إنشاء عناصر مخصصة.

تعلّم DXPR Builderالتوسعةالبناء عليه

كل عنصر في DXPR Builder كائن JavaScript مدفوع إلى window.dxprBuilder.dxpr_elements. فيحوّل المحرّر كل كائن إلى صنف عنصر مع تبويبي النمط والحركة مرفقين أصلًا. توضح هذه الصفحة كيفية تعريف العناصر المشحونة في 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 (تبويب النمط وتبويب الحركة ومعرّف HTML وفئات HTML ومعاملات الموضع)؛
    • تُلحق المعاملات الموروثة بـ params الخاصة بالتعريف؛
    • تحوّل كل معامل عبر make_param_type() التي تطابق param.type مع أنواع المعاملات المسجلة؛
    • تمزج خصائص التعريف في نموذج الصنف الأولي.

يجب لذلك أن يكون التعريف على المصفوفة بعد عمل dxpr_elements.js وقبل عمل dxpr_builder.js. وتُحمَّل الملفات الستة كلها مع السمة defer، فترتيب وسوم النصوص في المستند هو ترتيب التنفيذ.

تعريف العنصر

هذا عنصر التنبيه من 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 من عناصر مثل الصورة التي تخزّن القيم في سمات <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_* الخاصة بالحركة. فتبويب النمط يملك color أصلًا، فلا يُكتَب معامل ثانٍ بهذا الاسم أبدًا في data-azat-color.

ما يُحفَظ

update_data() في dxpr_builder.js تكتب العنصر إلى HTML:

  • data-azb="<base>" على العنصر الجذري؛
  • data-azat-<param_name>="<value>" لكل معامل تختلف قيمته عن الافتراضي، مرمّزة بترميز URL ما لم يضبط المعامل safe: true؛
  • معاملات تبويب النمط كـ style مضمّنة وفئات، أبدًا كسمات data-azat-*؛
  • data-azcnt="true" على عنصر المحتوى عندما يُضبَط has_content.

يستقبل المشاهِدون الذين لا يحصلون على المحرّر HTML هذا دون سماته data-az*: فيجرّدها DxprBuilderService::parseDocumentForCleanup()، عدا قائمة ثابتة يُبقيها على العناصر المتحركة وعلى العناصر الديناميكية المدمجة مثل الدوّار. وإذا احتاج العنصر 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 في القوالب، لكن لا شيء في المحرّر يقرأ تلك القائمة. فليست مسار تسجيل.
  • تبويبا النمط والحركة يُضافان لكم عبر register_animated_element()؛ فلا تعيدوا تعريف تلك المعاملات.

ما الخطوة التالية؟

  • واجهة الخطافات لخطافات PHP التي تضيف فئات الأدوات ومجموعات الأيقونات.
  • البنية لكيفية تلاؤم المحرّر ومُنسِّق الحقل معًا.