display_builder-1.0.x-dev/components/display_builder/display_builder.js

components/display_builder/display_builder.js
/**
 * @file
 * Specific behaviors for the display builder.
 */
((Drupal, once) => {
  /**
   * Disable all links in builder and preview islands.
   *
   * @param {HTMLElement} builder - The builder element to disable links inside
   * @listens event:click
   */
  function disableInsideLinks(builder) {
    if (
      !builder.classList.contains('db-island-builder') &&
      !builder.classList.contains('db-island-preview')
    ) {
      return;
    }
    builder.querySelectorAll('a').forEach((link) => {
      if (link.closest('div').classList === 'contextual') {
        return;
      }
      link.addEventListener('click', (event) => event.preventDefault());
    });
  }

  /**
   * Gets the trigger type from an event.
   *
   * @param {CustomEventPrototype} event
   *   The event object containing detail.target with attributes.
   * @return {string}
   *   The trigger type (e.g. 'click', 'dragend') extracted from hx-trigger attribute.
   *   Returns empty string if no hx-trigger attribute exists.
   */
  function getTrigger(event) {
    let trigger =
      'hx-trigger' in event.detail.target.attributes
        ? event.detail.target.attributes['hx-trigger'].value
        : '';
    // 'click consume' must return 'click'.
    trigger = trigger.split(' ')[0].trim();
    return trigger;
  }

  /**
   * Checks if an event target is a dropzone element.
   *
   * @param {CustomEventPrototype} event - The event object to check
   * @return {boolean} True if the event target has the db-dropzone class
   */
  function isDropzone(event) {
    return event.detail.elt.classList.contains('db-dropzone');
  }

  /**
   * Adds position and instance values to an event's parameters.
   *
   * @param {CustomEventPrototype} event - The event to add values to
   * @return {CustomEventPrototype} The modified event with added parameters
   */
  function addVals(event) {
    const dropzone = event.detail.elt;
    const draggable = event.detail.triggeringEvent.target;
    const position = Array.from(dropzone.children).indexOf(draggable);
    if ('hx-vals' in draggable.attributes) {
      event.detail.parameters = JSON.parse(
        draggable.attributes['hx-vals'].value,
      );
    } else if (draggable.dataset?.instanceId) {
      event.detail.parameters = {
        instance_id: draggable.dataset.instanceId,
      };
    }
    event.detail.parameters.position = position;

    return event;
  }

  /**
   * Sets up event listeners for HTMX requests on a builder element.
   *
   * @param {HTMLElement} builder - The builder element to attach events to
   * @listens htmx:configRequest
   * @listens htmx:beforeRequest
   * @listens htmx:afterRequest
   */
  function alterHtmxEvents(builder) {
    builder.addEventListener('htmx:configRequest', (event) => {
      // Add draggable data & drop position to request.
      if (getTrigger(event) === 'dragend' && isDropzone(event)) {
        event = addVals(event);
      }
    });

    builder.addEventListener('htmx:beforeRequest', (event) => {
      // Class used for opacity until request is finished.
      builder.classList.add('db-htmx-before-request');
      // Don't trigger api_instance_get request if already the active instance.
      // Instead open the contextual edit actions.
      const activeInstanceId = builder.getAttribute('data-active-instance');
      const currentPath = event.detail.requestConfig.path;
      if (
        currentPath.endsWith(`/instance/${activeInstanceId}`) &&
        event.detail.requestConfig.verb === 'get'
      ) {
        event.preventDefault();
        builder.classList.remove('db-htmx-before-request');
        // Handle second click to open the modal.
        const editAction = builder.querySelector('[data-menu-action="edit"]');
        if (!editAction || editAction.dataset.ModalIsOpen) return;
        editAction.click();
      }
    });

    builder.addEventListener('htmx:afterRequest', (event) => {
      builder.classList.remove('db-htmx-before-request');
      const url = new URL(event.detail.xhr.responseURL);
      const instances = builder.querySelectorAll('[data-instance-id]');
      Array.from(instances).forEach((instance) => {
        if (instance.attributes['hx-get']?.value === url.pathname) {
          builder.setAttribute(
            'data-active-instance',
            instance.dataset.instanceId,
          );
        }
      });
    });
  }

  /**
   * Initialize the display builder mechanics with drag and drop and htmx.
   *
   * @type {Drupal~behavior}
   *
   * @prop {Drupal~behaviorAttach} attach
   *   Attaches the behaviors for display builder functionality.
   */
  Drupal.behaviors.displayBuilder = {
    attach(context, settings) {
      const debug = settings.dbDebug;
      once('dbInit', '.db-display-builder', context).forEach((builder) => {
        alterHtmxEvents(builder);
        Drupal.displayBuilder.initDrawer(builder, debug);
      });
      once('dbIslandInit', '.db-island-view', context).forEach((builder) => {
        disableInsideLinks(builder);
      });
    },
  };
})(Drupal, once);

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc