/** * @file * Non-error Drupal status messages (status/warning) auto-dismiss after a * couple seconds; error messages always require an explicit click on * Gin's own dismiss button (.js-message-button-hide) -- something that * needs attention shouldn't vanish on its own. * * Drupal.Message.add() (used both for messages already in the initial * page render and for MessageCommand-driven AJAX messages) just appends a * DOM node directly -- it never re-fires Drupal.attachBehaviors(), so a * behaviors/once()-based approach would miss anything added after the * initial page load. A MutationObserver sees every insertion path. */ (function (Drupal) { 'use strict'; var AUTO_DISMISS_MS = 2000; function dismiss(el) { var btn = el.querySelector('.js-message-button-hide'); if (btn) { btn.click(); } else { el.style.opacity = 0; el.classList.add('visually-hidden'); } } function handleMessage(el) { if (el.dataset.figliAutoDismissSeen) return; el.dataset.figliAutoDismissSeen = '1'; if (el.classList.contains('messages--error')) return; setTimeout(function () { dismiss(el); }, AUTO_DISMISS_MS); } function scan(root) { root.querySelectorAll('.messages-list__item').forEach(handleMessage); } /** * Lifts the [data-drupal-messages] region to a direct child. * * Gin renders it deep inside its layout (main.page-content > region * highlighted), where ancestor stacking contexts neutralize the * region's position:fixed + high z-index (admin-chrome.css): the whole * subtree then stacks below the jQuery UI modal overlay -- a direct * child -- which is why messages inserted while a modal is open * (MessageCommand-driven, e.g. the répartition-sum error on the * ligne_comptable modal form) rendered unreadably *under* the overlay. * As a child, the region's z-index competes directly with the * overlay/dialog and wins. Core itself puts the fallback wrapper at * body level (Drupal.Message.defaultWrapper(), misc/message.js), so * this is also where messages already land on a page without the * region; MessageCommand re-queries the region at response time, so * moving it after page load breaks no insertion path. */ function liftMessages() { var region = document.querySelector('[data-drupal-messages]'); if (region && region.parentElement !== document.body) { document.body.appendChild(region); } } function init() { liftMessages(); scan(document); new MutationObserver(function (mutations) { mutations.forEach(function (mutation) { mutation.addedNodes.forEach(function (node) { if (node.nodeType !== 1) return; if (node.classList && node.classList.contains('messages-list__item')) { handleMessage(node); } // A modal (or a re-rendered messages region) entering the DOM: // re-check the lift right when it matters -- the region must // already be body-level when the dialog's overlay and any // MessageCommand insertions show up. matches() first because // querySelector() never matches the node itself. liftMessages() // is idempotent, so over-triggering is harmless. if (node.matches('.ui-dialog, [data-drupal-messages]') || node.querySelector('.ui-dialog, [data-drupal-messages]')) { liftMessages(); } if (node.querySelectorAll) { scan(node); } }); }); }).observe(document.body, { childList: true, subtree: true }); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })(Drupal);