diff --git a/messages/uniworx/de.msg b/messages/uniworx/de.msg index be30c01cf..f620cecf7 100644 --- a/messages/uniworx/de.msg +++ b/messages/uniworx/de.msg @@ -208,6 +208,7 @@ CorByProportionOnly proportion@Rational: #{display proportion} Anteile CorByProportionIncludingTutorial proportion@Rational: #{display proportion} Anteile - Tutorium CorByProportionExcludingTutorial proportion@Rational: #{display proportion} Anteile + Tutorium +RowCount count@Int64: #{display count} #{pluralDE count "Eintrag" "Einträge"} insgesamt DeleteRow: Zeile entfernen ProportionNegative: Anteile dürfen nicht negativ sein CorrectorUpdated: Korrektor erfolgreich aktualisiert @@ -241,7 +242,7 @@ MultiFileUploadInfo: (Mehrere Dateien mit Shift oder Strg auswählen) NrColumn: Nr SelectColumn: Auswahl -DBTablePagesize: Einträge +DBTablePagesize: Einträge pro Seite DBTablePagesizeAll: Alle CorrDownload: Herunterladen @@ -610,4 +611,4 @@ DeleteCopyStringIfSure n@Int: Wenn Sie sich sicher sind, dass Sie #{pluralDE n " DeleteConfirmation: Bestätigung DeleteConfirmationWrong: Bestätigung muss genau dem angezeigten Text entsprechen. -DBTIRowsMissing n@Int: #{pluralDE n "Eine Zeile ist" "Einige Zeile sind"} aus der Datenbank verschwunden, seit das Formular für Sie generiert wurde \ No newline at end of file +DBTIRowsMissing n@Int: #{pluralDE n "Eine Zeile ist" "Einige Zeile sind"} aus der Datenbank verschwunden, seit das Formular für Sie generiert wurde diff --git a/src/Handler/Utils/Table/Pagination.hs b/src/Handler/Utils/Table/Pagination.hs index 9b205b1a9..368758bea 100644 --- a/src/Handler/Utils/Table/Pagination.hs +++ b/src/Handler/Utils/Table/Pagination.hs @@ -602,11 +602,11 @@ dbTable PSValidator{..} dbtable@DBTable{ dbtIdent = dbtIdent'@(toPathPiece -> db | otherwise = def + referencePagesize = psLimit . snd . runPSValidator dbtable $ Just prevPi + (((filterRes, filterWdgt), filterEnc), ((pagesizeRes, pagesizeWdgt), pagesizeEnc)) <- mdo (filterRes'@((filterRes, _), _)) <- runFormGet . identForm FIDDBTableFilter . addPIHiddenField dbtable (prevPi & _piFilter .~ Nothing & _piPage .~ Nothing & _piLimit .~ (formResult' pagesizeRes <|> piLimit prevPi)) . renderAForm FormDBTableFilter $ dbtFilterUI (piFilter prevPi) - let referencePagesize = psLimit . snd . runPSValidator dbtable $ Just prevPi - (pagesizeRes'@((pagesizeRes, _), _)) <- lift . runFormGet . identForm FIDDBTablePagesize . addPIHiddenField dbtable (prevPi & _piPage .~ Nothing & _piLimit .~ Nothing & _piFilter .~ (formResult' filterRes <|> piFilter prevPi)) . renderAForm FormDBTablePagesize $ areq (pagesizeField referencePagesize) (fslI MsgDBTablePagesize & addAutosubmit & addName (wIdent "pagesize") & addClass "select--pagesize") (Just referencePagesize) <* autosubmitButton @@ -760,6 +760,15 @@ dbColonnade :: (Headedness h, Monoid x) -> Colonnade h r (DBCell (ReaderT SqlBackend (HandlerT UniWorX IO)) x) dbColonnade = id +pagesizeOptions :: PagesizeLimit -- ^ Current/previous value + -> NonNull [PagesizeLimit] +pagesizeOptions psLim = impureNonNull . Set.toAscList . Set.fromList $ psLim : PagesizeAll : map PagesizeLimit opts + where + opts :: [Int64] + opts = filter (> 0) $ opts' <> map (`div` 2) opts' + + opts' = [ 10^n | n <- [1..3]] + pagesizeField :: PagesizeLimit -> Field Handler PagesizeLimit pagesizeField psLim = selectField $ do MsgRenderer mr <- getMsgRenderer @@ -767,16 +776,9 @@ pagesizeField psLim = selectField $ do optText (PagesizeLimit l) = tshow l optText PagesizeAll = mr MsgDBTablePagesizeAll - toOptionList = flip OptionList fromPathPiece . map (\o -> Option (optText o) o $ toPathPiece o) . Set.toAscList . Set.fromList - return $ toOptionList limOpts - where - limOpts :: [PagesizeLimit] - limOpts = psLim : PagesizeAll : map PagesizeLimit opts + toOptionList = flip OptionList fromPathPiece . map (\o -> Option (optText o) o $ toPathPiece o) + return . toOptionList . toNullable $ pagesizeOptions psLim - opts :: [Int64] - opts = filter (> 0) $ opts' <> map (`div` 2) opts' - - opts' = [ 10^n | n <- [1..3]] --- DBCell utility functions diff --git a/templates/courses.hamlet b/templates/courses.hamlet index cc9106b21..6cc6a0c13 100644 --- a/templates/courses.hamlet +++ b/templates/courses.hamlet @@ -1,3 +1,2 @@
-
- ^{coursesTable} + ^{coursesTable} diff --git a/templates/default-layout.lucius b/templates/default-layout.lucius index 08c29a53a..729e4c672 100644 --- a/templates/default-layout.lucius +++ b/templates/default-layout.lucius @@ -335,6 +335,8 @@ input[type="button"].btn-info:hover, /* SCROLLTABLE */ .scrolltable { overflow: auto; + box-shadow: 0 0 3px 0 var(--color-grey); + margin-bottom: 15px; } @media (max-width: 425px) { diff --git a/templates/standalone/inputs.lucius b/templates/standalone/inputs.lucius index 4bbb74800..3b0d11372 100644 --- a/templates/standalone/inputs.lucius +++ b/templates/standalone/inputs.lucius @@ -277,6 +277,8 @@ input[type="checkbox"]:checked::after { [disabled] + label { pointer-events: none; border: none; + opacity: 0.6; + filter: grayscale(1); } } diff --git a/templates/table/layout.hamlet b/templates/table/layout.hamlet index bc0fbd289..1a455c968 100644 --- a/templates/table/layout.hamlet +++ b/templates/table/layout.hamlet @@ -3,10 +3,17 @@ $if null rows && (dbsEmptyStyle == DBESNoHeading) _{dbsEmptyMessage} $else ^{table} + +
+
+ _{MsgRowCount rowCount} + $# Since the current pagesize is always a member of pagesizeOptions we don't need to check `pageCount > 1` + $if toEnum (fromIntegral rowCount) > minimum (pagesizeOptions referencePagesize) +
+ ^{pagesizeWdgt} + $if pageCount > 1
- - ^{pagesizeWdgt}
    $forall p <- pageNumbers
  • diff --git a/templates/table/layout.julius b/templates/table/layout.julius index 38feadbbc..59cc3c040 100644 --- a/templates/table/layout.julius +++ b/templates/table/layout.julius @@ -1,94 +1,148 @@ (function collonadeClosure() { 'use strict'; - document.addEventListener('setup', function DOMContentLoaded(e) { + window.utils = window.utils || {}; - console.log('dbtable', e); + window.utils.asyncTable = function(wrapper) { - if (e.detail.module && e.detail.module !== 'dbtable') - return; + var tableIdent = #{String dbtIdent}; + var shortCircuitHeader = #{String (toPathPiece HeaderDBTableShortcircuit)}; - function setupAsync(wrapper) { + var ths = []; + var pagination; + var pagesizeForm; - var table = wrapper.querySelector('#' + #{String dbtIdent}); - if (!table) + function init() { + var table = wrapper.querySelector('#' + tableIdent); + if (!table) { return; - - var ths = Array.from(table.querySelectorAll('th.sortable')); - var pagination = wrapper.querySelector('#' + #{String dbtIdent} + '-pagination'); + } + ths = Array.from(table.querySelectorAll('th.sortable')); + pagination = wrapper.querySelector('#' + tableIdent + '-pagination'); + pagesizeForm = wrapper.querySelector('#' + tableIdent + '-pagesize-form'); + + setupListeners(); + wrapper.classList.add('js-initialized'); + } + + function setupListeners() { ths.forEach(function(th) { th.addEventListener('click', clickHandler); }); if (pagination) { - Array.from(pagination.querySelectorAll('.page-link')) - .forEach(function(p) { - p.addEventListener('click', clickHandler); - }); - } - - function clickHandler(event) { - event.preventDefault(); - var url = new URL(window.location.origin + window.location.pathname + getClickDestination(this)); - updateTableFrom(url); - } - - function getClickDestination(el) { - console.log(el); - if (!el.querySelector('a')) { - return false; - } - return el.querySelector('a').getAttribute('href'); - } - - // fetches new sorted table from url with params and replaces contents of current table - function updateTableFrom(url) { - fetch(url, { - credentials: 'same-origin', - headers: { - 'Accept': 'text/html', - #{String (toPathPiece HeaderDBTableShortcircuit)}: #{String dbtIdent} - } - }).then(function(response) { - if (!response.ok) { - throw ('Looks like there was a problem fetching ' + url.toString() + '. Status Code: ' + response.status); - } - return response.text(); - }).then(function(data) { - // remove listeners - ths.forEach(function(th) { - th.removeEventListener('click', clickHandler); - }); - - // replace contents of table body - wrapper.innerHTML = data; - - // set up async functionality again - wrapper.classList.remove("js-initialized"); - document.dispatchEvent(new CustomEvent('setup', { - detail: { scope: wrapper }, - bubbles: true, - cancelable: true - })); - // table.querySelector('tbody').innerHTML = data; - }).catch(function(err) { - console.error(err); + var pageLinks = Array.from(pagination.querySelectorAll('.page-link')); + pageLinks.forEach(function(p) { + p.addEventListener('click', clickHandler); }); } - wrapper.classList.add("js-initialized"); + if (pagesizeForm) { + var pagesizeSelect = pagesizeForm.querySelector('[name=' + tableIdent + '-pagesize]') + pagesizeSelect.addEventListener('change', changeHandler); + } } - var selector = '#' + #{String $ dbtIdent} + '-table-wrapper:not(.js-initialized)'; - var wrapperEl = e.detail.scope.querySelector(selector); - if (wrapperEl) - setupAsync(wrapperEl); - else if (e.detail.scope.matches(selector)) - setupAsync(e.detail.scope); - }); + function removeListeners() { + ths.forEach(function(th) { + th.removeEventListener('click', clickHandler); + }); + + if (pagination) { + var pageLinks = Array.from(pagination.querySelectorAll('.page-link')); + pageLinks.forEach(function(p) { + p.removeEventListener('click', clickHandler); + }); + } + + if (pagesizeForm) { + var pagesizeSelect = pagesizeForm.querySelector('[name=' + tableIdent + '-pagesize]') + pagesizeSelect.removeEventListener('change', changeHandler); + } + } + + function clickHandler(event) { + event.preventDefault(); + var url = new URL(window.location.origin + window.location.pathname + getClickDestination(this)); + updateTableFrom(url); + } + + function getClickDestination(el) { + if (!el.querySelector('a')) { + return ''; + } + return el.querySelector('a').getAttribute('href'); + } + + function changeHandler(event) { + var currentTableUrl = wrapper.dataset.currentUrl || window.location.href; + var url = getUrlWithUpdatedPagesize(currentTableUrl, event.target.value); + url = getUrlWithResetPagenumber(url); + updateTableFrom(url); + } + + function getUrlWithUpdatedPagesize(url, pagesize) { + if (url.indexOf('pagesize') >= 0) { + return url.replace(/pagesize=(\d+)/, 'pagesize=' + pagesize); + } else if (url.indexOf('?') >= 0) { + return url += '&' + tableIdent + '-pagesize=' + pagesize; + } + + return url += '?' + tableIdent + '-pagesize=' + pagesize; + } + + function getUrlWithResetPagenumber(url) { + return url.replace(/-page=\d+/, '-page=0'); + } + + function updateWrapperContents(newHtml) { + wrapper.innerHTML = newHtml; + wrapper.classList.remove("js-initialized"); + + // setup the wrapper and its components to behave async again + window.utils.asyncTable(wrapper); + + // make sure to hide any new submit buttons + document.dispatchEvent(new CustomEvent('setup', { + detail: { + scope: wrapper, + module: 'autoSubmit' + } + })); + } + + // fetches new sorted table from url with params and replaces contents of current table + function updateTableFrom(url) { + + fetch(url, { + credentials: 'same-origin', + headers: { + 'Accept': 'text/html', + [shortCircuitHeader]: tableIdent + } + }).then(function(response) { + if (!response.ok) { + throw new Error('Looks like there was a problem fetching ' + url + '. Status Code: ' + response.status); + } + return response.text(); + }).then(function(data) { + wrapper.dataset.currentUrl = url; + removeListeners(); + updateWrapperContents(data); + }).catch(function(err) { + console.error(err); + }); + } + + init(); + }; })(); document.addEventListener('DOMContentLoaded', function() { - document.dispatchEvent(new CustomEvent('setup', { detail: { scope: document.body, module: 'dbtable' }, bubbles: true, cancelable: true })); + var selector = '#' + #{String $ dbtIdent} + '-table-wrapper:not(.js-initialized)'; + var wrapper = document.querySelector(selector); + if (wrapper) { + window.utils.asyncTable(wrapper); + } }); diff --git a/templates/table/layout.lucius b/templates/table/layout.lucius index c34095855..c35fcecde 100644 --- a/templates/table/layout.lucius +++ b/templates/table/layout.lucius @@ -1,24 +1,29 @@ +/* TABLE FOOTER */ +.table-footer { + display: flex; + flex-flow: row-reverse; + justify-content: space-between; +} + /* PAGINATION */ .pagination { margin-top: 20px; - display: flex; - flex-direction: row; - - .pagesize { - float: left; - flex-grow: 0; - } + overflow: auto; .pages { text-align: center; - flex-grow: 1; + white-space: nowrap; margin: 0; .page-link { - margin: 0 7px; + margin-top: 7px; display: inline-block; background-color: var(--color-grey); + + .page-link { + margin-left: 7px; + } + a { color: var(--color-lightwhite); padding: 7px 13px; @@ -42,10 +47,6 @@ pointer-events: none; } } - - &:last-child { - margin-right: 0; - } } } } diff --git a/templates/widgets/asidenav.julius b/templates/widgets/asidenav.julius index 059a38f53..238ca1f58 100644 --- a/templates/widgets/asidenav.julius +++ b/templates/widgets/asidenav.julius @@ -17,16 +17,7 @@ }; })(); -document.addEventListener('setup', function(e) { - if (e.detail.module && e.detail.module !== 'asidenav') - return; - - var asidenavEl = e.detail.scope.querySelector('.main__aside'); - - window.utils.aside(asidenavEl); - -}); - document.addEventListener('DOMContentLoaded', function() { - document.dispatchEvent(new CustomEvent('setup', { detail: { scope: document.body, module: 'asidenav' }, bubbles: true, cancelable: true })) + var asidenavEl = document.querySelector('.main__aside'); + window.utils.aside(asidenavEl); }); diff --git a/templates/widgets/form.hamlet b/templates/widgets/form.hamlet index 4fb9bef79..06578df77 100644 --- a/templates/widgets/form.hamlet +++ b/templates/widgets/form.hamlet @@ -3,6 +3,7 @@ $newline never $case formLayout $of FormDBTablePagesize $forall view <- fieldViews +