| 1 |
/** |
| 2 |
* WPSubsToast — transient confirmation, bottom-right. |
| 3 |
* |
| 4 |
* Usage: |
| 5 |
* WPSubsToast.show("Saved"); // green tick |
| 6 |
* WPSubsToast.show("Could not save", "error"); // red cross |
| 7 |
* |
| 8 |
* For something that already happened and needs no decision. Anything the |
| 9 |
* merchant has to act on belongs in the page: a toast leaves on its own and |
| 10 |
* takes its message with it. |
| 11 |
* |
| 12 |
* Markup is built here rather than templated, so any script can raise one |
| 13 |
* without the page having to reserve a slot for it. |
| 14 |
*/ |
| 15 |
(function () { |
| 16 |
"use strict"; |
| 17 |
|
| 18 |
var LIFETIME = 4000; |
| 19 |
|
| 20 |
/** |
| 21 |
* The shared bottom-right stack, created on first use. |
| 22 |
* |
| 23 |
* @return {HTMLElement} Host element. |
| 24 |
*/ |
| 25 |
function host() { |
| 26 |
var el = document.querySelector(".wpsubs-toast-host"); |
| 27 |
if (!el) { |
| 28 |
el = document.createElement("div"); |
| 29 |
el.className = "wpsubs-toast-host"; |
| 30 |
// Announced politely: a confirmation should not interrupt what a screen |
| 31 |
// reader is already saying. |
| 32 |
el.setAttribute("aria-live", "polite"); |
| 33 |
el.setAttribute("aria-atomic", "false"); |
| 34 |
document.body.appendChild(el); |
| 35 |
} |
| 36 |
return el; |
| 37 |
} |
| 38 |
|
| 39 |
/** |
| 40 |
* Tick or cross, as an inline SVG so it needs no icon font. |
| 41 |
* |
| 42 |
* @param {string} type "success" or "error". |
| 43 |
* @return {HTMLElement} Icon element. |
| 44 |
*/ |
| 45 |
function icon(type) { |
| 46 |
var wrap = document.createElement("span"); |
| 47 |
wrap.className = "wpsubs-toast__icon"; |
| 48 |
wrap.setAttribute("aria-hidden", "true"); |
| 49 |
wrap.innerHTML = |
| 50 |
"error" === type |
| 51 |
? '<svg viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M2 2l8 8M10 2l-8 8"/></svg>' |
| 52 |
: '<svg viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1.5 6.5l3 3 6-6"/></svg>'; |
| 53 |
return wrap; |
| 54 |
} |
| 55 |
|
| 56 |
/** |
| 57 |
* Raise a toast. |
| 58 |
* |
| 59 |
* @param {string} message Text to show. |
| 60 |
* @param {string} [type] "success" (default) or "error". |
| 61 |
* @return {?HTMLElement} The toast, or null when there is nothing to say. |
| 62 |
*/ |
| 63 |
function show(message, type) { |
| 64 |
if (!message) { |
| 65 |
return null; |
| 66 |
} |
| 67 |
|
| 68 |
var toast = document.createElement("div"); |
| 69 |
toast.className = "wpsubs-toast" + ("error" === type ? " wpsubs-toast--error" : ""); |
| 70 |
|
| 71 |
var text = document.createElement("span"); |
| 72 |
text.className = "wpsubs-toast__text"; |
| 73 |
text.textContent = message; |
| 74 |
|
| 75 |
toast.appendChild(icon(type)); |
| 76 |
toast.appendChild(text); |
| 77 |
host().appendChild(toast); |
| 78 |
|
| 79 |
// Nothing to trigger: the toast is visible as soon as it is in the DOM, |
| 80 |
// and the entry keyframe plays over that. Leaving is a class the CSS may |
| 81 |
// or may not animate; the removal below happens either way. |
| 82 |
window.setTimeout(function () { |
| 83 |
toast.classList.add("is-out"); |
| 84 |
window.setTimeout(function () { |
| 85 |
if (toast.parentNode) { |
| 86 |
toast.parentNode.removeChild(toast); |
| 87 |
} |
| 88 |
}, 260); |
| 89 |
}, LIFETIME); |
| 90 |
|
| 91 |
return toast; |
| 92 |
} |
| 93 |
|
| 94 |
window.WPSubsToast = { show: show }; |
| 95 |
})(); |
| 96 |
|