settings_tray.es6.js 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. /**
  2. * @file
  3. * Drupal's Settings Tray library.
  4. *
  5. * @private
  6. */
  7. (($, Drupal) => {
  8. const blockConfigureSelector = '[data-settings-tray-edit]';
  9. const toggleEditSelector = '[data-drupal-settingstray="toggle"]';
  10. const itemsToToggleSelector =
  11. '[data-off-canvas-main-canvas], #toolbar-bar, [data-drupal-settingstray="editable"] a, [data-drupal-settingstray="editable"] button';
  12. const contextualItemsSelector =
  13. '[data-contextual-id] a, [data-contextual-id] button';
  14. const quickEditItemSelector = '[data-quickedit-entity-id]';
  15. /**
  16. * Prevent default click events except contextual links.
  17. *
  18. * In edit mode the default action of click events is suppressed.
  19. *
  20. * @param {jQuery.Event} event
  21. * The click event.
  22. */
  23. function preventClick(event) {
  24. // Do not prevent contextual links.
  25. if ($(event.target).closest('.contextual-links').length) {
  26. return;
  27. }
  28. event.preventDefault();
  29. }
  30. /**
  31. * Close any active toolbar tray before entering edit mode.
  32. */
  33. function closeToolbarTrays() {
  34. $(Drupal.toolbar.models.toolbarModel.get('activeTab')).trigger('click');
  35. }
  36. /**
  37. * Disables the QuickEdit module editor if open.
  38. */
  39. function disableQuickEdit() {
  40. $('.quickedit-toolbar button.action-cancel').trigger('click');
  41. }
  42. /**
  43. * Closes/removes off-canvas.
  44. */
  45. function closeOffCanvas() {
  46. $('.ui-dialog-off-canvas .ui-dialog-titlebar-close').trigger('click');
  47. }
  48. /**
  49. * Gets all items that should be toggled with class during edit mode.
  50. *
  51. * @return {jQuery}
  52. * Items that should be toggled.
  53. */
  54. function getItemsToToggle() {
  55. return $(itemsToToggleSelector).not(contextualItemsSelector);
  56. }
  57. /**
  58. * Helper to switch edit mode state.
  59. *
  60. * @param {boolean} editMode
  61. * True enable edit mode, false disable edit mode.
  62. */
  63. function setEditModeState(editMode) {
  64. if (!document.querySelector('[data-off-canvas-main-canvas]')) {
  65. throw new Error(
  66. 'data-off-canvas-main-canvas is missing from settings-tray-page-wrapper.html.twig',
  67. );
  68. }
  69. editMode = !!editMode;
  70. const $editButton = $(toggleEditSelector);
  71. let $editables;
  72. // Turn on edit mode.
  73. if (editMode) {
  74. $editButton.text(Drupal.t('Editing'));
  75. closeToolbarTrays();
  76. $editables = $('[data-drupal-settingstray="editable"]').once(
  77. 'settingstray',
  78. );
  79. if ($editables.length) {
  80. // Use event capture to prevent clicks on links.
  81. document
  82. .querySelector('[data-off-canvas-main-canvas]')
  83. .addEventListener('click', preventClick, true);
  84. /**
  85. * When a click occurs try and find the settings-tray edit link
  86. * and click it.
  87. */
  88. $editables.not(contextualItemsSelector).on('click.settingstray', e => {
  89. // Contextual links are allowed to function in Edit mode.
  90. if (
  91. $(e.target).closest('.contextual').length ||
  92. !localStorage.getItem('Drupal.contextualToolbar.isViewing')
  93. ) {
  94. return;
  95. }
  96. $(e.currentTarget)
  97. .find(blockConfigureSelector)
  98. .trigger('click');
  99. disableQuickEdit();
  100. });
  101. $(quickEditItemSelector)
  102. .not(contextualItemsSelector)
  103. .on('click.settingstray', e => {
  104. /**
  105. * For all non-contextual links or the contextual QuickEdit link
  106. * close the off-canvas dialog.
  107. */
  108. if (
  109. !$(e.target)
  110. .parent()
  111. .hasClass('contextual') ||
  112. $(e.target)
  113. .parent()
  114. .hasClass('quickedit')
  115. ) {
  116. closeOffCanvas();
  117. }
  118. // Do not trigger if target is quick edit link to avoid loop.
  119. if (
  120. $(e.target)
  121. .parent()
  122. .hasClass('contextual') ||
  123. $(e.target)
  124. .parent()
  125. .hasClass('quickedit')
  126. ) {
  127. return;
  128. }
  129. $(e.currentTarget)
  130. .find('li.quickedit a')
  131. .trigger('click');
  132. });
  133. }
  134. }
  135. // Disable edit mode.
  136. else {
  137. $editables = $('[data-drupal-settingstray="editable"]').removeOnce(
  138. 'settingstray',
  139. );
  140. if ($editables.length) {
  141. document
  142. .querySelector('[data-off-canvas-main-canvas]')
  143. .removeEventListener('click', preventClick, true);
  144. $editables.off('.settingstray');
  145. $(quickEditItemSelector).off('.settingstray');
  146. }
  147. $editButton.text(Drupal.t('Edit'));
  148. closeOffCanvas();
  149. disableQuickEdit();
  150. }
  151. getItemsToToggle().toggleClass('js-settings-tray-edit-mode', editMode);
  152. $('.edit-mode-inactive').toggleClass('visually-hidden', editMode);
  153. }
  154. /**
  155. * Helper to check the state of the settings-tray mode.
  156. *
  157. * @todo don't use a class for this.
  158. *
  159. * @return {boolean}
  160. * State of the settings-tray edit mode.
  161. */
  162. function isInEditMode() {
  163. return $('#toolbar-bar').hasClass('js-settings-tray-edit-mode');
  164. }
  165. /**
  166. * Helper to toggle Edit mode.
  167. */
  168. function toggleEditMode() {
  169. setEditModeState(!isInEditMode());
  170. }
  171. /**
  172. * Prepares Ajax links to work with off-canvas and Settings Tray module.
  173. */
  174. function prepareAjaxLinks() {
  175. // Find all Ajax instances that use the 'off_canvas' renderer.
  176. Drupal.ajax.instances
  177. /**
  178. * If there is an element and the renderer is 'off_canvas' then we want
  179. * to add our changes.
  180. */
  181. .filter(
  182. instance =>
  183. instance &&
  184. $(instance.element).attr('data-dialog-renderer') === 'off_canvas',
  185. )
  186. /**
  187. * Loop through all Ajax instances that use the 'off_canvas' renderer to
  188. * set active editable ID.
  189. */
  190. .forEach(instance => {
  191. // Check to make sure existing dialogOptions aren't overridden.
  192. if (!instance.options.data.hasOwnProperty('dialogOptions')) {
  193. instance.options.data.dialogOptions = {};
  194. }
  195. instance.options.data.dialogOptions.settingsTrayActiveEditableId = $(
  196. instance.element,
  197. )
  198. .parents('.settings-tray-editable')
  199. .attr('id');
  200. instance.progress = { type: 'fullscreen' };
  201. });
  202. }
  203. /**
  204. * Reacts to contextual links being added.
  205. *
  206. * @param {jQuery.Event} event
  207. * The `drupalContextualLinkAdded` event.
  208. * @param {object} data
  209. * An object containing the data relevant to the event.
  210. *
  211. * @listens event:drupalContextualLinkAdded
  212. */
  213. $(document).on('drupalContextualLinkAdded', (event, data) => {
  214. /**
  215. * When contextual links are add we need to set extra properties on the
  216. * instances in Drupal.ajax.instances for them to work with Edit Mode.
  217. */
  218. prepareAjaxLinks();
  219. // When the first contextual link is added to the page set Edit Mode.
  220. $('body')
  221. .once('settings_tray.edit_mode_init')
  222. .each(() => {
  223. const editMode =
  224. localStorage.getItem('Drupal.contextualToolbar.isViewing') ===
  225. 'false';
  226. if (editMode) {
  227. setEditModeState(true);
  228. }
  229. });
  230. /**
  231. * Bind a listener to all 'Quick edit' links for blocks. Click "Edit"
  232. * button in toolbar to force Contextual Edit which starts Settings Tray
  233. * edit mode also.
  234. */
  235. data.$el.find(blockConfigureSelector).on('click.settingstray', () => {
  236. if (!isInEditMode()) {
  237. $(toggleEditSelector)
  238. .trigger('click')
  239. .trigger('click.settings_tray');
  240. }
  241. /**
  242. * Always disable QuickEdit regardless of whether "EditMode" was just
  243. * enabled.
  244. */
  245. disableQuickEdit();
  246. });
  247. });
  248. $(document).on('keyup.settingstray', e => {
  249. if (isInEditMode() && e.keyCode === 27) {
  250. Drupal.announce(Drupal.t('Exited edit mode.'));
  251. toggleEditMode();
  252. }
  253. });
  254. /**
  255. * Toggle the js-settings-tray-edit-mode class on items that we want to
  256. * disable while in edit mode.
  257. *
  258. * @type {Drupal~behavior}
  259. *
  260. * @prop {Drupal~behaviorAttach} attach
  261. * Toggle the js-settings-tray-edit-mode class.
  262. */
  263. Drupal.behaviors.toggleEditMode = {
  264. attach() {
  265. $(toggleEditSelector)
  266. .once('settingstray')
  267. .on('click.settingstray', toggleEditMode);
  268. },
  269. };
  270. // Manage Active editable class on opening and closing of the dialog.
  271. $(window).on({
  272. 'dialog:beforecreate': (event, dialog, $element, settings) => {
  273. if ($element.is('#drupal-off-canvas')) {
  274. $('body .settings-tray-active-editable').removeClass(
  275. 'settings-tray-active-editable',
  276. );
  277. const $activeElement = $(`#${settings.settingsTrayActiveEditableId}`);
  278. if ($activeElement.length) {
  279. $activeElement.addClass('settings-tray-active-editable');
  280. }
  281. }
  282. },
  283. 'dialog:beforeclose': (event, dialog, $element) => {
  284. if ($element.is('#drupal-off-canvas')) {
  285. $('body .settings-tray-active-editable').removeClass(
  286. 'settings-tray-active-editable',
  287. );
  288. }
  289. },
  290. });
  291. })(jQuery, Drupal);