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