Merge branch 'js-utils-cleanup'
This commit is contained in:
commit
55c1385e3e
@ -7,7 +7,7 @@ import Import.NoFoundation
|
|||||||
import Database.Persist.Sql (SqlBackendCanRead)
|
import Database.Persist.Sql (SqlBackendCanRead)
|
||||||
|
|
||||||
import Utils.Form
|
import Utils.Form
|
||||||
|
|
||||||
import Data.CaseInsensitive (CI)
|
import Data.CaseInsensitive (CI)
|
||||||
import qualified Data.CaseInsensitive as CI
|
import qualified Data.CaseInsensitive as CI
|
||||||
|
|
||||||
@ -54,4 +54,4 @@ dummyLogin = AuthPlugin{..}
|
|||||||
apDispatch _ _ = notFound
|
apDispatch _ _ = notFound
|
||||||
apLogin toMaster = do
|
apLogin toMaster = do
|
||||||
(login, loginEnctype) <- handlerToWidget . generateFormPost $ renderAForm FormStandard dummyForm
|
(login, loginEnctype) <- handlerToWidget . generateFormPost $ renderAForm FormStandard dummyForm
|
||||||
$(widgetFile "widgets/dummy-login-form")
|
$(widgetFile "widgets/dummy-login-form/dummy-login-form")
|
||||||
|
|||||||
@ -36,7 +36,7 @@ data CampusMessage = MsgCampusIdentNote
|
|||||||
| MsgCampusSubmit
|
| MsgCampusSubmit
|
||||||
| MsgCampusInvalidCredentials
|
| MsgCampusInvalidCredentials
|
||||||
deriving (Eq, Ord, Enum, Bounded, Read, Show, Generic, Typeable)
|
deriving (Eq, Ord, Enum, Bounded, Read, Show, Generic, Typeable)
|
||||||
|
|
||||||
|
|
||||||
findUser :: LdapConf -> Ldap -> Text -> [Ldap.Attr] -> IO [Ldap.SearchEntry]
|
findUser :: LdapConf -> Ldap -> Text -> [Ldap.Attr] -> IO [Ldap.SearchEntry]
|
||||||
findUser LdapConf{..} ldap campusIdent = Ldap.search ldap ldapBase userSearchSettings userFilter
|
findUser LdapConf{..} ldap campusIdent = Ldap.search ldap ldapBase userSearchSettings userFilter
|
||||||
@ -48,7 +48,7 @@ findUser LdapConf{..} ldap campusIdent = Ldap.search ldap ldapBase userSearchSet
|
|||||||
, Ldap.time ldapSearchTimeout
|
, Ldap.time ldapSearchTimeout
|
||||||
, Ldap.derefAliases Ldap.DerefAlways
|
, Ldap.derefAliases Ldap.DerefAlways
|
||||||
]
|
]
|
||||||
|
|
||||||
userPrincipalName :: Ldap.Attr
|
userPrincipalName :: Ldap.Attr
|
||||||
userPrincipalName = Ldap.Attr "userPrincipalName"
|
userPrincipalName = Ldap.Attr "userPrincipalName"
|
||||||
|
|
||||||
@ -105,7 +105,7 @@ campusLogin conf@LdapConf{..} pool = AuthPlugin{..}
|
|||||||
apDispatch _ _ = notFound
|
apDispatch _ _ = notFound
|
||||||
apLogin toMaster = do
|
apLogin toMaster = do
|
||||||
(login, loginEnctype) <- handlerToWidget . generateFormPost $ renderAForm FormStandard campusForm
|
(login, loginEnctype) <- handlerToWidget . generateFormPost $ renderAForm FormStandard campusForm
|
||||||
$(widgetFile "widgets/campus-login-form")
|
$(widgetFile "widgets/campus-login/campus-login-form")
|
||||||
|
|
||||||
data CampusUserException = CampusUserLdapError LdapPoolError
|
data CampusUserException = CampusUserLdapError LdapPoolError
|
||||||
| CampusUserHostNotResolved String
|
| CampusUserHostNotResolved String
|
||||||
|
|||||||
@ -35,7 +35,7 @@ hashForm = HashLogin
|
|||||||
<*> areq passwordField (fslpI MsgPWHashPassword "Passwort") Nothing
|
<*> areq passwordField (fslpI MsgPWHashPassword "Passwort") Nothing
|
||||||
<* submitButton
|
<* submitButton
|
||||||
|
|
||||||
|
|
||||||
hashLogin :: ( YesodAuth site
|
hashLogin :: ( YesodAuth site
|
||||||
, YesodPersist site
|
, YesodPersist site
|
||||||
, SqlBackendCanRead (YesodPersistBackend site)
|
, SqlBackendCanRead (YesodPersistBackend site)
|
||||||
@ -90,5 +90,5 @@ hashLogin pwHashAlgo = AuthPlugin{..}
|
|||||||
apDispatch _ _ = notFound
|
apDispatch _ _ = notFound
|
||||||
apLogin toMaster = do
|
apLogin toMaster = do
|
||||||
(login, loginEnctype) <- handlerToWidget . generateFormPost $ renderAForm FormStandard hashForm
|
(login, loginEnctype) <- handlerToWidget . generateFormPost $ renderAForm FormStandard hashForm
|
||||||
$(widgetFile "widgets/hash-login-form")
|
$(widgetFile "widgets/hash-login-form/hash-login-form")
|
||||||
|
|
||||||
|
|||||||
@ -982,19 +982,19 @@ siteLayout' headingOverride widget = do
|
|||||||
-- you to use normal widget features in default-layout.
|
-- you to use normal widget features in default-layout.
|
||||||
|
|
||||||
navbar :: Widget
|
navbar :: Widget
|
||||||
navbar = $(widgetFile "widgets/navbar")
|
navbar = $(widgetFile "widgets/navbar/navbar")
|
||||||
asidenav :: Widget
|
asidenav :: Widget
|
||||||
asidenav = $(widgetFile "widgets/asidenav")
|
asidenav = $(widgetFile "widgets/asidenav/asidenav")
|
||||||
footer :: Widget
|
footer :: Widget
|
||||||
footer = $(widgetFile "widgets/footer")
|
footer = $(widgetFile "widgets/footer/footer")
|
||||||
alerts :: Widget
|
alerts :: Widget
|
||||||
alerts = $(widgetFile "widgets/alerts/alerts")
|
alerts = $(widgetFile "widgets/alerts/alerts")
|
||||||
contentHeadline :: Maybe Widget
|
contentHeadline :: Maybe Widget
|
||||||
contentHeadline = headingOverride <|> (pageHeading =<< mcurrentRoute)
|
contentHeadline = headingOverride <|> (pageHeading =<< mcurrentRoute)
|
||||||
breadcrumbsWgt :: Widget
|
breadcrumbsWgt :: Widget
|
||||||
breadcrumbsWgt = $(widgetFile "widgets/breadcrumbs")
|
breadcrumbsWgt = $(widgetFile "widgets/breadcrumbs/breadcrumbs")
|
||||||
pageaction :: Widget
|
pageaction :: Widget
|
||||||
pageaction = $(widgetFile "widgets/pageaction")
|
pageaction = $(widgetFile "widgets/pageaction/pageaction")
|
||||||
-- functions to determine if there are page-actions (primary or secondary)
|
-- functions to determine if there are page-actions (primary or secondary)
|
||||||
hasPageActions, hasSecondaryPageActions, hasPrimaryPageActions :: Bool
|
hasPageActions, hasSecondaryPageActions, hasPrimaryPageActions :: Bool
|
||||||
hasPageActions = hasPrimaryPageActions || hasSecondaryPageActions
|
hasPageActions = hasPrimaryPageActions || hasSecondaryPageActions
|
||||||
@ -1002,25 +1002,35 @@ siteLayout' headingOverride widget = do
|
|||||||
hasPrimaryPageActions = any (is _PageActionPrime) $ toListOf (traverse . _1 . _menuItemType) menuTypes
|
hasPrimaryPageActions = any (is _PageActionPrime) $ toListOf (traverse . _1 . _menuItemType) menuTypes
|
||||||
|
|
||||||
pc <- widgetToPageContent $ do
|
pc <- widgetToPageContent $ do
|
||||||
addScript $ StaticR js_vendor_zepto_js
|
-- 3rd party
|
||||||
addScript $ StaticR js_vendor_flatpickr_js
|
addScript $ StaticR js_vendor_flatpickr_js
|
||||||
addScript $ StaticR js_polyfills_fetchPolyfill_js
|
addScript $ StaticR js_vendor_zepto_js
|
||||||
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_flatpickr_css
|
||||||
addStylesheet $ StaticR css_vendor_fontawesome_css
|
addStylesheet $ StaticR css_vendor_fontawesome_css
|
||||||
|
-- fonts
|
||||||
addStylesheet $ StaticR css_fonts_css
|
addStylesheet $ StaticR css_fonts_css
|
||||||
addStylesheet $ StaticR css_utils_tabber_css
|
-- polyfills
|
||||||
|
addScript $ StaticR js_polyfills_fetchPolyfill_js
|
||||||
|
addScript $ StaticR js_polyfills_urlPolyfill_js
|
||||||
|
-- JavaScript utils
|
||||||
|
addScript $ StaticR js_utils_alerts_js
|
||||||
|
addScript $ StaticR js_utils_asidenav_js
|
||||||
|
addScript $ StaticR js_utils_asyncTable_js
|
||||||
|
addScript $ StaticR js_utils_form_js
|
||||||
|
addScript $ StaticR js_utils_inputs_js
|
||||||
|
addScript $ StaticR js_utils_setup_js
|
||||||
|
addScript $ StaticR js_utils_showHide_js
|
||||||
|
addScript $ StaticR js_utils_tabber_js
|
||||||
addStylesheet $ StaticR css_utils_alerts_scss
|
addStylesheet $ StaticR css_utils_alerts_scss
|
||||||
|
addStylesheet $ StaticR css_utils_asidenav_scss
|
||||||
|
addStylesheet $ StaticR css_utils_form_scss
|
||||||
|
addStylesheet $ StaticR css_utils_inputs_scss
|
||||||
|
addStylesheet $ StaticR css_utils_showHide_scss
|
||||||
|
addStylesheet $ StaticR css_utils_tabber_scss
|
||||||
|
addStylesheet $ StaticR css_utils_tooltip_scss
|
||||||
|
-- widgets
|
||||||
$(widgetFile "default-layout")
|
$(widgetFile "default-layout")
|
||||||
$(widgetFile "standalone/modal")
|
$(widgetFile "standalone/modal")
|
||||||
$(widgetFile "standalone/showHide")
|
|
||||||
$(widgetFile "standalone/inputs")
|
|
||||||
$(widgetFile "standalone/tooltip")
|
|
||||||
$(widgetFile "standalone/tabber")
|
|
||||||
$(widgetFile "standalone/datepicker")
|
|
||||||
withUrlRenderer $(hamletFile "templates/default-layout-wrapper.hamlet")
|
withUrlRenderer $(hamletFile "templates/default-layout-wrapper.hamlet")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -162,7 +162,7 @@ colRating = sortable (Just "rating") (i18nCell MsgRating) $ \DBRow{ dbrOutput=(E
|
|||||||
cid <- encrypt subId
|
cid <- encrypt subId
|
||||||
return $ CSubmissionR tid ssh csh sheetName cid CorrectionR
|
return $ CSubmissionR tid ssh csh sheetName cid CorrectionR
|
||||||
in mconcat
|
in mconcat
|
||||||
[ anchorCellM mkRoute $(widgetFile "widgets/rating")
|
[ anchorCellM mkRoute $(widgetFile "widgets/rating/rating")
|
||||||
, writerCell $ do
|
, writerCell $ do
|
||||||
let
|
let
|
||||||
summary :: SheetTypeSummary
|
summary :: SheetTypeSummary
|
||||||
|
|||||||
@ -297,7 +297,7 @@ registerForm registered msecret extra = do
|
|||||||
(Just _) | not registered -> bimap Just Just <$> mreq textField (fslpI MsgCourseSecret "Code") Nothing
|
(Just _) | not registered -> bimap Just Just <$> mreq textField (fslpI MsgCourseSecret "Code") Nothing
|
||||||
_ -> return (Nothing,Nothing)
|
_ -> return (Nothing,Nothing)
|
||||||
(btnRes, btnView) <- mreq (buttonField $ bool BtnRegister BtnDeregister registered) "buttonField ignores settings anyway" Nothing
|
(btnRes, btnView) <- mreq (buttonField $ bool BtnRegister BtnDeregister registered) "buttonField ignores settings anyway" Nothing
|
||||||
let widget = $(widgetFile "widgets/registerForm")
|
let widget = $(widgetFile "widgets/register-form/register-form")
|
||||||
let msecretRes | Just res <- msecretRes' = Just <$> res
|
let msecretRes | Just res <- msecretRes' = Just <$> res
|
||||||
| otherwise = FormSuccess Nothing
|
| otherwise = FormSuccess Nothing
|
||||||
return (btnRes *> ((==msecret) <$> msecretRes), widget) -- checks that correct button was pressed, and ignores result of btnRes
|
return (btnRes *> ((==msecret) <$> msecretRes), widget) -- checks that correct button was pressed, and ignores result of btnRes
|
||||||
|
|||||||
@ -221,7 +221,7 @@ getProfileDataR = do
|
|||||||
-- Delete Button
|
-- Delete Button
|
||||||
(btnWdgt, btnEnctype) <- generateFormPost (buttonForm :: Form ButtonDelete)
|
(btnWdgt, btnEnctype) <- generateFormPost (buttonForm :: Form ButtonDelete)
|
||||||
defaultLayout $ do
|
defaultLayout $ do
|
||||||
let delWdgt = $(widgetFile "widgets/data-delete")
|
let delWdgt = $(widgetFile "widgets/data-delete/data-delete")
|
||||||
$(widgetFile "profileData")
|
$(widgetFile "profileData")
|
||||||
$(widgetFile "dsgvDisclaimer")
|
$(widgetFile "dsgvDisclaimer")
|
||||||
|
|
||||||
|
|||||||
@ -205,7 +205,7 @@ getSheetListR tid ssh csh = do
|
|||||||
mkRoute = do
|
mkRoute = do
|
||||||
cid' <- mkCid
|
cid' <- mkCid
|
||||||
return $ CSubmissionR tid ssh csh sheetName cid' CorrectionR
|
return $ CSubmissionR tid ssh csh sheetName cid' CorrectionR
|
||||||
acell = anchorCellM mkRoute $(widgetFile "widgets/rating")
|
acell = anchorCellM mkRoute $(widgetFile "widgets/rating/rating")
|
||||||
in cellTell acell $ stats submissionRatingPoints
|
in cellTell acell $ stats submissionRatingPoints
|
||||||
|
|
||||||
, sortable Nothing -- (Just "percent")
|
, sortable Nothing -- (Just "percent")
|
||||||
|
|||||||
@ -182,7 +182,7 @@ postAdminUserR uuid = do
|
|||||||
return (school, cbAdmin, cbLecturer)
|
return (school, cbAdmin, cbLecturer)
|
||||||
let result = forM boxRights $ \(Entity sid _, (resAdmin,_), (resLecturer, _)) ->
|
let result = forM boxRights $ \(Entity sid _, (resAdmin,_), (resLecturer, _)) ->
|
||||||
(,,) <$> pure sid <*> resAdmin <*> resLecturer
|
(,,) <$> pure sid <*> resAdmin <*> resLecturer
|
||||||
return (result,$(widgetFile "widgets/user-rights-form"))
|
return (result,$(widgetFile "widgets/user-rights-form/user-rights-form"))
|
||||||
let userRightsAction changes = do
|
let userRightsAction changes = do
|
||||||
void . runDB $
|
void . runDB $
|
||||||
forM changes $ \(sid, userAdmin, userLecturer) ->
|
forM changes $ \(sid, userAdmin, userLecturer) ->
|
||||||
|
|||||||
@ -90,9 +90,9 @@ getDeleteR DeleteRoute{..} = do
|
|||||||
(deleteFormWdgt, deleteFormEnctype) <- generateFormPost $ confirmForm' drRecords confirmString
|
(deleteFormWdgt, deleteFormEnctype) <- generateFormPost $ confirmForm' drRecords confirmString
|
||||||
|
|
||||||
Just targetRoute <- getCurrentRoute
|
Just targetRoute <- getCurrentRoute
|
||||||
|
|
||||||
sendResponse =<<
|
sendResponse =<<
|
||||||
defaultLayout $(widgetFile "widgets/delete-confirmation")
|
defaultLayout $(widgetFile "widgets/delete-confirmation/delete-confirmation")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -591,9 +591,9 @@ multiAction acts defAction = do
|
|||||||
widgets <- mapM mToWidget results
|
widgets <- mapM mToWidget results
|
||||||
let actionWidgets = Map.foldrWithKey accWidget [] widgets
|
let actionWidgets = Map.foldrWithKey accWidget [] widgets
|
||||||
accWidget _act Nothing = id
|
accWidget _act Nothing = id
|
||||||
accWidget act (Just w) = cons $(widgetFile "widgets/multiAction")
|
accWidget act (Just w) = cons $(widgetFile "widgets/multi-action/multi-action")
|
||||||
actionResults = Map.map fst results
|
actionResults = Map.map fst results
|
||||||
return ((actionResults Map.!) =<< actionRes, $(widgetFile "widgets/multiActionCollect"))
|
return ((actionResults Map.!) =<< actionRes, $(widgetFile "widgets/multi-action/multi-action-collect"))
|
||||||
|
|
||||||
multiActionA :: (RenderMessage UniWorX action, PathPiece action, Ord action, Eq action)
|
multiActionA :: (RenderMessage UniWorX action, PathPiece action, Ord action, Eq action)
|
||||||
=> FieldSettings UniWorX
|
=> FieldSettings UniWorX
|
||||||
|
|||||||
@ -25,7 +25,7 @@ gradeSummaryWidget title sts =
|
|||||||
hasMarkedPasses = positiveSum $ numMarkedPasses sumSummaries
|
hasMarkedPasses = positiveSum $ numMarkedPasses sumSummaries
|
||||||
hasPoints = positiveSum $ numSheetsPoints sumSummaries
|
hasPoints = positiveSum $ numSheetsPoints sumSummaries
|
||||||
hasMarkedPoints = positiveSum $ numMarkedPoints sumSummaries
|
hasMarkedPoints = positiveSum $ numMarkedPoints sumSummaries
|
||||||
rowWdgts = [ $(widgetFile "widgets/gradingSummaryRow")
|
rowWdgts = [ $(widgetFile "widgets/grading-summary/grading-summary-row")
|
||||||
| (sumHeader,summary) <-
|
| (sumHeader,summary) <-
|
||||||
[ (MsgSheetTypeNormal' ,normalSummary)
|
[ (MsgSheetTypeNormal' ,normalSummary)
|
||||||
, (MsgSheetTypeBonus' ,bonusSummary)
|
, (MsgSheetTypeBonus' ,bonusSummary)
|
||||||
@ -33,4 +33,4 @@ gradeSummaryWidget title sts =
|
|||||||
] ]
|
] ]
|
||||||
in if 0 == numSheets sumSummaries
|
in if 0 == numSheets sumSummaries
|
||||||
then mempty
|
then mempty
|
||||||
else $(widgetFile "widgets/gradingSummary")
|
else $(widgetFile "widgets/grading-summary/grading-summary")
|
||||||
|
|||||||
@ -576,7 +576,7 @@ dbTable PSValidator{..} dbtable@DBTable{ dbtIdent = dbtIdent'@(toPathPiece -> db
|
|||||||
, let t' = toPathPiece $ SortingSetting t d
|
, let t' = toPathPiece $ SortingSetting t d
|
||||||
]
|
]
|
||||||
wIdent :: Text -> Text
|
wIdent :: Text -> Text
|
||||||
wIdent = toPathPiece . WithIdent dbtIdent
|
wIdent = toPathPiece . WithIdent dbtIdent
|
||||||
dbsAttrs'
|
dbsAttrs'
|
||||||
| not $ null dbtIdent = ("id", dbtIdent) : dbsAttrs
|
| not $ null dbtIdent = ("id", dbtIdent) : dbsAttrs
|
||||||
| otherwise = dbsAttrs
|
| otherwise = dbsAttrs
|
||||||
@ -802,7 +802,7 @@ cellTooltip :: (RenderMessage UniWorX msg, IsDBTable m a) => msg -> DBCell m a -
|
|||||||
cellTooltip msg = cellContents.mapped %~ (<> tipWdgt)
|
cellTooltip msg = cellContents.mapped %~ (<> tipWdgt)
|
||||||
where
|
where
|
||||||
tipWdgt = [whamlet|
|
tipWdgt = [whamlet|
|
||||||
<div .js-tooltip>
|
<div .tooltip>
|
||||||
<div .tooltip__handle>
|
<div .tooltip__handle>
|
||||||
<div .tooltip__content>_{msg}
|
<div .tooltip__content>_{msg}
|
||||||
|]
|
|]
|
||||||
|
|||||||
@ -9,7 +9,7 @@ modal modalTrigger modalContent = do
|
|||||||
let modalDynamic = isLeft modalContent
|
let modalDynamic = isLeft modalContent
|
||||||
modalId <- newIdent
|
modalId <- newIdent
|
||||||
triggerId <- newIdent
|
triggerId <- newIdent
|
||||||
$(widgetFile "widgets/modal")
|
$(widgetFile "widgets/modal/modal")
|
||||||
case modalContent of
|
case modalContent of
|
||||||
Left route -> do
|
Left route -> do
|
||||||
route' <- toTextUrl route
|
route' <- toTextUrl route
|
||||||
|
|||||||
@ -41,7 +41,7 @@ data FormLayout = FormStandard | FormDBTableFilter | FormDBTablePagesize
|
|||||||
renderAForm :: Monad m => FormLayout -> FormRender m a
|
renderAForm :: Monad m => FormLayout -> FormRender m a
|
||||||
renderAForm formLayout aform fragment = do
|
renderAForm formLayout aform fragment = do
|
||||||
(res, ($ []) -> fieldViews) <- aFormToForm aform
|
(res, ($ []) -> fieldViews) <- aFormToForm aform
|
||||||
let widget = $(widgetFile "widgets/form")
|
let widget = $(widgetFile "widgets/form/form")
|
||||||
return (res, widget)
|
return (res, widget)
|
||||||
|
|
||||||
--------------------
|
--------------------
|
||||||
@ -367,7 +367,7 @@ reorderField optList = Field{..}
|
|||||||
isSel n = (==) (either (const $ map optionInternalValue olOptions) id val !! pred n) . optionInternalValue
|
isSel n = (==) (either (const $ map optionInternalValue olOptions) id val !! pred n) . optionInternalValue
|
||||||
nums = map (id &&& withNum theId) [1..length olOptions]
|
nums = map (id &&& withNum theId) [1..length olOptions]
|
||||||
withNum t n = tshow n <> "." <> t
|
withNum t n = tshow n <> "." <> t
|
||||||
$(widgetFile "widgets/permutation")
|
$(widgetFile "widgets/permutation/permutation")
|
||||||
|
|
||||||
optionsFinite :: ( MonadHandler m
|
optionsFinite :: ( MonadHandler m
|
||||||
, Finite a
|
, Finite a
|
||||||
|
|||||||
@ -4,12 +4,11 @@
|
|||||||
z-index: 1;
|
z-index: 1;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
flex: 0 0 0;
|
|
||||||
flex-basis: var(--asidenav-width-lg, 20%);
|
|
||||||
min-height: calc(100% - var(--header-height));
|
|
||||||
transition: all .2s ease-out;
|
|
||||||
width: var(--asidenav-width-lg, 20%);
|
width: var(--asidenav-width-lg, 20%);
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
flex: 0 0 0;
|
||||||
|
flex-basis: var(--asidenav-width-lg, 20%);
|
||||||
|
transition: all .2s ease-out;
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -72,6 +71,14 @@
|
|||||||
|
|
||||||
.asidenav {
|
.asidenav {
|
||||||
color: var(--color-font);
|
color: var(--color-font);
|
||||||
|
min-height: calc(100% - var(--header-height));
|
||||||
|
height: 400px;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.asidenav__box {
|
.asidenav__box {
|
||||||
@ -183,9 +190,7 @@
|
|||||||
/* LIST-ITEM */
|
/* LIST-ITEM */
|
||||||
|
|
||||||
.asidenav__list-item {
|
.asidenav__list-item {
|
||||||
position: relative;
|
|
||||||
color: var(--color-font);
|
color: var(--color-font);
|
||||||
min-height: 50px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
@ -207,10 +212,8 @@
|
|||||||
text-shadow: none;
|
text-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.asidenav__nested-list {
|
.asidenav__nested-list-wrapper {
|
||||||
transform: translateX(100%);
|
display: block;
|
||||||
opacity: 1;
|
|
||||||
width: 200px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -242,7 +245,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 7px 10px;
|
padding: 8px 3px;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
color: var(--color-font);
|
color: var(--color-font);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -258,17 +261,17 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* hover sub-menus */
|
/* hover sub-menus */
|
||||||
.asidenav__nested-list {
|
.asidenav__nested-list-wrapper {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
z-index: 10;
|
||||||
right: 0;
|
display: none;
|
||||||
color: var(--color-font);
|
color: var(--color-font);
|
||||||
transform: translateX(0);
|
background-color: var(--color-grey-light);
|
||||||
opacity: 0;
|
box-shadow: 1px 1px 1px 0px var(--color-grey);
|
||||||
width: 0;
|
}
|
||||||
overflow: hidden;
|
|
||||||
z-index: -1;
|
.asidenav__nested-list {
|
||||||
box-shadow: 0 0 13px rgba(0, 0, 0, 0.4);
|
min-width: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 425px) {
|
@media (max-width: 425px) {
|
||||||
@ -280,19 +283,16 @@
|
|||||||
|
|
||||||
.asidenav__nested-list-item {
|
.asidenav__nested-list-item {
|
||||||
position: relative;
|
position: relative;
|
||||||
color: var(--color-lightwhite);
|
|
||||||
background-color: var(--color-dark);
|
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: var(--color-darker);
|
background-color: var(--color-lightwhite);
|
||||||
}
|
}
|
||||||
|
|
||||||
.asidenav__link-wrapper {
|
.asidenav__link-wrapper {
|
||||||
padding-left: 13px;
|
padding-left: 13px;
|
||||||
padding-right: 13px;
|
padding-right: 13px;
|
||||||
border-left: 20px solid white;
|
|
||||||
transition: all .2s ease;
|
transition: all .2s ease;
|
||||||
color: var(--color-lightwhite);
|
color: var(--color-font);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1,7 +1,3 @@
|
|||||||
.js-show-hide {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.js-show-hide__toggle {
|
.js-show-hide__toggle {
|
||||||
position: relative;
|
position: relative;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@ -17,7 +17,7 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 0 13px;
|
padding: 0 13px;
|
||||||
margin: 0 2px;
|
margin: 0 2px;
|
||||||
background-color: #b3b7c1;
|
background-color: var(--color-dark);
|
||||||
color: white;
|
color: white;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
@ -35,5 +35,5 @@
|
|||||||
.tab-opener.tab-visible {
|
.tab-opener.tab-visible {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
color: rgb(52, 48, 58);
|
color: rgb(52, 48, 58);
|
||||||
border-bottom-color: #5F98C2;
|
border-bottom-color: var(--color-primary);
|
||||||
}
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
.js-tooltip {
|
.tooltip {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
height: 1.5rem;
|
height: 1.5rem;
|
||||||
@ -67,7 +67,7 @@
|
|||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
|
|
||||||
.js-tooltip {
|
.tooltip {
|
||||||
display: block;
|
display: block;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
|
|
||||||
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
window.utils = window.utils || {};
|
window.utils = window.utils || {};
|
||||||
|
|
||||||
|
var ALERTS_CLASS = 'alerts';
|
||||||
var ALERTS_TOGGLER_CLASS = 'alerts__toggler';
|
var ALERTS_TOGGLER_CLASS = 'alerts__toggler';
|
||||||
var ALERTS_TOGGLER_VISIBLE_CLASS = 'alerts__toggler--visible';
|
var ALERTS_TOGGLER_VISIBLE_CLASS = 'alerts__toggler--visible';
|
||||||
var ALERTS_TOGGLER_APPEAR_DELAY = 120;
|
var ALERTS_TOGGLER_APPEAR_DELAY = 120;
|
||||||
@ -18,7 +19,11 @@
|
|||||||
window.utils.alerts = function(alertsEl) {
|
window.utils.alerts = function(alertsEl) {
|
||||||
|
|
||||||
if (alertsEl.classList.contains(JS_INITIALIZED_CLASS)) {
|
if (alertsEl.classList.contains(JS_INITIALIZED_CLASS)) {
|
||||||
return;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!alertsEl || !alertsEl.classList.contains(ALERTS_CLASS)) {
|
||||||
|
throw new Error('utils.alerts has to be called with alerts element');
|
||||||
}
|
}
|
||||||
|
|
||||||
var togglerCheckRequested = false;
|
var togglerCheckRequested = false;
|
||||||
|
|||||||
59
static/js/utils/asidenav.js
Normal file
59
static/js/utils/asidenav.js
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
(function() {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
window.utils = window.utils || {};
|
||||||
|
|
||||||
|
var FAVORITES_BTN_CLASS = 'navbar__list-item--favorite';
|
||||||
|
var FAVORITES_BTN_ACTIVE_CLASS = 'navbar__list-item--active';
|
||||||
|
var ASIDENAV_EXPANDED_CLASS = 'main__aside--expanded';
|
||||||
|
var ASIDENAV_LIST_ITEM_CLASS = 'asidenav__list-item';
|
||||||
|
var ASIDENAV_SUBMENU_CLASS = 'asidenav__nested-list-wrapper';
|
||||||
|
|
||||||
|
window.utils.aside = function(asideEl) {
|
||||||
|
|
||||||
|
if (!asideEl) {
|
||||||
|
throw new Error('asideEl not defined');
|
||||||
|
}
|
||||||
|
|
||||||
|
function initFavoritesButton() {
|
||||||
|
var favoritesBtn = document.querySelector('.' + FAVORITES_BTN_CLASS);
|
||||||
|
favoritesBtn.addEventListener('click', function(event) {
|
||||||
|
favoritesBtn.classList.toggle(FAVORITES_BTN_ACTIVE_CLASS);
|
||||||
|
asideEl.classList.toggle(ASIDENAV_EXPANDED_CLASS);
|
||||||
|
event.preventDefault();
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function initAsidenavSubmenus() {
|
||||||
|
var asidenavLinksWithSubmenus = Array.from(asideEl.querySelectorAll('.' + ASIDENAV_LIST_ITEM_CLASS))
|
||||||
|
.map(function(listItem) {
|
||||||
|
var submenu = listItem.querySelector('.' + ASIDENAV_SUBMENU_CLASS);
|
||||||
|
return { listItem, submenu };
|
||||||
|
}).filter(function(union) {
|
||||||
|
return union.submenu !== null;
|
||||||
|
});
|
||||||
|
|
||||||
|
asidenavLinksWithSubmenus.forEach(function(union) {
|
||||||
|
union.listItem.addEventListener('mouseover', createMouseoverHandler(union));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createMouseoverHandler(union) {
|
||||||
|
return function mouseoverHanlder(event) {
|
||||||
|
var rectListItem = union.listItem.getBoundingClientRect();
|
||||||
|
var rectSubMenu = union.submenu.getBoundingClientRect();
|
||||||
|
|
||||||
|
union.submenu.style.left = (rectListItem.left + rectListItem.width) + 'px';
|
||||||
|
if (window.innerHeight - rectListItem.top < rectSubMenu.height) {
|
||||||
|
union.submenu.style.top = (rectListItem.top + rectListItem.height - rectSubMenu.height) + 'px';
|
||||||
|
} else {
|
||||||
|
union.submenu.style.top = rectListItem.top + 'px';
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
initFavoritesButton();
|
||||||
|
initAsidenavSubmenus();
|
||||||
|
};
|
||||||
|
})();
|
||||||
202
static/js/utils/asyncTable.js
Normal file
202
static/js/utils/asyncTable.js
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
(function collonadeClosure() {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
window.utils = window.utils || {};
|
||||||
|
|
||||||
|
var HEADER_HEIGHT = 80;
|
||||||
|
var RESET_OPTIONS = [ 'scrollTo' ];
|
||||||
|
|
||||||
|
window.utils.asyncTable = function(wrapper, options) {
|
||||||
|
|
||||||
|
options = options || {};
|
||||||
|
var tableIdent = options.dbtIdent;
|
||||||
|
var shortCircuitHeader = options ? options.headerDBTableShortcircuit : null;
|
||||||
|
|
||||||
|
var ths = [];
|
||||||
|
var pageLinks = [];
|
||||||
|
var pagesizeForm;
|
||||||
|
var scrollTable;
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
var table = wrapper.querySelector('#' + tableIdent);
|
||||||
|
|
||||||
|
if (!table) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollTable = wrapper.querySelector('.scrolltable');
|
||||||
|
|
||||||
|
// sortable table headers
|
||||||
|
ths = Array.from(table.querySelectorAll('th.sortable')).map(function(th) {
|
||||||
|
return { element: th };
|
||||||
|
});
|
||||||
|
|
||||||
|
// pagination links
|
||||||
|
var pagination = wrapper.querySelector('#' + tableIdent + '-pagination');
|
||||||
|
if (pagination) {
|
||||||
|
pageLinks = Array.from(pagination.querySelectorAll('.page-link')).map(function(link) {
|
||||||
|
return { element: link };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// pagesize form
|
||||||
|
pagesizeForm = wrapper.querySelector('#' + tableIdent + '-pagesize-form');
|
||||||
|
|
||||||
|
// take options into account
|
||||||
|
if (options && options.scrollTo) {
|
||||||
|
window.scrollTo(options.scrollTo);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options && options.horizPos && scrollTable) {
|
||||||
|
scrollTable.scrollLeft = options.horizPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
setupListeners();
|
||||||
|
wrapper.classList.add('js-initialized');
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupListeners() {
|
||||||
|
ths.forEach(function(th) {
|
||||||
|
th.clickHandler = function(event) {
|
||||||
|
var boundClickHandler = clickHandler.bind(this);
|
||||||
|
var horizPos = (scrollTable || {}).scrollLeft;
|
||||||
|
boundClickHandler(event, { horizPos });
|
||||||
|
};
|
||||||
|
th.element.addEventListener('click', th.clickHandler);
|
||||||
|
});
|
||||||
|
|
||||||
|
pageLinks.forEach(function(link) {
|
||||||
|
link.clickHandler = function(event) {
|
||||||
|
var boundClickHandler = clickHandler.bind(this);
|
||||||
|
var tableBoundingRect = scrollTable.getBoundingClientRect();
|
||||||
|
var tableOptions = {};
|
||||||
|
if (tableBoundingRect.top < HEADER_HEIGHT) {
|
||||||
|
tableOptions.scrollTo = {
|
||||||
|
top: (scrollTable.offsetTop || 0) - HEADER_HEIGHT,
|
||||||
|
left: scrollTable.offsetLeft || 0,
|
||||||
|
behavior: 'smooth',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
boundClickHandler(event, tableOptions);
|
||||||
|
}
|
||||||
|
link.element.addEventListener('click', link.clickHandler);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (pagesizeForm) {
|
||||||
|
var pagesizeSelect = pagesizeForm.querySelector('[name=' + tableIdent + '-pagesize]');
|
||||||
|
pagesizeSelect.addEventListener('change', changePagesizeHandler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeListeners() {
|
||||||
|
ths.forEach(function(th) {
|
||||||
|
th.element.removeEventListener('click', th.clickHandler);
|
||||||
|
});
|
||||||
|
|
||||||
|
pageLinks.forEach(function(link) {
|
||||||
|
link.element.removeEventListener('click', link.clickHandler);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (pagesizeForm) {
|
||||||
|
var pagesizeSelect = pagesizeForm.querySelector('[name=' + tableIdent + '-pagesize]')
|
||||||
|
pagesizeSelect.removeEventListener('change', changePagesizeHandler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clickHandler(event, tableOptions) {
|
||||||
|
event.preventDefault();
|
||||||
|
var url = new URL(window.location.origin + window.location.pathname + getClickDestination(this));
|
||||||
|
updateTableFrom(url, tableOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getClickDestination(el) {
|
||||||
|
if (!el.querySelector('a')) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return el.querySelector('a').getAttribute('href');
|
||||||
|
}
|
||||||
|
|
||||||
|
function changePagesizeHandler(event) {
|
||||||
|
var currentTableUrl = options.currentUrl || window.location.href;
|
||||||
|
var url = getUrlWithUpdatedPagesize(currentTableUrl, event.target.value);
|
||||||
|
url = new URL(getUrlWithResetPagenumber(url));
|
||||||
|
updateTableFrom(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUrlWithUpdatedPagesize(url, pagesize) {
|
||||||
|
if (url.indexOf('pagesize') >= 0) {
|
||||||
|
return url.replace(/pagesize=(\d+|all)/, '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');
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetches new sorted table from url with params and replaces contents of current table
|
||||||
|
function updateTableFrom(url, tableOptions) {
|
||||||
|
tableOptions = tableOptions || {};
|
||||||
|
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.href + '. Status Code: ' + response.status);
|
||||||
|
}
|
||||||
|
return response.text();
|
||||||
|
}).then(function(data) {
|
||||||
|
tableOptions.currentUrl = url.href;
|
||||||
|
removeListeners();
|
||||||
|
updateWrapperContents(data, tableOptions);
|
||||||
|
}).catch(function(err) {
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateWrapperContents(newHtml, tableOptions) {
|
||||||
|
tableOptions = tableOptions || {};
|
||||||
|
wrapper.innerHTML = newHtml;
|
||||||
|
wrapper.classList.remove("js-initialized");
|
||||||
|
|
||||||
|
// setup the wrapper and its components to behave async again
|
||||||
|
window.utils.teardown('asyncTable');
|
||||||
|
window.utils.teardown('form');
|
||||||
|
// merge global options and table specific options
|
||||||
|
var resetOptions = {};
|
||||||
|
Object.keys(options)
|
||||||
|
.filter(function(key) {
|
||||||
|
return !RESET_OPTIONS.includes(key);
|
||||||
|
})
|
||||||
|
.forEach(function(key) {
|
||||||
|
resetOptions[key] = options[key];
|
||||||
|
});
|
||||||
|
var combinedOptions = {};
|
||||||
|
combinedOptions = Object.keys(tableOptions)
|
||||||
|
.filter(function(key) {
|
||||||
|
return tableOptions.hasOwnProperty(key);
|
||||||
|
})
|
||||||
|
.map(function(key) {
|
||||||
|
return { key, value: tableOptions[key] }
|
||||||
|
})
|
||||||
|
.reduce(function(cumulatedOpts, opt) {
|
||||||
|
cumulatedOpts[opt.key] = opt.value;
|
||||||
|
return cumulatedOpts;
|
||||||
|
}, resetOptions);
|
||||||
|
|
||||||
|
window.utils.setup('asyncTable', wrapper, combinedOptions);
|
||||||
|
|
||||||
|
Array.from(wrapper.querySelectorAll('form')).forEach(function(form) {
|
||||||
|
window.utils.setup('form', form);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
init();
|
||||||
|
};
|
||||||
|
})();
|
||||||
@ -1,4 +0,0 @@
|
|||||||
window.addEventListener('touchstart', function onFirstTouch() {
|
|
||||||
document.body.classList.add('touch-supported');
|
|
||||||
window.removeEventListener('touchstart', onFirstTouch, false);
|
|
||||||
}, false);
|
|
||||||
@ -3,13 +3,57 @@
|
|||||||
|
|
||||||
window.utils = window.utils || {};
|
window.utils = window.utils || {};
|
||||||
|
|
||||||
|
var JS_INITIALIZED = 'js-initialized';
|
||||||
|
var SUBMIT_BUTTON_SELECTOR = '[type="submit"]:not([formnovalidate])';
|
||||||
|
var AUTOSUBMIT_BUTTON_SELECTOR = '[type="submit"][data-autosubmit]';
|
||||||
|
|
||||||
|
function formValidator(inputs) {
|
||||||
|
var done = true;
|
||||||
|
inputs.forEach(function(inp) {
|
||||||
|
var len = inp.value.trim().length;
|
||||||
|
if (done && len === 0) {
|
||||||
|
done = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return done;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.utils.form = function(form, options) {
|
||||||
|
|
||||||
|
if (form.classList.contains(JS_INITIALIZED)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
form.classList.add(JS_INITIALIZED);
|
||||||
|
|
||||||
|
// reactive buttons
|
||||||
|
var submitBtn = form.querySelector(SUBMIT_BUTTON_SELECTOR);
|
||||||
|
if (submitBtn) {
|
||||||
|
window.utils.setup('reactiveButton', form, { button: submitBtn });
|
||||||
|
}
|
||||||
|
|
||||||
|
// conditonal fieldsets
|
||||||
|
var fieldSets = Array.from(form.querySelectorAll('fieldset[data-conditional-id][data-conditional-value]'));
|
||||||
|
window.utils.setup('interactiveFieldset', form, { fieldSets });
|
||||||
|
|
||||||
|
// hide autoSubmit submit button
|
||||||
|
window.utils.setup('autoSubmit', form, options);
|
||||||
|
};
|
||||||
|
|
||||||
// registers input-listener for each element in <inputs> (array) and
|
// registers input-listener for each element in <inputs> (array) and
|
||||||
// enables <button> if <validation> for these inputs returns true
|
// enables <button> if <formValidator> for these inputs returns true
|
||||||
window.utils.reactiveButton = function(form, button, validation) {
|
window.utils.reactiveButton = function(form, options) {
|
||||||
|
options = options || {};
|
||||||
|
var button = options.button;
|
||||||
var requireds = Array.from(form.querySelectorAll('[required]'));
|
var requireds = Array.from(form.querySelectorAll('[required]'));
|
||||||
|
|
||||||
|
if (!button) {
|
||||||
|
throw new Error('Please provide both a button to reactiveButton');
|
||||||
|
}
|
||||||
|
|
||||||
if (requireds.length == 0) {
|
if (requireds.length == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof button.dataset.formnorequired !== 'undefined' && button.dataset.formnorequired !== null) {
|
if (typeof button.dataset.formnorequired !== 'undefined' && button.dataset.formnorequired !== null) {
|
||||||
button.addEventListener('click', function() {
|
button.addEventListener('click', function() {
|
||||||
form.submit();
|
form.submit();
|
||||||
@ -27,7 +71,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
function updateButtonState() {
|
function updateButtonState() {
|
||||||
if (validation.call(null, requireds) === true) {
|
if (formValidator(requireds) === true) {
|
||||||
button.removeAttribute('disabled');
|
button.removeAttribute('disabled');
|
||||||
} else {
|
} else {
|
||||||
button.setAttribute('disabled', 'true');
|
button.setAttribute('disabled', 'true');
|
||||||
@ -35,7 +79,14 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
window.utils.interactiveFieldset = function(form, fieldSets) {
|
window.utils.interactiveFieldset = function(form, options) {
|
||||||
|
options = options || {};
|
||||||
|
var fieldSets = options.fieldSets;
|
||||||
|
|
||||||
|
if (!fieldSets) {
|
||||||
|
throw new Error('interactiveFieldset must be passed fieldSets via options');
|
||||||
|
}
|
||||||
|
|
||||||
var fields = fieldSets.map(function(fs) {
|
var fields = fieldSets.map(function(fs) {
|
||||||
return {
|
return {
|
||||||
fieldSet: fs,
|
fieldSet: fs,
|
||||||
@ -64,58 +115,11 @@
|
|||||||
updateFields();
|
updateFields();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
window.utils.autoSubmit = function(form, options) {
|
||||||
|
var button = form.querySelector(AUTOSUBMIT_BUTTON_SELECTOR);
|
||||||
|
if (button) {
|
||||||
|
button.classList.add('hidden');
|
||||||
|
}
|
||||||
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
document.addEventListener('setup', function(e) {
|
|
||||||
if (e.detail.module && e.detail.module !== 'showHide')
|
|
||||||
return;
|
|
||||||
|
|
||||||
var forms = e.detail.scope.querySelectorAll('form');
|
|
||||||
Array.from(forms).forEach(function(form) {
|
|
||||||
// auto reactiveButton submit-buttons with required fields
|
|
||||||
var submitBtns = Array.from(form.querySelectorAll('[type=submit]:not([formnovalidate])'));
|
|
||||||
submitBtns.forEach(function(submitBtn) {
|
|
||||||
window.utils.reactiveButton(form, submitBtn, validateForm);
|
|
||||||
});
|
|
||||||
|
|
||||||
// auto conditonal fieldsets
|
|
||||||
var fieldSets = Array.from(form.querySelectorAll('fieldset[data-conditional-id][data-conditional-value]'));
|
|
||||||
window.utils.interactiveFieldset(form, fieldSets);
|
|
||||||
});
|
|
||||||
|
|
||||||
function validateForm(inputs) {
|
|
||||||
var done = true;
|
|
||||||
inputs.forEach(function(inp) {
|
|
||||||
var len = inp.value.trim().length;
|
|
||||||
if (done && len === 0) {
|
|
||||||
done = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return done;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
document.dispatchEvent(new CustomEvent('setup', { detail: { scope: document.body, module: 'showHide' }, bubbles: true, cancelable: true }))
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
document.addEventListener('setup', function(e) {
|
|
||||||
if (e.detail.module && e.detail.module !== 'autoSubmit')
|
|
||||||
return;
|
|
||||||
|
|
||||||
Array.from(e.detail.scope.querySelectorAll('[data-autosubmit]:not(.js-initialized)')).forEach(function(elem) {
|
|
||||||
if ((elem instanceof HTMLInputElement && elem.type == 'submit') || (elem instanceof HTMLButtonElement && elem.type == 'submit')) {
|
|
||||||
var ancestor = elem.closest('.form-group');
|
|
||||||
var target = ancestor || elem;
|
|
||||||
|
|
||||||
target.classList.add('hidden');
|
|
||||||
}
|
|
||||||
|
|
||||||
elem.classList.add('js-initialized');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
document.dispatchEvent(new CustomEvent('setup', { detail: { scope: document.body, module: 'autoSubmit' }, bubbles: true, cancelable: true }))
|
|
||||||
});
|
|
||||||
171
static/js/utils/inputs.js
Normal file
171
static/js/utils/inputs.js
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
(function() {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
window.utils = window.utils || {};
|
||||||
|
|
||||||
|
var JS_INITIALIZED_CLASS = 'js-initialized';
|
||||||
|
|
||||||
|
function isNotInitialized(element) {
|
||||||
|
return !element.classList.contains(JS_INITIALIZED_CLASS);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.utils.inputs = function(wrapper, options) {
|
||||||
|
// checkboxes / radios
|
||||||
|
var checkboxes = Array.from(wrapper.querySelectorAll('input[type="checkbox"], input[type="radio"]'));
|
||||||
|
checkboxes.filter(isNotInitialized).forEach(window.utils.checkboxRadio);
|
||||||
|
|
||||||
|
// file-uploads
|
||||||
|
var fileUploads = Array.from(wrapper.querySelectorAll('input[type="file"]'));
|
||||||
|
fileUploads.filter(isNotInitialized).forEach(function(input) {
|
||||||
|
window.utils.fileUpload(input, options);
|
||||||
|
});
|
||||||
|
|
||||||
|
// file-checkboxes
|
||||||
|
var fileCheckboxes = Array.from(wrapper.querySelectorAll('.file-checkbox'));
|
||||||
|
fileCheckboxes.filter(isNotInitialized).forEach(function(inp) {
|
||||||
|
window.utils.fileCheckbox(inp);
|
||||||
|
inp.classList.add(JS_INITIALIZED_CLASS);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// (multiple) dynamic file uploads
|
||||||
|
// expects i18n object with following strings:
|
||||||
|
// »filesSelected«: label of multi-upload button after selection
|
||||||
|
// example: "Dateien ausgewählt" (will be prepended by number of selected files)
|
||||||
|
// »selectFile«: label of single-upload button before selection
|
||||||
|
// example: "Datei auswählen"
|
||||||
|
// »selectFiles«: label of multi-upload button before selection
|
||||||
|
// example: "Datei(en) auswählen"
|
||||||
|
|
||||||
|
var FILE_UPLOAD_INPUT_LIST_CLASS = 'file-input__list';
|
||||||
|
var FILE_UPLOAD_INPUT_UNPACK_CHECKBOX_CLASS = 'file-input__unpack';
|
||||||
|
var FILE_UPLOAD_INPUT_LABEL_CLASS = 'file-input__label';
|
||||||
|
var FILE_UPLOAD_INPUT_HIDDEN_CLASS = 'file-input__input--hidden';
|
||||||
|
|
||||||
|
window.utils.fileUpload = function(input, options) {
|
||||||
|
var isMulti = input.hasAttribute('multiple');
|
||||||
|
var fileList = isMulti ? addFileList() : null;
|
||||||
|
var label = addFileLabel();
|
||||||
|
var i18n = options.i18n;
|
||||||
|
|
||||||
|
if (!i18n) {
|
||||||
|
throw new Error('window.utils.fileUpload(input, options) needs to be passed i18n object via options');
|
||||||
|
}
|
||||||
|
input.classList.add(JS_INITIALIZED_CLASS);
|
||||||
|
|
||||||
|
function renderFileList(files) {
|
||||||
|
fileList.innerHTML = '';
|
||||||
|
Array.from(files).forEach(function(file, index) {
|
||||||
|
var fileDisplayEl = document.createElement('li');
|
||||||
|
fileDisplayEl.innerHTML = file.name;
|
||||||
|
fileList.appendChild(fileDisplayEl);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateLabel(files) {
|
||||||
|
if (files.length) {
|
||||||
|
if (isMulti) {
|
||||||
|
label.innerText = files.length + ' ' + i18n.filesSelected;
|
||||||
|
} else {
|
||||||
|
label.innerHTML = files[0].name;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
resetFileLabel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addFileList() {
|
||||||
|
var list = document.createElement('ol');
|
||||||
|
list.classList.add(FILE_UPLOAD_INPUT_LIST_CLASS);
|
||||||
|
var unpackEl = input.parentElement.querySelector('.' + FILE_UPLOAD_INPUT_UNPACK_CHECKBOX_CLASS);
|
||||||
|
if (unpackEl) {
|
||||||
|
input.parentElement.insertBefore(list, unpackEl);
|
||||||
|
} else {
|
||||||
|
input.parentElement.appendChild(list);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addFileLabel() {
|
||||||
|
var label = document.createElement('label');
|
||||||
|
label.classList.add(FILE_UPLOAD_INPUT_LABEL_CLASS);
|
||||||
|
label.setAttribute('for', input.id);
|
||||||
|
input.parentElement.insertBefore(label, input);
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetFileLabel() {
|
||||||
|
if (isMulti) {
|
||||||
|
label.innerText = i18n.selectFiles;
|
||||||
|
} else {
|
||||||
|
label.innerText = i18n.selectFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// initial setup
|
||||||
|
resetFileLabel();
|
||||||
|
input.classList.add(FILE_UPLOAD_INPUT_HIDDEN_CLASS);
|
||||||
|
input.addEventListener('change', function() {
|
||||||
|
input.dispatchEvent(new Event('input'));
|
||||||
|
if (isMulti) {
|
||||||
|
renderFileList(input.files);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateLabel(input.files);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// to remove previously uploaded files
|
||||||
|
|
||||||
|
var FILE_UPLOAD_CONTAINER_CLASS = 'file-container';
|
||||||
|
var FILE_UPLOAD_CONTAINER_CHECKED_CLASS = 'file-container--checked';
|
||||||
|
|
||||||
|
window.utils.fileCheckbox = function(input) {
|
||||||
|
// adds eventlistener(s)
|
||||||
|
function addListener(container) {
|
||||||
|
input.addEventListener('change', function(event) {
|
||||||
|
container.classList.toggle(FILE_UPLOAD_CONTAINER_CHECKED_CLASS, this.checked);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// initial setup
|
||||||
|
function setup() {
|
||||||
|
var cont = input.parentNode;
|
||||||
|
while (cont !== document.body) {
|
||||||
|
if (cont.matches('.' + FILE_UPLOAD_CONTAINER_CLASS)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cont = cont.parentNode;
|
||||||
|
}
|
||||||
|
addListener(cont);
|
||||||
|
input.classList.add(JS_INITIALIZED_CLASS);
|
||||||
|
cont.classList.add(JS_INITIALIZED_CLASS);
|
||||||
|
}
|
||||||
|
setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
// turns native checkboxes and radio buttons into custom ones
|
||||||
|
window.utils.checkboxRadio = function(input) {
|
||||||
|
|
||||||
|
var type = input.getAttribute('type');
|
||||||
|
|
||||||
|
if (!input.parentElement.classList.contains(type)) {
|
||||||
|
var parentEl = input.parentElement;
|
||||||
|
var siblingEl = input.nextElementSibling;
|
||||||
|
var wrapperEl = document.createElement('div');
|
||||||
|
var labelEl = document.createElement('label');
|
||||||
|
wrapperEl.classList.add(type);
|
||||||
|
labelEl.setAttribute('for', input.id);
|
||||||
|
wrapperEl.appendChild(input);
|
||||||
|
wrapperEl.appendChild(labelEl);
|
||||||
|
input.classList.add(JS_INITIALIZED_CLASS);
|
||||||
|
|
||||||
|
if (siblingEl) {
|
||||||
|
parentEl.insertBefore(wrapperEl, siblingEl);
|
||||||
|
} else {
|
||||||
|
parentEl.appendChild(wrapperEl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
})();
|
||||||
58
static/js/utils/setup.js
Normal file
58
static/js/utils/setup.js
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
(function() {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
window.utils = window.utils || {};
|
||||||
|
|
||||||
|
var registeredSetupListeners = {};
|
||||||
|
|
||||||
|
window.utils.setup = function(utilType, scope, options) {
|
||||||
|
|
||||||
|
if (!utilType || !scope) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
options = options || {};
|
||||||
|
|
||||||
|
var listener = function(event) {
|
||||||
|
|
||||||
|
if (event.detail.targetUtil !== utilType) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.setupFunction) {
|
||||||
|
options.setupFunction(scope, options);
|
||||||
|
} else {
|
||||||
|
var util = window.utils[utilType];
|
||||||
|
if (!util) {
|
||||||
|
throw new Error('"' + utilType + '" is not a known js util');
|
||||||
|
}
|
||||||
|
|
||||||
|
util(scope, options);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (registeredSetupListeners[utilType] && !options.singleton) {
|
||||||
|
registeredSetupListeners[utilType].push(listener);
|
||||||
|
} else {
|
||||||
|
window.utils.teardown(utilType);
|
||||||
|
registeredSetupListeners[utilType] = [ listener ];
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('setup', listener);
|
||||||
|
|
||||||
|
document.dispatchEvent(new CustomEvent('setup', {
|
||||||
|
detail: { targetUtil: utilType, module: 'none' },
|
||||||
|
bubbles: true,
|
||||||
|
cancelable: true,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
window.utils.teardown = function(utilType) {
|
||||||
|
if (registeredSetupListeners[utilType]) {
|
||||||
|
registeredSetupListeners[utilType].forEach(function(listener) {
|
||||||
|
document.removeEventListener('setup', listener);
|
||||||
|
});
|
||||||
|
delete registeredSetupListeners[utilType];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})();
|
||||||
55
static/js/utils/showHide.js
Normal file
55
static/js/utils/showHide.js
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
(function() {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
window.utils = window.utils || {};
|
||||||
|
|
||||||
|
var LOCAL_STORAGE_SHOW_HIDE = 'SHOW_HIDE';
|
||||||
|
/**
|
||||||
|
* div
|
||||||
|
* div.js-show-hide__toggle
|
||||||
|
* toggle here
|
||||||
|
* div
|
||||||
|
* content here
|
||||||
|
*/
|
||||||
|
window.utils.showHide = function(wrapper, options) {
|
||||||
|
function addEventHandler(el) {
|
||||||
|
el.addEventListener('click', function elClickListener() {
|
||||||
|
var newState = el.parentElement.classList.toggle('js-show-hide--collapsed');
|
||||||
|
updateLSState(el.dataset.shIndex || null, newState);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateLSState(index, state) {
|
||||||
|
if (!index) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var lsData = fromLocalStorage();
|
||||||
|
lsData[index] = state;
|
||||||
|
window.localStorage.setItem(LOCAL_STORAGE_SHOW_HIDE, JSON.stringify(lsData));
|
||||||
|
}
|
||||||
|
|
||||||
|
function collapsedStateInLocalStorage(index) {
|
||||||
|
return fromLocalStorage()[index] || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fromLocalStorage() {
|
||||||
|
return JSON.parse(window.localStorage.getItem(LOCAL_STORAGE_SHOW_HIDE)) || {};
|
||||||
|
}
|
||||||
|
|
||||||
|
Array
|
||||||
|
.from(wrapper.querySelectorAll('.js-show-hide__toggle'))
|
||||||
|
.forEach(function(el) {
|
||||||
|
var index = el.dataset.shIndex || null;
|
||||||
|
el.parentElement.classList.toggle(
|
||||||
|
'js-show-hide--collapsed',
|
||||||
|
collapsedStateInLocalStorage(index) || el.dataset.collapsed === 'true'
|
||||||
|
);
|
||||||
|
Array.from(el.parentElement.children).forEach(function(el) {
|
||||||
|
if (!el.classList.contains('js-show-hide__toggle')) {
|
||||||
|
el.classList.add('js-show-hide__target');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
addEventHandler(el);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
})();
|
||||||
48
templates/default-layout.julius
Normal file
48
templates/default-layout.julius
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
function setupDatepicker(wrapper) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var config = {
|
||||||
|
dtLocal: {
|
||||||
|
enableTime: true,
|
||||||
|
altInput: true,
|
||||||
|
altFormat: "j. F Y, H:i", // maybe interpolate these formats for locale
|
||||||
|
dateFormat: "Y-m-dTH:i",
|
||||||
|
time_24hr: true
|
||||||
|
},
|
||||||
|
d: {
|
||||||
|
altFormat: "j. F Y",
|
||||||
|
dateFormat: "Y-m-d",
|
||||||
|
altInput: true
|
||||||
|
},
|
||||||
|
t: {
|
||||||
|
enableTime: true,
|
||||||
|
noCalendar: true,
|
||||||
|
altFormat: "H:i",
|
||||||
|
dateFormat: "H:i",
|
||||||
|
altInput: true,
|
||||||
|
time_24hr: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Array.from(wrapper.querySelectorAll('input[type="date"]')).forEach(function(el) {
|
||||||
|
flatpickr(el, config.d);
|
||||||
|
});
|
||||||
|
Array.from(wrapper.querySelectorAll('input[type="time"]')).forEach(function(el) {
|
||||||
|
flatpickr(el, config.t);
|
||||||
|
});
|
||||||
|
Array.from(wrapper.querySelectorAll('input[type="datetime-local"]')).forEach(function(el) {
|
||||||
|
flatpickr(el, config.dtLocal);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
var I18N = {
|
||||||
|
filesSelected: 'Dateien ausgewählt', // TODO: interpolate these to be translated
|
||||||
|
selectFile: 'Datei auswählen',
|
||||||
|
selectFiles: 'Datei(en) auswählen',
|
||||||
|
};
|
||||||
|
|
||||||
|
window.utils.setup('flatpickr', document.body, { setupFunction: setupDatepicker });
|
||||||
|
window.utils.setup('showHide', document.body);
|
||||||
|
window.utils.setup('inputs', document.body, { i18n: I18N });
|
||||||
|
});
|
||||||
21
templates/formPageI18n.julius
Normal file
21
templates/formPageI18n.julius
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
|
||||||
|
var themeSelector = document.querySelector('#theme-select');
|
||||||
|
|
||||||
|
if (themeSelector) {
|
||||||
|
themeSelector.addEventListener('change', function() {
|
||||||
|
// get rid of old themes on body
|
||||||
|
var options = Array.from(themeSelector.options)
|
||||||
|
.forEach(function (option) {
|
||||||
|
document.body.classList.remove(optionToTheme(option));
|
||||||
|
});
|
||||||
|
// add newly selected theme
|
||||||
|
document.body.classList.add(optionToTheme(themeSelector.selectedOptions[0]));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function optionToTheme(option) {
|
||||||
|
return optionValue = 'theme--' + option.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
@ -3,7 +3,7 @@ $forall FileUploadInfo{..} <- fileInfos
|
|||||||
<div .file-container :fuiChecked:.file-container--checked>
|
<div .file-container :fuiChecked:.file-container--checked>
|
||||||
<label .file-container__label.btn for=#{fuiHtmlId}>#{fuiTitle}
|
<label .file-container__label.btn for=#{fuiHtmlId}>#{fuiTitle}
|
||||||
<div .checkbox>
|
<div .checkbox>
|
||||||
<input .file-container__checkbox.js-file-checkbox id=#{fuiHtmlId} name=#{fieldName} :fuiChecked:checked value=#{toPathPiece fuiId} type="checkbox">
|
<input .file-container__checkbox.file-checkbox id=#{fuiHtmlId} name=#{fieldName} :fuiChecked:checked value=#{toPathPiece fuiId} type="checkbox">
|
||||||
<label for=#{fuiHtmlId}>
|
<label for=#{fuiHtmlId}>
|
||||||
|
|
||||||
$# new files
|
$# new files
|
||||||
@ -15,6 +15,6 @@ $# new files
|
|||||||
<div .file-input__unpack>
|
<div .file-input__unpack>
|
||||||
<label for=#{fieldId}_zip>ZIPs automatisch entpacken
|
<label for=#{fieldId}_zip>ZIPs automatisch entpacken
|
||||||
<input type=checkbox id=#{fieldId}_zip name=#{fieldName} value=#{unpackZips}>
|
<input type=checkbox id=#{fieldId}_zip name=#{fieldName} value=#{unpackZips}>
|
||||||
<div class="js-tooltip">
|
<div class="tooltip">
|
||||||
<div class="tooltip__handle">
|
<div class="tooltip__handle">
|
||||||
<div class="tooltip__content">Entpackt hochgeladene Zip-Dateien (*.zip) automatisch und fügt den Inhalt dem Stamm-Verzeichnis der Abgabe hinzu.
|
<div class="tooltip__content">Entpackt hochgeladene Zip-Dateien (*.zip) automatisch und fügt den Inhalt dem Stamm-Verzeichnis der Abgabe hinzu.
|
||||||
|
|||||||
@ -1,17 +0,0 @@
|
|||||||
document.addEventListener('setup', function (e) {
|
|
||||||
|
|
||||||
var themeSelector = e.detail.scope.querySelector('#theme-select');
|
|
||||||
themeSelector.addEventListener('change', function() {
|
|
||||||
// get rid of old themes on body
|
|
||||||
var options = Array.from(themeSelector.options)
|
|
||||||
.forEach(function (option) {
|
|
||||||
document.body.classList.remove(optionToTheme(option));
|
|
||||||
});
|
|
||||||
// add newly selected theme
|
|
||||||
document.body.classList.add(optionToTheme(themeSelector.selectedOptions[0]));
|
|
||||||
});
|
|
||||||
|
|
||||||
function optionToTheme(option) {
|
|
||||||
return optionValue = 'theme--' + option.value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@ -21,7 +21,7 @@ $maybe descr <- sheetDescription sheet
|
|||||||
<dd .deflist__dd>_{sheetSubmissionMode sheet}
|
<dd .deflist__dd>_{sheetSubmissionMode sheet}
|
||||||
$case sheetSubmissionMode sheet
|
$case sheetSubmissionMode sheet
|
||||||
$of CorrectorSubmissions
|
$of CorrectorSubmissions
|
||||||
<div .js-tooltip>
|
<div .tooltip>
|
||||||
<div .tooltip__handle>
|
<div .tooltip__handle>
|
||||||
<div .tooltip__content>_{MsgSheetCorrectorSubmissionsTip}
|
<div .tooltip__content>_{MsgSheetCorrectorSubmissionsTip}
|
||||||
$of _
|
$of _
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
<!-- only here to be able to include datepicker using `toWidget` -->
|
|
||||||
@ -1,43 +0,0 @@
|
|||||||
document.addEventListener('setup', function(e) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
if (e.detail.module && e.detail.module !== 'datepicker')
|
|
||||||
return;
|
|
||||||
|
|
||||||
var config = {
|
|
||||||
dtLocal: {
|
|
||||||
enableTime: true,
|
|
||||||
altInput: true,
|
|
||||||
altFormat: "j. F Y, H:i",
|
|
||||||
dateFormat: "Y-m-dTH:i",
|
|
||||||
time_24hr: true
|
|
||||||
},
|
|
||||||
d: {
|
|
||||||
altFormat: "j. F Y",
|
|
||||||
dateFormat: "Y-m-d",
|
|
||||||
altInput: true
|
|
||||||
},
|
|
||||||
t: {
|
|
||||||
enableTime: true,
|
|
||||||
noCalendar: true,
|
|
||||||
altFormat: "H:i",
|
|
||||||
dateFormat: "H:i",
|
|
||||||
altInput: true,
|
|
||||||
time_24hr: true
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Array.from(e.detail.scope.querySelectorAll('input[type="date"]')).forEach(function(el) {
|
|
||||||
flatpickr(el, config.d);
|
|
||||||
});
|
|
||||||
Array.from(e.detail.scope.querySelectorAll('input[type="time"]')).forEach(function(el) {
|
|
||||||
flatpickr(el, config.t);
|
|
||||||
});
|
|
||||||
Array.from(e.detail.scope.querySelectorAll('input[type="datetime-local"]')).forEach(function(el) {
|
|
||||||
flatpickr(el, config.dtLocal);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
document.dispatchEvent(new CustomEvent('setup', { detail: { scope: document.body, module: 'datepicker' }, bubbles: true, cancelable: true }));
|
|
||||||
});
|
|
||||||
@ -1 +0,0 @@
|
|||||||
<!-- only here to be able to include inputs using `toWidget` -->
|
|
||||||
@ -1,147 +0,0 @@
|
|||||||
(function() {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
window.utils = window.utils || {};
|
|
||||||
|
|
||||||
// allows for multiple file uploads with separate inputs
|
|
||||||
window.utils.initializeFileUpload = function(input) {
|
|
||||||
var isMulti = input.hasAttribute('multiple');
|
|
||||||
var fileList = isMulti ? addFileList() : null;
|
|
||||||
var label = addFileLabel();
|
|
||||||
|
|
||||||
function renderFileList(files) {
|
|
||||||
fileList.innerHTML = '';
|
|
||||||
Array.from(files).forEach(function(file, index) {
|
|
||||||
var fileDisplayEl = document.createElement('li');
|
|
||||||
fileDisplayEl.innerHTML = file.name;
|
|
||||||
fileList.appendChild(fileDisplayEl);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateLabel(files) {
|
|
||||||
if (files.length) {
|
|
||||||
if (isMulti) {
|
|
||||||
label.innerText = files.length + ' Dateien ausgwählt';
|
|
||||||
} else {
|
|
||||||
label.innerHTML = files[0].name;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
resetFileLabel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function addFileList() {
|
|
||||||
var list = document.createElement('ol');
|
|
||||||
list.classList.add('file-input__list');
|
|
||||||
var unpackEl = input.parentElement.querySelector('.file-input__unpack');
|
|
||||||
if (unpackEl) {
|
|
||||||
input.parentElement.insertBefore(list, unpackEl);
|
|
||||||
} else {
|
|
||||||
input.parentElement.appendChild(list);
|
|
||||||
}
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
function addFileLabel() {
|
|
||||||
var label = document.createElement('label');
|
|
||||||
label.classList.add('file-input__label');
|
|
||||||
label.setAttribute('for', input.id);
|
|
||||||
input.parentElement.insertBefore(label, input);
|
|
||||||
return label;
|
|
||||||
}
|
|
||||||
|
|
||||||
function resetFileLabel() {
|
|
||||||
// interpolate translated String here
|
|
||||||
label.innerText = 'Datei' + (isMulti ? 'en' : '') + ' auswählen';
|
|
||||||
}
|
|
||||||
|
|
||||||
// initial setup
|
|
||||||
resetFileLabel();
|
|
||||||
input.classList.add('file-input__input--hidden');
|
|
||||||
input.addEventListener('change', function() {
|
|
||||||
input.dispatchEvent(new Event('input'));
|
|
||||||
if (isMulti) {
|
|
||||||
renderFileList(input.files);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateLabel(input.files);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// to remove previously uploaded files
|
|
||||||
window.utils.reactiveFileCheckbox = function(input) {
|
|
||||||
// adds eventlistener(s)
|
|
||||||
function addListener(container) {
|
|
||||||
input.addEventListener('change', function(event) {
|
|
||||||
container.classList.toggle('file-container--checked', this.checked);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// initial setup
|
|
||||||
function setup() {
|
|
||||||
var cont = input.parentNode;
|
|
||||||
while (cont !== document.body) {
|
|
||||||
if (cont.matches('.file-container')) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
cont = cont.parentNode;
|
|
||||||
}
|
|
||||||
addListener(cont);
|
|
||||||
}
|
|
||||||
setup();
|
|
||||||
}
|
|
||||||
|
|
||||||
window.utils.initializeCheckboxRadio = function(input, type) {
|
|
||||||
|
|
||||||
if (!input.parentElement.classList.contains(type)) {
|
|
||||||
var parentEl = input.parentElement;
|
|
||||||
var siblingEl = input.nextElementSibling;
|
|
||||||
var wrapperEl = document.createElement('div');
|
|
||||||
var labelEl = document.createElement('label');
|
|
||||||
wrapperEl.classList.add(type);
|
|
||||||
labelEl.setAttribute('for', input.id);
|
|
||||||
wrapperEl.appendChild(input);
|
|
||||||
wrapperEl.appendChild(labelEl);
|
|
||||||
|
|
||||||
if (siblingEl) {
|
|
||||||
parentEl.insertBefore(wrapperEl, siblingEl);
|
|
||||||
} else {
|
|
||||||
parentEl.appendChild(wrapperEl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
document.addEventListener('setup', function(e) {
|
|
||||||
if (e.detail.module && e.detail.module !== 'inputs')
|
|
||||||
return;
|
|
||||||
|
|
||||||
// initialize checkboxes
|
|
||||||
Array.from(e.detail.scope.querySelectorAll('input[type="checkbox"]:not(.js-initialized)')).forEach(function(inp) {
|
|
||||||
window.utils.initializeCheckboxRadio(inp, 'checkbox');
|
|
||||||
inp.classList.add("js-initialized");
|
|
||||||
});
|
|
||||||
|
|
||||||
// initialize radios
|
|
||||||
Array.from(e.detail.scope.querySelectorAll('input[type="radio"]:not(.js-initialized)')).forEach(function(inp) {
|
|
||||||
window.utils.initializeCheckboxRadio(inp, 'radio');
|
|
||||||
inp.classList.add("js-initialized");
|
|
||||||
});
|
|
||||||
|
|
||||||
// initialize file-upload-fields
|
|
||||||
Array.from(e.detail.scope.querySelectorAll('input[type="file"]:not(.js-initialized)')).forEach(function(inp) {
|
|
||||||
window.utils.initializeFileUpload(inp);
|
|
||||||
inp.classList.add("js-initialized");
|
|
||||||
});
|
|
||||||
|
|
||||||
// initialize file-checkbox-fields
|
|
||||||
Array.from(e.detail.scope.querySelectorAll('.js-file-checkbox:not(.js-initialized)')).forEach(function(inp) {
|
|
||||||
window.utils.reactiveFileCheckbox(inp);
|
|
||||||
inp.classList.add("js-initialized");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
document.dispatchEvent(new CustomEvent('setup', { detail: { scope: document.body, module: 'inputs' }, bubbles: true, cancelable: true }));
|
|
||||||
});
|
|
||||||
@ -1 +0,0 @@
|
|||||||
<!-- only here to be able to include showHide using `toWidget` -->
|
|
||||||
@ -1,58 +0,0 @@
|
|||||||
/**
|
|
||||||
* div
|
|
||||||
* div.js-show-hide__toggle
|
|
||||||
* toggle here
|
|
||||||
* div
|
|
||||||
* content here
|
|
||||||
*/
|
|
||||||
|
|
||||||
document.addEventListener('setup', function(e) {
|
|
||||||
if (e.detail.module && e.detail.module !== 'showHide')
|
|
||||||
return;
|
|
||||||
|
|
||||||
var LSNAME = 'SHOW_HIDE';
|
|
||||||
|
|
||||||
function addEventHandler(el) {
|
|
||||||
el.addEventListener('click', function elClickListener() {
|
|
||||||
var newState = el.parentElement.classList.toggle('js-show-hide--collapsed');
|
|
||||||
updateLSState(el.dataset.shIndex || null, newState);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateLSState(index, state) {
|
|
||||||
if (!index) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
var lsData = fromLocalStorage();
|
|
||||||
lsData[index] = state;
|
|
||||||
window.localStorage.setItem(LSNAME, JSON.stringify(lsData));
|
|
||||||
}
|
|
||||||
|
|
||||||
function collapsedStateInLocalStorage(index) {
|
|
||||||
return fromLocalStorage()[index] || null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function fromLocalStorage() {
|
|
||||||
return JSON.parse(window.localStorage.getItem(LSNAME)) || {};
|
|
||||||
}
|
|
||||||
|
|
||||||
Array
|
|
||||||
.from(e.detail.scope.querySelectorAll('.js-show-hide__toggle'))
|
|
||||||
.forEach(function(el) {
|
|
||||||
var index = el.dataset.shIndex || null;
|
|
||||||
el.parentElement.classList.toggle(
|
|
||||||
'js-show-hide--collapsed',
|
|
||||||
collapsedStateInLocalStorage(index) || el.dataset.collapsed === 'true'
|
|
||||||
);
|
|
||||||
Array.from(el.parentElement.children).forEach(function(el) {
|
|
||||||
if (!el.classList.contains('js-show-hide__toggle')) {
|
|
||||||
el.classList.add('js-show-hide__target');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
addEventHandler(el);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
document.dispatchEvent(new CustomEvent('setup', { detail: { scope: document.body, module: 'showHide' }, bubbles: true, cancelable: true }))
|
|
||||||
});
|
|
||||||
@ -1 +0,0 @@
|
|||||||
<!-- only here to be able to include tabber using `toWidget` -->
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
.tab-opener {
|
|
||||||
background-color: var(--color-dark);
|
|
||||||
|
|
||||||
&.tab-visible {
|
|
||||||
border-bottom-color: var(--color-primary);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1 +0,0 @@
|
|||||||
<!-- only here to be able to include tooltips using `toWidget` -->
|
|
||||||
@ -1,67 +0,0 @@
|
|||||||
(function() {;
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
window.utils = window.utils || {};
|
|
||||||
|
|
||||||
window.utils.tooltip = function(tt) {
|
|
||||||
var handle = tt.querySelector('.tooltip__handle');
|
|
||||||
var content = tt.querySelector('.tooltip__content');
|
|
||||||
|
|
||||||
var left = false;
|
|
||||||
|
|
||||||
// initially set content to hidden
|
|
||||||
content.classList.add('hidden');
|
|
||||||
|
|
||||||
handle.addEventListener('mouseenter', function() {
|
|
||||||
left = false;
|
|
||||||
content.classList.toggle('to-left', handle.getBoundingClientRect().left + 300 > window.innerWidth);
|
|
||||||
content.classList.remove('hidden');
|
|
||||||
});
|
|
||||||
|
|
||||||
handle.addEventListener('mouseleave', function() {
|
|
||||||
left = true;
|
|
||||||
window.setTimeout(function() {
|
|
||||||
if (left) {
|
|
||||||
content.classList.add('hidden');
|
|
||||||
}
|
|
||||||
}, 250);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
window.utils.tooltipFromAttribute = function(el) {
|
|
||||||
var tt = document.createElement('div');
|
|
||||||
var handle = document.createElement('div');
|
|
||||||
var content = document.createElement('div');
|
|
||||||
|
|
||||||
tt.classList.add('js-tooltip');
|
|
||||||
handle.classList.add('tooltip__handle');
|
|
||||||
content.classList.add('tooltip__content', 'hidden');
|
|
||||||
|
|
||||||
handle.innerText = '?';
|
|
||||||
content.innerHTML = el.getAttribute('data-tooltip');
|
|
||||||
|
|
||||||
tt.appendChild(handle);
|
|
||||||
tt.appendChild(content);
|
|
||||||
|
|
||||||
if (el.nextSiblingElement) {
|
|
||||||
el.parentElement.insertBefore(tt, el.nextSiblingElement);
|
|
||||||
} else {
|
|
||||||
el.parentElement.appendChild(tt);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
document.addEventListener('setup', function(e) {
|
|
||||||
// JS-TOOLTIPS NOT USED CURRENTLY.
|
|
||||||
|
|
||||||
// initialize tooltips set via `data-tooltip`
|
|
||||||
// Array.from(e.detail.scope.querySelectorAll('[data-tooltip]')).forEach(function(el) {
|
|
||||||
// window.utils.tooltipFromAttribute(el)
|
|
||||||
// });
|
|
||||||
|
|
||||||
// initialize tooltips
|
|
||||||
// Array.from(e.detail.scope.querySelectorAll('.js-tooltip')).forEach(function(tt) {
|
|
||||||
// window.utils.tooltip(tt);
|
|
||||||
// });
|
|
||||||
});
|
|
||||||
@ -1,186 +1,10 @@
|
|||||||
(function collonadeClosure() {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
window.utils = window.utils || {};
|
|
||||||
|
|
||||||
window.utils.asyncTable = function(wrapper, options) {
|
|
||||||
|
|
||||||
var tableIdent = wrapper.dataset.dbtIdent;
|
|
||||||
var shortCircuitHeader = #{String (toPathPiece HeaderDBTableShortcircuit)};
|
|
||||||
|
|
||||||
var ths = [];
|
|
||||||
var pageLinks = [];
|
|
||||||
var pagesizeForm;
|
|
||||||
var scrollTable;
|
|
||||||
|
|
||||||
function init() {
|
|
||||||
var table = wrapper.querySelector('#' + tableIdent);
|
|
||||||
if (!table) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
scrollTable = wrapper.querySelector('.scrolltable');
|
|
||||||
|
|
||||||
// sortable table headers
|
|
||||||
ths = Array.from(table.querySelectorAll('th.sortable')).map(function(th) {
|
|
||||||
return { element: th };
|
|
||||||
});
|
|
||||||
|
|
||||||
// pagination links
|
|
||||||
var pagination = wrapper.querySelector('#' + tableIdent + '-pagination');
|
|
||||||
if (pagination) {
|
|
||||||
pageLinks = Array.from(pagination.querySelectorAll('.page-link')).map(function(link) {
|
|
||||||
return { element: link };
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// pagesize form
|
|
||||||
pagesizeForm = wrapper.querySelector('#' + tableIdent + '-pagesize-form');
|
|
||||||
|
|
||||||
// take options into account
|
|
||||||
if (options && options.scrollTo) {
|
|
||||||
window.scrollTo(options.scrollTo);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options && options.horizPos && scrollTable) {
|
|
||||||
scrollTable.scrollLeft = options.horizPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
setupListeners();
|
|
||||||
wrapper.classList.add('js-initialized');
|
|
||||||
}
|
|
||||||
|
|
||||||
function setupListeners() {
|
|
||||||
ths.forEach(function(th) {
|
|
||||||
th.clickHandler = function(event) {
|
|
||||||
var boundClickHandler = clickHandler.bind(this);
|
|
||||||
var horizPos = (scrollTable || {}).scrollLeft;
|
|
||||||
boundClickHandler(event, { horizPos });
|
|
||||||
};
|
|
||||||
th.element.addEventListener('click', th.clickHandler);
|
|
||||||
});
|
|
||||||
|
|
||||||
pageLinks.forEach(function(link) {
|
|
||||||
link.clickHandler = function(event) {
|
|
||||||
var boundClickHandler = clickHandler.bind(this);
|
|
||||||
var wrapperBoundingRect = wrapper.getBoundingClientRect();
|
|
||||||
var options = {};
|
|
||||||
if (wrapperBoundingRect.top < 160) {
|
|
||||||
options.scrollTo = {
|
|
||||||
top: (wrapper.offsetTop || 0) - 60,
|
|
||||||
left: wrapper.offsetLeft || 0,
|
|
||||||
behavior: 'smooth',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
boundClickHandler(event, options);
|
|
||||||
}
|
|
||||||
link.element.addEventListener('click', link.clickHandler);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (pagesizeForm) {
|
|
||||||
var pagesizeSelect = pagesizeForm.querySelector('[name=' + tableIdent + '-pagesize]');
|
|
||||||
pagesizeSelect.addEventListener('change', changePagesizeHandler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeListeners() {
|
|
||||||
ths.forEach(function(th) {
|
|
||||||
th.element.removeEventListener('click', th.clickHandler);
|
|
||||||
});
|
|
||||||
|
|
||||||
pageLinks.forEach(function(link) {
|
|
||||||
link.element.removeEventListener('click', link.clickHandler);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (pagesizeForm) {
|
|
||||||
var pagesizeSelect = pagesizeForm.querySelector('[name=' + tableIdent + '-pagesize]')
|
|
||||||
pagesizeSelect.removeEventListener('change', changePagesizeHandler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function clickHandler(event, options) {
|
|
||||||
event.preventDefault();
|
|
||||||
var url = new URL(window.location.origin + window.location.pathname + getClickDestination(this));
|
|
||||||
updateTableFrom(url, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getClickDestination(el) {
|
|
||||||
if (!el.querySelector('a')) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
return el.querySelector('a').getAttribute('href');
|
|
||||||
}
|
|
||||||
|
|
||||||
function changePagesizeHandler(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+|all)/, '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, options) {
|
|
||||||
wrapper.innerHTML = newHtml;
|
|
||||||
wrapper.classList.remove("js-initialized");
|
|
||||||
|
|
||||||
// setup the wrapper and its components to behave async again
|
|
||||||
window.utils.asyncTable(wrapper, options);
|
|
||||||
|
|
||||||
// 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, options) {
|
|
||||||
|
|
||||||
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, options);
|
|
||||||
}).catch(function(err) {
|
|
||||||
console.error(err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
init();
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
var dbtIdent = #{String $ dbtIdent};
|
var dbtIdent = #{String $ dbtIdent};
|
||||||
|
var headerDBTableShortcircuit = #{String (toPathPiece HeaderDBTableShortcircuit)};
|
||||||
var selector = '#' + dbtIdent + '-table-wrapper:not(.js-initialized)';
|
var selector = '#' + dbtIdent + '-table-wrapper:not(.js-initialized)';
|
||||||
var wrapper = document.querySelector(selector);
|
var wrapper = document.querySelector(selector);
|
||||||
|
|
||||||
if (wrapper) {
|
if (wrapper) {
|
||||||
wrapper.dataset.dbtIdent = dbtIdent;
|
window.utils.setup('asyncTable', wrapper, { headerDBTableShortcircuit, dbtIdent });
|
||||||
window.utils.asyncTable(wrapper);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
<div #alerts .alerts>
|
<div #alerts-1 .alerts> <!-- make wIdent work here instead of '#alerts-1' -->
|
||||||
<div .alerts__toggler>
|
<div .alerts__toggler>
|
||||||
$forall (status, msg) <- mmsgs
|
$forall (status, msg) <- mmsgs
|
||||||
$with status2 <- bool status "info" (status == "")
|
$with status2 <- bool status "info" (status == "")
|
||||||
<div class="alert alert-#{status2}">
|
<div .alert.alert-#{status2}>
|
||||||
<div .alert__closer>
|
<div .alert__closer>
|
||||||
<div .alert__icon>
|
<div .alert__icon>
|
||||||
<div .alert__content>
|
<div .alert__content>
|
||||||
|
|||||||
@ -1,18 +1,4 @@
|
|||||||
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.addEventListener('DOMContentLoaded', function() {
|
||||||
document.dispatchEvent(new CustomEvent('setup', { detail: { scope: document.body, module: 'alerts' }, bubbles: true, cancelable: true }))
|
var alertsElement = document.querySelector('#' + 'alerts-1');
|
||||||
|
window.utils.setup('alerts', alertsElement);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,23 +0,0 @@
|
|||||||
(function() {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
window.utils = window.utils || {};
|
|
||||||
|
|
||||||
window.utils.aside = function(asideEl) {
|
|
||||||
|
|
||||||
function init() {
|
|
||||||
var favoritesBtn = document.querySelector('.navbar__list-item--favorite');
|
|
||||||
favoritesBtn.addEventListener('click', function(event) {
|
|
||||||
favoritesBtn.classList.toggle('navbar__list-item--active');
|
|
||||||
asideEl.classList.toggle('main__aside--expanded');
|
|
||||||
event.preventDefault();
|
|
||||||
}, true);
|
|
||||||
}
|
|
||||||
init();
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
var asidenavEl = document.querySelector('.main__aside');
|
|
||||||
window.utils.aside(asidenavEl);
|
|
||||||
});
|
|
||||||
@ -16,10 +16,11 @@ $newline never
|
|||||||
<a .asidenav__link-wrapper href=@{courseRoute}>
|
<a .asidenav__link-wrapper href=@{courseRoute}>
|
||||||
<div .asidenav__link-shorthand>#{courseShorthand}
|
<div .asidenav__link-shorthand>#{courseShorthand}
|
||||||
<div .asidenav__link-label>#{courseName}
|
<div .asidenav__link-label>#{courseName}
|
||||||
<ul .asidenav__nested-list.list--iconless>
|
<div .asidenav__nested-list-wrapper>
|
||||||
$forall (MenuItem{menuItemType, menuItemLabel}, route) <- pageActions
|
<ul .asidenav__nested-list.list--iconless>
|
||||||
$case menuItemType
|
$forall (MenuItem{menuItemType, menuItemLabel}, route) <- pageActions
|
||||||
$of PageActionPrime
|
$case menuItemType
|
||||||
<li .asidenav__nested-list-item>
|
$of PageActionPrime
|
||||||
<a .asidenav__link-wrapper href=#{route}>_{menuItemLabel}
|
<li .asidenav__nested-list-item>
|
||||||
$of _
|
<a .asidenav__link-wrapper href=#{route}>_{menuItemLabel}
|
||||||
|
$of _
|
||||||
4
templates/widgets/asidenav/asidenav.julius
Normal file
4
templates/widgets/asidenav/asidenav.julius
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
var asidenavEl = document.querySelector('.main__aside');
|
||||||
|
window.utils.setup('aside', asidenavEl);
|
||||||
|
});
|
||||||
@ -13,7 +13,7 @@ $case formLayout
|
|||||||
<label .form-group__label for=#{fvId view}>
|
<label .form-group__label for=#{fvId view}>
|
||||||
#{fvLabel view}
|
#{fvLabel view}
|
||||||
$maybe tooltip <- fvTooltip view
|
$maybe tooltip <- fvTooltip view
|
||||||
<div .js-tooltip>
|
<div .tooltip>
|
||||||
<div .tooltip__handle>
|
<div .tooltip__handle>
|
||||||
<div .tooltip__content>^{tooltip}
|
<div .tooltip__content>^{tooltip}
|
||||||
<div .form-group__input>
|
<div .form-group__input>
|
||||||
5
templates/widgets/form/form.julius
Normal file
5
templates/widgets/form/form.julius
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
Array.from(document.querySelectorAll('form')).forEach(function(form) {
|
||||||
|
window.utils.setup('form', form);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -51,4 +51,4 @@ $#
|
|||||||
\ (_{title $ getSum $ summary ^. _numSheetsPoints})
|
\ (_{title $ getSum $ summary ^. _numSheetsPoints})
|
||||||
$# Kurze Alternative mit Hashtag-Symbol für "Anzahl"
|
$# Kurze Alternative mit Hashtag-Symbol für "Anzahl"
|
||||||
$# \ (##{display $ summary ^. _numSheetsPoints})
|
$# \ (##{display $ summary ^. _numSheetsPoints})
|
||||||
<td .table__td>#{display $ summary ^. _numSheets}
|
<td .table__td>#{display $ summary ^. _numSheets}
|
||||||
Loading…
Reference in New Issue
Block a user