Skip to content

Commit

Permalink
[ADP-3454] Have smooth data swapping in UI (#4784)
Browse files Browse the repository at this point in the history
This PR remove some ghost appearance of old things during data swapping
in the deposit UI

ADP-3454
  • Loading branch information
paolino authored Sep 26, 2024
2 parents 1075983 + f5328e4 commit cc68d3f
Show file tree
Hide file tree
Showing 13 changed files with 276 additions and 94 deletions.
1 change: 1 addition & 0 deletions lib/ui/cardano-wallet-ui.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ library
Cardano.Wallet.UI.Common.Handlers.SSE
Cardano.Wallet.UI.Common.Handlers.State
Cardano.Wallet.UI.Common.Handlers.Wallet
Cardano.Wallet.UI.Common.Html.Copy
Cardano.Wallet.UI.Common.Html.Html
Cardano.Wallet.UI.Common.Html.Htmx
Cardano.Wallet.UI.Common.Html.Lib
Expand Down
116 changes: 116 additions & 0 deletions lib/ui/src/Cardano/Wallet/UI/Common/Html/Copy.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE NumericUnderscores #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeApplications #-}

module Cardano.Wallet.UI.Common.Html.Copy
( copyButton
, initClipboardScript
, copyableHidden
, offscreenCss
)
where

import Prelude

import Data.String.Interpolate
( i
)
import Data.Text
( Text
)
import Lucid
( Attribute
, HtmlT
, Term (..)
, ToHtml (..)
, button_
, class_
, height_
, id_
, script_
, style_
, svg_
, width_
)
import Lucid.Base
( makeAttribute
)

-- | A button that copies the content of a field to the clipboard.
copyButton
:: Monad m
=> Text
-- ^ Field id
-> HtmlT m ()
copyButton field' = do
button_
[ class_ "btn copy-button"
, id_ button
, makeAttribute "data-clipboard-target" fieldId
]
buttonImage
where
fieldId = "#" <> field'
button = field' <> "-copy-button"

buttonImage :: Monad m => HtmlT m ()
buttonImage = svg_
[ class_ "bi bi-copy"
, width_ "16"
, height_ "16"
, fill_ "currentColor"
, viewBox_ "0 0 16 16"
]
$ do
path_
[ fillRule_ "evenodd"
, d_ drawCopyButton
]
mempty

drawCopyButton :: Text
drawCopyButton = "M4 2a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2zm2-1a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zM2 5a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-1h1v1a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h1v1z"

fill_ :: Text -> Attribute
fill_ = makeAttribute "fill"

viewBox_ :: Text -> Attribute
viewBox_ = makeAttribute "viewBox"

d_ :: Text -> Attribute
d_ = makeAttribute "d"

fillRule_ :: Text -> Attribute
fillRule_ = makeAttribute "fill-rule"

path_ :: Term arg result => arg -> result
path_ = term "path"

initClipboardScript :: Monad m => HtmlT m ()
initClipboardScript = script_ "var clipboard = new ClipboardJS('.copy-button');"

copyableHidden :: Text -> [Attribute]
copyableHidden identity =
[ class_ "offscreen"
, makeAttribute "aria-hidden" "true"
, id_ identity
]

offscreenCss :: Monad m => HtmlT m ()
offscreenCss =
style_ []
$ toHtml @Text
[i|
.offscreen {
position: absolute;
left: -9999px;
top: auto;
width: 1px;
height: 1px;
overflow: hidden;
}
|]
44 changes: 13 additions & 31 deletions lib/ui/src/Cardano/Wallet/UI/Common/Html/Pages/Lib.hs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE NumericUnderscores #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeApplications #-}
Expand All @@ -22,7 +21,7 @@ module Cardano.Wallet.UI.Common.Html.Pages.Lib
, showAda
, showAdaOfLoveLace
, showThousandDots
, copyButton
, fadeInId
)
where

Expand All @@ -46,9 +45,6 @@ import Cardano.Wallet.UI.Lib.ListOf
import Control.Monad.Operational
( singleton
)
import Data.String.Interpolate
( i
)
import Data.Text
( Text
)
Expand All @@ -58,13 +54,12 @@ import Lucid
, HtmlT
, ToHtml (..)
, b_
, button_
, class_
, div_
, id_
, role_
, scope_
, script_
, style_
, table_
, td_
, tr_
Expand Down Expand Up @@ -113,7 +108,7 @@ data AssocRow m
assocRowH :: AssocRow m -> Monad m => HtmlT m ()
assocRowH AssocRow{..} = tr_ ([scope_ "row"] <> rowAttributes) $ do
td_ [scope_ "col"] $ b_ key
td_ [scope_ "col"] val
td_ [scope_ "col", class_ "d-flex justify-content-end"] val

-- | Render a list of 'AssocRow' as a table. We use 'listOf' to allow 'do' notation
-- in the definition of the rows
Expand All @@ -139,6 +134,13 @@ fieldHtml as = field as . toHtml
fieldShow :: (Show a, Monad m) => [Attribute] -> Text -> a -> ListOf (AssocRow m)
fieldShow attrs key val = field attrs (toHtml key) (toHtml $ show val)

fadeInId :: Monad m => HtmlT m ()
fadeInId =
style_ []
$ toHtml @Text
".smooth.htmx-added { transition: opacity: 0.1s ease-in; opacity: 0} \
\.smooth { opacity: 1; transition: opacity 0.1s ease-out; }"

-- | A tag that can self populate with data that is fetched as GET from a link
-- whenever some specific events are received from an SSE endpoint.
-- It also self populate on load.
Expand All @@ -152,7 +154,7 @@ sseH
-> Monad m
=> HtmlT m ()
sseH link target events = do
do
do
div_
[ hxTrigger_ triggered
, hxGet_ $ linkText link
Expand All @@ -163,6 +165,7 @@ sseH link target events = do
[ id_ target
, hxGet_ $ linkText link
, hxTrigger_ "load"
, class_ "smooth"
]
""
where
Expand All @@ -172,7 +175,7 @@ sseH link target events = do
sseInH :: Text -> [Text] -> Html ()
sseInH target events =
div_
[hxExt_ "sse"
[ hxExt_ "sse"
]
$ div_
[ hxTarget_ $ "#" <> target
Expand Down Expand Up @@ -217,24 +220,3 @@ showThousandDots = reverse . showThousandDots' . reverse . show
(a, b) = splitAt 3 xs
in
a <> if null b then [] else "." <> showThousandDots' b

-- | A button that copies the content of a field to the clipboard.
copyButton
:: Monad m
=> Text
-- ^ Field id
-> HtmlT m ()
copyButton field' = do
button_ [class_ "btn btn-outline-secondary", id_ button] "Copy"
script_ $ copyButtonScript button field'
where
button = field' <> "-copy-button"

copyButtonScript :: Text -> Text -> Text
copyButtonScript button field' =
[i|
document.getElementById('#{button}').addEventListener('click', function() {
var mnemonic = document.getElementById('#{field'}').innerText;
navigator.clipboard.writeText(mnemonic);
});
|]
10 changes: 4 additions & 6 deletions lib/ui/src/Cardano/Wallet/UI/Common/Html/Pages/Template/Body.hs
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ where

import Prelude

import Cardano.Wallet.UI.Common.Html.Copy
( initClipboardScript
)
import Cardano.Wallet.UI.Common.Html.Htmx
( hxSse_
)
import Cardano.Wallet.UI.Common.Html.Lib
( linkText
)
import Cardano.Wallet.UI.Common.Html.Pages.Template.Footer
( footerH
)
import Data.Text
( Text
)
Expand Down Expand Up @@ -45,6 +45,4 @@ bodyH sseLink header body = do
div_ [hxSse_ $ sseConnectFromLink sseLink] $
div_ [class_ "container-fluid"] $ do
div_ [class_ "main"] body
div_
[class_ "footer mt-5"]
footerH
initClipboardScript
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,13 @@ footerH =
$ do
ul_ [class_ "nav flex-column"] $ do
li_
[class_ "nav-item mb-2"]
[class_ "nav-item"]
"© 2024 Cardano Foundation, HAL team"
li_ [class_ "nav-item mb-2"] $ do
li_ [class_ "nav-item"] $ do
span_ "Source code on "
githubLinkH

div_ [class_ "row d-md-flex align-items-center"]
$ div_
[class_ "mb-3 mb-md-0 text-body-secondary"]
[class_ "mb-md-0 text-body-secondary"]
"Powered by Haskell, Htmx, Servant, Lucid, Bootstrap"
52 changes: 49 additions & 3 deletions lib/ui/src/Cardano/Wallet/UI/Common/Html/Pages/Template/Head.hs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeApplications #-}

module Cardano.Wallet.UI.Common.Html.Pages.Template.Head
( pageFromBodyH
Expand All @@ -9,13 +10,19 @@ where

import Prelude

import Cardano.Wallet.UI.Common.Html.Copy
( offscreenCss
)
import Cardano.Wallet.UI.Common.Html.Htmx
( useHtmxExtension
, useHtmxVersion
)
import Cardano.Wallet.UI.Common.Html.Lib
( linkText
)
import Cardano.Wallet.UI.Common.Html.Pages.Lib
( fadeInId
)
import Data.Text
( Text
)
Expand All @@ -35,6 +42,7 @@ import Lucid
, name_
, rel_
, src_
, style_
, term
, title_
)
Expand Down Expand Up @@ -86,6 +94,18 @@ popperScript =
]
$ pure ()

clipboardScript :: Monad m => HtmlT m ()
clipboardScript =
term
"script"
[ src_
"https://cdn.jsdelivr.net/npm/clipboard@2.0.11/dist/clipboard.min.js"
, integrity_
"sha384-J08i8An/QeARD9ExYpvphB8BsyOj3Gh2TSh1aLINKO3L0cMSH2dN3E22zFoXEi0Q"
, crossorigin_ "anonymous"
]
$ pure ()

-- | Render a favicon link.
favicon :: Link -> Monad m => HtmlT m ()
favicon path =
Expand All @@ -94,22 +114,48 @@ favicon path =
, href_ $ linkText path
]

pageFromBodyH :: Monad m => Link -> PageConfig -> HtmlT m () -> HtmlT m ()
-- make the body centered and have a max-width
bodyCss :: Monad m => HtmlT m ()
bodyCss =
style_ []
$ toHtml @Text
"html {max-width:1200px; margin: 0 auto;};"

-- https://stackoverflow.com/questions/11088938/is-this-the-best-way-to-make-the-body-max-width-and-centered
-- this is for modals to appear at the center of the screen even on big screens
-- where the body is centered and has a max-width
modalCssWorkaround :: Monad m => HtmlT m ()
modalCssWorkaround =
style_ []
$ toHtml @Text
".modal {padding-right: 0px!important;}\
\.modal-open {padding-right: 0px!important;}"

pageFromBodyH :: Monad m => Link -> PageConfig -> HtmlT m () -> HtmlT m ()
pageFromBodyH faviconLink PageConfig{..} body =
html_ [term "data-bs-theme" "dark"]
$ do
head_ $ do
title_ $ toHtml title
meta_ [charset_ "utf-8"]
meta_ [name_ "viewport", content_ "width=device-width, initial-scale=1.0"]
meta_
[ name_ "viewport"
, content_ "width=device-width, initial-scale=1.0"
]
popperScript
bootstrapLink
bootstrapScript
bootstrapIcons
clipboardScript
favicon faviconLink
useHtmxVersion (1, 9, 12)
useHtmxExtension "json-enc"
body_ body
bodyCss
modalCssWorkaround
offscreenCss
body_ $ do
fadeInId
body

data PageConfig = PageConfig
{ prefix :: Text
Expand Down
Loading

0 comments on commit cc68d3f

Please sign in to comment.