/** * @file * Drupal's Settings Tray library. * * @private */ (($, Drupal) => { const blockConfigureSelector = '[data-settings-tray-edit]'; const toggleEditSelector = '[data-drupal-settingstray="toggle"]'; const itemsToToggleSelector = '[data-off-canvas-main-canvas], #toolbar-bar, [data-drupal-settingstray="editable"] a, [data-drupal-settingstray="editable"] button'; const contextualItemsSelector = '[data-contextual-id] a, [data-contextual-id] button'; const quickEditItemSelector = '[data-quickedit-entity-id]'; /** * Prevent default click events except contextual links. * * In edit mode the default action of click events is suppressed. * * @param {jQuery.Event} event * The click event. */ function preventClick(event) { // Do not prevent contextual links. if ($(event.target).closest('.contextual-links').length) { return; } event.preventDefault(); } /** * Close any active toolbar tray before entering edit mode. */ function closeToolbarTrays() { $(Drupal.toolbar.models.toolbarModel.get('activeTab')).trigger('click'); } /** * Disables the QuickEdit module editor if open. */ function disableQuickEdit() { $('.quickedit-toolbar button.action-cancel').trigger('click'); } /** * Closes/removes off-canvas. */ function closeOffCanvas() { $('.ui-dialog-off-canvas .ui-dialog-titlebar-close').trigger('click'); } /** * Gets all items that should be toggled with class during edit mode. * * @return {jQuery} * Items that should be toggled. */ function getItemsToToggle() { return $(itemsToToggleSelector).not(contextualItemsSelector); } /** * Helper to switch edit mode state. * * @param {boolean} editMode * True enable edit mode, false disable edit mode. */ function setEditModeState(editMode) { if (!document.querySelector('[data-off-canvas-main-canvas]')) { throw new Error( 'data-off-canvas-main-canvas is missing from settings-tray-page-wrapper.html.twig', ); } editMode = !!editMode; const $editButton = $(toggleEditSelector); let $editables; // Turn on edit mode. if (editMode) { $editButton.text(Drupal.t('Editing')); closeToolbarTrays(); $editables = $('[data-drupal-settingstray="editable"]').once( 'settingstray', ); if ($editables.length) { // Use event capture to prevent clicks on links. document .querySelector('[data-off-canvas-main-canvas]') .addEventListener('click', preventClick, true); /** * When a click occurs try and find the settings-tray edit link * and click it. */ $editables.not(contextualItemsSelector).on('click.settingstray', e => { // Contextual links are allowed to function in Edit mode. if ( $(e.target).closest('.contextual').length || !localStorage.getItem('Drupal.contextualToolbar.isViewing') ) { return; } $(e.currentTarget) .find(blockConfigureSelector) .trigger('click'); disableQuickEdit(); }); $(quickEditItemSelector) .not(contextualItemsSelector) .on('click.settingstray', e => { /** * For all non-contextual links or the contextual QuickEdit link * close the off-canvas dialog. */ if ( !$(e.target) .parent() .hasClass('contextual') || $(e.target) .parent() .hasClass('quickedit') ) { closeOffCanvas(); } // Do not trigger if target is quick edit link to avoid loop. if ( $(e.target) .parent() .hasClass('contextual') || $(e.target) .parent() .hasClass('quickedit') ) { return; } $(e.currentTarget) .find('li.quickedit a') .trigger('click'); }); } } // Disable edit mode. else { $editables = $('[data-drupal-settingstray="editable"]').removeOnce( 'settingstray', ); if ($editables.length) { document .querySelector('[data-off-canvas-main-canvas]') .removeEventListener('click', preventClick, true); $editables.off('.settingstray'); $(quickEditItemSelector).off('.settingstray'); } $editButton.text(Drupal.t('Edit')); closeOffCanvas(); disableQuickEdit(); } getItemsToToggle().toggleClass('js-settings-tray-edit-mode', editMode); $('.edit-mode-inactive').toggleClass('visually-hidden', editMode); } /** * Helper to check the state of the settings-tray mode. * * @todo don't use a class for this. * * @return {boolean} * State of the settings-tray edit mode. */ function isInEditMode() { return $('#toolbar-bar').hasClass('js-settings-tray-edit-mode'); } /** * Helper to toggle Edit mode. */ function toggleEditMode() { setEditModeState(!isInEditMode()); } /** * Prepares Ajax links to work with off-canvas and Settings Tray module. */ function prepareAjaxLinks() { // Find all Ajax instances that use the 'off_canvas' renderer. Drupal.ajax.instances /** * If there is an element and the renderer is 'off_canvas' then we want * to add our changes. */ .filter( instance => instance && $(instance.element).attr('data-dialog-renderer') === 'off_canvas', ) /** * Loop through all Ajax instances that use the 'off_canvas' renderer to * set active editable ID. */ .forEach(instance => { // Check to make sure existing dialogOptions aren't overridden. if (!instance.options.data.hasOwnProperty('dialogOptions')) { instance.options.data.dialogOptions = {}; } instance.options.data.dialogOptions.settingsTrayActiveEditableId = $( instance.element, ) .parents('.settings-tray-editable') .attr('id'); instance.progress = { type: 'fullscreen' }; }); } /** * Reacts to contextual links being added. * * @param {jQuery.Event} event * The `drupalContextualLinkAdded` event. * @param {object} data * An object containing the data relevant to the event. * * @listens event:drupalContextualLinkAdded */ $(document).on('drupalContextualLinkAdded', (event, data) => { /** * When contextual links are add we need to set extra properties on the * instances in Drupal.ajax.instances for them to work with Edit Mode. */ prepareAjaxLinks(); // When the first contextual link is added to the page set Edit Mode. $('body') .once('settings_tray.edit_mode_init') .each(() => { const editMode = localStorage.getItem('Drupal.contextualToolbar.isViewing') === 'false'; if (editMode) { setEditModeState(true); } }); /** * Bind a listener to all 'Quick edit' links for blocks. Click "Edit" * button in toolbar to force Contextual Edit which starts Settings Tray * edit mode also. */ data.$el.find(blockConfigureSelector).on('click.settingstray', () => { if (!isInEditMode()) { $(toggleEditSelector) .trigger('click') .trigger('click.settings_tray'); } /** * Always disable QuickEdit regardless of whether "EditMode" was just * enabled. */ disableQuickEdit(); }); }); $(document).on('keyup.settingstray', e => { if (isInEditMode() && e.keyCode === 27) { Drupal.announce(Drupal.t('Exited edit mode.')); toggleEditMode(); } }); /** * Toggle the js-settings-tray-edit-mode class on items that we want to * disable while in edit mode. * * @type {Drupal~behavior} * * @prop {Drupal~behaviorAttach} attach * Toggle the js-settings-tray-edit-mode class. */ Drupal.behaviors.toggleEditMode = { attach() { $(toggleEditSelector) .once('settingstray') .on('click.settingstray', toggleEditMode); }, }; // Manage Active editable class on opening and closing of the dialog. $(window).on({ 'dialog:beforecreate': (event, dialog, $element, settings) => { if ($element.is('#drupal-off-canvas')) { $('body .settings-tray-active-editable').removeClass( 'settings-tray-active-editable', ); const $activeElement = $(`#${settings.settingsTrayActiveEditableId}`); if ($activeElement.length) { $activeElement.addClass('settings-tray-active-editable'); } } }, 'dialog:beforeclose': (event, dialog, $element) => { if ($element.is('#drupal-off-canvas')) { $('body .settings-tray-active-editable').removeClass( 'settings-tray-active-editable', ); } }, }); })(jQuery, Drupal);