91 lines
3.3 KiB
Haskell
91 lines
3.3 KiB
Haskell
-- SPDX-FileCopyrightText: 2022 Gregor Kleen <gregor.kleen@ifi.lmu.de>,Sarah Vaupel <vaupel.sarah@campus.lmu.de>
|
|
--
|
|
-- SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
module Utils.Frontend.Modal
|
|
( Modal(..)
|
|
, customModal
|
|
, modal, btnModal, msgModal
|
|
, addMessageModal
|
|
) where
|
|
|
|
import ClassyPrelude.Yesod
|
|
|
|
import Control.Lens
|
|
import Utils.Route
|
|
import Utils.Message
|
|
import Utils.Form (ButtonClass())
|
|
|
|
import Settings (widgetFile)
|
|
|
|
import Control.Monad.Random.Class (uniform)
|
|
import Control.Monad.Trans.Random (evalRandTIO)
|
|
|
|
|
|
data Modal site = Modal
|
|
{ modalTriggerId
|
|
, modalId :: Maybe Text
|
|
, modalTrigger :: Maybe Text {- Dynamic URL -} -> Text {- TriggerId -} -> WidgetFor site ()
|
|
, modalContent :: Either (SomeRoute site) (WidgetFor site ())
|
|
}
|
|
|
|
customModal :: Modal site -> WidgetFor site ()
|
|
customModal Modal{..} = do
|
|
triggerId' <- maybe newIdent return modalTriggerId
|
|
|
|
$(widgetFile "widgets/modal/modal")
|
|
|
|
route <- traverse toTextUrl $ modalContent ^? _Left
|
|
modalTrigger route triggerId'
|
|
|
|
-- | Create a link to a modal
|
|
modal :: WidgetFor site () -- ^ Widget that represents the link
|
|
-> Either (SomeRoute site) (WidgetFor site ()) -- ^ Modal contant: either dynamic link or static widget
|
|
-> WidgetFor site () -- ^ result widget
|
|
modal modalTrigger' modalContent = customModal Modal{..}
|
|
where
|
|
modalTriggerId = Nothing
|
|
modalId = Nothing
|
|
modalTrigger mRoute triggerId = $(widgetFile "widgets/modal/trigger")
|
|
|
|
-- | Variant of `modal` that looks like a button
|
|
btnModal :: (RenderMessage site a, PathPiece (ButtonClass site))
|
|
=> a -- ^ Button Text
|
|
-> [ButtonClass site] -- ^ Button class
|
|
-> Either (SomeRoute site) (WidgetFor site ()) -- ^ Modal contant: either dynamic link or static widget
|
|
-> WidgetFor site () -- ^ result widget
|
|
btnModal btl bcs = modal fakeBtn
|
|
where
|
|
fakeBtn = [whamlet|<button :not (onull bcs):class=#{unwords $ map toPathPiece bcs}>
|
|
_{btl}
|
|
|]
|
|
|
|
-- | Variant of `modal` for use in messages (uses a different id generator to avoid collisions)
|
|
msgModal :: WidgetFor site ()
|
|
-> Either (SomeRoute site) (WidgetFor site ())
|
|
-> WidgetFor site ()
|
|
msgModal modalTrigger' modalContent = do
|
|
let
|
|
randomIdentifier :: MonadIO m => m Text
|
|
-- ^ Generates valid CSS-Identifiers with roughly 128 bits of entropy
|
|
--
|
|
-- See https://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
|
|
randomIdentifier = fmap pack . evalRandTIO $ do
|
|
prefix <- uniform $ ['a'..'z'] ++ ['A'..'Z']
|
|
suffix <- replicateM 21 . uniform $ ['a'..'z'] ++ ['A'..'Z'] ++ ['0'..'9']
|
|
return $ prefix : suffix
|
|
modalTriggerId <- Just <$> randomIdentifier
|
|
modalId <- Just <$> randomIdentifier
|
|
customModal Modal{..}
|
|
where
|
|
modalTrigger mRoute triggerId = $(widgetFile "widgets/modal/trigger")
|
|
|
|
-- | add message alert with a short trigger widget, whose larger content is displayed in a modal
|
|
addMessageModal :: forall m site.
|
|
( MonadHandler m
|
|
, HandlerSite m ~ site
|
|
, Yesod site
|
|
) => MessageStatus -> WidgetFor site () -> Either (SomeRoute site) (WidgetFor site ()) -> m ()
|
|
addMessageModal ms trigger content = addMessageWidget ms $ msgModal trigger content
|
|
|