Merge branch 'master' of gitlab.cip.ifi.lmu.de:jost/UniWorX

This commit is contained in:
SJost 2019-02-16 07:36:54 +01:00
commit d3e0f462f4
11 changed files with 195 additions and 112 deletions

View File

@ -114,6 +114,7 @@ dependencies:
- memcached-binary
- directory-tree
- lifted-base
- hsass
other-extensions:
- GeneralizedNewtypeDeriving

View File

@ -974,6 +974,8 @@ siteLayout' headingOverride widget = do
asidenav = $(widgetFile "widgets/asidenav")
footer :: Widget
footer = $(widgetFile "widgets/footer")
alerts :: Widget
alerts = $(widgetFile "widgets/alerts/alerts")
contentHeadline :: Maybe Widget
contentHeadline = headingOverride <|> (pageHeading =<< mcurrentRoute)
breadcrumbsWgt :: Widget
@ -993,17 +995,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_scss
$(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")

View File

@ -6,6 +6,7 @@ module Settings.StaticFiles
import ClassyPrelude
import Settings (appStaticDir, compileTimeAppSettings)
import Settings.StaticFiles.Generator
import Yesod.EmbeddedStatic
-- This generates easy references to files in the static directory at compile time,
@ -23,4 +24,4 @@ import Yesod.EmbeddedStatic
#define DEV_BOOL False
#endif
mkEmbeddedStatic DEV_BOOL "embeddedStatic" [embedDir $ appStaticDir compileTimeAppSettings]
mkEmbeddedStatic DEV_BOOL "embeddedStatic" . pure . staticGenerator $ appStaticDir compileTimeAppSettings

View File

@ -0,0 +1,72 @@
module Settings.StaticFiles.Generator
( staticGenerator
) where
import ClassyPrelude
import Yesod.EmbeddedStatic.Types
import Yesod.EmbeddedStatic
import System.FilePath
import System.Directory.Tree
import Network.Mime
import Language.Haskell.TH
import Language.Haskell.TH.Syntax
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as LBS
import qualified Data.Map as Map
import qualified Text.Sass.Compilation as Sass
import Text.Sass.Options
import Data.Default
import qualified Data.Foldable as Fold
staticGenerator :: FilePath -> Generator
staticGenerator staticDir = do
dirTree' <- runIO $ readDirectoryWith toEntries staticDir
Fold.forM_ (fst <$> zipPaths dirTree') addDependentFile
return . Fold.fold $ dirTree dirTree'
where
toEntries :: FilePath -- ^ Absolute path
-> IO [Entry]
toEntries loc = compile (mimeByExt mimeMap defaultMimeType $ pack loc) (makeRelative staticDir loc) loc
mimeMap = defaultMimeMap `mappend` Map.fromList
[ ("sass", "text/x-sass")
, ("scss", "text/x-scss")
]
compile :: MimeType
-> Location -- ^ Relative location
-> FilePath -- ^ Absolute filepath
-> IO [Entry]
compile "text/x-scss" sassLoc fp = return . pure $ def
{ ebHaskellName = Just $ pathToName sassLoc
, ebLocation
, ebMimeType = "text/css"
, ebProductionContent = either (fail <=< Sass.errorMessage) (return . LBS.fromStrict) =<< Sass.compileFile fp def
, ebDevelReload = [| either (fail <=< Sass.errorMessage) (return . LBS.fromStrict) =<< Sass.compileFile $(litE $ stringL fp) def |]
}
where
ebLocation = sassLoc -<.> "css"
compile "text/x-sass" sassLoc fp = return . pure $ def
{ ebHaskellName = Just $ pathToName sassLoc
, ebLocation
, ebMimeType = "text/css"
, ebProductionContent = either (fail <=< Sass.errorMessage) (return . LBS.fromStrict) =<< Sass.compileFile fp (def { sassIsIndentedSyntax = True })
, ebDevelReload = [| either (fail <=< Sass.errorMessage) (return . LBS.fromStrict) =<< Sass.compileFile $(litE $ stringL fp) (def { sassIsIndentedSyntax = True }) |]
}
where
ebLocation = sassLoc -<.> "css"
compile ebMimeType ebLocation fp = return . pure $ def
{ ebHaskellName = Just $ pathToName ebLocation
, ebLocation
, ebMimeType
, ebProductionContent = LBS.fromStrict <$> BS.readFile fp
, ebDevelReload = [| LBS.fromStrict <$> BS.readFile $(litE $ stringL fp) |]
}

View File

@ -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;
}
}

86
static/js/utils/alerts.js Normal file
View File

@ -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, .alert-success';
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);
}, true);
window.setTimeout(function() {
togglerEl.classList.toggle(ALERTS_TOGGLER_VISIBLE_CLASS, alertsHidden);
togglerCheckRequested = false;
}, ALERTS_TOGGLER_APPEAR_DELAY);
}
initToggler();
alertElements.forEach(initAlert);
};
})();

View File

@ -30,12 +30,7 @@ $if not isModal
^{widget}
<!-- alerts -->
<div #alerts .alerts>
$forall (status, msg) <- mmsgs
$with status2 <- bool status "info" (status == "")
<div class="alert alert-#{status2}">
<div .alert__content>
#{msg}
^{alerts}
<!-- footer -->
$if not isModal

View File

@ -1 +0,0 @@
<!-- only here to be able to include alerts using `toWidget` -->

View File

@ -1,101 +0,0 @@
(function() {
'use strict';
window.utils = window.utils || {};
var ALERT_INVISIBLE_CLASS = 'alert--invisible';
var TOGGLER_INVISIBLE_CLASS = 'alerts__toggler--visible';
var ALERT_AUTO_DISAPPEAR_DELAY = 10;
var alertsShowingToggler = false;
window.utils.alerts = function(alertsEl) {
var toggler = alertsEl.querySelector('.alerts__toggler');
function makeToggler() {
toggler = document.createElement('DIV');
toggler.classList.add('alerts__toggler');
toggler.addEventListener('click', function() {
Array.from(alertsEl.querySelectorAll('.alert')).forEach(function(alert) {
alert.classList.remove(ALERT_INVISIBLE_CLASS);
toggler.classList.remove(TOGGLER_INVISIBLE_CLASS);
});
checkToggler();
});
alertsEl.appendChild(toggler);
alertsEl.classList.add('js-initialized');
}
function makeAlert(alertEl) {
var iconEl = document.createElement('DIV');
var closeEl = document.createElement('DIV');
var dataDecay = alertEl.dataset.decay;
var autoDecay = ALERT_AUTO_DISAPPEAR_DELAY;
if (dataDecay) {
autoDecay = parseInt(dataDecay, 10);
}
iconEl.classList.add('alert__icon');
closeEl.classList.add('alert__close');
closeEl.addEventListener('click', function(event) {
closeAlert(alertEl);
});
alertEl.insertBefore(iconEl, alertEl.children[0]);
alertEl.insertBefore(closeEl, alertEl.children[0]);
// auto-hide info and success-alerts after 3 seconds
if (autoDecay > 0 && !alertEl.matches('.alert-warning, .alert-error')) {
window.setTimeout(function() {
closeAlert(alertEl);
}, autoDecay * 1000);
}
alertEl.classList.add('js-initialized');
}
function closeAlert(alertEl) {
alertEl.classList.add(ALERT_INVISIBLE_CLASS);
checkToggler();
}
function checkToggler() {
var hidden = true;
Array.from(alertsEl.querySelectorAll('.alert')).forEach(function(alert) {
if (hidden && !alert.classList.contains(ALERT_INVISIBLE_CLASS)) {
hidden = false;
}
});
if (!alertsShowingToggler) {
alertsShowingToggler = true;
window.setTimeout(function() {
toggler.classList.toggle(TOGGLER_INVISIBLE_CLASS, hidden);
alertsShowingToggler = false;
}, 120);
}
}
if (!alertsEl.classList.contains('js-initialized') || !toggler)
makeToggler();
Array.from(alertsEl.querySelectorAll('.alert:not(.js-initialized)')).map(makeAlert);
}
})();
document.addEventListener('setup', function(e) {
if (e.detail.module && e.detail.module !== 'alerts')
return;
// setup alerts
if (e.detail.scope.classList.contains('alerts')) {
window.utils.alerts(e.detail.scope);
} else {
var alertsEl = e.detail.scope.querySelector('.alerts');
if (alertsEl)
window.utils.alerts(alertsEl);
}
});
document.addEventListener('DOMContentLoaded', function() {
document.dispatchEvent(new CustomEvent('setup', { detail: { scope: document.body, module: 'alerts' }, bubbles: true, cancelable: true }))
});

View File

@ -0,0 +1,9 @@
<div #alerts .alerts>
<div .alerts__toggler>
$forall (status, msg) <- mmsgs
$with status2 <- bool status "info" (status == "")
<div class="alert alert-#{status2}">
<div .alert__closer>
<div .alert__icon>
<div .alert__content>
#{msg}

View File

@ -0,0 +1,18 @@
document.addEventListener('setup', function(e) {
if (!e.detail.module || e.detail.module !== 'alerts') {
return;
}
// setup alerts
if (e.detail.scope.classList.contains('alerts')) {
window.utils.alerts(e.detail.scope);
} else {
var alertsEl = e.detail.scope.querySelector('.alerts');
if (alertsEl)
window.utils.alerts(alertsEl);
}
});
document.addEventListener('DOMContentLoaded', function() {
document.dispatchEvent(new CustomEvent('setup', { detail: { scope: document.body, module: 'alerts' }, bubbles: true, cancelable: true }))
});