mercury_editor-2.0.x-dev/source/js/edit-screen.js

source/js/edit-screen.js
((Drupal, drupalSettings, $, once) => {
  // Catch all Ajax errors and send to home page.
  // @todo Consider a more elegant / graceful way to handle errors.
  $(document).ajaxError((event, xhr) => {
    if (xhr.status >= 400) {
      // eslint-disable-next-line no-alert
      alert(Drupal.t('Something went wrong. Please try again.'));
      window.location.href = `${window.location.origin}${window.location.pathname}?_ts=${Date.now()}`;
    }
  });

  // The width of the sidebar.
  let sidebarWidth;
  // The state of the sidebar.
  let sidebarState = 'open';

  /**
   * Open the sidebar.
   */
  function openSidebar() {
    if (sidebarState === 'open') {
      return;
    }
    sidebarState = 'open';
    // When re-opening the sidebar, set the width to the default width.
    sidebarWidth = localStorage.getItem('mercury-dialog-dock-default-width');
    if (sidebarWidth) {
      document.documentElement.style.setProperty(
        '--me-dialog-dock-right-width',
        `${sidebarWidth}px`,
      );
    }
  }

  /**
   * Close the sidebar.
   */
  function closeSidebar() {
    if (sidebarState === 'closed') {
      return;
    }
    sidebarState = 'closed';
    // When closing the sidebar, set the width to 10px.
    document.documentElement.style.setProperty(
      '--me-dialog-dock-right-width',
      '10px',
    );
    localStorage.setItem('mercury-dialog-dock-collapsed', 'true');
  }

  window.addEventListener('message', (event) => {
    if (event.data.type === 'openSidebar') {
      openSidebar();
    }
  });

  /**
   * Set the size of the preview iframe.
   * @param {String} w The width of the preview iframe.
   * @param {String} h The height of the preview iframe.
   */
  function setPreviewViewportSize(w, h) {
    const iframe = document.querySelector('#me-preview');
    if (w) {
      iframe.style.width = w;
    } else {
      iframe.style.removeProperty('width');
    }
    if (h) {
      iframe.style.height = h;
    } else {
      iframe.style.removeProperty('height');
    }
  }

  /**
   * Save the content.
   * @param {Event} event The click event.
   */
  function save() {
    const saveBtn = document.querySelector(
      '[data-drupal-selector="edit-submit"]:not([disabled])',
    );
    if (saveBtn) {
      const inputs =
        saveBtn.closest('form').querySelectorAll('input, textarea, select') ||
        [];
      // Checks visible form inputs for validity errors.
      const invalid = Array.from(inputs).filter(
        (input) =>
          !!(
            input.offsetWidth ||
            input.offsetHeight ||
            input.getClientRects().length
          ) && !input.validity.valid,
      );
      if (invalid.length) {
        invalid[0].focus();
        invalid[0].reportValidity();
      } else {
        saveBtn.dispatchEvent(new Event('mousedown'));
      }
    }
  }

  /**
   * Perform an undo or redo action.
   * @param {String} which Either 'undo' or 'redo'.
   */
  function undoRedo(which) {
    const match = window.location.pathname.match(
      /^\/mercury-editor\/([0-9a-fA-F-]+)/,
    );
    const id = match ? match[1] : null;
    Drupal.ajax({
      url: `/mercury-editor/${id}/${which}`,
      submit: {},
      success: () => Drupal.ajax({ url: `/mercury-editor/${id}` }).execute(),
    }).execute();
  }

  /**
   * Initialize the toolbar.
   */
  function initToolbar() {
    const previewIframeWrapper = document.querySelector('#me-iframe-wrapper');
    function mobileViewport() {
      const presetsSelect = document.querySelector('.me-mobile-presets');
      if (presetsSelect) {
        const preset =
          presetsSelect.options[presetsSelect.selectedIndex ?? 0].value;
        const presetValues = preset.split('x');
        setPreviewViewportSize(
          `${presetValues[0]}px`,
          `${Math.min(
            presetValues[1],
            window.innerHeight -
              document.getElementById('me-toolbar').offsetHeight -
              20,
          )}px`,
        );
      } else {
        setPreviewViewportSize(
          '390px',
          `${Math.min(
            '844',
            window.innerHeight -
              document.getElementById('me-toolbar').offsetHeight -
              20,
          )}px`,
        );
      }
    }
    const presetsSelect = document.querySelector('.me-mobile-presets');
    if (presetsSelect) {
      presetsSelect.addEventListener('change', mobileViewport);
    }
    document
      .querySelector('#me-mobile-toggle-btn')
      .addEventListener('click', (e) => {
        if (presetsSelect) {
          presetsSelect.style.display = 'block';
        }
        previewIframeWrapper.classList.add('is-device-preview');
        document
          .querySelector('#me-desktop-toggle-btn')
          .classList.remove('active');
        e.currentTarget.classList.add('active');
        mobileViewport();
        window.addEventListener('resize', mobileViewport);
        e.preventDefault();
        e.stopPropagation();
        return false;
      });
    document
      .querySelector('#me-desktop-toggle-btn')
      .addEventListener('click', (e) => {
        if (presetsSelect) {
          presetsSelect.style.display = 'none';
        }
        previewIframeWrapper.classList.remove('is-device-preview');
        document
          .querySelector('#me-mobile-toggle-btn')
          .classList.remove('active');
        e.currentTarget.classList.add('active');
        window.removeEventListener('resize', mobileViewport);
        setPreviewViewportSize('100cqw', '100cqw');
        e.preventDefault();
        e.stopPropagation();
        return false;
      });
    document.querySelector('#me-undo').addEventListener('click', () => {
      document.querySelector('.lpb-dialog')?.close();
    });
    document.querySelector('#me-redo').addEventListener('click', () => {
      document.querySelector('.lpb-dialog')?.close();
    });

    document
      .querySelector('#me-undo')
      .addEventListener('click', () => undoRedo('undo'));

    document
      .querySelector('#me-redo')
      .addEventListener('click', () => undoRedo('redo'));

    document.addEventListener('mercuryEditorUpdateState', (event) => {
      const { stateIndex, stateCount } = event.detail;
      const undoBtn = document.querySelector('#me-undo');
      const redoBtn = document.querySelector('#me-redo');
      if (stateIndex === 0) {
        undoBtn.classList.add('disabled');
      } else {
        undoBtn.classList.remove('disabled');
      }
      if (stateCount === 0 || stateIndex === stateCount - 1) {
        redoBtn.classList.add('disabled');
      } else {
        redoBtn.classList.remove('disabled');
      }
    });

    const saveBtn = document.querySelector(
      '[data-drupal-selector="edit-submit"]:not([disabled])',
    );
    if (saveBtn) {
      document.querySelector('#me-save-btn').addEventListener('click', save);
    } else {
      document.querySelector('#me-save-btn').remove();
    }

    // Store the default width of the dock.
    if (
      drupalSettings.mercuryEditor &&
      drupalSettings.mercuryEditor.defaultWidth
    ) {
      localStorage.setItem(
        'mercury-dialog-dock-default-width',
        drupalSettings.mercuryEditor.defaultWidth,
      );
    }

    const isTrayCollapsed =
      localStorage.getItem('mercury-dialog-dock-collapsed') === 'true';
    sidebarState = isTrayCollapsed ? 'closed' : 'open';

    const sidebarToggle = document.querySelector('#me-sidebar-toggle-btn');
    sidebarToggle.addEventListener('click', (e) => {
      if (sidebarState === 'open') {
        closeSidebar();
      } else {
        openSidebar();
      }
      e.preventDefault();
      e.stopPropagation();
      return false;
    });

    // Listen for dock resize events.
    document.addEventListener('mercury:dockResize', (e) => {
      // The width of the resize event.
      const { width } = e.detail;
      const contentElements = e.target.shadowRoot.querySelectorAll(
        'header, main, footer',
      );
      // If width is greater than 10, the dock is open.
      if (width > 10) {
        sidebarState = 'open';
        contentElements.forEach((el) => {
          el.style.display = '';
        });
        sidebarToggle.classList.remove('me-button--sidebar-expand');
        sidebarToggle.classList.add('me-button--sidebar-collapse');
        sidebarToggle.innerHTML = `<span>${Drupal.t('Hide sidebar')}</span>`;
        sidebarToggle.setAttribute('title', Drupal.t('Hide sidebar'));
        localStorage.removeItem('mercury-dialog-dock-collapsed');
      } else {
        sidebarState = 'closed';
        sidebarToggle.classList.remove('me-button--sidebar-collapse');
        sidebarToggle.classList.add('me-button--sidebar-expand');
        sidebarToggle.innerHTML = `<span>${Drupal.t('Show sidebar')}</span>`;
        sidebarToggle.setAttribute('title', Drupal.t('Show sidebar'));
        localStorage.setItem('mercury-dialog-dock-collapsed', 'true');
        contentElements.forEach((el) => {
          el.style.display = 'none';
        });
      }
      localStorage.setItem('mercury-dialog-dock-width', width);
    });
  }
  /**
   * Toggles mouse pointer events on the preview iFrame.
   *
   * When dragging the dialog border, the iFrame intercepts mouse events and
   * prematurely stops the drag behavior. Toggling pointer events prevents
   * this behavior.
   *
   * @param {Event} e
   *   The mouseup or mousedown event
   */
  function iFramePointerEventsToggle(e) {
    const iframe = document.querySelector('#me-preview');
    if (iframe) {
      iframe.style.pointerEvents = e.type === 'mouseup' ? 'auto' : 'none';
    }
  }

  /**
   * Attaches the behavior to the edit screen.
   */
  Drupal.behaviors.mercuryEditorEditScreen = {
    attach(context) {
      // After saving the host ME entity, check if a component form is open and
      // if so, re-focus the component to reload its data and form, preventing
      // stale form data issues.
      if (context?.classList?.contains('me-entity-form')) {
        const uuid = document.querySelector('[name="uuid"]')?.value;
        if (uuid) {
          const iframe = document.querySelector('#me-preview');
          if (iframe && iframe.contentWindow) {
            iframe.contentWindow.postMessage({
              type: 'componentSelected',
              settings: { uuid },
            });
          }
        }
      }
      // Move focus to the first input with error, if any.
      const firstError = once(
        'me-first-error',
        '.js-form-item.error',
        context,
      )[0];
      if (firstError) {
        firstError.focus();
        firstError.scrollIntoView({
          behavior: 'smooth',
        });
      }
      // Initialize the toolbar.
      if (once('me-toolbar', '#me-toolbar', context).length) {
        initToolbar();
      }
      // Open the edit tray dialog.
      if (once('me-edit-tray', '#me-edit-screen', context).length) {
        // Opens the dialog and attaches event listeners.
        const mercuryDialogElement = context.querySelector('#me-edit-screen');
        if (mercuryDialogElement) {
          const mercuryDialog = Drupal.mercuryDialog(mercuryDialogElement);
          mercuryDialog.show();
          // Attaches a custom beforeSerialize() method to Drupal.Ajax.
          // This method adds the ajaxPreviewPageState to the ajax request.
          if (
            typeof Drupal.Ajax !== 'undefined' &&
            typeof Drupal.Ajax.prototype.beforeSerializeMercuryEditor ===
              'undefined'
          ) {
            Drupal.Ajax.prototype.beforeSerializeMercuryEditor =
              Drupal.Ajax.prototype.beforeSerialize;
            Drupal.Ajax.prototype.beforeSerialize = function beforeSerialize(
              element,
              options,
            ) {
              this.beforeSerializeMercuryEditor(element, options);
              const pageState = drupalSettings.ajaxPreviewPageState || {};
              options.data['ajax_preview_page_state[theme]'] = pageState.theme;
              options.data['ajax_preview_page_state[theme_token]'] =
                pageState.theme_token;
              options.data['ajax_preview_page_state[libraries]'] =
                pageState.libraries;
            };
          }
          // Remove min-width from the dialog.
          // @todo Refactor once dialog min-width is addressed.
          // var style = document.createElement( 'style' )
          // style.innerHTML = 'dialog { min-width: 0 !important; }'
          // mercuryDialogElement.shadowRoot.appendChild( style );
          // @todo Refactor if we have drag events from dialog.
          document.addEventListener('mousedown', iFramePointerEventsToggle);
          document.addEventListener('mouseup', iFramePointerEventsToggle);
        }
      }
      // Set the iframe URL once other js files have loaded.
      if (once('me-preview-iframe', '#me-preview', context).length) {
        const iframe = document.querySelector('#me-preview');
        iframe.src = iframe.getAttribute('data-src');
      }
    },
  };
  /**
   * Ajax command to show save success feedback.
   */
  Drupal.AjaxCommands.prototype.mercuryEditorSaveSuccess = () => {
    const btn = document.querySelector('#me-save-btn');
    btn.classList.add('me-button--saved');
    btn.innerHTML = `${Drupal.t('Saved!')}`;
    setTimeout(() => {
      btn.classList.remove('me-button--saved');
      btn.innerHTML = `${Drupal.t('Save changes')}`;
    }, 2000);
    // Create a div element.
    const message = document.createElement('div');
    message.className = 'me-toolbar__save-status';
    // Use the current time.
    const now = new Date();
    const time = now.toLocaleTimeString([], {
      hour: '2-digit',
      minute: '2-digit',
    });
    message.innerHTML = Drupal.t('Changes saved at @time.', { '@time': time });
    // Remove any existing messages.
    document
      .querySelectorAll('.me-toolbar__save-status')
      .forEach((el) => el.remove());
    // Prepend the message to the "me-toolbar__edit-controls" element.
    document.querySelector('.me-toolbar__edit-controls').prepend(message);
    // Fade out and remove the message after 3 seconds.
    setTimeout(() => {
      message.style.opacity = '0';
      setTimeout(() => {
        message.remove();
      }, 1000);
    }, 3000);
  };
})(Drupal, drupalSettings, jQuery, once, Drupal.debounce);

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

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