UNPKG

mini-toastr

Version:
236 lines (203 loc) 6.26 kB
export function fadeOut (element, cb) { if (element.style.opacity && element.style.opacity > 0.05) { element.style.opacity = element.style.opacity - 0.05 } else if (element.style.opacity && element.style.opacity <= 0.1) { if (element.parentNode) { element.parentNode.removeChild(element) if (cb) cb() } } else { element.style.opacity = 0.9 } setTimeout(() => fadeOut.apply(this, [element, cb]), 1000 / 30 ) } export const LIB_NAME = 'mini-toastr' export const ERROR = 'error' export const WARN = 'warn' export const SUCCESS = 'success' export const INFO = 'info' export const CONTAINER_CLASS = LIB_NAME export const NOTIFICATION_CLASS = `${LIB_NAME}__notification` export const TITLE_CLASS = `${LIB_NAME}-notification__title` export const ICON_CLASS = `${LIB_NAME}-notification__icon` export const MESSAGE_CLASS = `${LIB_NAME}-notification__message` export const ERROR_CLASS = `-${ERROR}` export const WARN_CLASS = `-${WARN}` export const SUCCESS_CLASS = `-${SUCCESS}` export const INFO_CLASS = `-${INFO}` export const DEFAULT_TIMEOUT = 3000 const EMPTY_STRING = '' export function flatten (obj, into, prefix) { into = into || {} prefix = prefix || EMPTY_STRING for (const k in obj) { if (obj.hasOwnProperty(k)) { const prop = obj[k] if (prop && typeof prop === 'object' && !(prop instanceof Date || prop instanceof RegExp)) { flatten(prop, into, prefix + k + ' ') } else { if (into[prefix] && typeof into[prefix] === 'object') { into[prefix][k] = prop } else { into[prefix] = {} into[prefix][k] = prop } } } } return into } export function makeCss (obj) { const flat = flatten(obj) let str = JSON.stringify(flat, null, 2) str = str.replace(/"([^"]*)": {/g, '$1 {') .replace(/"([^"]*)"/g, '$1') .replace(/(\w*-?\w*): ([\w\d .#]*),?/g, '$1: $2;') .replace(/},/g, '}\n') .replace(/ &([.:])/g, '$1') str = str.substr(1, str.lastIndexOf('}') - 1) return str } export function appendStyles (css) { let head = document.head || document.getElementsByTagName('head')[0] let styleElem = makeNode('style') styleElem.id = `${LIB_NAME}-styles` styleElem.type = 'text/css' if (styleElem.styleSheet) { styleElem.styleSheet.cssText = css } else { styleElem.appendChild(document.createTextNode(css)) } head.appendChild(styleElem) } export const config = { types: {ERROR, WARN, SUCCESS, INFO}, animation: fadeOut, timeout: DEFAULT_TIMEOUT, icons: {}, appendTarget: document.body, node: makeNode(), allowHtml: false, style: { [`.${CONTAINER_CLASS}`]: { position: 'fixed', 'z-index': 99999, right: '12px', top: '12px' }, [`.${NOTIFICATION_CLASS}`]: { cursor: 'pointer', padding: '12px 18px', margin: '0 0 6px 0', 'background-color': '#000', opacity: 0.8, color: '#fff', 'border-radius': '3px', 'box-shadow': '#3c3b3b 0 0 12px', width: '300px', [`&.${ERROR_CLASS}`]: { 'background-color': '#D5122B' }, [`&.${WARN_CLASS}`]: { 'background-color': '#F5AA1E' }, [`&.${SUCCESS_CLASS}`]: { 'background-color': '#7AC13E' }, [`&.${INFO_CLASS}`]: { 'background-color': '#4196E1' }, '&:hover': { opacity: 1, 'box-shadow': '#000 0 0 12px' } }, [`.${TITLE_CLASS}`]: { 'font-weight': '500' }, [`.${MESSAGE_CLASS}`]: { display: 'inline-block', 'vertical-align': 'middle', width: '240px', padding: '0 12px' } } } export function makeNode (type = 'div') { return document.createElement(type) } export function createIcon (node, type, config) { const iconNode = makeNode(config.icons[type].nodeType) const attrs = config.icons[type].attrs for (const k in attrs) { if (attrs.hasOwnProperty(k)) { iconNode.setAttribute(k, attrs[k]) } } node.appendChild(iconNode) } export function addElem (node, text, className, config) { const elem = makeNode() elem.className = className if (config.allowHtml) { elem.innerHTML = text } else { elem.appendChild(document.createTextNode(text)) } node.appendChild(elem) } export function getTypeClass (type) { if (type === SUCCESS) return SUCCESS_CLASS if (type === WARN) return WARN_CLASS if (type === ERROR) return ERROR_CLASS if (type === INFO) return INFO_CLASS return EMPTY_STRING } const miniToastr = { config, isInitialised: false, showMessage (message, title, type, timeout, cb, overrideConf) { const config = {} Object.assign(config, this.config) Object.assign(config, overrideConf) const notificationElem = makeNode() notificationElem.className = `${NOTIFICATION_CLASS} ${getTypeClass(type)}` notificationElem.onclick = function () { config.animation(notificationElem, null) } if (title) addElem(notificationElem, title, TITLE_CLASS, config) if (config.icons[type]) createIcon(notificationElem, type, config) if (message) addElem(notificationElem, message, MESSAGE_CLASS, config) config.node.insertBefore(notificationElem, config.node.firstChild) setTimeout(() => config.animation(notificationElem, cb), timeout || config.timeout ) if (cb) cb() return this }, init (aConfig) { const newConfig = {} Object.assign(newConfig, config) Object.assign(newConfig, aConfig) this.config = newConfig const cssStr = makeCss(newConfig.style) appendStyles(cssStr) newConfig.node.id = CONTAINER_CLASS newConfig.node.className = CONTAINER_CLASS newConfig.appendTarget.appendChild(newConfig.node) Object.keys(newConfig.types).forEach(v => { this[newConfig.types[v]] = function (message, title, timeout, cb, config) { this.showMessage(message, title, newConfig.types[v], timeout, cb, config) return this }.bind(this) } ) this.isInitialised = true return this }, setIcon (type, nodeType = 'i', attrs = []) { attrs.class = attrs.class ? attrs.class + ' ' + ICON_CLASS : ICON_CLASS this.config.icons[type] = {nodeType, attrs} } } export default miniToastr