diff --git a/src/Foundation.hs b/src/Foundation.hs index 6cb21a242..0e48f4823 100644 --- a/src/Foundation.hs +++ b/src/Foundation.hs @@ -980,17 +980,18 @@ siteLayout headingOverride widget = do addScript $ StaticR js_polyfills_urlPolyfill_js addScript $ StaticR js_utils_featureChecker_js addScript $ StaticR js_utils_tabber_js + addScript $ StaticR js_utils_alerts_js addStylesheet $ StaticR css_vendor_flatpickr_css addStylesheet $ StaticR css_vendor_fontawesome_css addStylesheet $ StaticR css_fonts_css addStylesheet $ StaticR css_utils_tabber_css + addStylesheet $ StaticR css_utils_alerts_css $(widgetFile "default-layout") $(widgetFile "standalone/modal") $(widgetFile "standalone/showHide") $(widgetFile "standalone/inputs") $(widgetFile "standalone/tooltip") $(widgetFile "standalone/tabber") - $(widgetFile "standalone/alerts") $(widgetFile "standalone/datepicker") withUrlRenderer $(hamletFile "templates/default-layout-wrapper.hamlet") diff --git a/start.sh b/start.sh index 24abcd36c..bc9a04a6a 100755 --- a/start.sh +++ b/start.sh @@ -19,4 +19,6 @@ if [[ -d .stack-work-run ]]; then trap move-back EXIT fi +node-sass ./static/scss -o ./static/css + stack exec -- yesod devel $@ diff --git a/static/css/utils/alerts.css b/static/css/utils/alerts.css new file mode 100644 index 000000000..670e28696 --- /dev/null +++ b/static/css/utils/alerts.css @@ -0,0 +1,182 @@ +/* ALERTS */ +/** + .alert + Regular Info Alert + Disappears automatically after 30 seconds + Disappears after x seconds if explicitly specified via data-decay='x' + Can be told not to disappear with data-decay='0' + + .alert-success + Disappears automatically after 30 seconds + + .alert-warning + Does not disappear + Orange regardless of user's selected theme + + .alert-error + Does not disappear + Red regardless of user's selected theme + + */ +.alerts { + position: fixed; + bottom: 0; + right: 5%; + z-index: 20; + text-align: right; + display: flex; + flex-direction: column; } + +.alerts__toggler { + width: 40px; + height: 40px; + position: absolute; + top: 400px; + left: 50%; + transform: translateX(-50%); + cursor: pointer; } + .alerts__toggler::before { + content: '\f077'; + position: absolute; + font-family: "Font Awesome 5 Free"; + left: 50%; + top: 0; + height: 30px; + display: flex; + align-items: center; + justify-content: center; + width: 30px; + color: var(--color-grey); + font-size: 30px; + transform: translateX(-50%); } + +.alerts__toggler--visible { + top: -40px; + opacity: 1; + transition: top 0.5s cubic-bezier(0.73, 1.25, 0.61, 1), opacity 0.5s cubic-bezier(0.73, 1.25, 0.61, 1); } + +@media (max-width: 425px) { + .alerts { + left: 5%; } } + +.alert { + position: relative; + display: block; + background-color: var(--color-lightblack); + font-size: 1rem; + color: var(--color-lightwhite); + z-index: 0; + padding: 0 50px; + padding-right: 60px; + animation: slide-in-alert .2s ease-out forwards; + margin-bottom: 10px; + transition: margin-bottom .2s ease-out; } + +.alert a { + color: var(--color-lightwhite); } + +@keyframes slide-in-alert { + from { + transform: translateY(120%); } + to { + transform: translateY(0); } } + +@keyframes slide-out-alert { + from { + transform: translateY(0); + max-height: 200px; } + to { + transform: translateY(250%); + opacity: 0; + max-height: 0; + overflow: hidden; } } + +@media (min-width: 425px) { + .alert { + max-width: 400px; } } + +.alert--invisible { + animation: slide-out-alert .2s ease-out forwards; + margin-bottom: 0; } + +.alert__content { + padding: 8px 0; + min-height: 40px; + position: relative; + display: flex; + font-weight: 600; + align-items: center; + text-align: left; } + +.alert__icon { + text-align: right; + position: absolute; + left: 0px; + top: 0; + width: 50px; + height: 100%; + z-index: 40; } + .alert__icon::before { + content: '\f05a'; + position: absolute; + font-family: "Font Awesome 5 Free"; + font-size: 24px; + top: 50%; + left: 50%; + display: flex; + align-items: center; + justify-content: center; + transform: translate(-50%, -50%); + border-radius: 50%; + width: 30px; + height: 30px; } + +.alert__closer { + cursor: pointer; + text-align: right; + position: absolute; + right: 0px; + top: 0; + width: 60px; + height: 100%; + transition: all .3s ease; + z-index: 40; } + .alert__closer:hover { + transform: scale(1.05, 1.05); } + .alert__closer:hover::before { + box-shadow: 0 0 4px white; + background-color: rgba(255, 255, 255, 0.1); + color: white; } + .alert__closer::before { + content: '\f00d'; + position: absolute; + font-family: "Font Awesome 5 Free"; + top: 50%; + left: 50%; + display: flex; + align-items: center; + justify-content: center; + transform: translate(-50%, -50%); + border-radius: 50%; + width: 30px; + height: 30px; + transition: all .15s ease; } + +@media (max-width: 768px) { + .alert__closer { + width: 40px; } } + +.alert-success { + background-color: var(--color-success); } + .alert-success .alert__icon::before { + content: '\f058'; } + +.alert-warning { + background-color: var(--color-warning); } + .alert-warning .alert__icon::before { + content: '\f06a'; } + +.alert-error { + background-color: var(--color-error); } + .alert-error .alert__icon::before { + content: '\f071'; } diff --git a/static/js/utils/alerts.js b/static/js/utils/alerts.js new file mode 100644 index 000000000..980cb955c --- /dev/null +++ b/static/js/utils/alerts.js @@ -0,0 +1,86 @@ +(function() { + 'use strict'; + + window.utils = window.utils || {}; + + var ALERTS_TOGGLER_CLASS = 'alerts__toggler'; + var ALERTS_TOGGLER_VISIBLE_CLASS = 'alerts__toggler--visible'; + var ALERTS_TOGGLER_APPEAR_DELAY = 120; + + var ALERT_CLASS = 'alert'; + var ALERT_CLOSER_CLASS = 'alert__closer'; + var ALERT_INVISIBLE_CLASS = 'alert--invisible'; + var ALERT_AUTO_HIDE_DELAY = 10; + var ALERT_AUTOCLOSING_MATCHER = 'alert-info'; + + var JS_INITIALIZED_CLASS = 'js-initialized'; + + window.utils.alerts = function(alertsEl) { + + if (alertsEl.classList.contains(JS_INITIALIZED_CLASS)) { + return; + } + + var togglerCheckRequested = false; + + var togglerEl = alertsEl.querySelector('.' + ALERTS_TOGGLER_CLASS); + + var alertElements = Array.from(alertsEl.querySelectorAll('.' + ALERT_CLASS)) + .filter(function(alert) { + return !alert.classList.contains(JS_INITIALIZED_CLASS); + }); + + function initToggler() { + togglerEl.addEventListener('click', function() { + alertElements.forEach(function(alertEl) { + toggleAlert(alertEl, true); + }); + togglerEl.classList.remove(ALERTS_TOGGLER_VISIBLE_CLASS); + }); + alertsEl.classList.add(JS_INITIALIZED_CLASS); + } + + function initAlert(alertEl) { + var autoHideDelay = ALERT_AUTO_HIDE_DELAY; + if (alertEl.dataset.decay) { + autoHideDelay = parseInt(alertEl.dataset.decay, 10); + } + + var closeEl = alertEl.querySelector('.' + ALERT_CLOSER_CLASS); + closeEl.addEventListener('click', function() { + toggleAlert(alertEl); + }); + + if (autoHideDelay > 0 && alertEl.matches(ALERT_AUTOCLOSING_MATCHER)) { + window.setTimeout(function() { + toggleAlert(alertEl); + }, autoHideDelay * 1000); + } + + alertEl.classList.add(JS_INITIALIZED_CLASS); + } + + function toggleAlert(alertEl, visible) { + alertEl.classList.toggle(ALERT_INVISIBLE_CLASS, !visible); + checkToggler(); + } + + function checkToggler() { + if (togglerCheckRequested) { + return; + } + + var alertsHidden = alertElements.reduce(function(acc, alert) { + return acc || alert.classList.contains(ALERT_INVISIBLE_CLASS); + }, false); + + window.setTimeout(function() { + toggler.classList.toggle(ALERTS_TOGGLER_VISIBLE_CLASS, alertsHidden); + togglerCheckRequested = false; + }, ALERTS_TOGGLER_APPEAR_DELAY); + } + + initToggler(); + alertElements.forEach(initAlert); + }; +})(); diff --git a/templates/standalone/alerts.lucius b/static/scss/utils/alerts.scss similarity index 99% rename from templates/standalone/alerts.lucius rename to static/scss/utils/alerts.scss index bc5603220..3256eef96 100644 --- a/templates/standalone/alerts.lucius +++ b/static/scss/utils/alerts.scss @@ -156,7 +156,7 @@ } } -.alert__close { +.alert__closer { cursor: pointer; text-align: right; position: absolute; @@ -196,7 +196,7 @@ @media (max-width: 768px) { - .alert__close { + .alert__closer { width: 40px; } } diff --git a/templates/default-layout.hamlet b/templates/default-layout.hamlet index bf936affd..c01ab4e45 100644 --- a/templates/default-layout.hamlet +++ b/templates/default-layout.hamlet @@ -30,12 +30,16 @@ $if not isModal ^{widget} +