Source: messages/toast.js

/**
 * @namespace qui.messages.toast
 */

import $ from '$qui/lib/jquery.module.js'

import StockIcon   from '$qui/icons/stock-icon.js'
import * as Theme  from '$qui/theme.js'
import {asap}      from '$qui/utils/misc.js'
import * as Window from '$qui/window.js'


const TOAST_MESSAGE_TIMEOUT = 5000 /* 5 seconds */

let toastMessageTimeoutHandle = null
let toastMessageContainer = null
let toastMessageShowingTimeoutHandle = null


/**
 * Show a toast message for a limited period of time.
 * @alias qui.messages.toast.show
 * @param {?String|jQuery} message the message to show
 * @param {?String} type message type (`"info"`, `"warning"` or `"error"`)
 * @param {Number} [timeout] the message display timeout, in milliseconds (defaults to 5 seconds)
 * @param {Boolean} [closeable] whether the message can be closed by the user (defaults to `true`)
 */
export function show({message, type, timeout = TOAST_MESSAGE_TIMEOUT, closeable = true}) {
    let messageSpan = toastMessageContainer.find('span.qui-toast-message')

    if (toastMessageTimeoutHandle) {
        clearTimeout(toastMessageTimeoutHandle)
        toastMessageTimeoutHandle = null
    }

    if (message) {
        Window.$body.addClass('toast-message-visible')
        toastMessageContainer.removeClass('info warning error multiline closeable')

        messageSpan.html(message)
        if (type) {
            toastMessageContainer.addClass(type)
        }

        /* Test if the message wraps */
        let normalHeight = messageSpan.height()
        toastMessageContainer.find('div.qui-toast-message').css('white-space', 'normal')
        let wrappedHeight = messageSpan.height()
        toastMessageContainer.find('div.qui-toast-message').css('white-space', '')

        if (normalHeight < wrappedHeight) {
            toastMessageContainer.addClass('multiline')
        }

        if (timeout !== 0) {
            toastMessageTimeoutHandle = setTimeout(function () {
                show({message: null, type: 'info'})
            }, timeout)
        }

        if (closeable !== false) {
            toastMessageContainer.addClass('closeable')
        }

        /* Showing flag timeout */
        if (toastMessageShowingTimeoutHandle) {
            clearTimeout(toastMessageShowingTimeoutHandle)
        }

        toastMessageShowingTimeoutHandle = asap(function () {
            toastMessageShowingTimeoutHandle = null
        })
    }
    else if (Window.$body.hasClass('toast-message-visible')) { /* Hide */
        Window.$body.removeClass('toast-message-visible')
        Theme.afterTransition(function () {
            if (Window.$body.hasClass('toast-message-visible')) {
                return /* Reshown in the meantime */
            }

            messageSpan.html('')
            toastMessageContainer.removeClass('info warning error multiline')
        })
    }
}

/**
 * Hide a currently displayed toast message.
 * @alias qui.messages.toast.hide
 */
export function hide() {
    show({message: null, type: 'info'})
}

/**
 * Convenience function for {@link qui.messages.toast.show} with default timeout and type `"info"`.
 * @alias qui.messages.toast.info
 * @param {String|jQuery} message the message to show
 */
export function info(message) {
    show({message: message, type: 'info'})
}

/**
 * Convenience function for {@link qui.messages.toast.show} with default timeout and type `"warning"`.
 * @alias qui.messages.toast.warning
 * @param {String|jQuery} message the message to show
 */
export function warning(message) {
    show({message: message, type: 'warning'})
}

/**
 * Convenience function for {@link qui.messages.toast.show} with default timeout and type `"error"`.
 * @alias qui.messages.toast.error
 * @param {String|jQuery} message the message to show
 */
export function error(message) {
    show({message: message, type: 'error'})
}


export function init() {
    toastMessageContainer = $('<div></div>', {class: 'qui-toast-message-container'})
    toastMessageContainer.append($('<div></div>', {class: 'qui-toast-message-icon'}))

    let messageDiv = $('<div></div>', {class: 'qui-toast-message'})
    messageDiv.append($('<span></span>', {class: 'qui-toast-message'}))
    messageDiv.append($('<div></div>', {class: 'qui-base-button qui-toast-message-close-button'}))
    toastMessageContainer.append(messageDiv)

    Window.$body.append(toastMessageContainer)

    /* Close button */
    let closeButton = toastMessageContainer.find('div.qui-toast-message-close-button')
    new StockIcon({
        name: 'close', variant: 'interactive', activeVariant: 'interactive-active'
    }).alter({scale: 0.75}).applyTo(closeButton)

    closeButton.on('click', function () {
        show({message: null, type: null})
    })

    /* Close when clicked anywhere */
    Window.$body.on('click', function (e) {
        if (toastMessageShowingTimeoutHandle) {
            return /* Showing is in progress */
        }

        if (!Window.$body.hasClass('toast-message-visible')) {
            return /* Message not visible */
        }

        if (!toastMessageContainer.hasClass('closeable')) {
            return /* Message not closeable */
        }

        if (toastMessageContainer.has(e.target).length) {
            return /* Clicked on the message container itself */
        }

        show({message: null, type: null})
    })
}