Merge branch 'master' of gitlab.cip.ifi.lmu.de:jost/UniWorX
This commit is contained in:
commit
d3e0f462f4
@ -114,6 +114,7 @@ dependencies:
|
|||||||
- memcached-binary
|
- memcached-binary
|
||||||
- directory-tree
|
- directory-tree
|
||||||
- lifted-base
|
- lifted-base
|
||||||
|
- hsass
|
||||||
|
|
||||||
other-extensions:
|
other-extensions:
|
||||||
- GeneralizedNewtypeDeriving
|
- GeneralizedNewtypeDeriving
|
||||||
|
|||||||
@ -974,6 +974,8 @@ siteLayout' headingOverride widget = do
|
|||||||
asidenav = $(widgetFile "widgets/asidenav")
|
asidenav = $(widgetFile "widgets/asidenav")
|
||||||
footer :: Widget
|
footer :: Widget
|
||||||
footer = $(widgetFile "widgets/footer")
|
footer = $(widgetFile "widgets/footer")
|
||||||
|
alerts :: Widget
|
||||||
|
alerts = $(widgetFile "widgets/alerts/alerts")
|
||||||
contentHeadline :: Maybe Widget
|
contentHeadline :: Maybe Widget
|
||||||
contentHeadline = headingOverride <|> (pageHeading =<< mcurrentRoute)
|
contentHeadline = headingOverride <|> (pageHeading =<< mcurrentRoute)
|
||||||
breadcrumbsWgt :: Widget
|
breadcrumbsWgt :: Widget
|
||||||
@ -993,17 +995,18 @@ siteLayout' headingOverride widget = do
|
|||||||
addScript $ StaticR js_polyfills_urlPolyfill_js
|
addScript $ StaticR js_polyfills_urlPolyfill_js
|
||||||
addScript $ StaticR js_utils_featureChecker_js
|
addScript $ StaticR js_utils_featureChecker_js
|
||||||
addScript $ StaticR js_utils_tabber_js
|
addScript $ StaticR js_utils_tabber_js
|
||||||
|
addScript $ StaticR js_utils_alerts_js
|
||||||
addStylesheet $ StaticR css_vendor_flatpickr_css
|
addStylesheet $ StaticR css_vendor_flatpickr_css
|
||||||
addStylesheet $ StaticR css_vendor_fontawesome_css
|
addStylesheet $ StaticR css_vendor_fontawesome_css
|
||||||
addStylesheet $ StaticR css_fonts_css
|
addStylesheet $ StaticR css_fonts_css
|
||||||
addStylesheet $ StaticR css_utils_tabber_css
|
addStylesheet $ StaticR css_utils_tabber_css
|
||||||
|
addStylesheet $ StaticR css_utils_alerts_scss
|
||||||
$(widgetFile "default-layout")
|
$(widgetFile "default-layout")
|
||||||
$(widgetFile "standalone/modal")
|
$(widgetFile "standalone/modal")
|
||||||
$(widgetFile "standalone/showHide")
|
$(widgetFile "standalone/showHide")
|
||||||
$(widgetFile "standalone/inputs")
|
$(widgetFile "standalone/inputs")
|
||||||
$(widgetFile "standalone/tooltip")
|
$(widgetFile "standalone/tooltip")
|
||||||
$(widgetFile "standalone/tabber")
|
$(widgetFile "standalone/tabber")
|
||||||
$(widgetFile "standalone/alerts")
|
|
||||||
$(widgetFile "standalone/datepicker")
|
$(widgetFile "standalone/datepicker")
|
||||||
withUrlRenderer $(hamletFile "templates/default-layout-wrapper.hamlet")
|
withUrlRenderer $(hamletFile "templates/default-layout-wrapper.hamlet")
|
||||||
|
|
||||||
|
|||||||
@ -6,6 +6,7 @@ module Settings.StaticFiles
|
|||||||
import ClassyPrelude
|
import ClassyPrelude
|
||||||
|
|
||||||
import Settings (appStaticDir, compileTimeAppSettings)
|
import Settings (appStaticDir, compileTimeAppSettings)
|
||||||
|
import Settings.StaticFiles.Generator
|
||||||
import Yesod.EmbeddedStatic
|
import Yesod.EmbeddedStatic
|
||||||
|
|
||||||
-- This generates easy references to files in the static directory at compile time,
|
-- This generates easy references to files in the static directory at compile time,
|
||||||
@ -23,4 +24,4 @@ import Yesod.EmbeddedStatic
|
|||||||
#define DEV_BOOL False
|
#define DEV_BOOL False
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
mkEmbeddedStatic DEV_BOOL "embeddedStatic" [embedDir $ appStaticDir compileTimeAppSettings]
|
mkEmbeddedStatic DEV_BOOL "embeddedStatic" . pure . staticGenerator $ appStaticDir compileTimeAppSettings
|
||||||
|
|||||||
72
src/Settings/StaticFiles/Generator.hs
Normal file
72
src/Settings/StaticFiles/Generator.hs
Normal 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) |]
|
||||||
|
}
|
||||||
@ -156,7 +156,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.alert__close {
|
.alert__closer {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -196,7 +196,7 @@
|
|||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
|
|
||||||
.alert__close {
|
.alert__closer {
|
||||||
width: 40px;
|
width: 40px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
86
static/js/utils/alerts.js
Normal file
86
static/js/utils/alerts.js
Normal 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);
|
||||||
|
};
|
||||||
|
})();
|
||||||
@ -30,12 +30,7 @@ $if not isModal
|
|||||||
^{widget}
|
^{widget}
|
||||||
|
|
||||||
<!-- alerts -->
|
<!-- alerts -->
|
||||||
<div #alerts .alerts>
|
^{alerts}
|
||||||
$forall (status, msg) <- mmsgs
|
|
||||||
$with status2 <- bool status "info" (status == "")
|
|
||||||
<div class="alert alert-#{status2}">
|
|
||||||
<div .alert__content>
|
|
||||||
#{msg}
|
|
||||||
|
|
||||||
<!-- footer -->
|
<!-- footer -->
|
||||||
$if not isModal
|
$if not isModal
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
<!-- only here to be able to include alerts using `toWidget` -->
|
|
||||||
@ -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 }))
|
|
||||||
});
|
|
||||||
9
templates/widgets/alerts/alerts.hamlet
Normal file
9
templates/widgets/alerts/alerts.hamlet
Normal 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}
|
||||||
18
templates/widgets/alerts/alerts.julius
Normal file
18
templates/widgets/alerts/alerts.julius
Normal 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 }))
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue
Block a user