UNPKG

notie

Version:

notie - a clean and simple notification, input, and selection suite for javascript, with no dependencies

965 lines (789 loc) 27.8 kB
/* notie - a clean and simple notification suite for javascript, with no dependencies Copyright (c) 2016 Jared Reich Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php Project demo: https://jaredreich.com/projects/notie Version: 3.3.0 */ var notie = (function () { // Default options var options = { colorSuccess: '', colorWarning: '', colorError: '', colorInfo: '', colorNeutral: '', colorText: '', dateMonths: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], animationDelay: 300, backgroundClickDismiss: true } function setOptions (customOptions) { // Custom options for (var key in customOptions) { options[key] = customOptions[key] } } // alert // ************** // create alert container var alertOuter = document.createElement('div') alertOuter.id = 'notie-alert-outer' // Hide alert on click alertOuter.onclick = function () { alertHide() } // add alert to body document.body.appendChild(alertOuter) // create alert inner container var alertInner = document.createElement('div') alertInner.id = 'notie-alert-inner' alertOuter.appendChild(alertInner) // create alert content container var alertContent = document.createElement('div') alertContent.id = 'notie-alert-content' alertInner.appendChild(alertContent) // Initialize alert text var alertText = document.createElement('span') alertText.id = 'notie-alert-text' alertContent.appendChild(alertText) // alert helper variables var alertIsShowing = false var alertTimeout1 var alertTimeout2 var wasClickedCounter = 0 function alert (type, message, seconds) { if (options.colorText.length > 0) alertText.style.color = options.colorText blur() wasClickedCounter++ setTimeout(function () { wasClickedCounter-- }, (options.animationDelay + 10)) if (wasClickedCounter === 1) { if (alertIsShowing) { alertHide(function () { alertShow(type, message, seconds) }) } else { alertShow(type, message, seconds) } } } function alertShow (type, message, seconds) { alertIsShowing = true var duration = 0 if (typeof seconds === 'undefined' || seconds === 0) { duration = 86400000 } else if (seconds > 0 && seconds < 1) { duration = 1000 } else { duration = seconds * 1000 } // Remove all color classes first removeClass(alertOuter, 'notie-background-success') removeClass(alertOuter, 'notie-background-warning') removeClass(alertOuter, 'notie-background-error') removeClass(alertOuter, 'notie-background-info') // Set notie type (background color) switch (type) { case 1: case 'success': if (options.colorSuccess.length > 0) alertOuter.style.backgroundColor = options.colorSuccess else addClass(alertOuter, 'notie-background-success') break case 2: case 'warning': if (options.colorWarning.length > 0) alertOuter.style.backgroundColor = options.colorWarning else addClass(alertOuter, 'notie-background-warning') break case 3: case 'error': if (options.colorError.length > 0) alertOuter.style.backgroundColor = options.colorError else addClass(alertOuter, 'notie-background-error') break case 4: case 'info': if (options.colorInfo.length > 0) alertOuter.style.backgroundColor = options.colorInfo else addClass(alertOuter, 'notie-background-info') break } // Set notie text alertText.innerHTML = message alertOuter.style.top = '-10000px' alertOuter.style.display = 'table' alertOuter.style.top = '-' + alertOuter.offsetHeight - 5 + 'px' alertTimeout1 = setTimeout(function () { addClass(alertOuter, 'notie-transition') alertOuter.style.top = 0 alertTimeout2 = setTimeout(function () { alertHide(function () { // Nothing }) }, duration) }, 20) } function alertHide (callback) { clearTimeout(alertTimeout1) clearTimeout(alertTimeout2) alertOuter.style.top = '-' + alertOuter.offsetHeight - 5 + 'px' setTimeout(function () { removeClass(alertOuter, 'notie-transition') alertOuter.style.top = '-10000px' alertIsShowing = false if (callback) callback() }, (options.animationDelay + 10)) } // confirm // ************** var confirmOuter = document.createElement('div') confirmOuter.id = 'notie-confirm-outer' var confirmInner = document.createElement('div') confirmInner.id = 'notie-confirm-inner' confirmOuter.appendChild(confirmInner) var confirmText = document.createElement('span') confirmText.id = 'notie-confirm-text' confirmInner.appendChild(confirmText) var confirmYes = document.createElement('div') confirmYes.id = 'notie-confirm-yes' confirmOuter.appendChild(confirmYes) var confirmNo = document.createElement('div') confirmNo.id = 'notie-confirm-no' confirmOuter.appendChild(confirmNo) var confirmTextYes = document.createElement('span') confirmTextYes.id = 'notie-confirm-text-yes' confirmYes.appendChild(confirmTextYes) var confirmTextNo = document.createElement('span') confirmTextNo.id = 'notie-confirm-text-no' confirmNo.appendChild(confirmTextNo) var confirmBackground = document.createElement('div') confirmBackground.id = 'notie-confirm-background' addClass(confirmBackground, 'notie-transition') // Hide notie.confirm on no click and background click confirmBackground.onclick = function () { if (options.backgroundClickDismiss) { confirmHide() } } // Attach confirm elements to the body element document.body.appendChild(confirmOuter) document.body.appendChild(confirmBackground) // confirm helper variables var confirmIsShowing = false function confirm (title, yesText, noText, yesCallback, noCallback) { if (options.colorInfo.length > 0) confirmInner.style.backgroundColor = options.colorInfo if (options.colorSuccess.length > 0) confirmYes.style.backgroundColor = options.colorSuccess if (options.colorError.length > 0) confirmNo.style.backgroundColor = options.colorError if (options.colorText.length > 0) { confirmText.style.color = options.colorText confirmTextYes.style.color = options.colorText confirmTextNo.style.color = options.colorText } blur() if (alertIsShowing) { // Hide notie.alert alertHide(function () { confirmShow(title, yesText, noText, yesCallback, noCallback) }) } else { confirmShow(title, yesText, noText, yesCallback, noCallback) } } function confirmShow (title, yesText, noText, yesCallback, noCallback) { scrollDisable() // Yes callback function confirmYes.onclick = function () { confirmHide() if (yesCallback) { setTimeout(function () { yesCallback() }, (options.animationDelay + 10)) } } // No callback function confirmNo.onclick = function () { confirmHide() if (noCallback) { setTimeout(function () { noCallback() }, (options.animationDelay + 10)) } } function confirmShowInner () { // Set confirm text confirmText.innerHTML = title confirmTextYes.innerHTML = yesText confirmTextNo.innerHTML = noText // Get confirm's height confirmOuter.style.top = '-10000px' confirmOuter.style.display = 'table' confirmOuter.style.top = '-' + confirmOuter.offsetHeight - 5 + 'px' confirmBackground.style.display = 'block' setTimeout(function () { addClass(confirmOuter, 'notie-transition') confirmOuter.style.top = 0 confirmBackground.style.opacity = '0.75' setTimeout(function () { confirmIsShowing = true }, (options.animationDelay + 10)) }, 20) } if (confirmIsShowing) { confirmHide() setTimeout(function () { confirmShowInner() }, (options.animationDelay + 10)) } else { confirmShowInner() } } function confirmHide () { confirmOuter.style.top = '-' + confirmOuter.offsetHeight - 5 + 'px' confirmBackground.style.opacity = '0' setTimeout(function () { removeClass(confirmOuter, 'notie-transition') confirmOuter.style.top = '-10000px' confirmBackground.style.display = 'none' scrollEnable() confirmIsShowing = false }, (options.animationDelay + 10)) } // input // ********** var inputOuter = document.createElement('div') inputOuter.id = 'notie-input-outer' var inputBackground = document.createElement('div') inputBackground.id = 'notie-input-background' addClass(inputBackground, 'notie-transition') var inputInner = document.createElement('div') inputInner.id = 'notie-input-inner' inputOuter.appendChild(inputInner) var inputField = document.createElement('input') inputField.id = 'notie-input-field' inputField.setAttribute('autocomplete', 'off') inputField.setAttribute('autocorrect', 'off') inputField.setAttribute('autocapitalize', 'off') inputField.setAttribute('spellcheck', 'false') inputOuter.appendChild(inputField) var inputYes = document.createElement('div') inputYes.id = 'notie-input-yes' inputOuter.appendChild(inputYes) var inputNo = document.createElement('div') inputNo.id = 'notie-input-no' inputOuter.appendChild(inputNo) var inputText = document.createElement('span') inputText.id = 'notie-input-text' inputInner.appendChild(inputText) var inputTextYes = document.createElement('span') inputTextYes.id = 'notie-input-text-yes' inputYes.appendChild(inputTextYes) var inputTextNo = document.createElement('span') inputTextNo.id = 'notie-input-text-no' inputNo.appendChild(inputTextNo) // Attach input elements to the body element document.body.appendChild(inputOuter) document.body.appendChild(inputBackground) // Hide input on no click and background click inputBackground.onclick = function () { if (options.backgroundClickDismiss) { inputHide() } } // input helper variables var inputIsShowing = false function input (settings, title, submitText, cancelText, submitCallback, cancelCallback) { if (options.colorInfo.length > 0) inputInner.style.backgroundColor = options.colorInfo if (options.colorSuccess.length > 0) inputYes.style.backgroundColor = options.colorSuccess if (options.colorError.length > 0) inputNo.style.backgroundColor = options.colorError if (options.colorText.length > 0) { inputText.style.color = options.colorText inputTextYes.style.color = options.colorText inputTextNo.style.color = options.colorText } blur() if (typeof settings.type !== 'undefined' && settings.type) { inputField.setAttribute('type', settings.type) } else { inputField.setAttribute('type', 'text') } if (typeof settings.placeholder !== 'undefined' && settings.placeholder) { inputField.setAttribute('placeholder', settings.placeholder) } else { // Do not set placeholder } if (typeof settings.prefilledValue !== 'undefined' && settings.prefilledValue) { inputField.value = settings.prefilledValue } else { inputField.value = '' } if (alertIsShowing) { // Hide alert alertHide(function () { inputShow(title, submitText, cancelText, submitCallback, cancelCallback) }) } else { inputShow(title, submitText, cancelText, submitCallback, cancelCallback) } } function inputShow (title, submitText, cancelText, submitCallback, cancelCallback) { scrollDisable() // Yes callback function inputYes.onclick = function () { inputHide() if (submitCallback) { setTimeout(function () { submitCallback(inputField.value) }, (options.animationDelay + 10)) } } // No callback function inputNo.onclick = function () { inputHide() if (cancelCallback) { setTimeout(function () { cancelCallback(inputField.value) }, (options.animationDelay + 10)) } } function inputShowInner () { // Set input text inputText.innerHTML = title inputTextYes.innerHTML = submitText inputTextNo.innerHTML = cancelText // Get input's height inputOuter.style.top = '-10000px' inputOuter.style.display = 'table' inputOuter.style.top = '-' + inputOuter.offsetHeight - 5 + 'px' inputBackground.style.display = 'block' setTimeout(function () { addClass(inputOuter, 'notie-transition') inputOuter.style.top = 0 inputBackground.style.opacity = '0.75' setTimeout(function () { inputIsShowing = true // put focus on input field inputField.focus() }, (options.animationDelay + 10)) }, 20) } if (inputIsShowing) { inputHide() setTimeout(function () { inputShowInner() }, (options.animationDelay + 10)) } else { inputShowInner() } } function inputHide () { inputOuter.style.top = '-' + inputOuter.offsetHeight - 5 + 'px' inputBackground.style.opacity = '0' setTimeout(function () { removeClass(inputOuter, 'notie-transition') inputBackground.style.display = 'none' inputOuter.style.top = '-10000px' scrollEnable() inputIsShowing = false }, (options.animationDelay + 10)) } // select // ************** var selectOuter = document.createElement('div') selectOuter.id = 'notie-select-outer' var selectInner = document.createElement('div') selectInner.id = 'notie-select-inner' selectOuter.appendChild(selectInner) var selectText = document.createElement('span') selectText.id = 'notie-select-text' selectInner.appendChild(selectText) var selectBackground = document.createElement('div') selectBackground.id = 'notie-select-background' addClass(selectBackground, 'notie-transition') var selectChoices = document.createElement('div') selectChoices.id = 'notie-select-choices' selectOuter.appendChild(selectChoices) var selectCancel = document.createElement('div') selectCancel.id = 'notie-select-cancel' selectOuter.appendChild(selectCancel) // Attach select elements to the body element document.body.appendChild(selectOuter) document.body.appendChild(selectBackground) // Hide input on no click and background click selectBackground.onclick = function () { if (options.backgroundClickDismiss) { selectHide() } } selectCancel.onclick = function () { selectHide() } // select helper variables var selectIsShowing = false function select (title, cancelText, choices) { if (options.colorInfo.length > 0) selectInner.style.backgroundColor = options.colorInfo if (options.colorNeutral.length > 0) selectCancel.style.backgroundColor = options.colorNeutral if (options.colorText.length > 0) { selectText.style.color = options.colorText selectCancel.style.color = options.colorText } var funcs = [] for (var i = 0; i < arguments.length - 3; i++) { funcs[i] = arguments[i + 3] } if (funcs.length === choices.length) { blur() if (alertIsShowing) { // Hide notie.alert alertHide(function () { selectShow(title, cancelText, choices, funcs) }) } else { selectShow(title, cancelText, choices, funcs) } } else { throw new Error('notie.select number of choices must match number of functions') } } function selectShow (title, cancelText, choices, funcs) { scrollDisable() document.getElementById('notie-select-choices').innerHTML = '' selectCancel.innerHTML = cancelText var selectChoicePrevious for (var i = 0; i < choices.length; i++) { var selectChoice = document.createElement('div') selectChoice.innerHTML = choices[i].title addClass(selectChoice, 'notie-select-choice') selectChoices.appendChild(selectChoice) if (options.colorText.length > 0) selectChoice.style.color = options.colorText if (choices[i].type) { switch (choices[i].type) { case 1: if (options.colorSuccess.length > 0) selectChoice.style.backgroundColor = options.colorSuccess else addClass(selectChoice, 'notie-background-success') break case 2: if (options.colorWarning.length > 0) selectChoice.style.backgroundColor = options.colorWarning else addClass(selectChoice, 'notie-background-warning') break case 3: if (options.colorError.length > 0) selectChoice.style.backgroundColor = options.colorError else addClass(selectChoice, 'notie-background-error') break case 4: if (options.colorInfo.length > 0) selectChoice.style.backgroundColor = options.colorInfo else addClass(selectChoice, 'notie-background-info') break } } else if (choices[i].color) { selectChoice.style.backgroundColor = choices[i].color } selectChoice.style.backgroundColor = window.getComputedStyle(selectChoice).backgroundColor if (i > 0 && selectChoice.style.backgroundColor === selectChoicePrevious.style.backgroundColor) { addClass(selectChoicePrevious, 'notie-select-choice-bottom-border') } // onclick for this choice selectChoice.onclick = (function (i) { return function () { selectHide() setTimeout(function () { funcs[i]() }, (options.animationDelay + 10)) } })(i) selectChoicePrevious = selectChoice } function selectShowInner (title) { // Set select text selectText.innerHTML = title // Get select's height selectOuter.style.bottom = '-10000px' selectOuter.style.display = 'table' selectOuter.style.bottom = '-' + selectOuter.offsetHeight - 5 + 'px' selectBackground.style.display = 'block' setTimeout(function () { addClass(selectOuter, 'notie-transition') selectOuter.style.bottom = 0 selectBackground.style.opacity = '0.75' setTimeout(function () { selectIsShowing = true }, (options.animationDelay + 10)) }, 20) } if (selectIsShowing) { selectHide() setTimeout(function () { selectShowInner(title) }, (options.animationDelay + 10)) } else { selectShowInner(title) } } function selectHide () { selectOuter.style.bottom = '-' + selectOuter.offsetHeight - 5 + 'px' selectBackground.style.opacity = '0' setTimeout(function () { removeClass(selectOuter, 'notie-transition') selectOuter.style.bottom = '-10000px' selectBackground.style.display = 'none' scrollEnable() selectIsShowing = false }, (options.animationDelay + 10)) } function isShowing () { return alertIsShowing || confirmIsShowing || inputIsShowing || selectIsShowing || dateIsShowing } // date // ************** var dateOuter = document.createElement('div') dateOuter.id = 'notie-date-outer' var dateSelector = document.createElement('div') dateSelector.id = 'notie-date-selector' dateOuter.appendChild(dateSelector) var dateInner = document.createElement('div') dateInner.id = 'notie-date-inner' dateOuter.appendChild(dateInner) var dateUpArrow = '<div class="notie-date-arrow-up"></div>' var dateDownArrow = '<div class="notie-date-arrow-down"></div>' var dateUpMonth = document.createElement('div') dateUpMonth.className = 'notie-date-up' dateUpMonth.innerHTML = dateUpArrow dateSelector.appendChild(dateUpMonth) dateUpMonth.onclick = dateUpMonthClick var dateUpDay = document.createElement('div') dateUpDay.className = 'notie-date-up' dateUpDay.innerHTML = dateUpArrow dateSelector.appendChild(dateUpDay) dateUpDay.onclick = dateUpDayClick var dateUpYear = document.createElement('div') dateUpYear.className = 'notie-date-up' dateUpYear.innerHTML = dateUpArrow dateSelector.appendChild(dateUpYear) dateUpYear.onclick = dateUpYearClick var dateMonth = document.createElement('div') dateMonth.className = 'notie-date-text' dateSelector.appendChild(dateMonth) var dateDay = document.createElement('div') dateDay.className = 'notie-date-text' dateSelector.appendChild(dateDay) var dateYear = document.createElement('div') dateYear.className = 'notie-date-text' dateSelector.appendChild(dateYear) var dateDownMonth = document.createElement('div') dateDownMonth.className = 'notie-date-down' dateDownMonth.innerHTML = dateDownArrow dateSelector.appendChild(dateDownMonth) dateDownMonth.onclick = dateDownMonthClick var dateDownDay = document.createElement('div') dateDownDay.className = 'notie-date-down' dateDownDay.innerHTML = dateDownArrow dateSelector.appendChild(dateDownDay) dateDownDay.onclick = dateDownDayClick var dateDownYear = document.createElement('div') dateDownYear.className = 'notie-date-down' dateDownYear.innerHTML = dateDownArrow dateSelector.appendChild(dateDownYear) dateDownYear.onclick = dateDownYearClick var dateYes = document.createElement('div') dateYes.id = 'notie-date-yes' dateInner.appendChild(dateYes) var dateNo = document.createElement('div') dateNo.id = 'notie-date-no' dateInner.appendChild(dateNo) var dateBackground = document.createElement('div') dateBackground.id = 'notie-date-background' addClass(dateBackground, 'notie-transition') // Hide notie.date on no click and background click dateBackground.onclick = function () { if (options.backgroundClickDismiss) { dateHide() } } // Attach date elements to the body element document.body.appendChild(dateOuter) document.body.appendChild(dateBackground) // date helper variables var dateIsShowing = false var dateSelected function date (dateOptions) { if (options.colorInfo.length > 0) { dateInner.style.backgroundColor = options.colorInfo } if (options.colorSuccess.length > 0) dateYes.style.backgroundColor = options.colorSuccess if (options.colorError.length > 0) dateNo.style.backgroundColor = options.colorError if (options.colorText.length > 0) dateInner.style.color = options.colorText blur() if (alertIsShowing) { // Hide notie.alert alertHide(function () { dateShow(dateOptions) }) } else { dateShow(dateOptions) } } function dateShow (dateOptions) { scrollDisable() // Yes callback function dateYes.onclick = function () { dateHide() if (dateOptions.yesCallback) { setTimeout(function () { dateOptions.yesCallback(dateSelected) }, (options.animationDelay + 10)) } } // No callback function dateNo.onclick = function () { dateHide() if (dateOptions.noCallback) { setTimeout(function () { dateOptions.noCallback(dateSelected) }, (options.animationDelay + 10)) } } function dateShowInner () { dateSelected = dateOptions.initial || new Date() dateSet(dateSelected) dateYes.innerHTML = dateOptions.yesText || 'OK' dateNo.innerHTML = dateOptions.noText || 'Cancel' // Get dates's height dateOuter.style.top = '-10000px' dateOuter.style.display = 'table' dateOuter.style.top = '-' + dateOuter.offsetHeight - 5 + 'px' dateBackground.style.display = 'block' setTimeout(function () { addClass(dateOuter, 'notie-transition') dateOuter.style.top = 0 dateBackground.style.opacity = '0.75' setTimeout(function () { dateIsShowing = true }, (options.animationDelay + 10)) }, 20) } if (dateIsShowing) { dateHide() setTimeout(function () { dateShowInner() }, (options.animationDelay + 10)) } else { dateShowInner() } } function dateSet (date) { dateMonth.innerHTML = options.dateMonths[date.getMonth()] dateDay.innerHTML = date.getDate() dateYear.innerHTML = date.getFullYear() } function dateUpMonthClick () { dateSelected.setMonth(dateSelected.getMonth() - 1) dateSet(dateSelected) } function dateDownMonthClick () { dateSelected.setMonth(dateSelected.getMonth() + 1) dateSet(dateSelected) } function dateUpDayClick () { dateSelected.setDate(dateSelected.getDate() - 1) dateSet(dateSelected) } function dateDownDayClick () { dateSelected.setDate(dateSelected.getDate() + 1) dateSet(dateSelected) } function dateUpYearClick () { dateSelected.setFullYear(dateSelected.getFullYear() - 1) dateSet(dateSelected) } function dateDownYearClick () { dateSelected.setFullYear(dateSelected.getFullYear() + 1) dateSet(dateSelected) } function dateHide () { dateOuter.style.top = '-' + dateOuter.offsetHeight - 5 + 'px' dateBackground.style.opacity = '0' setTimeout(function () { removeClass(dateOuter, 'notie-transition') dateOuter.style.top = '-10000px' dateBackground.style.display = 'none' scrollEnable() dateIsShowing = false }, (options.animationDelay + 10)) } // Internal helper functions // ################# function addClass (element, className) { if (element.classList) { element.classList.add(className) } else { element.className += ' ' + className } } function removeClass (element, className) { if (element.classList) { element.classList.remove(className) } else { element.className = element.className.replace(new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'), ' ') } } function blur () { document.activeElement && document.activeElement.blur() } var originalBodyHeight, originalBodyOverflow function scrollDisable () { originalBodyHeight = document.body.style.height originalBodyOverflow = document.body.style.overflow document.body.style.height = '100%' document.body.style.overflow = 'hidden' } function scrollEnable () { document.body.style.height = originalBodyHeight document.body.style.overflow = originalBodyOverflow } // Event listener for keydown enter and escape keys window.addEventListener('keydown', function (event) { var enterClicked = (event.which === 13 || event.keyCode === 13) var escapeClicked = (event.which === 27 || event.keyCode === 27) if (alertIsShowing) { if (enterClicked || escapeClicked) { alertHide() } } else if (confirmIsShowing) { if (enterClicked) { confirmYes.click() } else if (escapeClicked) { confirmHide() } } else if (inputIsShowing) { if (enterClicked) { inputYes.click() } else if (escapeClicked) { inputHide() } } else if (selectIsShowing) { if (escapeClicked) { selectHide() } } else if (dateIsShowing) { if (enterClicked) { dateYes.click() } else if (escapeClicked) { dateHide() } } }) return { setOptions: setOptions, alert: alert, alertHide: alertHide, confirm: confirm, input: input, select: select, date: date, isShowing: isShowing } }()) // Node.js if (typeof module === 'object' && module.exports) { module.exports = notie }