diff --git a/package.json b/package.json new file mode 100644 index 0000000..dfb9736 --- /dev/null +++ b/package.json @@ -0,0 +1,6 @@ +{ + "name": "simpletoast", + "main": "simpletoast.js", + "browser": "simpletoast.js", + "private": true +} diff --git a/readme.md b/readme.md index bb37b10..f7ba6c2 100644 --- a/readme.md +++ b/readme.md @@ -80,3 +80,42 @@ You can provide a single button within an object, or an array of buttons. ], }); ``` + +### All Options +```javascript +const toast = new SimpleToast({ + title: '', + text: '', + buttons: [...button] || { + text: '', + className: '', + css: {}, + onclick(event, toast) { + // this; // toast reference + }, + }, + footer: '', + className: '' || [''] || { + toast: '' || [''], + button: '' || [''], + }, + css: { + toast: {}, + title: {}, + button: {}, + }, + timeout: 0, // Close toast after # milliseconds + onClose(reason, toast) { + // this; // toast reference + }, +}); + +// Methods on toast +toast.setText(newText); // Change text to newText +toast.exists(); // Does toast still exist? +toast.close(reason); // Close toast for optional reason + +SimpleToast.version; // Version in number form +SimpleToast.versionString; // Readable string of version +SimpleToast.count(); // Number of toasts open +``` diff --git a/simpletoast.js b/simpletoast.js index dbac3a3..a7bc134 100644 --- a/simpletoast.js +++ b/simpletoast.js @@ -1,23 +1,27 @@ /* * SimpleToast - A small library for toasts */ -(() => { +((root, factory) => { + // Do we care about frames? Until I get some tests in... no if (window !== window.top) return; - const version = buildVersion(1, 4, 1); - if (window.SimpleToast) { - if (SimpleToast.version) { - if (SimpleToast.version >= version.number) return; - } - console.log(`SimpleToast(v${version.string}): Overriding SimpleToast(v${SimpleToast.versionString || '[unknown]'})`); - } else { - console.log(`SimpleToast(v${version.string}): Loading`); + const boundToast = window.SimpleToast; + const localToast = factory(); + root.SimpleToast = localToast; + console.log(`SimpleToast(v${localToast.versionString}): Loaded`); + // Apply to window if SimpleToast doesn't exist or is outdated + if (root !== window && (!boundToast?.version || boundToast.version < localToast.version)) { + window.SimpleToast = localToast; + console.log(`SimpleToast(v${localToast.versionString}): Publicized`); } +})(this, () => { + const version = buildVersion(2, 0, 3); const style = { root: { display: 'flex', 'flex-direction': 'column-reverse', 'align-items': 'flex-end', position: 'fixed', + 'white-space': 'pre-wrap', bottom: 0, right: 0, zIndex: 1000, @@ -39,6 +43,10 @@ textShadow: '#3498db 1px 2px 1px', background: '#2980b9', }, + footer: { + display: 'block', + fontSize: '10px', + }, button: { height: '20px', margin: '-3px 0 0 3px', @@ -68,6 +76,16 @@ return old; } + function getClassName(clazz) { + if (Array.isArray(clazz)) { + return clazz.join(' '); + } + if (typeof clazz === 'string') { + return clazz; + } + return undefined; + } + const toasts = new Map(); let root = (() => { function create() { @@ -113,7 +131,7 @@ pending += 1; return; } - toast.close(); + toast.close('timeout'); }); if (pending) { startTimeout(); @@ -121,35 +139,55 @@ }, 1000); } - function Toast({title, text, className, css = {}, buttons, timeout}) { + function noop() {} + const blankToast = Object.freeze({ + setText: noop, + exists: () => false, + close: noop, + }); + function Toast({title, text, footer, className, css = {}, buttons, timeout, onClose} = {}) { if (typeof arguments[0] === 'string') { text = arguments[0]; } - if (!text) return; + if (!text) return blankToast; const id = count++; const el = document.createElement('div'); + const tel = el.appendChild(document.createElement('span')); + const body = el.appendChild(document.createElement('span')); + const fel = el.appendChild(document.createElement('span')); if (className) { - const clazz = className.toast || className; - el.className = Array.isArray(clazz) ? clazz.join(',') : (typeof clazz === 'string' ? clazz : undefined); + el.className = getClassName(className.toast || className); } applyCSS(el, style.toast); applyCSS(el, css.toast || css); // Add title, body if (title) { - const tel = document.createElement('span'); applyCSS(tel, style.title); applyCSS(tel, css.title); - tel.textContent = title; - el.appendChild(tel); + tel.innerHTML = title; + } + body.innerHTML = text; + if (footer) { + applyCSS(fel, style.footer); + applyCSS(fel, css.footer); + fel.innerHTML = footer; } - const body = document.createElement('span'); - body.textContent = text; - el.appendChild(body); + + const safeToast = {}; const toast = { - close: () => { + setText: (newText) => { + if (!newText || !toast.exists()) return; + body.innerHTML = newText; + }, + exists: () => toasts.has(id), + close: (closeType = 'unknown') => { + if (!toast.exists()) return; root.removeChild(el); toasts.delete(id); + if (typeof onClose === 'function') { + onClose.call(safeToast, closeType, safeToast); + } }, }; if (timeout) { @@ -163,51 +201,52 @@ buttons.forEach((button) => { if (!button.text) return; const elb = document.createElement('button'); - if (button.className || className && className.button) { - const clazz = button.className || className.button; - elb.className = Array.isArray(clazz) ? clazz.join(',') : clazz; + if (button.className || className?.button) { + elb.className = getClassName(button.className || className.button); } elb.innerHTML = button.text; applyCSS(elb, style.button); applyCSS(elb, css.button); applyCSS(elb, button.css); if (typeof button.onclick === 'function') { - elb.onclick = button.onclick; + elb.onclick = (e) => button.onclick.call(safeToast, e, safeToast); } let prev = {}; elb.onmouseover = () => { const hoverStyle = Object.assign( {}, style.button.mouseOver, - css.button && css.button.mouseOver, - button.css && button.css.mouseOver + css.button?.mouseOver, + button.css?.mouseOver ); - prev = applyCSS(hoverStyle); + prev = applyCSS(elb, hoverStyle); }; elb.onmouseout = () => { applyCSS(elb, prev); prev = {}; }; - el.appendChild(elb); + el.insertBefore(elb, fel); }); } - el.addEventListener('click', toast.close); + el.addEventListener('click', toast.close.bind(null, 'dismissed')); root.appendChild(el); toasts.set(id, toast); if (timeout) { startTimeout(); } - return toast; + Object.keys(blankToast).forEach((key) => safeToast[key] = toast[key]); + return safeToast; } Toast.version = version.number; Toast.versionString = version.string; - window.SimpleToast = Toast; + Toast.count = () => toasts.size; function buildVersion(major, minor = 0, patch = 0) { return { string: `${major}.${minor}${patch ? `.${patch}` : ''}`, - number: major * 1000000000 + minor * 1000 + patch, + number: major * 1000000000 + minor * 1000 + patch, }; } -})(); + return Object.freeze(Toast); +});