UNPKG

@atlassian/aui

Version:

Atlassian User Interface library

293 lines (253 loc) 10.5 kB
import $ from './jquery'; import * as deprecate from './internal/deprecation'; import * as logger from './internal/log'; import globalize from './internal/globalize'; import escapeHtml from './escape-html'; import keyCode from './key-code'; import skate from './internal/skate'; import {CLOSE_BUTTON, CLOSE_BUTTON_CLASS_SELECTOR} from './close-button'; var DEFAULT_FADEOUT_DURATION = 500; var DEFAULT_FADEOUT_DELAY = 5000; var FADEOUT_RESTORE_DURATION = 100; function createMessageConstructor(type) { /** * * @param context * @param {Object} obj - message configuration * @param {String} [obj.id] - ID to add to the message. * @param {String} [obj.title] - Plain-text title of the message. If provided, will appear above the message body. * @param {String} obj.body - Content of the message. Can be HTML content. * @param {boolean} [obj.closeable] - If true, the message can be manually closed by the end-user via the UI. * @param {boolean} [obj.removeOnHide] - If true, the message will be removed from the DOM after hide. * @param {boolean} [obj.fadeout] * @param {boolean} [obj.duration] * @param {boolean} [obj.delay] * @returns {*|HTMLElement} */ messages[type] = function (context, obj) { if (!obj) { obj = context; context = '#aui-message-bar'; } // Set up our template options obj.closeable = obj.closeable !== null && obj.closeable !== false; // clean the title value obj.title = (obj.title || '').toString().trim(); let $message = renderMessageElement(obj, type); insertMessageIntoContext($message, obj.insert, context); // Attach the optional extra behaviours if (obj.removeOnHide) { makeRemoveOnHide($message, obj.delay, obj.duration); } if (obj.closeable) { makeCloseable($message); } if (obj.fadeout) { makeFadeout($message, obj.delay, obj.duration); } return $message; }; } function makeRemoveOnHide(message, delay, duration) { $(message || '.aui-message.aui-remove-on-hide').each(function () { var $this = $(this); makeFadeout($this, delay, duration) }) } function makeCloseable(message) { $(message || 'div.aui-message.closeable').each(function () { var $this = $(this); var $closeIcons = $this.find(CLOSE_BUTTON_CLASS_SELECTOR); var $icon = $closeIcons.length > 0 ? $closeIcons.first() : $(CLOSE_BUTTON); $this.addClass('closeable'); $this.append($icon); initCloseMessageBoxOnClickAndKeypress($this); }); } function makeFadeout(message, delay, duration) { delay = (typeof delay !== 'undefined') ? delay : DEFAULT_FADEOUT_DELAY; duration = (typeof duration !== 'undefined') ? duration : DEFAULT_FADEOUT_DURATION; $(message || 'div.aui-message.fadeout').each(function () { var $this = $(this); //Store the component state to avoid collisions between animations var hasFocus = false; var isHover = false; //Small functions to keep the code easier to read and avoid code duplication function fadeOut(){ //Algorithm: //1. Stop all running animations (first arg), including any fade animation and delay // Do not jump to the end of the animation (second arg). This prevents the message to abruptly // jump to opacity:0 or opacity:1 //2. Wait <delay> ms before starting the fadeout //3. Start the fadeout with a duration of <duration> ms //4. Close the message at the end of the animation $this.stop(true,false).delay(delay).fadeOut(duration, function () { $this.closeMessage(); }); } function resetFadeOut(){ //Algorithm: //1. Stop all running animations (first arg), including any fade animation and delay // Do not jump to the end of the animation (second arg). This prevents the message to abruptly // jump to opacity:0 or opacity:1 //2. Fast animation to opacity:1 $this.stop(true,false).fadeTo(FADEOUT_RESTORE_DURATION, 1); } function shouldStartFadeOut(){ return !hasFocus && !isHover; } //Attach handlers for user interactions (focus and hover) $this .focusin(function () { hasFocus = true; resetFadeOut(); }) .focusout(function () { hasFocus = false; if (shouldStartFadeOut()) { fadeOut(); } }) .hover( function () { //should be called .hoverin(), but jQuery does not implement that method isHover = true; resetFadeOut(); }, function () { //should be called .hoverout(), but jQuery does not implement that method isHover = false; if (shouldStartFadeOut()) { fadeOut(); } } ); //Initial animation fadeOut(); }); } /** * Utility methods to display different message types to the user. * Usage: * <pre> * messages.info("#container", { * title: "Info", * body: "You can choose to have messages without Close functionality.", * closeable: false, * }); * </pre> */ var messages = { setup: function () { makeRemoveOnHide(); makeCloseable(); makeFadeout(); }, makeRemoveOnHide: makeRemoveOnHide, makeCloseable: makeCloseable, makeFadeout: makeFadeout, createMessage: createMessageConstructor }; function initCloseMessageBoxOnClickAndKeypress($message) { $message.unbind('click.aui-message').unbind('keydown.aui-message'); $message.on('click.aui-message', CLOSE_BUTTON_CLASS_SELECTOR, function (e) { $(e.target).closest('.aui-message').closeMessage(); }).on('keydown.aui-message', CLOSE_BUTTON_CLASS_SELECTOR, function (e) { if ((e.which === keyCode.ENTER) || (e.which === keyCode.SPACE)) { $(e.target).closest('.aui-message').closeMessage(); e.preventDefault(); // this is especially important when handling the space bar, as we don't want to page down } }); } function insertMessageIntoContext($message, insertWhere, context) { if (insertWhere === 'prepend') { $message.prependTo(context); } else if (insertWhere === 'before') { $message.insertBefore(context); } else if (insertWhere === 'after') { $message.insertAfter(context); } else { $message.appendTo(context); } } function renderMessageElement ({id, closeable, removeOnHide, fadeout, title, body}, type) { // Convert the options in to template values const titleHtml = title ? `<p class="title"><strong>${escapeHtml(title)}</strong></p>` : ''; const html = `<div class="aui-message">${titleHtml}</div>`; // Construct the message element const $message = $(html) .append($.parseHTML(body || '')) .addClass(removeOnHide ? 'aui-remove-on-hide' : '') .addClass(closeable ? 'closeable' : '') .addClass(fadeout ? 'fadeout' : '') .addClass(`aui-message-${type}`); // Add ID if supplied if (id) { if (/[#\'\"\.\s]/g.test(id)) { // reject IDs that don't comply with style guide (ie. they'll break stuff) logger.warn('Messages error: ID rejected, must not include spaces, hashes, dots or quotes.'); } else { $message.attr('id', id); } } return $message; } $.fn.closeMessage = function () { var $message = $(this); if ($message.hasClass('aui-message') && ($message.hasClass('closeable') || $message.hasClass('aui-remove-on-hide'))) { $message.stop(true); //Stop any running animation $message.trigger('messageClose', [this]); //messageClose event Deprecated as of 5.3 $message.remove(); $(document).trigger('aui-message-close', [this]); //must trigger on document since the element has been removed } }; createMessageConstructor('generic'); //Deprecated Oct 2017 createMessageConstructor('error'); createMessageConstructor('warning'); createMessageConstructor('info'); createMessageConstructor('confirmation'); createMessageConstructor('change'); createMessageConstructor('success'); //Deprecated Oct 2017 createMessageConstructor('hint'); //Deprecated Oct 2017 const MessageEl = skate('aui-message', { created: function (element) { var body = element.innerHTML; var type = element.getAttribute('type') || 'info'; element.innerHTML = ''; messages[type](element, { body: body, removeOnHide: element.getAttribute('removeOnHide'), closeable: element.getAttribute('closeable'), delay: element.getAttribute('delay'), duration: element.getAttribute('duration'), fadeout: element.getAttribute('fadeout'), title: element.getAttribute('title') }); } }); $(function () { messages.setup(); }); deprecate.prop(messages, 'makeCloseable', { extraInfo: 'Use the "closeable" option in the constructor instead. Docs: https://aui.atlassian.com/latest/docs/messages.html' }); deprecate.prop(messages, 'createMessage', { extraInfo: 'Use the provided convenience methods instead e.g. messages.info(). Docs: https://aui.atlassian.com/latest/docs/messages.html' }); deprecate.prop(messages, 'makeFadeout', { extraInfo: 'Use the "fadeout" option in the constructor instead. Docs: https://aui.atlassian.com/latest/docs/messages.html' }); deprecate.prop(messages, 'generic', { extraInfo: 'use the messages.info() method instead. Docs: https://aui.atlassian.com/latest/docs/messages.html' }); deprecate.prop(messages, 'hint', { extraInfo: 'use the messages.info() method instead. Docs: https://aui.atlassian.com/latest/docs/messages.html' }); deprecate.prop(messages, 'success', { extraInfo: 'use the messages.confirmation() method instead. Docs: https://aui.atlassian.com/latest/docs/messages.html' }); // Exporting // --------- globalize('messages', messages); export default messages; export { MessageEl };