mirror of
https://github.com/NoRedInk/noredink-ui.git
synced 2024-12-25 06:33:09 +03:00
Adds MessageV2
This commit is contained in:
parent
4e2bac6482
commit
bf5c6fc025
553
src/Nri/Ui/Message/V2.elm
Normal file
553
src/Nri/Ui/Message/V2.elm
Normal file
@ -0,0 +1,553 @@
|
||||
module Nri.Ui.Message.V1 exposing
|
||||
( tiny, large, banner
|
||||
, Theme(..), Content(..), mapContent, BannerAttribute
|
||||
, onDismiss
|
||||
, somethingWentWrong
|
||||
)
|
||||
|
||||
{-|
|
||||
|
||||
@docs tiny, large, banner
|
||||
@docs Theme, Content, mapContent, BannerAttribute
|
||||
@docs onDismiss
|
||||
|
||||
@docs somethingWentWrong
|
||||
|
||||
-}
|
||||
|
||||
import Accessibility.Styled as Html exposing (..)
|
||||
import Accessibility.Styled.Widget as Widget
|
||||
import Css exposing (..)
|
||||
import Css.Global
|
||||
import Html.Styled exposing (styled)
|
||||
import Html.Styled.Attributes exposing (css)
|
||||
import Html.Styled.Events exposing (onClick)
|
||||
import Markdown
|
||||
import Nri.Ui
|
||||
import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.Fonts.V1 as Fonts
|
||||
import Nri.Ui.Svg.V1 as NriSvg exposing (Svg)
|
||||
import Nri.Ui.UiIcon.V1 as UiIcon
|
||||
|
||||
|
||||
{-| `Error` / `Alert` / `Tip` / `Success`
|
||||
-}
|
||||
type Theme
|
||||
= Error
|
||||
| Alert
|
||||
| Tip
|
||||
| Success
|
||||
| Custom
|
||||
{ color : Color
|
||||
, backgroundColor : Color
|
||||
, icon : Svg
|
||||
}
|
||||
|
||||
|
||||
{-| Prefer using the simplest variant that meets your needs.
|
||||
|
||||
- `Plain`: provide a plain-text string
|
||||
- `Markdown`: provide a string that will be rendered as markdown
|
||||
- `Html`: provide custom HTML
|
||||
|
||||
-}
|
||||
type Content msg
|
||||
= Plain String
|
||||
| Markdown String
|
||||
| Html (List (Html msg))
|
||||
|
||||
|
||||
{-| Transform the messages produced by some `Content`.
|
||||
-}
|
||||
mapContent : (a -> b) -> Content a -> Content b
|
||||
mapContent f content =
|
||||
case content of
|
||||
Plain string ->
|
||||
Plain string
|
||||
|
||||
Markdown string ->
|
||||
Markdown string
|
||||
|
||||
Html html ->
|
||||
Html (List.map (Html.map f) html)
|
||||
|
||||
|
||||
{-| PRIVATE
|
||||
-}
|
||||
contentToHtml : Content msg -> List (Html msg)
|
||||
contentToHtml content =
|
||||
case content of
|
||||
Plain stringContent ->
|
||||
[ text stringContent ]
|
||||
|
||||
Markdown markdownContent ->
|
||||
Markdown.toHtml Nothing markdownContent |> List.map fromUnstyled
|
||||
|
||||
Html html ->
|
||||
html
|
||||
|
||||
|
||||
{-| Shows a tiny alert message. We commonly use these for validation errors and small hints to users.
|
||||
|
||||
import Nri.Ui.Message.V1 as Message
|
||||
|
||||
view =
|
||||
Message.tiny Message.Tip (Message.Markdown "Don't tip too much, or your waitress will **fall over**!")
|
||||
|
||||
NOTE: When using a `Custom` theme, `tiny` ignores the custom `backgroundColor`.
|
||||
|
||||
-}
|
||||
tiny : Theme -> Content msg -> Html msg
|
||||
tiny theme content =
|
||||
let
|
||||
config =
|
||||
case theme of
|
||||
Error ->
|
||||
{ icon =
|
||||
UiIcon.exclamation
|
||||
|> NriSvg.withColor Colors.purple
|
||||
|> NriSvg.withLabel "Error"
|
||||
, fontColor = Colors.purple
|
||||
}
|
||||
|
||||
Alert ->
|
||||
{ icon =
|
||||
UiIcon.exclamation
|
||||
|> NriSvg.withColor Colors.red
|
||||
|> NriSvg.withLabel "Alert"
|
||||
, fontColor = Colors.redDark
|
||||
}
|
||||
|
||||
Tip ->
|
||||
{ icon =
|
||||
UiIcon.bulb
|
||||
|> NriSvg.withColor Colors.yellow
|
||||
|> NriSvg.withLabel "Tip"
|
||||
, fontColor = Colors.navy
|
||||
}
|
||||
|
||||
Success ->
|
||||
{ icon =
|
||||
UiIcon.checkmarkInCircle
|
||||
|> NriSvg.withColor Colors.green
|
||||
|> NriSvg.withLabel "Success"
|
||||
, fontColor = Colors.greenDarkest
|
||||
}
|
||||
|
||||
Custom customTheme ->
|
||||
{ icon = customTheme.icon
|
||||
, fontColor = customTheme.color
|
||||
}
|
||||
in
|
||||
Nri.Ui.styled div
|
||||
"Nri-Ui-Message-V1--tiny"
|
||||
[ displayFlex
|
||||
, justifyContent start
|
||||
, paddingTop (px 6)
|
||||
, paddingBottom (px 8)
|
||||
]
|
||||
[]
|
||||
[ styled div
|
||||
[]
|
||||
[]
|
||||
[ Nri.Ui.styled div
|
||||
"Nri-Ui-Message-V1--tinyIconContainer"
|
||||
[ -- Content positioning
|
||||
displayFlex
|
||||
, alignItems center
|
||||
, justifyContent center
|
||||
, marginRight (px 5)
|
||||
, lineHeight (px 13)
|
||||
, flexShrink zero
|
||||
|
||||
-- Size
|
||||
, borderRadius (px 13)
|
||||
, height (px 20)
|
||||
, width (px 20)
|
||||
]
|
||||
[]
|
||||
[ NriSvg.toHtml config.icon ]
|
||||
]
|
||||
, styled div
|
||||
[ displayFlex
|
||||
, alignItems center
|
||||
]
|
||||
[]
|
||||
[ Nri.Ui.styled div
|
||||
"Nri-Ui-Message-V1--alert"
|
||||
[ color config.fontColor
|
||||
, Fonts.baseFont
|
||||
, fontSize (px 13)
|
||||
|
||||
--, lineHeight (px 20)
|
||||
, listStyleType none
|
||||
|
||||
-- This global selector and overrides are necessary due to
|
||||
-- old stylesheets used on the monolith that set the
|
||||
-- `.txt p { font-size: 18px; }` -- without these overrides,
|
||||
-- we may see giant ugly alerts.
|
||||
-- Remove these if you want to! but be emotionally prepped
|
||||
-- to deal with visual regressions. 🙏
|
||||
, Css.Global.descendants
|
||||
[ Css.Global.p
|
||||
[ margin zero
|
||||
|
||||
--, lineHeight (px 20)
|
||||
, fontSize (px 13)
|
||||
, Fonts.baseFont
|
||||
]
|
||||
]
|
||||
]
|
||||
[]
|
||||
(contentToHtml content)
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
{-| Shows a large alert or callout message. We commonly use these for highlighted tips, instructions, or asides in page copy.
|
||||
|
||||
import Nri.Ui.Message.V1 as Message
|
||||
|
||||
view =
|
||||
Message.large Message.Tip (Message.Plain "Two out of two parents agree: NoRedInk sounds like a fun place to work.")
|
||||
|
||||
-}
|
||||
large : Theme -> Content msg -> Html msg
|
||||
large theme content =
|
||||
let
|
||||
config =
|
||||
case theme of
|
||||
Error ->
|
||||
{ backgroundColor = Colors.purpleLight
|
||||
, fontColor = Colors.purpleDark
|
||||
, icon =
|
||||
UiIcon.exclamation
|
||||
|> NriSvg.withColor Colors.purple
|
||||
|> NriSvg.withLabel "Error"
|
||||
}
|
||||
|
||||
Alert ->
|
||||
{ backgroundColor = Colors.sunshine
|
||||
, fontColor = Colors.navy
|
||||
, icon =
|
||||
UiIcon.exclamation
|
||||
|> NriSvg.withColor Colors.ochre
|
||||
|> NriSvg.withLabel "Alert"
|
||||
}
|
||||
|
||||
Tip ->
|
||||
{ backgroundColor = Colors.sunshine
|
||||
, fontColor = Colors.navy
|
||||
, icon =
|
||||
UiIcon.bulb
|
||||
|> NriSvg.withColor Colors.navy
|
||||
|> NriSvg.withLabel "Tip"
|
||||
}
|
||||
|
||||
Success ->
|
||||
{ backgroundColor = Colors.greenLightest
|
||||
, fontColor = Colors.greenDarkest
|
||||
, icon =
|
||||
UiIcon.checkmarkInCircle
|
||||
|> NriSvg.withColor Colors.green
|
||||
|> NriSvg.withLabel "Success"
|
||||
}
|
||||
|
||||
Custom customTheme ->
|
||||
{ backgroundColor = customTheme.backgroundColor
|
||||
, fontColor = customTheme.color
|
||||
, icon = customTheme.icon
|
||||
}
|
||||
in
|
||||
Nri.Ui.styled div
|
||||
"Nri-Ui-Message-V1--large"
|
||||
[ width (pct 100)
|
||||
, backgroundColor config.backgroundColor
|
||||
, Fonts.baseFont
|
||||
, fontSize (px 15)
|
||||
, lineHeight (px 21)
|
||||
, fontWeight (int 600)
|
||||
, boxSizing borderBox
|
||||
, padding (px 20)
|
||||
, borderRadius (px 8)
|
||||
, color config.fontColor
|
||||
, displayFlex
|
||||
, alignItems center
|
||||
, Css.Global.descendants
|
||||
[ Css.Global.a
|
||||
[ textDecoration none
|
||||
, color Colors.azure
|
||||
, borderBottom3 (px 1) solid Colors.azure
|
||||
, visited [ color Colors.azure ]
|
||||
]
|
||||
]
|
||||
]
|
||||
[]
|
||||
[ styled div
|
||||
[ width (px 35)
|
||||
, marginRight (px 10)
|
||||
]
|
||||
[]
|
||||
[ NriSvg.toHtml config.icon
|
||||
]
|
||||
, styled div
|
||||
[ minWidth (px 100)
|
||||
, flexBasis (px 100)
|
||||
, flexGrow (int 1)
|
||||
]
|
||||
[]
|
||||
(contentToHtml content)
|
||||
]
|
||||
|
||||
|
||||
{-| PRIVATE
|
||||
-}
|
||||
type BannerAttribute msg
|
||||
= BannerAttribute (BannerConfig msg -> BannerConfig msg)
|
||||
|
||||
|
||||
{-| Adds a dismiss ("X" icon) to a banner which will produce the given `msg` when clicked.
|
||||
-}
|
||||
onDismiss : msg -> BannerAttribute msg
|
||||
onDismiss msg =
|
||||
BannerAttribute <|
|
||||
\config ->
|
||||
{ config | onDismiss = Just msg }
|
||||
|
||||
|
||||
{-| PRIVATE
|
||||
-}
|
||||
type alias BannerConfig msg =
|
||||
{ onDismiss : Maybe msg
|
||||
}
|
||||
|
||||
|
||||
{-| PRIVATE
|
||||
-}
|
||||
bannerConfigFromAttributes : List (BannerAttribute msg) -> BannerConfig msg
|
||||
bannerConfigFromAttributes attr =
|
||||
List.foldl (\(BannerAttribute set) -> set)
|
||||
{ onDismiss = Nothing }
|
||||
attr
|
||||
|
||||
|
||||
{-| Shows a banner alert message. This is even more prominent than `Message.large`.
|
||||
We commonly use these for flash messages at the top of pages.
|
||||
|
||||
import Nri.Ui.Message.V1 as Message
|
||||
|
||||
view =
|
||||
Message.banner Message.Success (Message.Plain "John Jacob Jingleheimer Schmidt has been dropped from First Period English.")
|
||||
|
||||
-}
|
||||
banner : Theme -> Content msg -> List (BannerAttribute msg) -> Html msg
|
||||
banner theme content attr =
|
||||
let
|
||||
config =
|
||||
case theme of
|
||||
Error ->
|
||||
{ backgroundColor = Colors.purpleLight
|
||||
, color = Colors.purpleDark
|
||||
, icon =
|
||||
UiIcon.exclamation
|
||||
|> NriSvg.withColor Colors.purple
|
||||
|> NriSvg.withLabel "Error"
|
||||
|> NriSvg.toHtml
|
||||
}
|
||||
|
||||
Alert ->
|
||||
{ backgroundColor = Colors.sunshine
|
||||
, color = Colors.navy
|
||||
, icon =
|
||||
UiIcon.exclamation
|
||||
|> NriSvg.withColor Colors.ochre
|
||||
|> NriSvg.withLabel "Alert"
|
||||
|> NriSvg.toHtml
|
||||
}
|
||||
|
||||
Tip ->
|
||||
{ backgroundColor = Colors.frost
|
||||
, color = Colors.navy
|
||||
, icon =
|
||||
inCircle
|
||||
{ backgroundColor = Colors.navy
|
||||
, color = Colors.mustard
|
||||
, height = Css.px 32
|
||||
, icon = UiIcon.bulb
|
||||
}
|
||||
}
|
||||
|
||||
Success ->
|
||||
{ backgroundColor = Colors.greenLightest
|
||||
, color = Colors.greenDarkest
|
||||
, icon =
|
||||
UiIcon.checkmarkInCircle
|
||||
|> NriSvg.withColor Colors.green
|
||||
|> NriSvg.withLabel "Success"
|
||||
|> NriSvg.toHtml
|
||||
}
|
||||
|
||||
Custom customTheme ->
|
||||
{ backgroundColor = customTheme.backgroundColor
|
||||
, color = customTheme.color
|
||||
, icon = NriSvg.toHtml customTheme.icon
|
||||
}
|
||||
|
||||
attributes =
|
||||
bannerConfigFromAttributes attr
|
||||
in
|
||||
styled div
|
||||
[ displayFlex
|
||||
, justifyContent center
|
||||
, alignItems center
|
||||
, backgroundColor config.backgroundColor
|
||||
, color config.color
|
||||
]
|
||||
[]
|
||||
[ styled span
|
||||
[ alignItems center
|
||||
, displayFlex
|
||||
, justifyContent center
|
||||
, padding (px 20)
|
||||
, width (Css.pct 100)
|
||||
, Css.Global.children
|
||||
[ Css.Global.button
|
||||
[ position relative
|
||||
, right (px 15)
|
||||
]
|
||||
]
|
||||
]
|
||||
[]
|
||||
[ styled div
|
||||
[ width (px 50)
|
||||
, height (px 50)
|
||||
, marginRight (px 20)
|
||||
, -- NOTE: I think it's normally best to avoid relying on flexShrink (and use flexGrow/flexBasis) instead,
|
||||
-- But using shrink here and on the next div lets us have the text content be centered rather than
|
||||
-- left-aligned when the content is shorter than one line
|
||||
flexShrink zero
|
||||
]
|
||||
[]
|
||||
[ config.icon ]
|
||||
, Nri.Ui.styled div
|
||||
"banner-alert-notification"
|
||||
[ fontSize (px 20)
|
||||
, fontWeight (int 700)
|
||||
, lineHeight (px 27)
|
||||
, maxWidth (px 600)
|
||||
, minWidth (px 100)
|
||||
, flexShrink (int 1)
|
||||
, Fonts.baseFont
|
||||
, Css.Global.descendants
|
||||
[ Css.Global.a
|
||||
[ textDecoration none
|
||||
, color Colors.azure
|
||||
, borderBottom3 (px 1) solid Colors.azure
|
||||
, visited [ color Colors.azure ]
|
||||
]
|
||||
]
|
||||
]
|
||||
[]
|
||||
(contentToHtml content)
|
||||
]
|
||||
, case attributes.onDismiss of
|
||||
Nothing ->
|
||||
text ""
|
||||
|
||||
Just msg ->
|
||||
bannerDismissButton msg
|
||||
]
|
||||
|
||||
|
||||
{-| Shows an appropriate error message for when something unhandled happened.
|
||||
|
||||
import Nri.Ui.Message.V1 as Message
|
||||
|
||||
view maybeDetailedErrorMessage =
|
||||
viewMaybe Message.somethingWentWrong maybeDetailedErrorMessage
|
||||
|
||||
-}
|
||||
somethingWentWrong : String -> Html msg
|
||||
somethingWentWrong errorMessageForEngineers =
|
||||
div []
|
||||
[ tiny Error (Plain "Sorry, something went wrong. Please try again later.")
|
||||
, details []
|
||||
[ summary
|
||||
[ css
|
||||
[ Fonts.baseFont
|
||||
, fontSize (px 14)
|
||||
, color Colors.gray45
|
||||
]
|
||||
]
|
||||
[ text "Details for NoRedInk engineers" ]
|
||||
, code
|
||||
[ css
|
||||
[ display block
|
||||
, whiteSpace normal
|
||||
, overflowWrap breakWord
|
||||
, color Colors.gray45
|
||||
, backgroundColor Colors.gray96
|
||||
, border3 (px 1) solid Colors.gray92
|
||||
, borderRadius (px 3)
|
||||
, padding2 (px 2) (px 4)
|
||||
, fontSize (px 12)
|
||||
, fontFamily monospace
|
||||
]
|
||||
]
|
||||
[ text errorMessageForEngineers ]
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
|
||||
--
|
||||
-- PRIVATE
|
||||
--
|
||||
|
||||
|
||||
inCircle :
|
||||
{ backgroundColor : Css.Color
|
||||
, color : Css.Color
|
||||
, height : Css.Px
|
||||
, icon : Svg
|
||||
}
|
||||
-> Html msg
|
||||
inCircle config =
|
||||
styled div
|
||||
[ borderRadius (pct 50)
|
||||
, height (pct 100)
|
||||
, backgroundColor config.backgroundColor
|
||||
, displayFlex
|
||||
, alignItems center
|
||||
, justifyContent center
|
||||
]
|
||||
[]
|
||||
[ config.icon
|
||||
|> NriSvg.withColor config.color
|
||||
|> NriSvg.withHeight config.height
|
||||
|> NriSvg.toHtml
|
||||
]
|
||||
|
||||
|
||||
bannerDismissButton : msg -> Html msg
|
||||
bannerDismissButton msg =
|
||||
Nri.Ui.styled div
|
||||
"dismiss-button-container"
|
||||
[ padding (px 25)
|
||||
]
|
||||
[]
|
||||
[ styled button
|
||||
[ borderWidth zero
|
||||
, backgroundColor unset
|
||||
, color Colors.azure
|
||||
, width (px 30)
|
||||
, height (px 30)
|
||||
, padding2 zero (px 7)
|
||||
, cursor pointer
|
||||
]
|
||||
[ onClick msg
|
||||
, Widget.label "Dismiss banner"
|
||||
]
|
||||
[ NriSvg.toHtml UiIcon.x
|
||||
]
|
||||
]
|
Loading…
Reference in New Issue
Block a user