mirror of
https://github.com/NoRedInk/noredink-ui.git
synced 2024-12-02 23:52:22 +03:00
Merge pull request #701 from NoRedInk/tessa/dependency-updates
New Version, to fix installation issues
This commit is contained in:
commit
e2b903ba6b
@ -1,20 +1,9 @@
|
||||
Nri.Ui.Accordion.V1,upgrade to V2
|
||||
Nri.Ui.Accordion.V1,upgrade to V3
|
||||
Nri.Ui.Button.V8,upgrade to V10
|
||||
Nri.Ui.ClickableSvg.V1,upgrade to V2
|
||||
Nri.Ui.Icon.V3,upgrade to V5
|
||||
Nri.Ui.Icon.V4,upgrade to V5
|
||||
Nri.Ui.InputStyles.V2,upgrade to V3
|
||||
Nri.Ui.Message.V1,upgrade to V3
|
||||
Nri.Ui.Message.V2,upgrade to V3
|
||||
Nri.Ui.Modal.V3,upgrade to V11
|
||||
Nri.Ui.Menu.V1,upgrade to V2
|
||||
Nri.Ui.Modal.V10,upgrade to V11
|
||||
Nri.Ui.RadioButton.V1,upgrade to V2
|
||||
Nri.Ui.SegmentedControl.V11,upgrade to V14
|
||||
Nri.Ui.SegmentedControl.V12,upgrade to V14
|
||||
Nri.Ui.SegmentedControl.V13,upgrade to V14
|
||||
Nri.Ui.Select.V5,upgrade to V7
|
||||
Nri.Ui.Table.V4,upgrade to V5
|
||||
Nri.Ui.Tabs.V6,upgrade to V7
|
||||
Nri.Ui.Text.V2,upgrade to V5
|
||||
Nri.Ui.Text.V4,upgrade to V5
|
||||
Nri.Ui.Tooltip.V1,upgrade to V2
|
||||
|
|
32
elm.json
32
elm.json
@ -7,7 +7,6 @@
|
||||
"exposed-modules": [
|
||||
"Nri.Ui",
|
||||
"Nri.Ui.Accordion.V1",
|
||||
"Nri.Ui.Accordion.V2",
|
||||
"Nri.Ui.Accordion.V3",
|
||||
"Nri.Ui.AssetPath",
|
||||
"Nri.Ui.AssignmentIcon.V2",
|
||||
@ -15,7 +14,6 @@
|
||||
"Nri.Ui.Button.V8",
|
||||
"Nri.Ui.Callout.V1",
|
||||
"Nri.Ui.Checkbox.V5",
|
||||
"Nri.Ui.ClickableSvg.V1",
|
||||
"Nri.Ui.ClickableSvg.V2",
|
||||
"Nri.Ui.ClickableText.V3",
|
||||
"Nri.Ui.Colors.Extra",
|
||||
@ -31,20 +29,13 @@
|
||||
"Nri.Ui.Heading.V2",
|
||||
"Nri.Ui.Html.Attributes.V2",
|
||||
"Nri.Ui.Html.V3",
|
||||
"Nri.Ui.Icon.V3",
|
||||
"Nri.Ui.Icon.V4",
|
||||
"Nri.Ui.Icon.V5",
|
||||
"Nri.Ui.InputStyles.V2",
|
||||
"Nri.Ui.InputStyles.V3",
|
||||
"Nri.Ui.Loading.V1",
|
||||
"Nri.Ui.Logo.V1",
|
||||
"Nri.Ui.MasteryIcon.V1",
|
||||
"Nri.Ui.Menu.V1",
|
||||
"Nri.Ui.Menu.V2",
|
||||
"Nri.Ui.Message.V1",
|
||||
"Nri.Ui.Message.V2",
|
||||
"Nri.Ui.Message.V3",
|
||||
"Nri.Ui.Modal.V3",
|
||||
"Nri.Ui.Modal.V10",
|
||||
"Nri.Ui.Modal.V11",
|
||||
"Nri.Ui.Page.V3",
|
||||
@ -53,9 +44,6 @@
|
||||
"Nri.Ui.PremiumCheckbox.V6",
|
||||
"Nri.Ui.RadioButton.V1",
|
||||
"Nri.Ui.RadioButton.V2",
|
||||
"Nri.Ui.SegmentedControl.V11",
|
||||
"Nri.Ui.SegmentedControl.V12",
|
||||
"Nri.Ui.SegmentedControl.V13",
|
||||
"Nri.Ui.SegmentedControl.V14",
|
||||
"Nri.Ui.Select.V5",
|
||||
"Nri.Ui.Select.V7",
|
||||
@ -68,8 +56,6 @@
|
||||
"Nri.Ui.Table.V5",
|
||||
"Nri.Ui.Tabs.V6",
|
||||
"Nri.Ui.Tabs.V7",
|
||||
"Nri.Ui.Text.V2",
|
||||
"Nri.Ui.Text.V4",
|
||||
"Nri.Ui.Text.V5",
|
||||
"Nri.Ui.Text.Writing.V1",
|
||||
"Nri.Ui.TextArea.V4",
|
||||
@ -80,8 +66,8 @@
|
||||
],
|
||||
"elm-version": "0.19.0 <= v < 0.20.0",
|
||||
"dependencies": {
|
||||
"BrianHicks/elm-particle": "1.3.1 <= v < 2.0.0",
|
||||
"elm/browser": "1.0.1 <= v < 2.0.0",
|
||||
"BrianHicks/elm-particle": "1.5.0 <= v < 2.0.0",
|
||||
"elm/browser": "1.0.2 <= v < 2.0.0",
|
||||
"elm/core": "1.0.1 <= v < 2.0.0",
|
||||
"elm/html": "1.0.0 <= v < 2.0.0",
|
||||
"elm/http": "2.0.0 <= v < 3.0.0",
|
||||
@ -89,18 +75,16 @@
|
||||
"elm/random": "1.0.0 <= v < 2.0.0",
|
||||
"elm/regex": "1.0.0 <= v < 2.0.0",
|
||||
"elm/svg": "1.0.1 <= v < 2.0.0",
|
||||
"elm-community/random-extra": "3.1.0 <= v < 4.0.0",
|
||||
"elm-community/random-extra": "3.2.0 <= v < 4.0.0",
|
||||
"elm-community/string-extra": "4.0.1 <= v < 5.0.0",
|
||||
"pablohirafuji/elm-markdown": "2.0.5 <= v < 3.0.0",
|
||||
"rtfeldman/elm-css": "16.1.0 <= v < 17.0.0",
|
||||
"tesk9/accessible-html": "4.0.0 <= v < 5.0.0",
|
||||
"tesk9/accessible-html-with-css": "2.1.1 <= v < 3.0.0",
|
||||
"tesk9/modal": "5.0.1 <= v < 6.0.0",
|
||||
"tesk9/palette": "2.0.0 <= v < 3.0.0",
|
||||
"wernerdegroot/listzipper": "3.1.1 <= v < 5.0.0"
|
||||
"rtfeldman/elm-css": "16.1.1 <= v < 17.0.0",
|
||||
"tesk9/accessible-html-with-css": "2.2.0 <= v < 3.0.0",
|
||||
"tesk9/palette": "3.0.1 <= v < 4.0.0"
|
||||
},
|
||||
"test-dependencies": {
|
||||
"avh4/elm-program-test": "3.3.0 <= v < 4.0.0",
|
||||
"elm-explorations/test": "1.2.2 <= v < 2.0.0"
|
||||
"elm-explorations/test": "1.2.2 <= v < 2.0.0",
|
||||
"tesk9/accessible-html": "4.1.0 <= v < 5.0.0"
|
||||
}
|
||||
}
|
||||
|
@ -35,15 +35,12 @@ hint = 'Use Accessibility.Widgetd.Widget'
|
||||
hint = 'Use Html.Styled'
|
||||
usages = [
|
||||
'styleguide-app/../src/Nri/Ui/Button/V8.elm',
|
||||
'styleguide-app/../src/Nri/Ui/Icon/V3.elm',
|
||||
'styleguide-app/../src/Nri/Ui/Icon/V4.elm',
|
||||
'styleguide-app/../src/Nri/Ui/Icon/V5.elm',
|
||||
'styleguide-app/Examples/Modal.elm',
|
||||
'styleguide-app/Main.elm',
|
||||
]
|
||||
|
||||
[forbidden."Nri.Ui.Accordion.V1"]
|
||||
hint = 'upgrade to V2'
|
||||
hint = 'upgrade to V3'
|
||||
|
||||
[forbidden."Nri.Ui.Button.V8"]
|
||||
hint = 'upgrade to V10'
|
||||
@ -63,6 +60,9 @@ hint = 'upgrade to V5'
|
||||
[forbidden."Nri.Ui.InputStyles.V2"]
|
||||
hint = 'upgrade to V3'
|
||||
|
||||
[forbidden."Nri.Ui.Menu.V1"]
|
||||
hint = 'upgrade to V2'
|
||||
|
||||
[forbidden."Nri.Ui.Message.V1"]
|
||||
hint = 'upgrade to V3'
|
||||
|
||||
@ -118,7 +118,4 @@ usages = [
|
||||
|
||||
[forbidden."Nri.Ui.Tooltip.V1"]
|
||||
hint = 'upgrade to V2'
|
||||
usages = [
|
||||
'styleguide-app/../src/Nri/Ui/ClickableSvg/V1.elm',
|
||||
'styleguide-app/../src/Nri/Ui/Menu/V1.elm',
|
||||
]
|
||||
usages = ['styleguide-app/../src/Nri/Ui/Menu/V1.elm']
|
||||
|
@ -1,383 +0,0 @@
|
||||
module Accessibility.Modal.Copy exposing
|
||||
( Model, init, subscriptions
|
||||
, update, Msg, close, open
|
||||
, view
|
||||
, Attribute
|
||||
, multipleFocusableElementView, onlyFocusableElementView
|
||||
, autofocusOnLastElement
|
||||
, overlayColor, custom, titleStyles
|
||||
)
|
||||
|
||||
{-| COPIED from <https://package.elm-lang.org/packages/tesk9/modal/latest/> for
|
||||
clean monolith upgrades. Remove this and go back to using tesk9/modal when possible!
|
||||
|
||||
import Accessibility.Modal as Modal
|
||||
import Html.Styled exposing (..)
|
||||
import Html.Styled.Events exposing (onClick)
|
||||
|
||||
view : Html Modal.Msg
|
||||
view =
|
||||
Modal.view identity
|
||||
"Example modal"
|
||||
[ Modal.onlyFocusableElementView
|
||||
(\onlyFocusableElementAttributes ->
|
||||
div []
|
||||
[ text "Welcome to this modal! I'm so happy to have you here with me."
|
||||
, button
|
||||
(onClick Modal.close :: onlyFocusableElementAttributes)
|
||||
[ text "Close Modal" ]
|
||||
]
|
||||
)
|
||||
]
|
||||
modal
|
||||
|
||||
@docs Model, init, subscriptions
|
||||
@docs update, Msg, close, open
|
||||
@docs view
|
||||
|
||||
@docs Attribute
|
||||
@docs multipleFocusableElementView, onlyFocusableElementView
|
||||
@docs autofocusOnLastElement
|
||||
@docs overlayColor, custom, titleStyles
|
||||
|
||||
-}
|
||||
|
||||
import Accessibility.Styled exposing (..)
|
||||
import Accessibility.Styled.Aria as Aria
|
||||
import Accessibility.Styled.Key as Key
|
||||
import Accessibility.Styled.Role as Role
|
||||
import Browser
|
||||
import Browser.Dom as Dom
|
||||
import Browser.Events
|
||||
import Css exposing (..)
|
||||
import Html.Styled as Root
|
||||
import Html.Styled.Attributes as Attributes exposing (css, id)
|
||||
import Html.Styled.Events exposing (onClick)
|
||||
import Task
|
||||
|
||||
|
||||
{-| -}
|
||||
type Model
|
||||
= Opened String
|
||||
| Closed
|
||||
|
||||
|
||||
{-| -}
|
||||
init : Model
|
||||
init =
|
||||
Closed
|
||||
|
||||
|
||||
type By
|
||||
= EscapeKey
|
||||
| OverlayClick
|
||||
| Other
|
||||
|
||||
|
||||
{-| -}
|
||||
type Msg
|
||||
= OpenModal String
|
||||
| CloseModal By
|
||||
| Focus String
|
||||
| Focused (Result Dom.Error ())
|
||||
|
||||
|
||||
{-| -}
|
||||
update : { dismissOnEscAndOverlayClick : Bool } -> Msg -> Model -> ( Model, Cmd Msg )
|
||||
update { dismissOnEscAndOverlayClick } msg model =
|
||||
case msg of
|
||||
OpenModal returnFocusTo ->
|
||||
( Opened returnFocusTo
|
||||
, Dom.focus autofocusId
|
||||
|> Task.onError (\_ -> Dom.focus firstId)
|
||||
|> Task.attempt Focused
|
||||
)
|
||||
|
||||
CloseModal by ->
|
||||
let
|
||||
closeModal returnFocusTo =
|
||||
( Closed, Task.attempt Focused (Dom.focus returnFocusTo) )
|
||||
in
|
||||
case ( model, by, dismissOnEscAndOverlayClick ) of
|
||||
( Opened returnFocusTo, _, True ) ->
|
||||
closeModal returnFocusTo
|
||||
|
||||
( Opened returnFocusTo, Other, False ) ->
|
||||
closeModal returnFocusTo
|
||||
|
||||
_ ->
|
||||
( model, Cmd.none )
|
||||
|
||||
Focus id ->
|
||||
( model, Task.attempt Focused (Dom.focus id) )
|
||||
|
||||
Focused _ ->
|
||||
( model, Cmd.none )
|
||||
|
||||
|
||||
type Autofocus
|
||||
= Default
|
||||
| Last
|
||||
|
||||
|
||||
type alias Config msg =
|
||||
{ overlayColor : Color
|
||||
, wrapMsg : Msg -> msg
|
||||
, modalStyle : Style
|
||||
, titleString : String
|
||||
, titleStyles : List Style
|
||||
, autofocusOn : Autofocus
|
||||
, content :
|
||||
{ onlyFocusableElement : List (Accessibility.Styled.Attribute msg)
|
||||
, firstFocusableElement : List (Accessibility.Styled.Attribute msg)
|
||||
, lastFocusableElement : List (Accessibility.Styled.Attribute msg)
|
||||
, autofocusOn : Accessibility.Styled.Attribute msg
|
||||
}
|
||||
-> Html msg
|
||||
}
|
||||
|
||||
|
||||
defaults : (Msg -> msg) -> String -> Config msg
|
||||
defaults wrapMsg t =
|
||||
{ overlayColor = rgba 128 0 70 0.7
|
||||
, wrapMsg = wrapMsg
|
||||
, modalStyle =
|
||||
batch
|
||||
[ backgroundColor (rgb 255 255 255)
|
||||
, borderRadius (px 8)
|
||||
, border3 (px 2) solid (rgb 127 0 127)
|
||||
, margin2 (px 80) auto
|
||||
, padding (px 20)
|
||||
, maxWidth (px 600)
|
||||
, minHeight (vh 40)
|
||||
]
|
||||
, titleString = t
|
||||
, titleStyles = []
|
||||
, autofocusOn = Default
|
||||
, content = \_ -> text ""
|
||||
}
|
||||
|
||||
|
||||
{-| -}
|
||||
type Attribute msg
|
||||
= Attribute (Config msg -> Config msg)
|
||||
|
||||
|
||||
{-| -}
|
||||
overlayColor : Color -> Attribute msg
|
||||
overlayColor color =
|
||||
Attribute (\config -> { config | overlayColor = color })
|
||||
|
||||
|
||||
{-| -}
|
||||
title : String -> Attribute msg
|
||||
title t =
|
||||
Attribute (\config -> { config | titleString = t })
|
||||
|
||||
|
||||
{-| -}
|
||||
titleStyles : List Style -> Attribute msg
|
||||
titleStyles styles =
|
||||
Attribute (\config -> { config | titleStyles = styles })
|
||||
|
||||
|
||||
{-| -}
|
||||
custom : List Style -> Attribute msg
|
||||
custom styles =
|
||||
Attribute (\config -> { config | modalStyle = batch styles })
|
||||
|
||||
|
||||
{-| -}
|
||||
autofocusOnLastElement : Attribute msg
|
||||
autofocusOnLastElement =
|
||||
Attribute (\config -> { config | autofocusOn = Last })
|
||||
|
||||
|
||||
{-| -}
|
||||
onlyFocusableElementView : (List (Accessibility.Styled.Attribute msg) -> Html msg) -> Attribute msg
|
||||
onlyFocusableElementView v =
|
||||
Attribute (\config -> { config | content = \{ onlyFocusableElement } -> v onlyFocusableElement })
|
||||
|
||||
|
||||
{-| -}
|
||||
multipleFocusableElementView :
|
||||
({ firstFocusableElement : List (Accessibility.Styled.Attribute msg)
|
||||
, lastFocusableElement : List (Accessibility.Styled.Attribute msg)
|
||||
, autofocusElement : Accessibility.Styled.Attribute msg
|
||||
}
|
||||
-> Html msg
|
||||
)
|
||||
-> Attribute msg
|
||||
multipleFocusableElementView v =
|
||||
Attribute
|
||||
(\config ->
|
||||
{ config
|
||||
| content =
|
||||
\{ firstFocusableElement, lastFocusableElement, autofocusOn } ->
|
||||
v
|
||||
{ firstFocusableElement = firstFocusableElement
|
||||
, lastFocusableElement = lastFocusableElement
|
||||
, autofocusElement = autofocusOn
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
{-| -}
|
||||
view :
|
||||
(Msg -> msg)
|
||||
-> String
|
||||
-> List (Attribute msg)
|
||||
-> Model
|
||||
-> Html msg
|
||||
view wrapMsg ti attributes model =
|
||||
let
|
||||
config =
|
||||
List.foldl (\(Attribute f) acc -> f acc) (defaults wrapMsg ti) attributes
|
||||
in
|
||||
case model of
|
||||
Opened _ ->
|
||||
div
|
||||
[ css
|
||||
[ position fixed
|
||||
, top zero
|
||||
, left zero
|
||||
, width (pct 100)
|
||||
, height (pct 100)
|
||||
, displayFlex
|
||||
, alignItems center
|
||||
]
|
||||
]
|
||||
[ viewBackdrop config
|
||||
, div
|
||||
[ css [ position relative, config.modalStyle ] ]
|
||||
[ viewModal config ]
|
||||
, Root.node "style" [] [ Root.text "body {overflow: hidden;} " ]
|
||||
]
|
||||
|
||||
Closed ->
|
||||
text ""
|
||||
|
||||
|
||||
viewBackdrop :
|
||||
{ a | wrapMsg : Msg -> msg, overlayColor : Color }
|
||||
-> Html msg
|
||||
viewBackdrop config =
|
||||
Root.div
|
||||
-- We use Root html here in order to allow clicking to exit out of
|
||||
-- the overlay. This behavior is available to non-mouse users as
|
||||
-- well via the ESC key, so imo it's fine to have this div
|
||||
-- be clickable but not focusable.
|
||||
[ css
|
||||
[ position absolute
|
||||
, width (pct 100)
|
||||
, height (pct 100)
|
||||
, backgroundColor config.overlayColor
|
||||
]
|
||||
, onClick (config.wrapMsg (CloseModal OverlayClick))
|
||||
]
|
||||
[]
|
||||
|
||||
|
||||
viewModal : Config msg -> Html msg
|
||||
viewModal config =
|
||||
section
|
||||
[ Role.dialog
|
||||
, Aria.labeledBy modalTitleId
|
||||
]
|
||||
[ h1 [ id modalTitleId, css config.titleStyles ] [ text config.titleString ]
|
||||
, config.content
|
||||
(case config.autofocusOn of
|
||||
Last ->
|
||||
{ onlyFocusableElement =
|
||||
[ Key.onKeyDown
|
||||
[ Key.tabBack (Focus firstId)
|
||||
, Key.tab (Focus firstId)
|
||||
]
|
||||
, id firstId
|
||||
]
|
||||
|> List.map (Attributes.map config.wrapMsg)
|
||||
, firstFocusableElement =
|
||||
[ Key.onKeyDown [ Key.tabBack (Focus autofocusId) ]
|
||||
, id firstId
|
||||
]
|
||||
|> List.map (Attributes.map config.wrapMsg)
|
||||
, lastFocusableElement =
|
||||
[ Key.onKeyDown [ Key.tab (Focus firstId) ]
|
||||
, id autofocusId
|
||||
]
|
||||
|> List.map (Attributes.map config.wrapMsg)
|
||||
, autofocusOn =
|
||||
id autofocusId
|
||||
|> Attributes.map config.wrapMsg
|
||||
}
|
||||
|
||||
_ ->
|
||||
{ onlyFocusableElement =
|
||||
[ Key.onKeyDown
|
||||
[ Key.tabBack (Focus firstId)
|
||||
, Key.tab (Focus firstId)
|
||||
]
|
||||
, id firstId
|
||||
]
|
||||
|> List.map (Attributes.map config.wrapMsg)
|
||||
, firstFocusableElement =
|
||||
[ Key.onKeyDown [ Key.tabBack (Focus lastId) ]
|
||||
, id firstId
|
||||
]
|
||||
|> List.map (Attributes.map config.wrapMsg)
|
||||
, lastFocusableElement =
|
||||
[ Key.onKeyDown [ Key.tab (Focus firstId) ]
|
||||
, id lastId
|
||||
]
|
||||
|> List.map (Attributes.map config.wrapMsg)
|
||||
, autofocusOn =
|
||||
id autofocusId
|
||||
|> Attributes.map config.wrapMsg
|
||||
}
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
modalTitleId : String
|
||||
modalTitleId =
|
||||
"modal__title"
|
||||
|
||||
|
||||
firstId : String
|
||||
firstId =
|
||||
"modal__first-focusable-element"
|
||||
|
||||
|
||||
lastId : String
|
||||
lastId =
|
||||
"modal__last-focusable-element"
|
||||
|
||||
|
||||
autofocusId : String
|
||||
autofocusId =
|
||||
"modal__autofocus-element"
|
||||
|
||||
|
||||
{-| Pass the id of the element that should receive focus when the modal closes.
|
||||
-}
|
||||
open : String -> Msg
|
||||
open =
|
||||
OpenModal
|
||||
|
||||
|
||||
{-| -}
|
||||
close : Msg
|
||||
close =
|
||||
CloseModal Other
|
||||
|
||||
|
||||
{-| -}
|
||||
subscriptions : Model -> Sub Msg
|
||||
subscriptions model =
|
||||
case model of
|
||||
Opened _ ->
|
||||
Browser.Events.onKeyDown (Key.escape (CloseModal EscapeKey))
|
||||
|
||||
Closed ->
|
||||
Sub.none
|
@ -1,13 +0,0 @@
|
||||
module List.Zipper.Extra exposing (from)
|
||||
|
||||
{-| migration module from List.Zipper 3.2 -> 4. We can drop this when we drop < 3.2 support
|
||||
-}
|
||||
|
||||
import List.Zipper
|
||||
|
||||
|
||||
from : List a -> a -> List a -> List.Zipper.Zipper a
|
||||
from before current after =
|
||||
List.Zipper.singleton current
|
||||
|> List.Zipper.mapBefore (always before)
|
||||
|> List.Zipper.mapAfter (always after)
|
@ -1,305 +0,0 @@
|
||||
module Nri.Ui.Accordion.V2 exposing (view, Caret(..), StyleOptions, HeaderLevel(..))
|
||||
|
||||
{-| Changes from V1:
|
||||
|
||||
- Combine view and viewKeyed so that nodes are always keyed
|
||||
- Removes viewCaret from the API -- it's possible to use the DisclosureIndicator directly
|
||||
- Changed implementation to follow recommendations from <https://www.w3.org/TR/wai-aria-practices-1.1/examples/accordion/accordion.html>
|
||||
- Adds Up and Down keyboard handling for navigating between the accordions
|
||||
- Adds aria-expanded and aria-controls
|
||||
- Changes accordion container to a section
|
||||
- removes tablist/tab/tabpanel roles
|
||||
- Adds heading levels for the accordion header (including style resets)
|
||||
|
||||
@docs view, Caret, StyleOptions, HeaderLevel
|
||||
|
||||
-}
|
||||
|
||||
import Accessibility.Styled exposing (Attribute, Html, button, div, section, text)
|
||||
import Accessibility.Styled.Aria as Aria
|
||||
import Accessibility.Styled.Key as Key
|
||||
import Accessibility.Styled.Role as Role
|
||||
import Accessibility.Styled.Widget as Widget
|
||||
import Css exposing (..)
|
||||
import Css.Global
|
||||
import Html.Styled.Attributes as Attributes
|
||||
import Html.Styled.Events exposing (onClick)
|
||||
import Html.Styled.Keyed
|
||||
import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.DisclosureIndicator.V2 as DisclosureIndicator
|
||||
import Nri.Ui.Fonts.V1 as Fonts
|
||||
|
||||
|
||||
{-| -}
|
||||
type alias StyleOptions =
|
||||
{ entryStyles : List Style
|
||||
, entryExpandedStyles : List Style
|
||||
, entryClosedStyles : List Style
|
||||
, headerStyles : List Style
|
||||
, headerExpandedStyles : List Style
|
||||
, headerClosedStyles : List Style
|
||||
, contentStyles : List Style
|
||||
}
|
||||
|
||||
|
||||
defaultStyleOptions : StyleOptions
|
||||
defaultStyleOptions =
|
||||
{ entryStyles =
|
||||
[ position relative
|
||||
, marginBottom (px 10)
|
||||
]
|
||||
, entryExpandedStyles = []
|
||||
, entryClosedStyles = []
|
||||
, headerStyles =
|
||||
[ displayFlex
|
||||
, alignItems center
|
||||
, boxSizing borderBox
|
||||
, minWidth (pct 100)
|
||||
, overflow hidden
|
||||
, padding2 (px 8) (px 15)
|
||||
, backgroundColor Colors.gray96
|
||||
|
||||
-- button resets
|
||||
, cursor pointer
|
||||
, borderWidth Css.zero
|
||||
, margin zero
|
||||
|
||||
-- fonts & text
|
||||
, textAlign left
|
||||
, Fonts.baseFont
|
||||
, fontSize (px 16)
|
||||
, fontWeight (int 600)
|
||||
, lineHeight (num 1.2)
|
||||
]
|
||||
, headerExpandedStyles =
|
||||
[ color Colors.navy
|
||||
, borderRadius4 (px 8) (px 8) (px 0) (px 0)
|
||||
]
|
||||
, headerClosedStyles =
|
||||
[ color Colors.azure
|
||||
, borderRadius (px 8)
|
||||
]
|
||||
, contentStyles = [ overflow hidden ]
|
||||
}
|
||||
|
||||
|
||||
{-| DefaultCaret is the blue caret
|
||||
-}
|
||||
type Caret
|
||||
= DefaultCaret
|
||||
| WhiteCaret
|
||||
| NoneCaret
|
||||
|
||||
|
||||
{-| Corresponds to h1, h2, h3 etc.
|
||||
Choose the correct header level given your page context.
|
||||
-}
|
||||
type HeaderLevel
|
||||
= H1
|
||||
| H2
|
||||
| H3
|
||||
| H4
|
||||
| H5
|
||||
| H6
|
||||
|
||||
|
||||
header : HeaderLevel -> Html msg -> Html msg
|
||||
header headerLevel content =
|
||||
let
|
||||
resets =
|
||||
[ margin zero
|
||||
, padding zero
|
||||
]
|
||||
in
|
||||
case headerLevel of
|
||||
H1 ->
|
||||
Accessibility.Styled.h1 [ Attributes.css resets ] [ content ]
|
||||
|
||||
H2 ->
|
||||
Accessibility.Styled.h2 [ Attributes.css resets ] [ content ]
|
||||
|
||||
H3 ->
|
||||
Accessibility.Styled.h3 [ Attributes.css resets ] [ content ]
|
||||
|
||||
H4 ->
|
||||
Accessibility.Styled.h4 [ Attributes.css resets ] [ content ]
|
||||
|
||||
H5 ->
|
||||
Accessibility.Styled.h5 [ Attributes.css resets ] [ content ]
|
||||
|
||||
H6 ->
|
||||
Accessibility.Styled.h6 [ Attributes.css resets ] [ content ]
|
||||
|
||||
|
||||
{-| -}
|
||||
view :
|
||||
{ entries : List { headerId : String, entry : entry, isExpanded : Bool }
|
||||
, headerLevel : HeaderLevel
|
||||
, viewHeader : entry -> Html msg
|
||||
, viewContent : entry -> Html msg
|
||||
, customStyles : Maybe (entry -> StyleOptions)
|
||||
, caret : Caret
|
||||
, toggle : entry -> Bool -> msg
|
||||
, focus : String -> msg
|
||||
}
|
||||
-> Html msg
|
||||
view { entries, headerLevel, viewHeader, viewContent, customStyles, caret, toggle, focus } =
|
||||
let
|
||||
arrowUpIds : List (Maybe String)
|
||||
arrowUpIds =
|
||||
lastHeaderId :: List.map (.headerId >> Just) entries
|
||||
|
||||
lastHeaderId : Maybe String
|
||||
lastHeaderId =
|
||||
Maybe.map .headerId (List.head (List.reverse entries))
|
||||
in
|
||||
div
|
||||
[ Attributes.class "accordion"
|
||||
, Attributes.attribute "aria-live" "polite"
|
||||
]
|
||||
[ Html.Styled.Keyed.node "div"
|
||||
[]
|
||||
(entries
|
||||
|> List.map2 (\id nextEntry -> ( id, nextEntry )) arrowUpIds
|
||||
|> List.foldr
|
||||
(\( previousId, { headerId, entry, isExpanded } ) ( nextId, acc ) ->
|
||||
let
|
||||
node =
|
||||
( "keyed-section__" ++ headerId
|
||||
, viewEntry
|
||||
{ headerId = headerId
|
||||
, headerLevel = headerLevel
|
||||
, viewHeader = viewHeader
|
||||
, viewContent = viewContent
|
||||
, styleOptions = customStyles
|
||||
, caret = caret
|
||||
, toggle = toggle
|
||||
, arrowUp = Maybe.map focus previousId
|
||||
, arrowDown = Maybe.map focus nextId
|
||||
, entry = entry
|
||||
, isExpanded = isExpanded
|
||||
}
|
||||
)
|
||||
in
|
||||
( Just headerId
|
||||
, node :: acc
|
||||
)
|
||||
)
|
||||
( Maybe.map .headerId (List.head entries), [] )
|
||||
|> Tuple.second
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
viewEntry :
|
||||
{ headerId : String
|
||||
, headerLevel : HeaderLevel
|
||||
, viewHeader : entry -> Html msg
|
||||
, viewContent : entry -> Html msg
|
||||
, styleOptions : Maybe (entry -> StyleOptions)
|
||||
, caret : Caret
|
||||
, toggle : entry -> Bool -> msg
|
||||
, arrowUp : Maybe msg
|
||||
, arrowDown : Maybe msg
|
||||
, entry : entry
|
||||
, isExpanded : Bool
|
||||
}
|
||||
-> Html msg
|
||||
viewEntry { headerId, headerLevel, viewHeader, viewContent, styleOptions, caret, toggle, entry, isExpanded, arrowDown, arrowUp } =
|
||||
let
|
||||
newStyleOptions =
|
||||
case Maybe.map (\toStyles -> toStyles entry) styleOptions of
|
||||
Just { entryStyles, entryExpandedStyles, entryClosedStyles, headerStyles, headerExpandedStyles, headerClosedStyles, contentStyles } ->
|
||||
{ entryStyles = defaultStyleOptions.entryStyles ++ entryStyles
|
||||
, entryExpandedStyles = defaultStyleOptions.entryExpandedStyles ++ entryExpandedStyles
|
||||
, entryClosedStyles = defaultStyleOptions.entryClosedStyles ++ entryClosedStyles
|
||||
, headerStyles = defaultStyleOptions.headerStyles ++ headerStyles
|
||||
, headerExpandedStyles = defaultStyleOptions.headerExpandedStyles ++ headerExpandedStyles
|
||||
, headerClosedStyles = defaultStyleOptions.headerClosedStyles ++ headerClosedStyles
|
||||
, contentStyles = defaultStyleOptions.contentStyles ++ contentStyles
|
||||
}
|
||||
|
||||
Nothing ->
|
||||
defaultStyleOptions
|
||||
|
||||
styles =
|
||||
if isExpanded then
|
||||
{ entry = newStyleOptions.entryStyles ++ newStyleOptions.entryExpandedStyles
|
||||
, header = newStyleOptions.headerStyles ++ newStyleOptions.headerExpandedStyles
|
||||
, content = newStyleOptions.contentStyles
|
||||
}
|
||||
|
||||
else
|
||||
{ entry = newStyleOptions.entryStyles ++ newStyleOptions.entryClosedStyles
|
||||
, header = newStyleOptions.headerStyles ++ newStyleOptions.headerClosedStyles
|
||||
, content = [ maxHeight (px 0) ]
|
||||
}
|
||||
|
||||
panelId =
|
||||
"accordion-panel__" ++ headerId
|
||||
in
|
||||
div
|
||||
[ entryClass isExpanded
|
||||
, Attributes.css styles.entry
|
||||
]
|
||||
[ header headerLevel <|
|
||||
button
|
||||
[ Attributes.id headerId
|
||||
, Attributes.class "accordion-entry-header"
|
||||
, Attributes.css styles.header
|
||||
, Widget.expanded isExpanded
|
||||
, Aria.controls panelId
|
||||
, onClick (toggle entry (not isExpanded))
|
||||
, Key.onKeyDown
|
||||
(List.filterMap identity
|
||||
[ Maybe.map Key.up arrowUp
|
||||
, Maybe.map Key.down arrowDown
|
||||
]
|
||||
)
|
||||
]
|
||||
[ viewCaret isExpanded caret
|
||||
, viewHeader entry
|
||||
]
|
||||
, section
|
||||
[ Attributes.id panelId
|
||||
, Aria.labelledBy headerId
|
||||
, Attributes.class "accordion-entry-panel"
|
||||
, Attributes.hidden (not isExpanded)
|
||||
, Attributes.css styles.content
|
||||
]
|
||||
[ viewContent entry ]
|
||||
]
|
||||
|
||||
|
||||
{-| Used for tests that rely on classnames, and couple edge cases where we need to override styles using sass
|
||||
-}
|
||||
entryClass : Bool -> Attribute msg
|
||||
entryClass expanded =
|
||||
Attributes.classList
|
||||
[ ( "accordion-entry", True )
|
||||
, ( "accordion-entry-state-expanded", expanded )
|
||||
, ( "accordion-entry-state-collapsed", not expanded )
|
||||
]
|
||||
|
||||
|
||||
{-| Just the caret!
|
||||
-}
|
||||
viewCaret : Bool -> Caret -> Html msg
|
||||
viewCaret expanded caret =
|
||||
case caret of
|
||||
DefaultCaret ->
|
||||
DisclosureIndicator.large
|
||||
[ marginRight (px 8)
|
||||
]
|
||||
expanded
|
||||
|
||||
WhiteCaret ->
|
||||
DisclosureIndicator.large
|
||||
[ marginRight (px 8)
|
||||
, Css.Global.descendants
|
||||
[ Css.Global.everything [ color Colors.white ] ]
|
||||
]
|
||||
expanded
|
||||
|
||||
NoneCaret ->
|
||||
text ""
|
@ -1,598 +0,0 @@
|
||||
module Nri.Ui.ClickableSvg.V1 exposing
|
||||
( button, link
|
||||
, Attribute
|
||||
, onClick
|
||||
, href, linkSpa, linkExternal, linkWithMethod, linkWithTracking, linkExternalWithTracking
|
||||
, width, height
|
||||
, disabled
|
||||
, withBorder
|
||||
, primary, secondary, danger, dangerSecondary
|
||||
, custom, css
|
||||
, withTooltipAbove, withTooltipBelow
|
||||
)
|
||||
|
||||
{-|
|
||||
|
||||
|
||||
# Post-release patches
|
||||
|
||||
- uses ClickableAttributes
|
||||
- adds tooltip helpers
|
||||
|
||||
|
||||
# Create a button or link
|
||||
|
||||
@docs button, link
|
||||
@docs Attribute
|
||||
|
||||
|
||||
## Behavior
|
||||
|
||||
@docs onClick
|
||||
@docs href, linkSpa, linkExternal, linkWithMethod, linkWithTracking, linkExternalWithTracking
|
||||
|
||||
|
||||
## Sizing
|
||||
|
||||
@docs width, height
|
||||
|
||||
|
||||
## State
|
||||
|
||||
@docs disabled
|
||||
|
||||
|
||||
## Customization
|
||||
|
||||
@docs withBorder
|
||||
@docs primary, secondary, danger, dangerSecondary
|
||||
|
||||
@docs custom, css
|
||||
|
||||
|
||||
## Tooltips
|
||||
|
||||
@docs withTooltipAbove, withTooltipBelow
|
||||
|
||||
-}
|
||||
|
||||
import Accessibility.Styled.Widget as Widget
|
||||
import ClickableAttributes exposing (ClickableAttributes)
|
||||
import Css exposing (Color, Style)
|
||||
import Html.Styled as Html exposing (Html)
|
||||
import Html.Styled.Attributes as Attributes
|
||||
import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.Svg.V1 as Svg exposing (Svg)
|
||||
import Nri.Ui.Tooltip.V1 as Tooltip exposing (Tooltip)
|
||||
|
||||
|
||||
{-| -}
|
||||
type Attribute msg
|
||||
= Attribute (ButtonOrLink msg -> ButtonOrLink msg)
|
||||
|
||||
|
||||
{-| -}
|
||||
button : String -> Svg -> List (Attribute msg) -> Html msg
|
||||
button name icon attributes =
|
||||
attributes
|
||||
|> List.foldl (\(Attribute attribute) b -> attribute b) (build name icon)
|
||||
|> renderButton
|
||||
|
||||
|
||||
{-| -}
|
||||
link : String -> Svg -> List (Attribute msg) -> Html msg
|
||||
link name icon attributes =
|
||||
attributes
|
||||
|> List.foldl (\(Attribute attribute) b -> attribute b) (build name icon)
|
||||
|> renderLink
|
||||
|
||||
|
||||
|
||||
-- LINKING, CLICKING, and TRACKING BEHAVIOR
|
||||
|
||||
|
||||
setClickableAttributes :
|
||||
(ClickableAttributes msg -> ClickableAttributes msg)
|
||||
-> Attribute msg
|
||||
setClickableAttributes apply =
|
||||
set
|
||||
(\attributes ->
|
||||
{ attributes | clickableAttributes = apply attributes.clickableAttributes }
|
||||
)
|
||||
|
||||
|
||||
{-| -}
|
||||
onClick : msg -> Attribute msg
|
||||
onClick msg =
|
||||
setClickableAttributes (ClickableAttributes.onClick msg)
|
||||
|
||||
|
||||
{-| -}
|
||||
href : String -> Attribute msg
|
||||
href url =
|
||||
setClickableAttributes (ClickableAttributes.href url)
|
||||
|
||||
|
||||
{-| Use this link for routing within a single page app.
|
||||
|
||||
This will make a normal <a> tag, but change the Events.onClick behavior to avoid reloading the page.
|
||||
|
||||
See <https://github.com/elm-lang/html/issues/110> for details on this implementation.
|
||||
|
||||
-}
|
||||
linkSpa : String -> Attribute msg
|
||||
linkSpa url =
|
||||
setClickableAttributes (ClickableAttributes.linkSpa url)
|
||||
|
||||
|
||||
{-| -}
|
||||
linkWithMethod : { method : String, url : String } -> Attribute msg
|
||||
linkWithMethod config =
|
||||
setClickableAttributes (ClickableAttributes.linkWithMethod config)
|
||||
|
||||
|
||||
{-| -}
|
||||
linkWithTracking : { track : msg, url : String } -> Attribute msg
|
||||
linkWithTracking config =
|
||||
setClickableAttributes (ClickableAttributes.linkWithTracking config)
|
||||
|
||||
|
||||
{-| -}
|
||||
linkExternal : String -> Attribute msg
|
||||
linkExternal url =
|
||||
setClickableAttributes (ClickableAttributes.linkExternal url)
|
||||
|
||||
|
||||
{-| -}
|
||||
linkExternalWithTracking : { track : msg, url : String } -> Attribute msg
|
||||
linkExternalWithTracking config =
|
||||
setClickableAttributes (ClickableAttributes.linkExternalWithTracking config)
|
||||
|
||||
|
||||
|
||||
-- SIZING
|
||||
|
||||
|
||||
{-| Default width is 17px.
|
||||
|
||||
Note: changing this width will change the width of the icon. The button or link
|
||||
may be wider if you add a border or margin to it.
|
||||
|
||||
-}
|
||||
width : Css.Px -> Attribute msg
|
||||
width px =
|
||||
set (\attributes -> { attributes | width = px })
|
||||
|
||||
|
||||
{-| Default height is 17px.
|
||||
|
||||
Note: changing this height will change the height of the icon. The button or link
|
||||
may be taller if you add a border or margin to it.
|
||||
|
||||
-}
|
||||
height : Css.Px -> Attribute msg
|
||||
height px =
|
||||
set (\attributes -> { attributes | height = px })
|
||||
|
||||
|
||||
|
||||
-- STATE
|
||||
|
||||
|
||||
{-| -}
|
||||
disabled : Bool -> Attribute msg
|
||||
disabled disabled_ =
|
||||
set (\attributes -> { attributes | disabled = disabled_ })
|
||||
|
||||
|
||||
|
||||
-- CUSTOMIZATION
|
||||
|
||||
|
||||
{-| Display a border around the icon.
|
||||
-}
|
||||
withBorder : Attribute msg
|
||||
withBorder =
|
||||
set (\config -> { config | hasBorder = True })
|
||||
|
||||
|
||||
type Theme
|
||||
= Primary
|
||||
| Secondary
|
||||
| Danger
|
||||
| DangerSecondary
|
||||
|
||||
|
||||
type alias AppliedTheme =
|
||||
{ main_ : Color
|
||||
, mainHovered : Color
|
||||
, background : Color
|
||||
, backgroundHovered : Color
|
||||
}
|
||||
|
||||
|
||||
disabledTheme : AppliedTheme
|
||||
disabledTheme =
|
||||
{ main_ = Colors.gray75
|
||||
, mainHovered = Colors.gray75
|
||||
, background = Colors.white
|
||||
, backgroundHovered = Colors.white
|
||||
}
|
||||
|
||||
|
||||
applyTheme : Theme -> AppliedTheme
|
||||
applyTheme theme =
|
||||
case theme of
|
||||
Primary ->
|
||||
{ main_ = Colors.white
|
||||
, mainHovered = Colors.white
|
||||
, background = Colors.azure
|
||||
, backgroundHovered = Colors.azureDark
|
||||
}
|
||||
|
||||
Secondary ->
|
||||
{ main_ = Colors.azure
|
||||
, mainHovered = Colors.azureDark
|
||||
, background = Colors.white
|
||||
, backgroundHovered = Colors.glacier
|
||||
}
|
||||
|
||||
Danger ->
|
||||
{ main_ = Colors.white
|
||||
, mainHovered = Colors.white
|
||||
, background = Colors.red
|
||||
, backgroundHovered = Colors.redDark
|
||||
}
|
||||
|
||||
DangerSecondary ->
|
||||
{ main_ = Colors.red
|
||||
, mainHovered = Colors.redDark
|
||||
, background = Colors.white
|
||||
, backgroundHovered = Colors.redLight
|
||||
}
|
||||
|
||||
|
||||
{-| white/transparent icon on an azure background.
|
||||
-}
|
||||
primary : Attribute msg
|
||||
primary =
|
||||
set (\attributes -> { attributes | theme = Primary })
|
||||
|
||||
|
||||
{-| This is the default: a blue icon on a transparent background, or a blue icon
|
||||
on a white/glacier icon with a blue border.
|
||||
-}
|
||||
secondary : Attribute msg
|
||||
secondary =
|
||||
set (\attributes -> { attributes | theme = Secondary })
|
||||
|
||||
|
||||
{-| White/transparent icon on a red background.
|
||||
-}
|
||||
danger : Attribute msg
|
||||
danger =
|
||||
set (\attributes -> { attributes | theme = Danger })
|
||||
|
||||
|
||||
{-| Red icon on a white/transparent background.
|
||||
-}
|
||||
dangerSecondary : Attribute msg
|
||||
dangerSecondary =
|
||||
set (\attributes -> { attributes | theme = DangerSecondary })
|
||||
|
||||
|
||||
{-| Use this helper to add custom attributes.
|
||||
|
||||
Do NOT use this helper to add css styles, as they may not be applied the way
|
||||
you want/expect if underlying Button styles change.
|
||||
Instead, please use the `css` helper.
|
||||
|
||||
-}
|
||||
custom : List (Html.Attribute msg) -> Attribute msg
|
||||
custom attributes =
|
||||
set
|
||||
(\config ->
|
||||
{ config
|
||||
| customAttributes = List.append config.customAttributes attributes
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
{-| -}
|
||||
css : List Style -> Attribute msg
|
||||
css styles =
|
||||
set
|
||||
(\config ->
|
||||
{ config
|
||||
| customStyles = List.append config.customStyles styles
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
|
||||
-- TOOLTIPS
|
||||
|
||||
|
||||
type alias TooltipSettings msg =
|
||||
{ position : Tooltip.Position
|
||||
, isOpen : Bool
|
||||
, onShow : Bool -> msg
|
||||
, id : String
|
||||
}
|
||||
|
||||
|
||||
showTooltip : String -> Maybe (TooltipSettings msg) -> Html msg -> Html msg
|
||||
showTooltip label maybeSettings buttonOrLink =
|
||||
case maybeSettings of
|
||||
Just { position, onShow, isOpen, id } ->
|
||||
let
|
||||
tooltipSettings =
|
||||
{ trigger = Tooltip.OnHover
|
||||
, onTrigger = onShow
|
||||
, isOpen = isOpen
|
||||
, triggerHtml = buttonOrLink
|
||||
, extraButtonAttrs = []
|
||||
, id = id ++ "__clickable-svg-tooltip"
|
||||
}
|
||||
in
|
||||
Tooltip.tooltip [ Html.text label ]
|
||||
|> Tooltip.withPosition position
|
||||
|> Tooltip.withWidth Tooltip.FitToContent
|
||||
|> Tooltip.withPadding Tooltip.SmallPadding
|
||||
|> Tooltip.primaryLabel tooltipSettings
|
||||
|
||||
Nothing ->
|
||||
buttonOrLink
|
||||
|
||||
|
||||
withTooltip : Tooltip.Position -> { id : String, isOpen : Bool, onShow : Bool -> msg } -> Attribute msg
|
||||
withTooltip position { id, isOpen, onShow } =
|
||||
set
|
||||
(\config ->
|
||||
{ config
|
||||
| tooltip =
|
||||
Just { position = position, id = id, isOpen = isOpen, onShow = onShow }
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
{-| DEPRECATED: prefer to use the Tooltip module directly.
|
||||
-}
|
||||
withTooltipAbove : { id : String, isOpen : Bool, onShow : Bool -> msg } -> Attribute msg
|
||||
withTooltipAbove =
|
||||
withTooltip Tooltip.OnTop
|
||||
|
||||
|
||||
{-| DEPRECATED: prefer to use the Tooltip module directly.
|
||||
-}
|
||||
withTooltipBelow : { id : String, isOpen : Bool, onShow : Bool -> msg } -> Attribute msg
|
||||
withTooltipBelow =
|
||||
withTooltip Tooltip.OnBottom
|
||||
|
||||
|
||||
|
||||
-- INTERNALS
|
||||
|
||||
|
||||
set :
|
||||
(ButtonOrLinkAttributes msg -> ButtonOrLinkAttributes msg)
|
||||
-> Attribute msg
|
||||
set with =
|
||||
Attribute (\(ButtonOrLink config) -> ButtonOrLink (with config))
|
||||
|
||||
|
||||
build : String -> Svg -> ButtonOrLink msg
|
||||
build label icon =
|
||||
ButtonOrLink
|
||||
{ clickableAttributes = ClickableAttributes.init
|
||||
, label = label
|
||||
, icon = icon
|
||||
, height = Css.px 17
|
||||
, width = Css.px 17
|
||||
, disabled = False
|
||||
, customAttributes = []
|
||||
, customStyles = []
|
||||
, tooltip = Nothing
|
||||
, hasBorder = False
|
||||
, theme = Secondary
|
||||
}
|
||||
|
||||
|
||||
type ButtonOrLink msg
|
||||
= ButtonOrLink (ButtonOrLinkAttributes msg)
|
||||
|
||||
|
||||
type alias ButtonOrLinkAttributes msg =
|
||||
{ clickableAttributes : ClickableAttributes msg
|
||||
, label : String
|
||||
, icon : Svg
|
||||
, height : Css.Px
|
||||
, width : Css.Px
|
||||
, disabled : Bool
|
||||
, customAttributes : List (Html.Attribute msg)
|
||||
, customStyles : List Style
|
||||
, tooltip : Maybe (TooltipSettings msg)
|
||||
, hasBorder : Bool
|
||||
, theme : Theme
|
||||
}
|
||||
|
||||
|
||||
renderButton : ButtonOrLink msg -> Html msg
|
||||
renderButton ((ButtonOrLink config) as button_) =
|
||||
Html.button
|
||||
([ Attributes.class "Nri-Ui-Clickable-Svg-V1__button"
|
||||
, Attributes.type_ "button"
|
||||
, Attributes.css (buttonOrLinkStyles config ++ config.customStyles)
|
||||
, Attributes.disabled config.disabled
|
||||
, Widget.label config.label
|
||||
]
|
||||
++ ClickableAttributes.toButtonAttributes config.clickableAttributes
|
||||
++ config.customAttributes
|
||||
)
|
||||
[ renderIcon config
|
||||
]
|
||||
|> showTooltip config.label config.tooltip
|
||||
|
||||
|
||||
type Link
|
||||
= Default
|
||||
| WithTracking
|
||||
| SinglePageApp
|
||||
| WithMethod String
|
||||
| External
|
||||
| ExternalWithTracking
|
||||
|
||||
|
||||
renderLink : ButtonOrLink msg -> Html msg
|
||||
renderLink ((ButtonOrLink config) as link_) =
|
||||
let
|
||||
( linkFunctionName, extraAttrs ) =
|
||||
ClickableAttributes.toLinkAttributes config.clickableAttributes
|
||||
in
|
||||
Html.a
|
||||
([ Attributes.class ("Nri-Ui-Clickable-Svg-" ++ linkFunctionName)
|
||||
, Attributes.css (buttonOrLinkStyles config ++ config.customStyles)
|
||||
, Widget.disabled config.disabled
|
||||
, Widget.label config.label
|
||||
]
|
||||
++ (if not config.disabled then
|
||||
extraAttrs
|
||||
|
||||
else
|
||||
[]
|
||||
)
|
||||
++ config.customAttributes
|
||||
)
|
||||
[ renderIcon config
|
||||
]
|
||||
|> showTooltip config.label config.tooltip
|
||||
|
||||
|
||||
renderIcon : ButtonOrLinkAttributes msg -> Html msg
|
||||
renderIcon config =
|
||||
config.icon
|
||||
|> Svg.withCss
|
||||
(if config.hasBorder then
|
||||
[ Css.width
|
||||
(Css.calc config.width
|
||||
Css.minus
|
||||
(Css.px <|
|
||||
(2 * withBorderHorizontalPadding)
|
||||
+ withBorderLeftBorderWidth
|
||||
+ withBorderRightBorderWidth
|
||||
)
|
||||
)
|
||||
, Css.height
|
||||
(Css.calc config.height
|
||||
Css.minus
|
||||
(Css.px <|
|
||||
withBorderTopPadding
|
||||
+ withBorderBottomPadding
|
||||
+ withBorderTopBorderWidth
|
||||
+ withBorderBottomBorderWidth
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
else
|
||||
[ Css.width config.width
|
||||
, Css.height config.height
|
||||
]
|
||||
)
|
||||
|> Svg.toHtml
|
||||
|
||||
|
||||
buttonOrLinkStyles : ButtonOrLinkAttributes msg -> List Style
|
||||
buttonOrLinkStyles config =
|
||||
let
|
||||
( { main_, mainHovered, background, backgroundHovered }, cursor ) =
|
||||
if config.disabled then
|
||||
( disabledTheme, Css.notAllowed )
|
||||
|
||||
else
|
||||
( applyTheme config.theme, Css.pointer )
|
||||
in
|
||||
[ Css.property "transition"
|
||||
"background-color 0.2s, color 0.2s, border-width 0s, border-color 0.2s"
|
||||
|
||||
-- Colors, text decoration, cursor
|
||||
, Css.textDecoration Css.none
|
||||
, Css.color main_
|
||||
, Css.visited [ Css.color main_ ]
|
||||
, Css.hover
|
||||
[ Css.textDecoration Css.none
|
||||
, Css.color mainHovered
|
||||
, Css.cursor cursor
|
||||
]
|
||||
|
||||
-- Margins, borders, padding
|
||||
, Css.margin Css.zero
|
||||
, Css.textAlign Css.center
|
||||
, Css.batch <|
|
||||
if config.hasBorder then
|
||||
[ Css.borderRadius (Css.px 8)
|
||||
, Css.borderColor main_
|
||||
, Css.borderStyle Css.solid
|
||||
, Css.borderTopWidth (Css.px withBorderTopBorderWidth)
|
||||
, Css.borderRightWidth (Css.px withBorderRightBorderWidth)
|
||||
, Css.borderBottomWidth (Css.px withBorderBottomBorderWidth)
|
||||
, Css.borderLeftWidth (Css.px withBorderLeftBorderWidth)
|
||||
, Css.backgroundColor background
|
||||
, Css.hover
|
||||
[ Css.borderColor mainHovered
|
||||
, Css.backgroundColor backgroundHovered
|
||||
]
|
||||
, Css.padding3
|
||||
(Css.px withBorderTopPadding)
|
||||
(Css.px withBorderHorizontalPadding)
|
||||
(Css.px withBorderBottomPadding)
|
||||
]
|
||||
|
||||
else
|
||||
[ Css.borderWidth Css.zero
|
||||
, Css.padding Css.zero
|
||||
, Css.backgroundColor Css.transparent
|
||||
]
|
||||
|
||||
-- Sizing
|
||||
, Css.display Css.inlineBlock
|
||||
, Css.boxSizing Css.borderBox
|
||||
, Css.width config.width
|
||||
, Css.height config.height
|
||||
, Css.lineHeight (Css.num 1)
|
||||
]
|
||||
|
||||
|
||||
withBorderTopBorderWidth : Float
|
||||
withBorderTopBorderWidth =
|
||||
1
|
||||
|
||||
|
||||
withBorderRightBorderWidth : Float
|
||||
withBorderRightBorderWidth =
|
||||
1
|
||||
|
||||
|
||||
withBorderBottomBorderWidth : Float
|
||||
withBorderBottomBorderWidth =
|
||||
2
|
||||
|
||||
|
||||
withBorderLeftBorderWidth : Float
|
||||
withBorderLeftBorderWidth =
|
||||
1
|
||||
|
||||
|
||||
withBorderTopPadding : Float
|
||||
withBorderTopPadding =
|
||||
4
|
||||
|
||||
|
||||
withBorderBottomPadding : Float
|
||||
withBorderBottomPadding =
|
||||
3
|
||||
|
||||
|
||||
withBorderHorizontalPadding : Float
|
||||
withBorderHorizontalPadding =
|
||||
5
|
@ -13,14 +13,14 @@ module Nri.Ui.Colors.Extra exposing
|
||||
|
||||
-}
|
||||
|
||||
import Color
|
||||
import Css
|
||||
import SolidColor exposing (SolidColor)
|
||||
|
||||
|
||||
{-| -}
|
||||
fromCssColor : Css.Color -> Color.Color
|
||||
fromCssColor : Css.Color -> SolidColor
|
||||
fromCssColor color =
|
||||
Color.fromRGB
|
||||
SolidColor.fromRGB
|
||||
( toFloat color.red
|
||||
, toFloat color.green
|
||||
, toFloat color.blue
|
||||
@ -28,11 +28,11 @@ fromCssColor color =
|
||||
|
||||
|
||||
{-| -}
|
||||
toCssColor : Color.Color -> Css.Color
|
||||
toCssColor : SolidColor -> Css.Color
|
||||
toCssColor color =
|
||||
let
|
||||
( red, green, blue ) =
|
||||
Color.toRGB color
|
||||
SolidColor.toRGB color
|
||||
in
|
||||
Css.rgb (round red) (round green) (round blue)
|
||||
|
||||
|
@ -1,901 +0,0 @@
|
||||
module Nri.Ui.Icon.V3 exposing
|
||||
( icon, decorativeIcon, link, linkExternal, linkSpa, button
|
||||
, IconType, IconSize(..), IconLinkSpaModel
|
||||
, activity
|
||||
, add
|
||||
, arrowDown
|
||||
, arrowLeft
|
||||
, arrowRight
|
||||
, assignmentStartButtonPrimary
|
||||
, assignmentStartButtonSecondary
|
||||
, assignmentTypeDiagnostic
|
||||
, assignmentTypePeerReview
|
||||
, assignmentTypeSelfReview
|
||||
, assignmentTypePractice
|
||||
, assignmentTypeQuickWrite
|
||||
, assignmentTypeQuiz
|
||||
, assignmentTypeWritingCycle
|
||||
, attention
|
||||
, bang
|
||||
, bulb
|
||||
, calendar
|
||||
, caret
|
||||
, checkMark
|
||||
, checkMarkSquiggily
|
||||
, checkMarkSvg
|
||||
, class
|
||||
, clever
|
||||
, clock
|
||||
, close
|
||||
, compassSvg
|
||||
, copy
|
||||
, custom
|
||||
, darkBlueCheckMark
|
||||
, document
|
||||
, download
|
||||
, edit
|
||||
, editWriting
|
||||
, equalitySign
|
||||
, exclamation
|
||||
, facebook
|
||||
, flag
|
||||
, flipper
|
||||
, footsteps
|
||||
, gardening
|
||||
, gear
|
||||
, greenCheckMark
|
||||
, guidedWrite
|
||||
, hat
|
||||
, help
|
||||
, helpSvg
|
||||
, highFive
|
||||
, key
|
||||
, keychain
|
||||
, late
|
||||
, leaderboard
|
||||
, lightBulb
|
||||
, lock
|
||||
, lockDeprecated
|
||||
, logo
|
||||
, masteryBadge
|
||||
, newspaper
|
||||
, notStarred
|
||||
, okay
|
||||
, openClose
|
||||
, peerReview
|
||||
, pen
|
||||
, performance
|
||||
, personBlue
|
||||
, preview
|
||||
, quickWrite
|
||||
, seeMore
|
||||
, share
|
||||
, skip
|
||||
, sort
|
||||
, sortArrow
|
||||
, speedometer
|
||||
, starred
|
||||
, submitting, rating, revising
|
||||
, thumbsUp
|
||||
, twitter
|
||||
, unarchive
|
||||
, writingAssignment
|
||||
, x
|
||||
, xSvg
|
||||
)
|
||||
|
||||
{-|
|
||||
|
||||
@docs icon, decorativeIcon, link, linkExternal, linkSpa, button
|
||||
@docs IconType, IconSize, IconLinkSpaModel
|
||||
@docs activity
|
||||
@docs add
|
||||
@docs arrowDown
|
||||
@docs arrowLeft
|
||||
@docs arrowRight
|
||||
@docs assignmentStartButtonPrimary
|
||||
@docs assignmentStartButtonSecondary
|
||||
@docs assignmentTypeDiagnostic
|
||||
@docs assignmentTypePeerReview
|
||||
@docs assignmentTypeSelfReview
|
||||
@docs assignmentTypePractice
|
||||
@docs assignmentTypeQuickWrite
|
||||
@docs assignmentTypeQuiz
|
||||
@docs assignmentTypeWritingCycle
|
||||
@docs attention
|
||||
@docs bang
|
||||
@docs bulb
|
||||
@docs calendar
|
||||
@docs caret
|
||||
@docs checkMark
|
||||
@docs checkMarkSquiggily
|
||||
@docs checkMarkSvg
|
||||
@docs class
|
||||
@docs clever
|
||||
@docs clock
|
||||
@docs close
|
||||
@docs compassSvg
|
||||
@docs copy
|
||||
@docs custom
|
||||
@docs darkBlueCheckMark
|
||||
@docs document
|
||||
@docs download
|
||||
@docs edit
|
||||
@docs editWriting
|
||||
@docs equalitySign
|
||||
@docs exclamation
|
||||
@docs facebook
|
||||
@docs flag
|
||||
@docs flipper
|
||||
@docs footsteps
|
||||
@docs gardening
|
||||
@docs gear
|
||||
@docs greenCheckMark
|
||||
@docs guidedWrite
|
||||
@docs hat
|
||||
@docs help
|
||||
@docs helpSvg
|
||||
@docs highFive
|
||||
@docs key
|
||||
@docs keychain
|
||||
@docs late
|
||||
@docs leaderboard
|
||||
@docs lightBulb
|
||||
@docs lock
|
||||
@docs lockDeprecated
|
||||
@docs logo
|
||||
@docs masteryBadge
|
||||
@docs newspaper
|
||||
@docs notStarred
|
||||
@docs okay
|
||||
@docs openClose
|
||||
@docs peerReview
|
||||
@docs pen
|
||||
@docs performance
|
||||
@docs personBlue
|
||||
@docs preview
|
||||
@docs quickWrite
|
||||
@docs seeMore
|
||||
@docs share
|
||||
@docs skip
|
||||
@docs sort
|
||||
@docs sortArrow
|
||||
@docs speedometer
|
||||
@docs starred
|
||||
@docs submitting, rating, revising
|
||||
@docs thumbsUp
|
||||
@docs twitter
|
||||
@docs unarchive
|
||||
@docs writingAssignment
|
||||
@docs x
|
||||
@docs xSvg
|
||||
|
||||
-}
|
||||
|
||||
import Accessibility.Role as Role
|
||||
import Accessibility.Styled exposing (..)
|
||||
import Css exposing (..)
|
||||
import EventExtras
|
||||
import Html as RootHtml
|
||||
import Html.Attributes as RootAttr exposing (..)
|
||||
import Html.Styled
|
||||
import Html.Styled.Attributes as Attributes exposing (css)
|
||||
import Html.Styled.Events as Events
|
||||
import Nri.Ui.AssetPath exposing (Asset(..))
|
||||
import Nri.Ui.Colors.V1
|
||||
import Svg exposing (svg, use)
|
||||
import Svg.Attributes exposing (xlinkHref)
|
||||
|
||||
|
||||
{-| -}
|
||||
type alias IconLinkModel =
|
||||
{ alt : String
|
||||
, url : String
|
||||
, icon : IconType
|
||||
, disabled : Bool
|
||||
, size : IconSize
|
||||
}
|
||||
|
||||
|
||||
{-| -}
|
||||
type alias IconLinkSpaModel route =
|
||||
{ alt : String
|
||||
, icon : IconType
|
||||
, disabled : Bool
|
||||
, size : IconSize
|
||||
, route : route
|
||||
}
|
||||
|
||||
|
||||
type alias IconButtonModel msg =
|
||||
{ alt : String
|
||||
, msg : msg
|
||||
, icon : IconType
|
||||
, disabled : Bool
|
||||
, size : IconSize
|
||||
}
|
||||
|
||||
|
||||
{-| An icon that can be rendered using the functions provided by this module.
|
||||
-}
|
||||
type IconType
|
||||
= ImgIcon Asset
|
||||
| SvgIcon String
|
||||
|
||||
|
||||
{-| Used for determining sizes on Icon.buttons and Icon.links
|
||||
-}
|
||||
type IconSize
|
||||
= Small
|
||||
| Medium
|
||||
|
||||
|
||||
{-| Create an icon that links to a part of NRI
|
||||
Uses our default icon styles (25 x 25 px, azure)
|
||||
-}
|
||||
link : IconLinkModel -> Html msg
|
||||
link =
|
||||
linkBase [ Attributes.target "_self" ]
|
||||
|
||||
|
||||
{-| Create an accessible icon button with an onClick handler
|
||||
Uses our default icon styles (25 x 25 px, azure)
|
||||
-}
|
||||
button : IconButtonModel msg -> Html msg
|
||||
button model =
|
||||
Accessibility.Styled.button
|
||||
[ css
|
||||
[ Css.batch
|
||||
[ backgroundColor transparent
|
||||
, border zero
|
||||
, color Nri.Ui.Colors.V1.azure
|
||||
, fontFamily inherit
|
||||
, Css.property "cursor" "pointer"
|
||||
, padding zero
|
||||
, focus
|
||||
[ backgroundColor transparent
|
||||
]
|
||||
]
|
||||
, sizeStyles model.size
|
||||
]
|
||||
, Events.onClick model.msg
|
||||
, Attributes.disabled model.disabled
|
||||
, Attributes.type_ "button"
|
||||
]
|
||||
[ icon
|
||||
{ alt = model.alt
|
||||
, icon = model.icon
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
{-| -}
|
||||
icon : { alt : String, icon : IconType } -> Html msg
|
||||
icon config =
|
||||
case config.icon of
|
||||
SvgIcon iconId ->
|
||||
svg svgStyle
|
||||
[ Svg.title [] [ RootHtml.text config.alt ]
|
||||
, use [ xlinkHref ("#" ++ iconId) ] []
|
||||
]
|
||||
|> Html.Styled.fromUnstyled
|
||||
|
||||
ImgIcon assetPath ->
|
||||
img config.alt
|
||||
[ Attributes.src (Nri.Ui.AssetPath.url assetPath)
|
||||
]
|
||||
|
||||
|
||||
{-| Use this icon for purely decorative content that would be distracting
|
||||
rather than helpful on a screenreader.
|
||||
-}
|
||||
decorativeIcon : IconType -> Html msg
|
||||
decorativeIcon iconType =
|
||||
case iconType of
|
||||
SvgIcon iconId ->
|
||||
svg
|
||||
(Role.img :: svgStyle)
|
||||
[ use [ xlinkHref ("#" ++ iconId) ] []
|
||||
]
|
||||
|> Html.Styled.fromUnstyled
|
||||
|
||||
ImgIcon assetPath ->
|
||||
decorativeImg [ Attributes.src (Nri.Ui.AssetPath.url assetPath) ]
|
||||
|
||||
|
||||
{-| Use this link for routing within a single page app.
|
||||
|
||||
This will make a normal <a> tag, but change the onClick behavior to avoid reloading the page.
|
||||
|
||||
-}
|
||||
linkSpa : (route -> String) -> (route -> msg) -> IconLinkSpaModel route -> Html msg
|
||||
linkSpa toUrl toMsg config =
|
||||
linkBase
|
||||
[ EventExtras.onClickPreventDefaultForLinkWithHref (toMsg config.route)
|
||||
]
|
||||
{ alt = config.alt
|
||||
, url = toUrl config.route
|
||||
, icon = config.icon
|
||||
, disabled = config.disabled
|
||||
, size = config.size
|
||||
}
|
||||
|
||||
|
||||
{-| Create an icon that links to an external site
|
||||
Uses our default icon styles (25 x 25 px, azure)
|
||||
-}
|
||||
linkExternal : IconLinkModel -> Html msg
|
||||
linkExternal =
|
||||
linkBase [ Attributes.target "_blank" ]
|
||||
|
||||
|
||||
linkBase : List (Attribute msg) -> IconLinkModel -> Html msg
|
||||
linkBase linkAttributes model =
|
||||
span
|
||||
[]
|
||||
[ Html.Styled.a
|
||||
(linkAttributes ++ defaultLinkAttributes model)
|
||||
[ icon { alt = model.alt, icon = model.icon }
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
defaultLinkAttributes : IconLinkModel -> List (Attribute msg)
|
||||
defaultLinkAttributes model =
|
||||
if model.disabled then
|
||||
[ css
|
||||
[ Css.cursor Css.notAllowed
|
||||
, linkStyles
|
||||
, sizeStyles model.size
|
||||
]
|
||||
]
|
||||
|
||||
else
|
||||
[ css [ linkStyles, sizeStyles model.size ]
|
||||
, Attributes.href model.url
|
||||
]
|
||||
|
||||
|
||||
linkStyles : Style
|
||||
linkStyles =
|
||||
Css.batch
|
||||
[ color Nri.Ui.Colors.V1.azure
|
||||
, display inlineBlock
|
||||
, fontFamily inherit
|
||||
, Css.property "cursor" "pointer"
|
||||
, padding zero
|
||||
, visited [ color Nri.Ui.Colors.V1.azure ]
|
||||
]
|
||||
|
||||
|
||||
sizeStyles : IconSize -> Style
|
||||
sizeStyles size =
|
||||
Css.batch <|
|
||||
case size of
|
||||
Small ->
|
||||
[ Css.width (px 20)
|
||||
, Css.height (px 20)
|
||||
]
|
||||
|
||||
Medium ->
|
||||
[ Css.width (px 25)
|
||||
, Css.height (px 25)
|
||||
]
|
||||
|
||||
|
||||
{-| -}
|
||||
activity : { r | activity : String } -> IconType
|
||||
activity assets =
|
||||
SvgIcon assets.activity
|
||||
|
||||
|
||||
{-| -}
|
||||
add : { r | icons_plusBlue_svg : Asset } -> IconType
|
||||
add assets =
|
||||
ImgIcon assets.icons_plusBlue_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
arrowDown : { r | arrowDown : String } -> IconType
|
||||
arrowDown assets =
|
||||
SvgIcon assets.arrowDown
|
||||
|
||||
|
||||
{-| -}
|
||||
arrowLeft : { r | leftArrowBlue_png : Asset } -> IconType
|
||||
arrowLeft assets =
|
||||
ImgIcon assets.leftArrowBlue_png
|
||||
|
||||
|
||||
{-| -}
|
||||
arrowRight : { r | icons_arrowRightBlue_svg : Asset } -> IconType
|
||||
arrowRight assets =
|
||||
ImgIcon assets.icons_arrowRightBlue_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
assignmentStartButtonPrimary : { r | assignmentStartButtonPrimary_svg : Asset } -> IconType
|
||||
assignmentStartButtonPrimary assets =
|
||||
ImgIcon assets.assignmentStartButtonPrimary_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
assignmentStartButtonSecondary : { r | assignmentStartButtonSecondary_svg : Asset } -> IconType
|
||||
assignmentStartButtonSecondary assets =
|
||||
ImgIcon assets.assignmentStartButtonSecondary_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
assignmentTypeDiagnostic : { r | diagnostic : String } -> IconType
|
||||
assignmentTypeDiagnostic assets =
|
||||
SvgIcon assets.diagnostic
|
||||
|
||||
|
||||
{-| -}
|
||||
assignmentTypePeerReview : { r | icons_peerReviewWhite_svg : Asset } -> IconType
|
||||
assignmentTypePeerReview assets =
|
||||
ImgIcon assets.icons_peerReviewWhite_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
assignmentTypeSelfReview : { r | icons_selfReviewWhite_svg : Asset } -> IconType
|
||||
assignmentTypeSelfReview assets =
|
||||
ImgIcon assets.icons_selfReviewWhite_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
assignmentTypePractice : { r | practice : String } -> IconType
|
||||
assignmentTypePractice assets =
|
||||
SvgIcon assets.practice
|
||||
|
||||
|
||||
{-| -}
|
||||
assignmentTypeQuickWrite : { r | icons_quickWriteWhite_svg : Asset } -> IconType
|
||||
assignmentTypeQuickWrite assets =
|
||||
ImgIcon assets.icons_quickWriteWhite_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
assignmentTypeQuiz : { r | quiz : String } -> IconType
|
||||
assignmentTypeQuiz assets =
|
||||
SvgIcon assets.quiz
|
||||
|
||||
|
||||
{-| -}
|
||||
assignmentTypeWritingCycle : { r | writingcycle : String } -> IconType
|
||||
assignmentTypeWritingCycle assets =
|
||||
SvgIcon assets.writingcycle
|
||||
|
||||
|
||||
{-| -}
|
||||
attention : { r | attention_svg : Asset } -> IconType
|
||||
attention assets =
|
||||
ImgIcon assets.attention_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
bang : { r | exclamationPoint_svg : Asset } -> IconType
|
||||
bang assets =
|
||||
ImgIcon assets.exclamationPoint_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
bulb : { r | bulb : String } -> IconType
|
||||
bulb assets =
|
||||
SvgIcon assets.bulb
|
||||
|
||||
|
||||
{-| -}
|
||||
calendar : { r | calendar : String } -> IconType
|
||||
calendar assets =
|
||||
SvgIcon assets.calendar
|
||||
|
||||
|
||||
{-| -}
|
||||
caret : { r | icons_arrowDownBlue_svg : Asset } -> IconType
|
||||
caret assets =
|
||||
ImgIcon assets.icons_arrowDownBlue_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
checkMark : { r | iconCheck_png : Asset } -> IconType
|
||||
checkMark assets =
|
||||
ImgIcon assets.iconCheck_png
|
||||
|
||||
|
||||
{-| -}
|
||||
checkMarkSquiggily : { r | squiggly_png : Asset } -> IconType
|
||||
checkMarkSquiggily assets =
|
||||
ImgIcon assets.squiggly_png
|
||||
|
||||
|
||||
{-| -}
|
||||
checkMarkSvg : { r | checkmark : String } -> IconType
|
||||
checkMarkSvg assets =
|
||||
SvgIcon assets.checkmark
|
||||
|
||||
|
||||
{-| -}
|
||||
class : { r | class : String } -> IconType
|
||||
class assets =
|
||||
SvgIcon assets.class
|
||||
|
||||
|
||||
{-| -}
|
||||
clever : { r | clever : String } -> IconType
|
||||
clever assets =
|
||||
SvgIcon assets.clever
|
||||
|
||||
|
||||
{-| -}
|
||||
clock : { r | clock : String } -> IconType
|
||||
clock assets =
|
||||
SvgIcon assets.clock
|
||||
|
||||
|
||||
{-| -}
|
||||
close : { r | icons_xBlue_svg : Asset } -> IconType
|
||||
close assets =
|
||||
ImgIcon assets.icons_xBlue_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
copy : { r | teach_assignments_copyWhite_svg : Asset } -> IconType
|
||||
copy assets =
|
||||
ImgIcon assets.teach_assignments_copyWhite_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
compassSvg : { r | compass : String } -> IconType
|
||||
compassSvg assets =
|
||||
SvgIcon assets.compass
|
||||
|
||||
|
||||
{-| -}
|
||||
custom : Asset -> IconType
|
||||
custom asset =
|
||||
ImgIcon asset
|
||||
|
||||
|
||||
{-| -}
|
||||
darkBlueCheckMark : { r | darkBlueCheckmark_svg : Asset } -> IconType
|
||||
darkBlueCheckMark assets =
|
||||
ImgIcon assets.darkBlueCheckmark_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
document : { r | document : String } -> IconType
|
||||
document assets =
|
||||
SvgIcon assets.document
|
||||
|
||||
|
||||
{-| -}
|
||||
download : { r | download : String } -> IconType
|
||||
download assets =
|
||||
SvgIcon assets.download
|
||||
|
||||
|
||||
{-| -}
|
||||
edit : { r | edit : String } -> IconType
|
||||
edit assets =
|
||||
SvgIcon assets.edit
|
||||
|
||||
|
||||
{-| -}
|
||||
editWriting : { r | editWriting : String } -> IconType
|
||||
editWriting assets =
|
||||
SvgIcon assets.editWriting
|
||||
|
||||
|
||||
{-| -}
|
||||
equalitySign : { r | icons_equals_svg : Asset } -> IconType
|
||||
equalitySign assets =
|
||||
ImgIcon assets.icons_equals_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
exclamation : { r | exclamation : String } -> IconType
|
||||
exclamation assets =
|
||||
SvgIcon assets.exclamation
|
||||
|
||||
|
||||
{-| -}
|
||||
facebook : { r | facebookBlue_svg : Asset } -> IconType
|
||||
facebook assets =
|
||||
ImgIcon assets.facebookBlue_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
flag : { r | iconFlag_png : Asset } -> IconType
|
||||
flag assets =
|
||||
ImgIcon assets.iconFlag_png
|
||||
|
||||
|
||||
{-| -}
|
||||
flipper : { r | flipper : String } -> IconType
|
||||
flipper assets =
|
||||
SvgIcon assets.flipper
|
||||
|
||||
|
||||
{-| -}
|
||||
footsteps : { r | footsteps : String } -> IconType
|
||||
footsteps assets =
|
||||
SvgIcon assets.footsteps
|
||||
|
||||
|
||||
{-| -}
|
||||
gardening : { r | startingOffBadge_png : Asset } -> IconType
|
||||
gardening assets =
|
||||
ImgIcon assets.startingOffBadge_png
|
||||
|
||||
|
||||
{-| -}
|
||||
gear : { r | gear : String } -> IconType
|
||||
gear assets =
|
||||
SvgIcon assets.gear
|
||||
|
||||
|
||||
{-| -}
|
||||
greenCheckMark : { r | smallCheckmark_png : Asset } -> IconType
|
||||
greenCheckMark assets =
|
||||
ImgIcon assets.smallCheckmark_png
|
||||
|
||||
|
||||
{-| -}
|
||||
guidedWrite : { r | icons_guidedWrite_svg : Asset } -> IconType
|
||||
guidedWrite assets =
|
||||
ImgIcon assets.icons_guidedWrite_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
hat : { r | hat : String } -> IconType
|
||||
hat assets =
|
||||
SvgIcon assets.hat
|
||||
|
||||
|
||||
{-| -}
|
||||
help : { r | icons_helpBlue_svg : Asset } -> IconType
|
||||
help assets =
|
||||
ImgIcon assets.icons_helpBlue_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
helpSvg : { r | help : String } -> IconType
|
||||
helpSvg assets =
|
||||
SvgIcon assets.help
|
||||
|
||||
|
||||
{-| -}
|
||||
highFive : { r | level3Badge_png : Asset } -> IconType
|
||||
highFive assets =
|
||||
ImgIcon assets.level3Badge_png
|
||||
|
||||
|
||||
{-| -}
|
||||
key : { r | key : String } -> IconType
|
||||
key assets =
|
||||
SvgIcon assets.key
|
||||
|
||||
|
||||
{-| -}
|
||||
keychain : { r | keychain : String } -> IconType
|
||||
keychain assets =
|
||||
SvgIcon assets.keychain
|
||||
|
||||
|
||||
{-| -}
|
||||
late : { r | icons_clockRed_svg : Asset } -> IconType
|
||||
late assets =
|
||||
ImgIcon assets.icons_clockRed_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
leaderboard : { r | leaderboard : String } -> IconType
|
||||
leaderboard assets =
|
||||
SvgIcon assets.leaderboard
|
||||
|
||||
|
||||
{-| -}
|
||||
lightBulb : { r | hint_png : Asset } -> IconType
|
||||
lightBulb assets =
|
||||
ImgIcon assets.hint_png
|
||||
|
||||
|
||||
{-| -}
|
||||
lock : { r | lock : String } -> IconType
|
||||
lock assets =
|
||||
SvgIcon assets.lock
|
||||
|
||||
|
||||
{-| -}
|
||||
lockDeprecated : { r | premiumLock_svg : Asset } -> IconType
|
||||
lockDeprecated assets =
|
||||
ImgIcon assets.premiumLock_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
logo : { r | logoRedBlack_svg : Asset } -> IconType
|
||||
logo assets =
|
||||
ImgIcon assets.logoRedBlack_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
masteryBadge : { r | masteryBadge : String } -> IconType
|
||||
masteryBadge assets =
|
||||
SvgIcon assets.masteryBadge
|
||||
|
||||
|
||||
{-| -}
|
||||
newspaper : { r | newspaper : String } -> IconType
|
||||
newspaper assets =
|
||||
SvgIcon assets.newspaper
|
||||
|
||||
|
||||
{-| -}
|
||||
notStarred : { r | commentNotStarred_png : Asset } -> IconType
|
||||
notStarred assets =
|
||||
ImgIcon assets.commentNotStarred_png
|
||||
|
||||
|
||||
{-| -}
|
||||
okay : { r | level2Badge_png : Asset } -> IconType
|
||||
okay assets =
|
||||
ImgIcon assets.level2Badge_png
|
||||
|
||||
|
||||
{-| -}
|
||||
openClose : { r | openClose : String } -> IconType
|
||||
openClose assets =
|
||||
SvgIcon assets.openClose
|
||||
|
||||
|
||||
{-| -}
|
||||
peerReview : { r | icons_peerReview_svg : Asset } -> IconType
|
||||
peerReview assets =
|
||||
ImgIcon assets.icons_peerReview_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
pen : { r | pen : Asset } -> IconType
|
||||
pen assets =
|
||||
ImgIcon assets.pen
|
||||
|
||||
|
||||
{-| -}
|
||||
performance : { r | performance : String } -> IconType
|
||||
performance assets =
|
||||
SvgIcon assets.performance
|
||||
|
||||
|
||||
{-| -}
|
||||
personBlue : { r | personBlue_svg : Asset } -> IconType
|
||||
personBlue assets =
|
||||
ImgIcon assets.personBlue_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
preview : { r | preview : String } -> IconType
|
||||
preview assets =
|
||||
SvgIcon assets.preview
|
||||
|
||||
|
||||
{-| -}
|
||||
quickWrite : { r | icons_quickWrite_svg : Asset } -> IconType
|
||||
quickWrite assets =
|
||||
ImgIcon assets.icons_quickWrite_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
seeMore : { r | seemore : String } -> IconType
|
||||
seeMore assets =
|
||||
SvgIcon assets.seemore
|
||||
|
||||
|
||||
{-| -}
|
||||
share : { r | share : String } -> IconType
|
||||
share assets =
|
||||
SvgIcon assets.share
|
||||
|
||||
|
||||
{-| -}
|
||||
skip : { r | skip : String } -> IconType
|
||||
skip assets =
|
||||
SvgIcon assets.skip
|
||||
|
||||
|
||||
{-| -}
|
||||
sort : { r | sort : String } -> IconType
|
||||
sort assets =
|
||||
SvgIcon assets.sort
|
||||
|
||||
|
||||
{-| -}
|
||||
sortArrow : { r | sortArrow : String } -> IconType
|
||||
sortArrow assets =
|
||||
SvgIcon assets.sortArrow
|
||||
|
||||
|
||||
{-| -}
|
||||
speedometer : { r | speedometer : String } -> IconType
|
||||
speedometer assets =
|
||||
SvgIcon assets.speedometer
|
||||
|
||||
|
||||
{-| -}
|
||||
starred : { r | commentStarred_png : Asset } -> IconType
|
||||
starred assets =
|
||||
ImgIcon assets.commentStarred_png
|
||||
|
||||
|
||||
{-| -}
|
||||
thumbsUp : { r | level1Badge_png : Asset } -> IconType
|
||||
thumbsUp assets =
|
||||
ImgIcon assets.level1Badge_png
|
||||
|
||||
|
||||
{-| -}
|
||||
twitter : { r | twitterBlue_svg : Asset } -> IconType
|
||||
twitter assets =
|
||||
ImgIcon assets.twitterBlue_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
unarchive : { r | unarchiveBlue2x_png : Asset } -> IconType
|
||||
unarchive assets =
|
||||
ImgIcon assets.unarchiveBlue2x_png
|
||||
|
||||
|
||||
{-| -}
|
||||
writingAssignment : { r | writingAssignment : String } -> IconType
|
||||
writingAssignment assets =
|
||||
SvgIcon assets.writingAssignment
|
||||
|
||||
|
||||
{-| -}
|
||||
x : { r | xWhite_svg : Asset } -> IconType
|
||||
x assets =
|
||||
ImgIcon assets.xWhite_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
xSvg : { r | x : String } -> IconType
|
||||
xSvg assets =
|
||||
SvgIcon assets.x
|
||||
|
||||
|
||||
{-| -}
|
||||
submitting : { r | submitting : String } -> IconType
|
||||
submitting assets =
|
||||
SvgIcon assets.submitting
|
||||
|
||||
|
||||
{-| -}
|
||||
rating : { r | rating : String } -> IconType
|
||||
rating assets =
|
||||
SvgIcon assets.rating
|
||||
|
||||
|
||||
{-| -}
|
||||
revising : { r | revising : String } -> IconType
|
||||
revising assets =
|
||||
SvgIcon assets.revising
|
||||
|
||||
|
||||
{-| Inlining SVG styles because styles.class doesn't work on SVG elements.
|
||||
The `className` property of an SVG element isn't a string, it's an object and so
|
||||
`styles.class` causes a runtime exception by attempting to overwrite it with
|
||||
a string. Another workaround is to use the `Svg.Attributes.class` attribute but
|
||||
since `withNamespace` hides a call to `Html.Attributes.class` we can't do it
|
||||
properly.
|
||||
-}
|
||||
svgStyle : List (RootHtml.Attribute msg)
|
||||
svgStyle =
|
||||
[ RootAttr.style "fill" "currentColor"
|
||||
, RootAttr.style "width" "100%"
|
||||
, RootAttr.style "height" "100%"
|
||||
|
||||
-- don't allow SVG to capture pointer events. This means that we can use it
|
||||
-- in links. See https://css-tricks.com/links-inline-svg-staying-target-events/
|
||||
-- for more details.
|
||||
, RootAttr.style "pointer-events" "none"
|
||||
]
|
@ -1,948 +0,0 @@
|
||||
module Nri.Ui.Icon.V4 exposing
|
||||
( icon, decorativeIcon
|
||||
, IconType, IconSize(..)
|
||||
, IconLinkModel, link, linkExternal
|
||||
, IconButtonModel, button
|
||||
, IconLinkSpaModel, linkSpa
|
||||
, activity
|
||||
, add
|
||||
, arrowDown
|
||||
, arrowLeft
|
||||
, arrowRight
|
||||
, assignmentStartButtonPrimary
|
||||
, assignmentStartButtonSecondary
|
||||
, assignmentTypeDiagnostic
|
||||
, assignmentTypeGuidedDraft
|
||||
, assignmentTypePeerReview
|
||||
, assignmentTypePractice
|
||||
, assignmentTypeQuickWrite
|
||||
, assignmentTypeQuiz
|
||||
, assignmentTypeSelfReview
|
||||
, assignmentTypeWritingCycle
|
||||
, attention
|
||||
, bang
|
||||
, bulb
|
||||
, calendar
|
||||
, caret
|
||||
, checkMark
|
||||
, checkMarkSquiggily
|
||||
, checkMarkSvg
|
||||
, class
|
||||
, clever
|
||||
, clock
|
||||
, close
|
||||
, compassSvg
|
||||
, copy
|
||||
, custom
|
||||
, darkBlueCheckMark
|
||||
, document
|
||||
, download
|
||||
, edit
|
||||
, editWriting
|
||||
, equalitySign
|
||||
, exclamation
|
||||
, facebook
|
||||
, flag
|
||||
, flipper
|
||||
, footsteps
|
||||
, gardening
|
||||
, gear
|
||||
, greenCheckMark
|
||||
, guidedWrite
|
||||
, hat
|
||||
, help
|
||||
, helpSvg
|
||||
, highFive
|
||||
, highlighter
|
||||
, key
|
||||
, keychain
|
||||
, late
|
||||
, leaderboard
|
||||
, lightBulb
|
||||
, lock
|
||||
, lockDeprecated
|
||||
, logo
|
||||
, masteryBadge
|
||||
, microscope
|
||||
, newspaper
|
||||
, notStarred
|
||||
, okay
|
||||
, openClose
|
||||
, peerReview
|
||||
, pen
|
||||
, performance
|
||||
, personBlue
|
||||
, preview
|
||||
, quickWrite
|
||||
, scale
|
||||
, search
|
||||
, seeMore
|
||||
, share
|
||||
, skip
|
||||
, sort
|
||||
, sortArrow
|
||||
, speedometer
|
||||
, starred
|
||||
, submitting, rating, revising
|
||||
, thumbsUp
|
||||
, twitter
|
||||
, unarchive
|
||||
, writingAssignment
|
||||
, x
|
||||
, xSvg
|
||||
)
|
||||
|
||||
{-|
|
||||
|
||||
@docs icon, decorativeIcon
|
||||
@docs IconType, IconSize
|
||||
@docs IconLinkModel, link, linkExternal
|
||||
@docs IconButtonModel, button
|
||||
@docs IconLinkSpaModel, linkSpa
|
||||
@docs activity
|
||||
@docs add
|
||||
@docs arrowDown
|
||||
@docs arrowLeft
|
||||
@docs arrowRight
|
||||
@docs assignmentStartButtonPrimary
|
||||
@docs assignmentStartButtonSecondary
|
||||
@docs assignmentTypeDiagnostic
|
||||
@docs assignmentTypeGuidedDraft
|
||||
@docs assignmentTypePeerReview
|
||||
@docs assignmentTypePractice
|
||||
@docs assignmentTypeQuickWrite
|
||||
@docs assignmentTypeQuiz
|
||||
@docs assignmentTypeSelfReview
|
||||
@docs assignmentTypeWritingCycle
|
||||
@docs attention
|
||||
@docs bang
|
||||
@docs bulb
|
||||
@docs calendar
|
||||
@docs caret
|
||||
@docs checkMark
|
||||
@docs checkMarkSquiggily
|
||||
@docs checkMarkSvg
|
||||
@docs class
|
||||
@docs clever
|
||||
@docs clock
|
||||
@docs close
|
||||
@docs compassSvg
|
||||
@docs copy
|
||||
@docs custom
|
||||
@docs darkBlueCheckMark
|
||||
@docs document
|
||||
@docs download
|
||||
@docs edit
|
||||
@docs editWriting
|
||||
@docs equalitySign
|
||||
@docs exclamation
|
||||
@docs facebook
|
||||
@docs flag
|
||||
@docs flipper
|
||||
@docs footsteps
|
||||
@docs gardening
|
||||
@docs gear
|
||||
@docs greenCheckMark
|
||||
@docs guidedWrite
|
||||
@docs hat
|
||||
@docs help
|
||||
@docs helpSvg
|
||||
@docs highFive
|
||||
@docs highlighter
|
||||
@docs key
|
||||
@docs keychain
|
||||
@docs late
|
||||
@docs leaderboard
|
||||
@docs lightBulb
|
||||
@docs lock
|
||||
@docs lockDeprecated
|
||||
@docs logo
|
||||
@docs masteryBadge
|
||||
@docs microscope
|
||||
@docs newspaper
|
||||
@docs notStarred
|
||||
@docs okay
|
||||
@docs openClose
|
||||
@docs peerReview
|
||||
@docs pen
|
||||
@docs performance
|
||||
@docs personBlue
|
||||
@docs preview
|
||||
@docs quickWrite
|
||||
@docs scale
|
||||
@docs search
|
||||
@docs seeMore
|
||||
@docs share
|
||||
@docs skip
|
||||
@docs sort
|
||||
@docs sortArrow
|
||||
@docs speedometer
|
||||
@docs starred
|
||||
@docs submitting, rating, revising
|
||||
@docs thumbsUp
|
||||
@docs twitter
|
||||
@docs unarchive
|
||||
@docs writingAssignment
|
||||
@docs x
|
||||
@docs xSvg
|
||||
|
||||
-}
|
||||
|
||||
import Accessibility.Role as Role
|
||||
import Accessibility.Styled exposing (..)
|
||||
import Css exposing (..)
|
||||
import EventExtras
|
||||
import Html as RootHtml
|
||||
import Html.Attributes as RootAttr exposing (..)
|
||||
import Html.Styled
|
||||
import Html.Styled.Attributes as Attributes exposing (css)
|
||||
import Html.Styled.Events as Events
|
||||
import Nri.Ui.AssetPath exposing (Asset(..))
|
||||
import Nri.Ui.Colors.V1
|
||||
import Svg exposing (svg, use)
|
||||
import Svg.Attributes exposing (xlinkHref)
|
||||
|
||||
|
||||
{-| -}
|
||||
type alias IconLinkModel =
|
||||
{ alt : String
|
||||
, url : String
|
||||
, icon : IconType
|
||||
, disabled : Bool
|
||||
, size : IconSize
|
||||
}
|
||||
|
||||
|
||||
{-| -}
|
||||
type alias IconLinkSpaModel route =
|
||||
{ alt : String
|
||||
, icon : IconType
|
||||
, disabled : Bool
|
||||
, size : IconSize
|
||||
, route : route
|
||||
}
|
||||
|
||||
|
||||
{-| -}
|
||||
type alias IconButtonModel msg =
|
||||
{ alt : String
|
||||
, msg : msg
|
||||
, icon : IconType
|
||||
, disabled : Bool
|
||||
, size : IconSize
|
||||
}
|
||||
|
||||
|
||||
{-| An icon that can be rendered using the functions provided by this module.
|
||||
-}
|
||||
type IconType
|
||||
= ImgIcon Asset
|
||||
| SvgIcon String
|
||||
|
||||
|
||||
{-| Used for determining sizes on Icon.buttons and Icon.links
|
||||
-}
|
||||
type IconSize
|
||||
= Small
|
||||
| Medium
|
||||
|
||||
|
||||
{-| Create an icon that links to a part of NRI
|
||||
Uses our default icon styles (25 x 25 px, azure)
|
||||
-}
|
||||
link : IconLinkModel -> Html msg
|
||||
link =
|
||||
linkBase [ Attributes.target "_self" ]
|
||||
|
||||
|
||||
{-| Create an accessible icon button with an onClick handler
|
||||
Uses our default icon styles (25 x 25 px, azure)
|
||||
-}
|
||||
button : IconButtonModel msg -> Html msg
|
||||
button model =
|
||||
Accessibility.Styled.button
|
||||
[ css
|
||||
[ Css.batch
|
||||
[ backgroundColor transparent
|
||||
, border zero
|
||||
, color Nri.Ui.Colors.V1.azure
|
||||
, fontFamily inherit
|
||||
, Css.property "cursor" "pointer"
|
||||
, padding zero
|
||||
, focus
|
||||
[ backgroundColor transparent
|
||||
]
|
||||
]
|
||||
, sizeStyles model.size
|
||||
]
|
||||
, Events.onClick model.msg
|
||||
, Attributes.disabled model.disabled
|
||||
, Attributes.type_ "button"
|
||||
]
|
||||
[ icon
|
||||
{ alt = model.alt
|
||||
, icon = model.icon
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
{-| -}
|
||||
icon : { alt : String, icon : IconType } -> Html msg
|
||||
icon config =
|
||||
case config.icon of
|
||||
SvgIcon iconId ->
|
||||
svg svgStyle
|
||||
[ Svg.title [] [ RootHtml.text config.alt ]
|
||||
, use [ xlinkHref ("#" ++ iconId) ] []
|
||||
]
|
||||
|> Html.Styled.fromUnstyled
|
||||
|
||||
ImgIcon assetPath ->
|
||||
img config.alt
|
||||
[ Attributes.src (Nri.Ui.AssetPath.url assetPath)
|
||||
]
|
||||
|
||||
|
||||
{-| Use this icon for purely decorative content that would be distracting
|
||||
rather than helpful on a screenreader.
|
||||
-}
|
||||
decorativeIcon : IconType -> Html msg
|
||||
decorativeIcon iconType =
|
||||
case iconType of
|
||||
SvgIcon iconId ->
|
||||
svg
|
||||
(Role.img :: svgStyle)
|
||||
[ use [ xlinkHref ("#" ++ iconId) ] []
|
||||
]
|
||||
|> Html.Styled.fromUnstyled
|
||||
|
||||
ImgIcon assetPath ->
|
||||
decorativeImg [ Attributes.src (Nri.Ui.AssetPath.url assetPath) ]
|
||||
|
||||
|
||||
{-| Use this link for routing within a single page app.
|
||||
|
||||
This will make a normal <a> tag, but change the onClick behavior to avoid reloading the page.
|
||||
|
||||
-}
|
||||
linkSpa : (route -> String) -> (route -> msg) -> IconLinkSpaModel route -> Html msg
|
||||
linkSpa toUrl toMsg config =
|
||||
linkBase
|
||||
[ EventExtras.onClickPreventDefaultForLinkWithHref (toMsg config.route)
|
||||
]
|
||||
{ alt = config.alt
|
||||
, url = toUrl config.route
|
||||
, icon = config.icon
|
||||
, disabled = config.disabled
|
||||
, size = config.size
|
||||
}
|
||||
|
||||
|
||||
{-| Create an icon that links to an external site
|
||||
Uses our default icon styles (25 x 25 px, azure)
|
||||
-}
|
||||
linkExternal : IconLinkModel -> Html msg
|
||||
linkExternal =
|
||||
linkBase [ Attributes.target "_blank" ]
|
||||
|
||||
|
||||
linkBase : List (Attribute msg) -> IconLinkModel -> Html msg
|
||||
linkBase linkAttributes model =
|
||||
span
|
||||
[]
|
||||
[ Html.Styled.a
|
||||
(linkAttributes ++ defaultLinkAttributes model)
|
||||
[ icon { alt = model.alt, icon = model.icon }
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
defaultLinkAttributes : IconLinkModel -> List (Attribute msg)
|
||||
defaultLinkAttributes model =
|
||||
if model.disabled then
|
||||
[ css
|
||||
[ Css.cursor Css.notAllowed
|
||||
, linkStyles
|
||||
, sizeStyles model.size
|
||||
]
|
||||
]
|
||||
|
||||
else
|
||||
[ css [ linkStyles, sizeStyles model.size ]
|
||||
, Attributes.href model.url
|
||||
]
|
||||
|
||||
|
||||
linkStyles : Style
|
||||
linkStyles =
|
||||
Css.batch
|
||||
[ color Nri.Ui.Colors.V1.azure
|
||||
, display inlineBlock
|
||||
, fontFamily inherit
|
||||
, Css.property "cursor" "pointer"
|
||||
, padding zero
|
||||
, visited [ color Nri.Ui.Colors.V1.azure ]
|
||||
]
|
||||
|
||||
|
||||
sizeStyles : IconSize -> Style
|
||||
sizeStyles size =
|
||||
Css.batch <|
|
||||
case size of
|
||||
Small ->
|
||||
[ Css.width (px 20)
|
||||
, Css.height (px 20)
|
||||
]
|
||||
|
||||
Medium ->
|
||||
[ Css.width (px 25)
|
||||
, Css.height (px 25)
|
||||
]
|
||||
|
||||
|
||||
{-| -}
|
||||
activity : { r | activity : String } -> IconType
|
||||
activity assets =
|
||||
SvgIcon assets.activity
|
||||
|
||||
|
||||
{-| -}
|
||||
add : { r | icons_plusBlue_svg : Asset } -> IconType
|
||||
add assets =
|
||||
ImgIcon assets.icons_plusBlue_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
arrowDown : { r | arrowDown : String } -> IconType
|
||||
arrowDown assets =
|
||||
SvgIcon assets.arrowDown
|
||||
|
||||
|
||||
{-| -}
|
||||
arrowLeft : { r | leftArrowBlue_png : Asset } -> IconType
|
||||
arrowLeft assets =
|
||||
ImgIcon assets.leftArrowBlue_png
|
||||
|
||||
|
||||
{-| -}
|
||||
arrowRight : { r | icons_arrowRightBlue_svg : Asset } -> IconType
|
||||
arrowRight assets =
|
||||
ImgIcon assets.icons_arrowRightBlue_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
assignmentStartButtonPrimary : { r | assignmentStartButtonPrimary_svg : Asset } -> IconType
|
||||
assignmentStartButtonPrimary assets =
|
||||
ImgIcon assets.assignmentStartButtonPrimary_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
assignmentStartButtonSecondary : { r | assignmentStartButtonSecondary_svg : Asset } -> IconType
|
||||
assignmentStartButtonSecondary assets =
|
||||
ImgIcon assets.assignmentStartButtonSecondary_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
assignmentTypeDiagnostic : { r | diagnostic : String } -> IconType
|
||||
assignmentTypeDiagnostic assets =
|
||||
SvgIcon assets.diagnostic
|
||||
|
||||
|
||||
{-| -}
|
||||
assignmentTypeGuidedDraft : { r | guidedDraft : String } -> IconType
|
||||
assignmentTypeGuidedDraft assets =
|
||||
SvgIcon assets.guidedDraft
|
||||
|
||||
|
||||
{-| -}
|
||||
assignmentTypePeerReview : { r | peerReview : String } -> IconType
|
||||
assignmentTypePeerReview assets =
|
||||
SvgIcon assets.peerReview
|
||||
|
||||
|
||||
{-| -}
|
||||
assignmentTypeSelfReview : { r | selfReview : String } -> IconType
|
||||
assignmentTypeSelfReview assets =
|
||||
SvgIcon assets.selfReview
|
||||
|
||||
|
||||
{-| -}
|
||||
assignmentTypePractice : { r | practice : String } -> IconType
|
||||
assignmentTypePractice assets =
|
||||
SvgIcon assets.practice
|
||||
|
||||
|
||||
{-| -}
|
||||
assignmentTypeQuickWrite : { r | quickWrite : String } -> IconType
|
||||
assignmentTypeQuickWrite assets =
|
||||
SvgIcon assets.quickWrite
|
||||
|
||||
|
||||
{-| -}
|
||||
assignmentTypeQuiz : { r | quiz : String } -> IconType
|
||||
assignmentTypeQuiz assets =
|
||||
SvgIcon assets.quiz
|
||||
|
||||
|
||||
{-| -}
|
||||
assignmentTypeWritingCycle : { r | writingcycle : String } -> IconType
|
||||
assignmentTypeWritingCycle assets =
|
||||
SvgIcon assets.writingcycle
|
||||
|
||||
|
||||
{-| -}
|
||||
attention : { r | attention_svg : Asset } -> IconType
|
||||
attention assets =
|
||||
ImgIcon assets.attention_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
bang : { r | exclamationPoint_svg : Asset } -> IconType
|
||||
bang assets =
|
||||
ImgIcon assets.exclamationPoint_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
bulb : { r | bulb : String } -> IconType
|
||||
bulb assets =
|
||||
SvgIcon assets.bulb
|
||||
|
||||
|
||||
{-| -}
|
||||
calendar : { r | calendar : String } -> IconType
|
||||
calendar assets =
|
||||
SvgIcon assets.calendar
|
||||
|
||||
|
||||
{-| -}
|
||||
caret : { r | icons_arrowDownBlue_svg : Asset } -> IconType
|
||||
caret assets =
|
||||
ImgIcon assets.icons_arrowDownBlue_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
checkMark : { r | iconCheck_png : Asset } -> IconType
|
||||
checkMark assets =
|
||||
ImgIcon assets.iconCheck_png
|
||||
|
||||
|
||||
{-| -}
|
||||
checkMarkSquiggily : { r | squiggly_png : Asset } -> IconType
|
||||
checkMarkSquiggily assets =
|
||||
ImgIcon assets.squiggly_png
|
||||
|
||||
|
||||
{-| -}
|
||||
checkMarkSvg : { r | checkmark : String } -> IconType
|
||||
checkMarkSvg assets =
|
||||
SvgIcon assets.checkmark
|
||||
|
||||
|
||||
{-| -}
|
||||
class : { r | class : String } -> IconType
|
||||
class assets =
|
||||
SvgIcon assets.class
|
||||
|
||||
|
||||
{-| -}
|
||||
clever : { r | clever : String } -> IconType
|
||||
clever assets =
|
||||
SvgIcon assets.clever
|
||||
|
||||
|
||||
{-| -}
|
||||
clock : { r | clock : String } -> IconType
|
||||
clock assets =
|
||||
SvgIcon assets.clock
|
||||
|
||||
|
||||
{-| -}
|
||||
close : { r | icons_xBlue_svg : Asset } -> IconType
|
||||
close assets =
|
||||
ImgIcon assets.icons_xBlue_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
copy : { r | teach_assignments_copyWhite_svg : Asset } -> IconType
|
||||
copy assets =
|
||||
ImgIcon assets.teach_assignments_copyWhite_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
compassSvg : { r | compass : String } -> IconType
|
||||
compassSvg assets =
|
||||
SvgIcon assets.compass
|
||||
|
||||
|
||||
{-| -}
|
||||
custom : Asset -> IconType
|
||||
custom asset =
|
||||
ImgIcon asset
|
||||
|
||||
|
||||
{-| -}
|
||||
darkBlueCheckMark : { r | darkBlueCheckmark_svg : Asset } -> IconType
|
||||
darkBlueCheckMark assets =
|
||||
ImgIcon assets.darkBlueCheckmark_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
document : { r | document : String } -> IconType
|
||||
document assets =
|
||||
SvgIcon assets.document
|
||||
|
||||
|
||||
{-| -}
|
||||
download : { r | download : String } -> IconType
|
||||
download assets =
|
||||
SvgIcon assets.download
|
||||
|
||||
|
||||
{-| -}
|
||||
edit : { r | edit : String } -> IconType
|
||||
edit assets =
|
||||
SvgIcon assets.edit
|
||||
|
||||
|
||||
{-| -}
|
||||
editWriting : { r | editWriting : String } -> IconType
|
||||
editWriting assets =
|
||||
SvgIcon assets.editWriting
|
||||
|
||||
|
||||
{-| -}
|
||||
equalitySign : { r | icons_equals_svg : Asset } -> IconType
|
||||
equalitySign assets =
|
||||
ImgIcon assets.icons_equals_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
exclamation : { r | exclamation : String } -> IconType
|
||||
exclamation assets =
|
||||
SvgIcon assets.exclamation
|
||||
|
||||
|
||||
{-| -}
|
||||
facebook : { r | facebookBlue_svg : Asset } -> IconType
|
||||
facebook assets =
|
||||
ImgIcon assets.facebookBlue_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
flag : { r | iconFlag_png : Asset } -> IconType
|
||||
flag assets =
|
||||
ImgIcon assets.iconFlag_png
|
||||
|
||||
|
||||
{-| -}
|
||||
flipper : { r | flipper : String } -> IconType
|
||||
flipper assets =
|
||||
SvgIcon assets.flipper
|
||||
|
||||
|
||||
{-| -}
|
||||
footsteps : { r | footsteps : String } -> IconType
|
||||
footsteps assets =
|
||||
SvgIcon assets.footsteps
|
||||
|
||||
|
||||
{-| -}
|
||||
gardening : { r | startingOffBadge_png : Asset } -> IconType
|
||||
gardening assets =
|
||||
ImgIcon assets.startingOffBadge_png
|
||||
|
||||
|
||||
{-| -}
|
||||
gear : { r | gear : String } -> IconType
|
||||
gear assets =
|
||||
SvgIcon assets.gear
|
||||
|
||||
|
||||
{-| -}
|
||||
greenCheckMark : { r | smallCheckmark_png : Asset } -> IconType
|
||||
greenCheckMark assets =
|
||||
ImgIcon assets.smallCheckmark_png
|
||||
|
||||
|
||||
{-| -}
|
||||
guidedWrite : { r | icons_guidedWrite_svg : Asset } -> IconType
|
||||
guidedWrite assets =
|
||||
ImgIcon assets.icons_guidedWrite_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
hat : { r | hat : String } -> IconType
|
||||
hat assets =
|
||||
SvgIcon assets.hat
|
||||
|
||||
|
||||
{-| -}
|
||||
help : { r | icons_helpBlue_svg : Asset } -> IconType
|
||||
help assets =
|
||||
ImgIcon assets.icons_helpBlue_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
helpSvg : { r | help : String } -> IconType
|
||||
helpSvg assets =
|
||||
SvgIcon assets.help
|
||||
|
||||
|
||||
{-| -}
|
||||
highFive : { r | level3Badge_png : Asset } -> IconType
|
||||
highFive assets =
|
||||
ImgIcon assets.level3Badge_png
|
||||
|
||||
|
||||
{-| -}
|
||||
highlighter : { r | highlighter : String } -> IconType
|
||||
highlighter assets =
|
||||
SvgIcon assets.highlighter
|
||||
|
||||
|
||||
{-| -}
|
||||
key : { r | key : String } -> IconType
|
||||
key assets =
|
||||
SvgIcon assets.key
|
||||
|
||||
|
||||
{-| -}
|
||||
keychain : { r | keychain : String } -> IconType
|
||||
keychain assets =
|
||||
SvgIcon assets.keychain
|
||||
|
||||
|
||||
{-| -}
|
||||
late : { r | icons_clockRed_svg : Asset } -> IconType
|
||||
late assets =
|
||||
ImgIcon assets.icons_clockRed_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
leaderboard : { r | leaderboard : String } -> IconType
|
||||
leaderboard assets =
|
||||
SvgIcon assets.leaderboard
|
||||
|
||||
|
||||
{-| -}
|
||||
lightBulb : { r | hint_png : Asset } -> IconType
|
||||
lightBulb assets =
|
||||
ImgIcon assets.hint_png
|
||||
|
||||
|
||||
{-| -}
|
||||
lock : { r | lock : String } -> IconType
|
||||
lock assets =
|
||||
SvgIcon assets.lock
|
||||
|
||||
|
||||
{-| -}
|
||||
lockDeprecated : { r | premiumLock_svg : Asset } -> IconType
|
||||
lockDeprecated assets =
|
||||
ImgIcon assets.premiumLock_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
logo : { r | logoRedBlack_svg : Asset } -> IconType
|
||||
logo assets =
|
||||
ImgIcon assets.logoRedBlack_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
masteryBadge : { r | masteryBadge : String } -> IconType
|
||||
masteryBadge assets =
|
||||
SvgIcon assets.masteryBadge
|
||||
|
||||
|
||||
{-| -}
|
||||
microscope : { r | microscope : String } -> IconType
|
||||
microscope assets =
|
||||
SvgIcon assets.microscope
|
||||
|
||||
|
||||
{-| -}
|
||||
newspaper : { r | newspaper : String } -> IconType
|
||||
newspaper assets =
|
||||
SvgIcon assets.newspaper
|
||||
|
||||
|
||||
{-| -}
|
||||
notStarred : { r | commentNotStarred_png : Asset } -> IconType
|
||||
notStarred assets =
|
||||
ImgIcon assets.commentNotStarred_png
|
||||
|
||||
|
||||
{-| -}
|
||||
okay : { r | level2Badge_png : Asset } -> IconType
|
||||
okay assets =
|
||||
ImgIcon assets.level2Badge_png
|
||||
|
||||
|
||||
{-| -}
|
||||
openClose : { r | openClose : String } -> IconType
|
||||
openClose assets =
|
||||
SvgIcon assets.openClose
|
||||
|
||||
|
||||
{-| -}
|
||||
peerReview : { r | icons_peerReview_svg : Asset } -> IconType
|
||||
peerReview assets =
|
||||
ImgIcon assets.icons_peerReview_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
pen : { r | pen : Asset } -> IconType
|
||||
pen assets =
|
||||
ImgIcon assets.pen
|
||||
|
||||
|
||||
{-| -}
|
||||
performance : { r | performance : String } -> IconType
|
||||
performance assets =
|
||||
SvgIcon assets.performance
|
||||
|
||||
|
||||
{-| -}
|
||||
personBlue : { r | personBlue_svg : Asset } -> IconType
|
||||
personBlue assets =
|
||||
ImgIcon assets.personBlue_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
preview : { r | preview : String } -> IconType
|
||||
preview assets =
|
||||
SvgIcon assets.preview
|
||||
|
||||
|
||||
{-| -}
|
||||
quickWrite : { r | icons_quickWrite_svg : Asset } -> IconType
|
||||
quickWrite assets =
|
||||
ImgIcon assets.icons_quickWrite_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
scale : { r | scale : String } -> IconType
|
||||
scale assets =
|
||||
SvgIcon assets.scale
|
||||
|
||||
|
||||
{-| -}
|
||||
search : { r | search : String } -> IconType
|
||||
search assets =
|
||||
SvgIcon assets.search
|
||||
|
||||
|
||||
{-| -}
|
||||
seeMore : { r | seemore : String } -> IconType
|
||||
seeMore assets =
|
||||
SvgIcon assets.seemore
|
||||
|
||||
|
||||
{-| -}
|
||||
share : { r | share : String } -> IconType
|
||||
share assets =
|
||||
SvgIcon assets.share
|
||||
|
||||
|
||||
{-| -}
|
||||
skip : { r | skip : String } -> IconType
|
||||
skip assets =
|
||||
SvgIcon assets.skip
|
||||
|
||||
|
||||
{-| -}
|
||||
sort : { r | sort : String } -> IconType
|
||||
sort assets =
|
||||
SvgIcon assets.sort
|
||||
|
||||
|
||||
{-| -}
|
||||
sortArrow : { r | sortArrow : String } -> IconType
|
||||
sortArrow assets =
|
||||
SvgIcon assets.sortArrow
|
||||
|
||||
|
||||
{-| -}
|
||||
speedometer : { r | speedometer : String } -> IconType
|
||||
speedometer assets =
|
||||
SvgIcon assets.speedometer
|
||||
|
||||
|
||||
{-| -}
|
||||
starred : { r | commentStarred_png : Asset } -> IconType
|
||||
starred assets =
|
||||
ImgIcon assets.commentStarred_png
|
||||
|
||||
|
||||
{-| -}
|
||||
thumbsUp : { r | level1Badge_png : Asset } -> IconType
|
||||
thumbsUp assets =
|
||||
ImgIcon assets.level1Badge_png
|
||||
|
||||
|
||||
{-| -}
|
||||
twitter : { r | twitterBlue_svg : Asset } -> IconType
|
||||
twitter assets =
|
||||
ImgIcon assets.twitterBlue_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
unarchive : { r | unarchiveBlue2x_png : Asset } -> IconType
|
||||
unarchive assets =
|
||||
ImgIcon assets.unarchiveBlue2x_png
|
||||
|
||||
|
||||
{-| -}
|
||||
writingAssignment : { r | writingAssignment : String } -> IconType
|
||||
writingAssignment assets =
|
||||
SvgIcon assets.writingAssignment
|
||||
|
||||
|
||||
{-| -}
|
||||
x : { r | xWhite_svg : Asset } -> IconType
|
||||
x assets =
|
||||
ImgIcon assets.xWhite_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
xSvg : { r | x : String } -> IconType
|
||||
xSvg assets =
|
||||
SvgIcon assets.x
|
||||
|
||||
|
||||
{-| -}
|
||||
submitting : { r | submitting : String } -> IconType
|
||||
submitting assets =
|
||||
SvgIcon assets.submitting
|
||||
|
||||
|
||||
{-| -}
|
||||
rating : { r | rating : String } -> IconType
|
||||
rating assets =
|
||||
SvgIcon assets.rating
|
||||
|
||||
|
||||
{-| -}
|
||||
revising : { r | revising : String } -> IconType
|
||||
revising assets =
|
||||
SvgIcon assets.revising
|
||||
|
||||
|
||||
{-| Inlining SVG styles because styles.class doesn't work on SVG elements.
|
||||
The `className` property of an SVG element isn't a string, it's an object and so
|
||||
`styles.class` causes a runtime exception by attempting to overwrite it with
|
||||
a string. Another workaround is to use the `Svg.Attributes.class` attribute but
|
||||
since `withNamespace` hides a call to `Html.Attributes.class` we can't do it
|
||||
properly.
|
||||
-}
|
||||
svgStyle : List (RootHtml.Attribute msg)
|
||||
svgStyle =
|
||||
[ RootAttr.style "fill" "currentColor"
|
||||
, RootAttr.style "width" "100%"
|
||||
, RootAttr.style "height" "100%"
|
||||
|
||||
-- don't allow SVG to capture pointer events. This means that we can use it
|
||||
-- in links. See https://css-tricks.com/links-inline-svg-staying-target-events/
|
||||
-- for more details.
|
||||
, RootAttr.style "pointer-events" "none"
|
||||
]
|
@ -1,754 +0,0 @@
|
||||
module Nri.Ui.Icon.V5 exposing
|
||||
( gardening, highFive, okay, thumbsUp, masteryBadge
|
||||
, starred, notStarred, flag
|
||||
, assignmentTypeDiagnostic, assignmentTypePractice, assignmentTypeQuiz, assignmentTypeQuickWrite, assignmentTypeGuidedDraft, assignmentTypePeerReview, assignmentTypeSelfReview, submitting, rating, revising, guidedWrite, assignmentTypeWritingCycle, writingAssignment
|
||||
, assignmentStartButtonPrimary, assignmentStartButtonSecondary
|
||||
, unarchive, share, seeMore, preview, performance, openClose, download
|
||||
, edit, editWriting
|
||||
, class, leaderboard, personBlue
|
||||
, facebook, twitter, clever
|
||||
, arrowDown, sortArrow
|
||||
, checkMarkSvg
|
||||
, xSvg
|
||||
, exclamation, attention
|
||||
, bulb, lightBulb, helpSvg
|
||||
, key, lock
|
||||
, calendar, clock
|
||||
, activity, compassSvg, document, flipper, footsteps
|
||||
, gear, pen, newspaper, sort, speedometer
|
||||
, skip, equalitySign
|
||||
, logo
|
||||
, IconSize(..), IconType
|
||||
, decorativeIcon, icon
|
||||
, IconButtonModel, button
|
||||
, IconLinkModel, link, linkExternal
|
||||
, IconLinkSpaModel, linkSpa
|
||||
)
|
||||
|
||||
{-|
|
||||
|
||||
|
||||
# Changes from V4
|
||||
|
||||
- Removes old icons
|
||||
|
||||
|
||||
# Icon types
|
||||
|
||||
|
||||
## Mastery Icons
|
||||
|
||||
@docs gardening, highFive, okay, thumbsUp, masteryBadge
|
||||
|
||||
|
||||
## Stars and Flags
|
||||
|
||||
@docs starred, notStarred, flag
|
||||
|
||||
|
||||
## Assignment Types
|
||||
|
||||
@docs assignmentTypeDiagnostic, assignmentTypePractice, assignmentTypeQuiz, assignmentTypeQuickWrite, assignmentTypeGuidedDraft, assignmentTypePeerReview, assignmentTypeSelfReview, submitting, rating, revising, guidedWrite, assignmentTypeWritingCycle, writingAssignment
|
||||
|
||||
|
||||
## Student Assignment Actions
|
||||
|
||||
@docs assignmentStartButtonPrimary, assignmentStartButtonSecondary
|
||||
|
||||
|
||||
## Teacher Assignment Actions
|
||||
|
||||
@docs unarchive, share, seeMore, preview, performance, openClose, download
|
||||
|
||||
|
||||
## Edit
|
||||
|
||||
@docs edit, editWriting
|
||||
|
||||
|
||||
## Humans
|
||||
|
||||
@docs class, leaderboard, personBlue
|
||||
|
||||
|
||||
## Social Media
|
||||
|
||||
@docs facebook, twitter, clever
|
||||
|
||||
|
||||
## Arrows and Carets
|
||||
|
||||
@docs arrowDown, sortArrow
|
||||
|
||||
|
||||
## Checkmarks
|
||||
|
||||
@docs checkMarkSvg
|
||||
|
||||
|
||||
## Xs
|
||||
|
||||
@docs xSvg
|
||||
|
||||
|
||||
## Bangs
|
||||
|
||||
@docs exclamation, attention
|
||||
|
||||
|
||||
## Bulbs and Tips
|
||||
|
||||
@docs bulb, lightBulb, helpSvg
|
||||
|
||||
|
||||
## Locks and keys
|
||||
|
||||
@docs key, lock
|
||||
|
||||
|
||||
## Time
|
||||
|
||||
@docs calendar, clock
|
||||
|
||||
|
||||
## Uncategorized (SVGs)
|
||||
|
||||
@docs activity, compassSvg, document, flipper, footsteps
|
||||
@docs gear, pen, newspaper, sort, speedometer
|
||||
@docs skip, equalitySign
|
||||
@docs logo
|
||||
|
||||
|
||||
# Using Icons
|
||||
|
||||
@docs IconSize, IconType
|
||||
@docs decorativeIcon, icon
|
||||
|
||||
|
||||
## Buttons
|
||||
|
||||
@docs IconButtonModel, button
|
||||
|
||||
|
||||
## Links
|
||||
|
||||
@docs IconLinkModel, link, linkExternal
|
||||
@docs IconLinkSpaModel, linkSpa
|
||||
|
||||
-}
|
||||
|
||||
import Accessibility.Role as Role
|
||||
import Accessibility.Styled exposing (..)
|
||||
import Css exposing (..)
|
||||
import EventExtras
|
||||
import Html as RootHtml
|
||||
import Html.Attributes as RootAttr exposing (..)
|
||||
import Html.Styled
|
||||
import Html.Styled.Attributes as Attributes exposing (css)
|
||||
import Html.Styled.Events as Events
|
||||
import Nri.Ui.AssetPath exposing (Asset(..))
|
||||
import Nri.Ui.Colors.V1
|
||||
import Nri.Ui.Html.Attributes.V2 as AttributeExtras
|
||||
import Svg exposing (svg, use)
|
||||
import Svg.Attributes exposing (xlinkHref)
|
||||
|
||||
|
||||
{-| -}
|
||||
type alias IconLinkModel =
|
||||
{ alt : String
|
||||
, url : String
|
||||
, icon : IconType
|
||||
, disabled : Bool
|
||||
, size : IconSize
|
||||
}
|
||||
|
||||
|
||||
{-| -}
|
||||
type alias IconLinkSpaModel route =
|
||||
{ alt : String
|
||||
, icon : IconType
|
||||
, disabled : Bool
|
||||
, size : IconSize
|
||||
, route : route
|
||||
}
|
||||
|
||||
|
||||
{-| -}
|
||||
type alias IconButtonModel msg =
|
||||
{ alt : String
|
||||
, msg : msg
|
||||
, icon : IconType
|
||||
, disabled : Bool
|
||||
, size : IconSize
|
||||
}
|
||||
|
||||
|
||||
{-| An icon that can be rendered using the functions provided by this module.
|
||||
-}
|
||||
type IconType
|
||||
= ImgIcon Asset
|
||||
| SvgIcon String
|
||||
|
||||
|
||||
{-| Used for determining sizes on Icon.buttons and Icon.links
|
||||
-}
|
||||
type IconSize
|
||||
= Small
|
||||
| Medium
|
||||
|
||||
|
||||
{-| Create an icon that links to a part of NRI
|
||||
Uses our default icon styles (25 x 25 px, azure)
|
||||
-}
|
||||
link : IconLinkModel -> Html msg
|
||||
link =
|
||||
linkBase [ Attributes.target "_self" ]
|
||||
|
||||
|
||||
{-| Create an accessible icon button with an onClick handler
|
||||
Uses our default icon styles (25 x 25 px, azure)
|
||||
-}
|
||||
button : IconButtonModel msg -> Html msg
|
||||
button model =
|
||||
Accessibility.Styled.button
|
||||
[ css
|
||||
[ Css.batch
|
||||
[ backgroundColor transparent
|
||||
, border zero
|
||||
, color Nri.Ui.Colors.V1.azure
|
||||
, fontFamily inherit
|
||||
, Css.property "cursor" "pointer"
|
||||
, padding zero
|
||||
, focus
|
||||
[ backgroundColor transparent
|
||||
]
|
||||
]
|
||||
, sizeStyles model.size
|
||||
]
|
||||
, Events.onClick model.msg
|
||||
, Attributes.disabled model.disabled
|
||||
, Attributes.type_ "button"
|
||||
]
|
||||
[ icon
|
||||
{ alt = model.alt
|
||||
, icon = model.icon
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
{-| -}
|
||||
icon : { alt : String, icon : IconType } -> Html msg
|
||||
icon config =
|
||||
case config.icon of
|
||||
SvgIcon iconId ->
|
||||
svg svgStyle
|
||||
[ Svg.title [] [ RootHtml.text config.alt ]
|
||||
, use [ xlinkHref ("#" ++ iconId) ] []
|
||||
]
|
||||
|> Html.Styled.fromUnstyled
|
||||
|
||||
ImgIcon assetPath ->
|
||||
img config.alt
|
||||
[ Attributes.src (Nri.Ui.AssetPath.url assetPath)
|
||||
]
|
||||
|
||||
|
||||
{-| Use this icon for purely decorative content that would be distracting
|
||||
rather than helpful on a screenreader.
|
||||
-}
|
||||
decorativeIcon : IconType -> Html msg
|
||||
decorativeIcon iconType =
|
||||
case iconType of
|
||||
SvgIcon iconId ->
|
||||
svg
|
||||
(Role.img :: svgStyle)
|
||||
[ use [ xlinkHref ("#" ++ iconId) ] []
|
||||
]
|
||||
|> Html.Styled.fromUnstyled
|
||||
|
||||
ImgIcon assetPath ->
|
||||
decorativeImg [ Attributes.src (Nri.Ui.AssetPath.url assetPath) ]
|
||||
|
||||
|
||||
{-| Use this link for routing within a single page app.
|
||||
|
||||
This will make a normal <a> tag, but change the onClick behavior to avoid reloading the page.
|
||||
|
||||
-}
|
||||
linkSpa : (route -> String) -> (route -> msg) -> IconLinkSpaModel route -> Html msg
|
||||
linkSpa toUrl toMsg config =
|
||||
linkBase
|
||||
[ EventExtras.onClickPreventDefaultForLinkWithHref (toMsg config.route)
|
||||
]
|
||||
{ alt = config.alt
|
||||
, url = toUrl config.route
|
||||
, icon = config.icon
|
||||
, disabled = config.disabled
|
||||
, size = config.size
|
||||
}
|
||||
|
||||
|
||||
{-| Create an icon that links to an external site
|
||||
Uses our default icon styles (25 x 25 px, azure)
|
||||
-}
|
||||
linkExternal : IconLinkModel -> Html msg
|
||||
linkExternal =
|
||||
linkBase AttributeExtras.targetBlank
|
||||
|
||||
|
||||
linkBase : List (Attribute msg) -> IconLinkModel -> Html msg
|
||||
linkBase linkAttributes model =
|
||||
span
|
||||
[]
|
||||
[ Html.Styled.a
|
||||
(linkAttributes ++ defaultLinkAttributes model)
|
||||
[ icon { alt = model.alt, icon = model.icon }
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
defaultLinkAttributes : IconLinkModel -> List (Attribute msg)
|
||||
defaultLinkAttributes model =
|
||||
if model.disabled then
|
||||
[ css
|
||||
[ Css.cursor Css.notAllowed
|
||||
, linkStyles
|
||||
, sizeStyles model.size
|
||||
]
|
||||
]
|
||||
|
||||
else
|
||||
[ css [ linkStyles, sizeStyles model.size ]
|
||||
, Attributes.href model.url
|
||||
]
|
||||
|
||||
|
||||
linkStyles : Style
|
||||
linkStyles =
|
||||
Css.batch
|
||||
[ color Nri.Ui.Colors.V1.azure
|
||||
, display inlineBlock
|
||||
, fontFamily inherit
|
||||
, Css.property "cursor" "pointer"
|
||||
, padding zero
|
||||
, visited [ color Nri.Ui.Colors.V1.azure ]
|
||||
]
|
||||
|
||||
|
||||
sizeStyles : IconSize -> Style
|
||||
sizeStyles size =
|
||||
Css.batch <|
|
||||
case size of
|
||||
Small ->
|
||||
[ Css.width (px 20)
|
||||
, Css.height (px 20)
|
||||
]
|
||||
|
||||
Medium ->
|
||||
[ Css.width (px 25)
|
||||
, Css.height (px 25)
|
||||
]
|
||||
|
||||
|
||||
{-| -}
|
||||
activity : { r | activity : String } -> IconType
|
||||
activity assets =
|
||||
SvgIcon assets.activity
|
||||
|
||||
|
||||
{-| -}
|
||||
arrowDown : { r | arrowDown : String } -> IconType
|
||||
arrowDown assets =
|
||||
SvgIcon assets.arrowDown
|
||||
|
||||
|
||||
{-| -}
|
||||
assignmentStartButtonPrimary : { r | assignmentStartButtonPrimary_svg : Asset } -> IconType
|
||||
assignmentStartButtonPrimary assets =
|
||||
ImgIcon assets.assignmentStartButtonPrimary_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
assignmentStartButtonSecondary : { r | assignmentStartButtonSecondary_svg : Asset } -> IconType
|
||||
assignmentStartButtonSecondary assets =
|
||||
ImgIcon assets.assignmentStartButtonSecondary_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
assignmentTypeDiagnostic : { r | diagnostic : String } -> IconType
|
||||
assignmentTypeDiagnostic assets =
|
||||
SvgIcon assets.diagnostic
|
||||
|
||||
|
||||
{-| -}
|
||||
assignmentTypeGuidedDraft : { r | guidedDraft : String } -> IconType
|
||||
assignmentTypeGuidedDraft assets =
|
||||
SvgIcon assets.guidedDraft
|
||||
|
||||
|
||||
{-| -}
|
||||
assignmentTypePeerReview : { r | peerReview : String } -> IconType
|
||||
assignmentTypePeerReview assets =
|
||||
SvgIcon assets.peerReview
|
||||
|
||||
|
||||
{-| -}
|
||||
assignmentTypeSelfReview : { r | selfReview : String } -> IconType
|
||||
assignmentTypeSelfReview assets =
|
||||
SvgIcon assets.selfReview
|
||||
|
||||
|
||||
{-| -}
|
||||
assignmentTypePractice : { r | practice : String } -> IconType
|
||||
assignmentTypePractice assets =
|
||||
SvgIcon assets.practice
|
||||
|
||||
|
||||
{-| -}
|
||||
assignmentTypeQuickWrite : { r | quickWrite : String } -> IconType
|
||||
assignmentTypeQuickWrite assets =
|
||||
SvgIcon assets.quickWrite
|
||||
|
||||
|
||||
{-| -}
|
||||
assignmentTypeQuiz : { r | quiz : String } -> IconType
|
||||
assignmentTypeQuiz assets =
|
||||
SvgIcon assets.quiz
|
||||
|
||||
|
||||
{-| -}
|
||||
assignmentTypeWritingCycle : { r | writingcycle : String } -> IconType
|
||||
assignmentTypeWritingCycle assets =
|
||||
SvgIcon assets.writingcycle
|
||||
|
||||
|
||||
{-| -}
|
||||
attention : { r | attention_svg : Asset } -> IconType
|
||||
attention assets =
|
||||
ImgIcon assets.attention_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
bulb : { r | bulb : String } -> IconType
|
||||
bulb assets =
|
||||
SvgIcon assets.bulb
|
||||
|
||||
|
||||
{-| -}
|
||||
calendar : { r | calendar : String } -> IconType
|
||||
calendar assets =
|
||||
SvgIcon assets.calendar
|
||||
|
||||
|
||||
{-| -}
|
||||
checkMarkSvg : { r | checkmark : String } -> IconType
|
||||
checkMarkSvg assets =
|
||||
SvgIcon assets.checkmark
|
||||
|
||||
|
||||
{-| -}
|
||||
class : { r | class : String } -> IconType
|
||||
class assets =
|
||||
SvgIcon assets.class
|
||||
|
||||
|
||||
{-| -}
|
||||
clever : { r | clever : String } -> IconType
|
||||
clever assets =
|
||||
SvgIcon assets.clever
|
||||
|
||||
|
||||
{-| -}
|
||||
clock : { r | clock : String } -> IconType
|
||||
clock assets =
|
||||
SvgIcon assets.clock
|
||||
|
||||
|
||||
{-| -}
|
||||
compassSvg : { r | compass : String } -> IconType
|
||||
compassSvg assets =
|
||||
SvgIcon assets.compass
|
||||
|
||||
|
||||
{-| -}
|
||||
document : { r | document : String } -> IconType
|
||||
document assets =
|
||||
SvgIcon assets.document
|
||||
|
||||
|
||||
{-| -}
|
||||
download : { r | download : String } -> IconType
|
||||
download assets =
|
||||
SvgIcon assets.download
|
||||
|
||||
|
||||
{-| -}
|
||||
edit : { r | edit : String } -> IconType
|
||||
edit assets =
|
||||
SvgIcon assets.edit
|
||||
|
||||
|
||||
{-| -}
|
||||
editWriting : { r | editWriting : String } -> IconType
|
||||
editWriting assets =
|
||||
SvgIcon assets.editWriting
|
||||
|
||||
|
||||
{-| -}
|
||||
equalitySign : { r | icons_equals_svg : Asset } -> IconType
|
||||
equalitySign assets =
|
||||
ImgIcon assets.icons_equals_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
exclamation : { r | exclamation : String } -> IconType
|
||||
exclamation assets =
|
||||
SvgIcon assets.exclamation
|
||||
|
||||
|
||||
{-| -}
|
||||
facebook : { r | facebookBlue_svg : Asset } -> IconType
|
||||
facebook assets =
|
||||
ImgIcon assets.facebookBlue_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
flag : { r | iconFlag_png : Asset } -> IconType
|
||||
flag assets =
|
||||
ImgIcon assets.iconFlag_png
|
||||
|
||||
|
||||
{-| -}
|
||||
flipper : { r | flipper : String } -> IconType
|
||||
flipper assets =
|
||||
SvgIcon assets.flipper
|
||||
|
||||
|
||||
{-| -}
|
||||
footsteps : { r | footsteps : String } -> IconType
|
||||
footsteps assets =
|
||||
SvgIcon assets.footsteps
|
||||
|
||||
|
||||
{-| -}
|
||||
gardening : { r | startingOffBadge_png : Asset } -> IconType
|
||||
gardening assets =
|
||||
ImgIcon assets.startingOffBadge_png
|
||||
|
||||
|
||||
{-| -}
|
||||
gear : { r | gear : String } -> IconType
|
||||
gear assets =
|
||||
SvgIcon assets.gear
|
||||
|
||||
|
||||
{-| -}
|
||||
guidedWrite : { r | icons_guidedWrite_svg : Asset } -> IconType
|
||||
guidedWrite assets =
|
||||
ImgIcon assets.icons_guidedWrite_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
helpSvg : { r | help : String } -> IconType
|
||||
helpSvg assets =
|
||||
SvgIcon assets.help
|
||||
|
||||
|
||||
{-| -}
|
||||
highFive : { r | level3Badge_png : Asset } -> IconType
|
||||
highFive assets =
|
||||
ImgIcon assets.level3Badge_png
|
||||
|
||||
|
||||
{-| -}
|
||||
key : { r | key : String } -> IconType
|
||||
key assets =
|
||||
SvgIcon assets.key
|
||||
|
||||
|
||||
{-| -}
|
||||
leaderboard : { r | leaderboard : String } -> IconType
|
||||
leaderboard assets =
|
||||
SvgIcon assets.leaderboard
|
||||
|
||||
|
||||
{-| -}
|
||||
lightBulb : { r | hint_png : Asset } -> IconType
|
||||
lightBulb assets =
|
||||
ImgIcon assets.hint_png
|
||||
|
||||
|
||||
{-| -}
|
||||
lock : { r | lock : String } -> IconType
|
||||
lock assets =
|
||||
SvgIcon assets.lock
|
||||
|
||||
|
||||
{-| -}
|
||||
logo : { r | logoRedBlack_svg : Asset } -> IconType
|
||||
logo assets =
|
||||
ImgIcon assets.logoRedBlack_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
masteryBadge : { r | masteryBadge : String } -> IconType
|
||||
masteryBadge assets =
|
||||
SvgIcon assets.masteryBadge
|
||||
|
||||
|
||||
{-| -}
|
||||
newspaper : { r | newspaper : String } -> IconType
|
||||
newspaper assets =
|
||||
SvgIcon assets.newspaper
|
||||
|
||||
|
||||
{-| -}
|
||||
notStarred : { r | commentNotStarred_png : Asset } -> IconType
|
||||
notStarred assets =
|
||||
ImgIcon assets.commentNotStarred_png
|
||||
|
||||
|
||||
{-| -}
|
||||
okay : { r | level2Badge_png : Asset } -> IconType
|
||||
okay assets =
|
||||
ImgIcon assets.level2Badge_png
|
||||
|
||||
|
||||
{-| -}
|
||||
openClose : { r | openClose : String } -> IconType
|
||||
openClose assets =
|
||||
SvgIcon assets.openClose
|
||||
|
||||
|
||||
{-| -}
|
||||
pen : { r | pen : Asset } -> IconType
|
||||
pen assets =
|
||||
ImgIcon assets.pen
|
||||
|
||||
|
||||
{-| -}
|
||||
performance : { r | performance : String } -> IconType
|
||||
performance assets =
|
||||
SvgIcon assets.performance
|
||||
|
||||
|
||||
{-| -}
|
||||
personBlue : { r | personBlue_svg : Asset } -> IconType
|
||||
personBlue assets =
|
||||
ImgIcon assets.personBlue_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
preview : { r | preview : String } -> IconType
|
||||
preview assets =
|
||||
SvgIcon assets.preview
|
||||
|
||||
|
||||
{-| -}
|
||||
seeMore : { r | seemore : String } -> IconType
|
||||
seeMore assets =
|
||||
SvgIcon assets.seemore
|
||||
|
||||
|
||||
{-| -}
|
||||
share : { r | share : String } -> IconType
|
||||
share assets =
|
||||
SvgIcon assets.share
|
||||
|
||||
|
||||
{-| -}
|
||||
skip : { r | skip : String } -> IconType
|
||||
skip assets =
|
||||
SvgIcon assets.skip
|
||||
|
||||
|
||||
{-| -}
|
||||
sort : { r | sort : String } -> IconType
|
||||
sort assets =
|
||||
SvgIcon assets.sort
|
||||
|
||||
|
||||
{-| -}
|
||||
sortArrow : { r | sortArrow : String } -> IconType
|
||||
sortArrow assets =
|
||||
SvgIcon assets.sortArrow
|
||||
|
||||
|
||||
{-| -}
|
||||
speedometer : { r | speedometer : String } -> IconType
|
||||
speedometer assets =
|
||||
SvgIcon assets.speedometer
|
||||
|
||||
|
||||
{-| -}
|
||||
starred : { r | commentStarred_png : Asset } -> IconType
|
||||
starred assets =
|
||||
ImgIcon assets.commentStarred_png
|
||||
|
||||
|
||||
{-| -}
|
||||
thumbsUp : { r | level1Badge_png : Asset } -> IconType
|
||||
thumbsUp assets =
|
||||
ImgIcon assets.level1Badge_png
|
||||
|
||||
|
||||
{-| -}
|
||||
twitter : { r | twitterBlue_svg : Asset } -> IconType
|
||||
twitter assets =
|
||||
ImgIcon assets.twitterBlue_svg
|
||||
|
||||
|
||||
{-| -}
|
||||
unarchive : { r | unarchiveBlue2x_png : Asset } -> IconType
|
||||
unarchive assets =
|
||||
ImgIcon assets.unarchiveBlue2x_png
|
||||
|
||||
|
||||
{-| -}
|
||||
writingAssignment : { r | writingAssignment : String } -> IconType
|
||||
writingAssignment assets =
|
||||
SvgIcon assets.writingAssignment
|
||||
|
||||
|
||||
{-| -}
|
||||
xSvg : { r | x : String } -> IconType
|
||||
xSvg assets =
|
||||
SvgIcon assets.x
|
||||
|
||||
|
||||
{-| -}
|
||||
submitting : { r | submitting : String } -> IconType
|
||||
submitting assets =
|
||||
SvgIcon assets.submitting
|
||||
|
||||
|
||||
{-| -}
|
||||
rating : { r | rating : String } -> IconType
|
||||
rating assets =
|
||||
SvgIcon assets.rating
|
||||
|
||||
|
||||
{-| -}
|
||||
revising : { r | revising : String } -> IconType
|
||||
revising assets =
|
||||
SvgIcon assets.revising
|
||||
|
||||
|
||||
{-| Inlining SVG styles because styles.class doesn't work on SVG elements.
|
||||
The `className` property of an SVG element isn't a string, it's an object and so
|
||||
`styles.class` causes a runtime exception by attempting to overwrite it with
|
||||
a string. Another workaround is to use the `Svg.Attributes.class` attribute but
|
||||
since `withNamespace` hides a call to `Html.Attributes.class` we can't do it
|
||||
properly.
|
||||
-}
|
||||
svgStyle : List (RootHtml.Attribute msg)
|
||||
svgStyle =
|
||||
[ RootAttr.style "fill" "currentColor"
|
||||
, RootAttr.style "width" "100%"
|
||||
, RootAttr.style "height" "100%"
|
||||
|
||||
-- don't allow SVG to capture pointer events. This means that we can use it
|
||||
-- in links. See https://css-tricks.com/links-inline-svg-staying-target-events/
|
||||
-- for more details.
|
||||
, RootAttr.style "pointer-events" "none"
|
||||
]
|
@ -1,222 +0,0 @@
|
||||
module Nri.Ui.InputStyles.V2 exposing
|
||||
( label, Theme(..), input
|
||||
, inputPaddingVertical, inputLineHeight, textAreaHeight, writingLineHeight, writingPadding, writingPaddingTop, writingMinHeight
|
||||
)
|
||||
|
||||
{-| InputStyles used by the TextInput and TextArea widgets.
|
||||
|
||||
@docs label, Theme, input
|
||||
|
||||
|
||||
## Shared hardcoded values
|
||||
|
||||
@docs inputPaddingVertical, inputLineHeight, textAreaHeight, writingLineHeight, writingPadding, writingPaddingTop, writingMinHeight
|
||||
|
||||
-}
|
||||
|
||||
import Css exposing (..)
|
||||
import Css.Global
|
||||
import Nri.Ui.Colors.V1 exposing (..)
|
||||
import Nri.Ui.Fonts.V1
|
||||
|
||||
|
||||
{-| -}
|
||||
type Theme
|
||||
= ContentCreation
|
||||
| Standard
|
||||
| Writing
|
||||
|
||||
|
||||
{-| -}
|
||||
label : Theme -> Bool -> Style
|
||||
label theme inError =
|
||||
let
|
||||
sharedStyles =
|
||||
batch
|
||||
[ backgroundColor white
|
||||
, left (px 10)
|
||||
, top (px 1)
|
||||
, fontSize (px 12)
|
||||
, Nri.Ui.Fonts.V1.baseFont
|
||||
, position absolute
|
||||
, fontWeight (int 600)
|
||||
, property "transition" "all 0.4s ease"
|
||||
]
|
||||
in
|
||||
case theme of
|
||||
Standard ->
|
||||
batch
|
||||
[ sharedStyles
|
||||
, padding2 zero (px 5)
|
||||
, fontSize (px 12)
|
||||
, color navy
|
||||
, if inError then
|
||||
batch [ color purple ]
|
||||
|
||||
else
|
||||
batch []
|
||||
]
|
||||
|
||||
ContentCreation ->
|
||||
batch
|
||||
[ sharedStyles
|
||||
, border3 (px 1) solid gray75
|
||||
, borderRadius (px 4)
|
||||
, padding2 zero (px 5)
|
||||
, fontSize (Css.px 11)
|
||||
, color gray45
|
||||
, padding2 (px 2) (px 5)
|
||||
]
|
||||
|
||||
Writing ->
|
||||
batch
|
||||
[ sharedStyles
|
||||
, padding2 zero (px 5)
|
||||
, border3 (px 1) solid gray75
|
||||
, borderRadius (px 4)
|
||||
, fontSize (px 15)
|
||||
, color navy
|
||||
, if inError then
|
||||
batch
|
||||
[ color purple
|
||||
, backgroundColor white
|
||||
, borderColor purple
|
||||
]
|
||||
|
||||
else
|
||||
batch []
|
||||
]
|
||||
|
||||
|
||||
{-| In order to use these styles in an input module, you will need to add the class "override-sass-styles". This is because sass styles in the monolith have higher precendence than the class styles here.
|
||||
-}
|
||||
input : Theme -> Bool -> Style
|
||||
input theme isInError =
|
||||
let
|
||||
sharedStyles =
|
||||
batch
|
||||
[ border3 (px 1) solid gray75
|
||||
, width (pct 100)
|
||||
, borderRadius (px 8)
|
||||
, pseudoClass "placeholder"
|
||||
[ color gray45
|
||||
]
|
||||
, color gray20
|
||||
|
||||
-- fix bootstrap
|
||||
, display inlineBlock
|
||||
, verticalAlign top
|
||||
, marginBottom zero
|
||||
, marginTop (px 9)
|
||||
, boxShadow6 inset zero (px 3) zero zero gray92
|
||||
, property "transition" "border-color 0.4s ease"
|
||||
, boxSizing borderBox
|
||||
, focus
|
||||
[ borderColor azure
|
||||
, outline none
|
||||
, boxShadow6 inset zero (px 3) zero zero glacier
|
||||
]
|
||||
, if isInError then
|
||||
batch
|
||||
[ borderColor purple
|
||||
, boxShadow6 inset zero (px 3) zero zero purpleLight
|
||||
, focus
|
||||
[ borderColor purple
|
||||
, boxShadow6 inset zero (px 3) zero zero purpleLight
|
||||
]
|
||||
]
|
||||
|
||||
else
|
||||
batch []
|
||||
]
|
||||
in
|
||||
batch
|
||||
[ Css.Global.withClass "override-sass-styles"
|
||||
[ case theme of
|
||||
Standard ->
|
||||
batch
|
||||
[ sharedStyles
|
||||
, padding2 inputPaddingVertical (px 15)
|
||||
, fontSize (px 15)
|
||||
, Nri.Ui.Fonts.V1.baseFont
|
||||
]
|
||||
|
||||
Writing ->
|
||||
batch
|
||||
[ sharedStyles
|
||||
, Nri.Ui.Fonts.V1.quizFont
|
||||
, fontSize (px 18)
|
||||
, lineHeight writingLineHeight
|
||||
, padding writingPadding
|
||||
, paddingTop writingPaddingTop
|
||||
, focus
|
||||
[ Css.Global.adjacentSiblings
|
||||
[ Css.Global.label
|
||||
[ backgroundColor azure
|
||||
, color white
|
||||
, borderColor azure
|
||||
, if isInError then
|
||||
batch
|
||||
[ backgroundColor purple
|
||||
, color white
|
||||
, borderColor purple
|
||||
]
|
||||
|
||||
else
|
||||
batch []
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
ContentCreation ->
|
||||
batch
|
||||
[ sharedStyles
|
||||
, padding2 inputPaddingVertical (px 15)
|
||||
, fontSize (px 15)
|
||||
, Nri.Ui.Fonts.V1.baseFont
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
{-| -}
|
||||
inputPaddingVertical : Px
|
||||
inputPaddingVertical =
|
||||
px 12
|
||||
|
||||
|
||||
{-| -}
|
||||
inputLineHeight : Px
|
||||
inputLineHeight =
|
||||
px 21
|
||||
|
||||
|
||||
{-| -}
|
||||
textAreaHeight : Px
|
||||
textAreaHeight =
|
||||
px 100
|
||||
|
||||
|
||||
{-| -}
|
||||
writingLineHeight : Px
|
||||
writingLineHeight =
|
||||
px 27
|
||||
|
||||
|
||||
{-| -}
|
||||
writingPadding : Px
|
||||
writingPadding =
|
||||
px 15
|
||||
|
||||
|
||||
{-| -}
|
||||
writingPaddingTop : Px
|
||||
writingPaddingTop =
|
||||
px 20
|
||||
|
||||
|
||||
{-| -}
|
||||
writingMinHeight : Px
|
||||
writingMinHeight =
|
||||
px 150
|
@ -307,9 +307,6 @@ viewEntry entry_ =
|
||||
|
||||
|
||||
{-| Display an icon button consistent with menu button.
|
||||
|
||||
Prefer to use ClickableSvg with the `withTooltipAbove` helper.
|
||||
|
||||
-}
|
||||
iconButton :
|
||||
{ icon : Svg.Svg
|
||||
@ -434,9 +431,6 @@ iconButtonWithMenu config =
|
||||
|
||||
|
||||
{-| Display an icon link consistent with menu button styles.
|
||||
|
||||
Prefer to use ClickableSvg with the `withTooltipAbove` helper.
|
||||
|
||||
-}
|
||||
iconLink :
|
||||
{ icon : Svg.Svg
|
||||
|
@ -1,553 +0,0 @@
|
||||
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
|
||||
]
|
||||
]
|
@ -1,723 +0,0 @@
|
||||
module Nri.Ui.Message.V2 exposing
|
||||
( somethingWentWrong
|
||||
, view, Attribute
|
||||
, tiny, large, banner
|
||||
, plaintext, markdown, html
|
||||
, tip, error, alert, success, customTheme
|
||||
, alertRole, alertDialogRole
|
||||
, onDismiss
|
||||
)
|
||||
|
||||
{-| Changes from V1:
|
||||
|
||||
- adds `alertRole`, `alertDialogRole` role attributes
|
||||
- rename `BannerAttribute` -> `Attribute`
|
||||
- accept `Attribute`s on any `Message` type
|
||||
- ☠️ remove `mapContent`
|
||||
- expose `plaintext`, `markdown`, and `html` Attribute helpers instead of having `Content(..)` in the view APIs
|
||||
- expose theme `Attribute` helpers instead of having `Theme(..)` in the view APIs
|
||||
- exposes a singular `view` function (`tiny`, `large`, and `banner` are now `Attribute`s)
|
||||
- uses `alertRole` in `somethingWentWrong`
|
||||
|
||||
|
||||
# View
|
||||
|
||||
@docs somethingWentWrong
|
||||
@docs view, Attribute
|
||||
|
||||
|
||||
## Size
|
||||
|
||||
@docs tiny, large, banner
|
||||
|
||||
|
||||
## Content
|
||||
|
||||
@docs plaintext, markdown, html
|
||||
|
||||
|
||||
## Theme
|
||||
|
||||
@docs tip, error, alert, success, customTheme
|
||||
|
||||
|
||||
## Role
|
||||
|
||||
@docs alertRole, alertDialogRole
|
||||
|
||||
|
||||
## Actions
|
||||
|
||||
@docs onDismiss
|
||||
|
||||
-}
|
||||
|
||||
import Accessibility.Styled as Html exposing (..)
|
||||
import Accessibility.Styled.Role as Role
|
||||
import Accessibility.Styled.Widget as Widget
|
||||
import Css exposing (..)
|
||||
import Css.Global
|
||||
import Html.Styled.Attributes exposing (css)
|
||||
import Html.Styled.Events exposing (onClick)
|
||||
import Markdown
|
||||
import Nri.Ui
|
||||
import Nri.Ui.ClickableSvg.V2 as ClickableSvg
|
||||
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
|
||||
|
||||
|
||||
{-|
|
||||
|
||||
view =
|
||||
Message.view
|
||||
[ Message.tip
|
||||
, Message.markdown "Don't tip too much, or your waitress will **fall over**!"
|
||||
]
|
||||
|
||||
-}
|
||||
view : List (Attribute msg) -> Html msg
|
||||
view attributes_ =
|
||||
let
|
||||
attributes =
|
||||
configFromAttributes attributes_
|
||||
|
||||
role =
|
||||
getRoleAttribute attributes.role
|
||||
|
||||
html_ =
|
||||
contentToHtml attributes.content
|
||||
|
||||
backgroundColor_ =
|
||||
getBackgroundColor attributes.size attributes.theme
|
||||
|
||||
color_ =
|
||||
getColor attributes.size attributes.theme
|
||||
|
||||
icon =
|
||||
getIcon attributes.size attributes.theme
|
||||
in
|
||||
Nri.Ui.styled div
|
||||
"Nri-Ui-Message-V2"
|
||||
[ Fonts.baseFont
|
||||
, color color_
|
||||
, boxSizing borderBox
|
||||
, styleOverrides
|
||||
]
|
||||
role
|
||||
[ case attributes.size of
|
||||
Tiny ->
|
||||
Nri.Ui.styled div
|
||||
"Nri-Ui-Message--tiny"
|
||||
[ displayFlex
|
||||
, justifyContent start
|
||||
, alignItems center
|
||||
, paddingTop (px 6)
|
||||
, paddingBottom (px 8)
|
||||
, fontSize (px 13)
|
||||
]
|
||||
[]
|
||||
[ Nri.Ui.styled div "Nri-Ui-Message--icon" [ alignSelf flexStart ] [] [ icon ]
|
||||
, div [] html_
|
||||
, case attributes.onDismiss of
|
||||
Nothing ->
|
||||
text ""
|
||||
|
||||
Just msg ->
|
||||
tinyDismissButton msg
|
||||
]
|
||||
|
||||
Large ->
|
||||
Nri.Ui.styled div
|
||||
"Nri-Ui-Message-large"
|
||||
[ displayFlex
|
||||
, alignItems center
|
||||
|
||||
-- Box
|
||||
, borderRadius (px 8)
|
||||
, padding (px 20)
|
||||
, backgroundColor_
|
||||
|
||||
-- Fonts
|
||||
, fontSize (px 15)
|
||||
, fontWeight (int 600)
|
||||
, lineHeight (px 21)
|
||||
]
|
||||
[]
|
||||
[ icon
|
||||
, div
|
||||
[ css
|
||||
[ minWidth (px 100)
|
||||
, flexBasis (px 100)
|
||||
, flexGrow (int 1)
|
||||
]
|
||||
]
|
||||
html_
|
||||
, case attributes.onDismiss of
|
||||
Nothing ->
|
||||
text ""
|
||||
|
||||
Just msg ->
|
||||
largeDismissButton msg
|
||||
]
|
||||
|
||||
Banner ->
|
||||
Nri.Ui.styled div
|
||||
"Nri-Ui-Message-banner"
|
||||
[ displayFlex
|
||||
, justifyContent center
|
||||
, alignItems center
|
||||
, backgroundColor_
|
||||
|
||||
-- Fonts
|
||||
, fontSize (px 20)
|
||||
, fontWeight (int 700)
|
||||
, lineHeight (px 27)
|
||||
]
|
||||
[]
|
||||
[ span
|
||||
[ css
|
||||
[ alignItems center
|
||||
, displayFlex
|
||||
, justifyContent center
|
||||
, padding (px 20)
|
||||
, width (Css.pct 100)
|
||||
]
|
||||
]
|
||||
[ 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
|
||||
]
|
||||
[]
|
||||
html_
|
||||
]
|
||||
, case attributes.onDismiss of
|
||||
Nothing ->
|
||||
text ""
|
||||
|
||||
Just msg ->
|
||||
bannerDismissButton msg
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
{-| Shows an appropriate error message for when something unhandled happened.
|
||||
|
||||
view maybeDetailedErrorMessage =
|
||||
viewMaybe Message.somethingWentWrong maybeDetailedErrorMessage
|
||||
|
||||
-}
|
||||
somethingWentWrong : String -> Html msg
|
||||
somethingWentWrong errorMessageForEngineers =
|
||||
div []
|
||||
[ view
|
||||
[ tiny
|
||||
, error
|
||||
, alertRole
|
||||
, plaintext "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 ]
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
{-| Shows a tiny alert message. We commonly use these for validation errors and small hints to users.
|
||||
|
||||
Message.view [ Message.tiny ]
|
||||
|
||||
This is the default size for a Message.
|
||||
|
||||
-}
|
||||
tiny : Attribute msg
|
||||
tiny =
|
||||
Attribute <| \config -> { config | size = Tiny }
|
||||
|
||||
|
||||
{-| Shows a large alert or callout message. We commonly use these for highlighted tips, instructions, or asides in page copy.
|
||||
|
||||
Message.view [ Message.large ]
|
||||
|
||||
-}
|
||||
large : Attribute msg
|
||||
large =
|
||||
Attribute <| \config -> { config | size = Large }
|
||||
|
||||
|
||||
{-| 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.
|
||||
|
||||
Message.view [ Message.banner ]
|
||||
|
||||
-}
|
||||
banner : Attribute msg
|
||||
banner =
|
||||
Attribute <| \config -> { config | size = Banner }
|
||||
|
||||
|
||||
{-| -}
|
||||
plaintext : String -> Attribute msg
|
||||
plaintext content =
|
||||
Attribute <| \config -> { config | content = Plain content }
|
||||
|
||||
|
||||
{-| -}
|
||||
markdown : String -> Attribute msg
|
||||
markdown content =
|
||||
Attribute <| \config -> { config | content = Markdown content }
|
||||
|
||||
|
||||
{-| -}
|
||||
html : List (Html msg) -> Attribute msg
|
||||
html content =
|
||||
Attribute <| \config -> { config | content = Html content }
|
||||
|
||||
|
||||
{-| This is the default theme for a Message.
|
||||
-}
|
||||
tip : Attribute msg
|
||||
tip =
|
||||
Attribute <| \config -> { config | theme = Tip }
|
||||
|
||||
|
||||
{-| -}
|
||||
error : Attribute msg
|
||||
error =
|
||||
Attribute <| \config -> { config | theme = Error }
|
||||
|
||||
|
||||
{-| -}
|
||||
alert : Attribute msg
|
||||
alert =
|
||||
Attribute <| \config -> { config | theme = Alert }
|
||||
|
||||
|
||||
{-| -}
|
||||
success : Attribute msg
|
||||
success =
|
||||
Attribute <| \config -> { config | theme = Success }
|
||||
|
||||
|
||||
{-| -}
|
||||
customTheme : { color : Color, backgroundColor : Color, icon : Svg } -> Attribute msg
|
||||
customTheme custom_ =
|
||||
Attribute <| \config -> { config | theme = Custom custom_ }
|
||||
|
||||
|
||||
{-| Adds a dismiss ("X" icon) to a message which will produce the given `msg` when clicked.
|
||||
-}
|
||||
onDismiss : msg -> Attribute msg
|
||||
onDismiss msg =
|
||||
Attribute <| \config -> { config | onDismiss = Just msg }
|
||||
|
||||
|
||||
{-| Use this attribute when a user's immediate attention on the Message is required.
|
||||
|
||||
For example, use this attribute when:
|
||||
|
||||
> - An invalid value was entered into a form field
|
||||
> - The user's login session is about to expire
|
||||
> - The connection to the server was lost, local changes will not be saved
|
||||
|
||||
-- Excerpted from [Using the alert role MDN docs](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_alert_role)
|
||||
|
||||
-}
|
||||
alertRole : Attribute msg
|
||||
alertRole =
|
||||
Attribute <| \config -> { config | role = Just AlertRole }
|
||||
|
||||
|
||||
{-| Use this attribute when (1) a user's immediate attention on the Message is required,
|
||||
(2) the Message contains interactible elements, and (3) you've correctly set up the Message to be
|
||||
modal (i.e., you've set up tab-wrapping, the body's overflow is hidden, the user
|
||||
can't interact with elements apart from the Message's contents...)
|
||||
|
||||
When you use this role, verify that you are using it correctly using [this
|
||||
MDN article](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_alertdialog_role).
|
||||
|
||||
-}
|
||||
alertDialogRole : Attribute msg
|
||||
alertDialogRole =
|
||||
Attribute <| \config -> { config | role = Just AlertDialog }
|
||||
|
||||
|
||||
|
||||
--
|
||||
-- PRIVATE
|
||||
--
|
||||
|
||||
|
||||
{-| Construct an `Attribute` using a helper like `onDismiss` or `alert`.
|
||||
-}
|
||||
type Attribute msg
|
||||
= Attribute (BannerConfig msg -> BannerConfig msg)
|
||||
|
||||
|
||||
{-| PRIVATE
|
||||
-}
|
||||
type alias BannerConfig msg =
|
||||
{ onDismiss : Maybe msg
|
||||
, role : Maybe Role
|
||||
, content : Content msg
|
||||
, theme : Theme
|
||||
, size : Size
|
||||
}
|
||||
|
||||
|
||||
{-| PRIVATE
|
||||
-}
|
||||
configFromAttributes : List (Attribute msg) -> BannerConfig msg
|
||||
configFromAttributes attr =
|
||||
List.foldl (\(Attribute set) -> set)
|
||||
{ onDismiss = Nothing
|
||||
, role = Nothing
|
||||
, content = Plain ""
|
||||
, theme = Tip
|
||||
, size = Tiny
|
||||
}
|
||||
attr
|
||||
|
||||
|
||||
|
||||
-- Size
|
||||
|
||||
|
||||
type Size
|
||||
= Tiny
|
||||
| Large
|
||||
| Banner
|
||||
|
||||
|
||||
|
||||
-- Message contents
|
||||
|
||||
|
||||
{-| 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))
|
||||
|
||||
|
||||
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_
|
||||
|
||||
|
||||
|
||||
-- Themes
|
||||
|
||||
|
||||
{-| `Error` / `Alert` / `Tip` / `Success`
|
||||
-}
|
||||
type Theme
|
||||
= Error
|
||||
| Alert
|
||||
| Tip
|
||||
| Success
|
||||
| Custom
|
||||
{ color : Color
|
||||
, backgroundColor : Color
|
||||
, icon : Svg
|
||||
}
|
||||
|
||||
|
||||
getColor : Size -> Theme -> Color
|
||||
getColor size theme =
|
||||
case theme of
|
||||
Custom { color } ->
|
||||
color
|
||||
|
||||
Error ->
|
||||
case size of
|
||||
Tiny ->
|
||||
Colors.purple
|
||||
|
||||
_ ->
|
||||
Colors.purpleDark
|
||||
|
||||
Alert ->
|
||||
case size of
|
||||
Tiny ->
|
||||
Colors.redDark
|
||||
|
||||
_ ->
|
||||
Colors.navy
|
||||
|
||||
Tip ->
|
||||
Colors.navy
|
||||
|
||||
Success ->
|
||||
Colors.greenDarkest
|
||||
|
||||
|
||||
getBackgroundColor : Size -> Theme -> Style
|
||||
getBackgroundColor size theme =
|
||||
case ( size, theme ) of
|
||||
( Tiny, _ ) ->
|
||||
Css.batch []
|
||||
|
||||
( Large, Tip ) ->
|
||||
Css.backgroundColor Colors.sunshine
|
||||
|
||||
( Banner, Tip ) ->
|
||||
Css.backgroundColor Colors.frost
|
||||
|
||||
( _, Error ) ->
|
||||
Css.backgroundColor Colors.purpleLight
|
||||
|
||||
( _, Alert ) ->
|
||||
Css.backgroundColor Colors.sunshine
|
||||
|
||||
( _, Success ) ->
|
||||
Css.backgroundColor Colors.greenLightest
|
||||
|
||||
( _, Custom { backgroundColor } ) ->
|
||||
Css.backgroundColor backgroundColor
|
||||
|
||||
|
||||
getIcon : Size -> Theme -> Html msg
|
||||
getIcon size theme =
|
||||
let
|
||||
( iconSize, marginRight ) =
|
||||
case size of
|
||||
Tiny ->
|
||||
( px 20, Css.marginRight (Css.px 5) )
|
||||
|
||||
Large ->
|
||||
( px 35, Css.marginRight (Css.px 10) )
|
||||
|
||||
Banner ->
|
||||
( px 50, Css.marginRight (Css.px 20) )
|
||||
in
|
||||
case theme of
|
||||
Error ->
|
||||
UiIcon.exclamation
|
||||
|> NriSvg.withColor Colors.purple
|
||||
|> NriSvg.withWidth iconSize
|
||||
|> NriSvg.withHeight iconSize
|
||||
|> NriSvg.withCss [ marginRight, Css.flexShrink Css.zero ]
|
||||
|> NriSvg.withLabel "Error"
|
||||
|> NriSvg.toHtml
|
||||
|
||||
Alert ->
|
||||
let
|
||||
color =
|
||||
case size of
|
||||
Tiny ->
|
||||
Colors.red
|
||||
|
||||
_ ->
|
||||
Colors.ochre
|
||||
in
|
||||
UiIcon.exclamation
|
||||
|> NriSvg.withColor color
|
||||
|> NriSvg.withWidth iconSize
|
||||
|> NriSvg.withHeight iconSize
|
||||
|> NriSvg.withCss [ marginRight, Css.flexShrink Css.zero ]
|
||||
|> NriSvg.withLabel "Alert"
|
||||
|> NriSvg.toHtml
|
||||
|
||||
Tip ->
|
||||
case size of
|
||||
Tiny ->
|
||||
UiIcon.bulb
|
||||
|> NriSvg.withColor Colors.yellow
|
||||
|> NriSvg.withWidth iconSize
|
||||
|> NriSvg.withHeight iconSize
|
||||
|> NriSvg.withCss [ marginRight, Css.flexShrink Css.zero ]
|
||||
|> NriSvg.withLabel "Tip"
|
||||
|> NriSvg.toHtml
|
||||
|
||||
Large ->
|
||||
UiIcon.bulb
|
||||
|> NriSvg.withColor Colors.navy
|
||||
|> NriSvg.withWidth iconSize
|
||||
|> NriSvg.withHeight iconSize
|
||||
|> NriSvg.withCss [ marginRight, Css.flexShrink Css.zero ]
|
||||
|> NriSvg.withLabel "Tip"
|
||||
|> NriSvg.toHtml
|
||||
|
||||
Banner ->
|
||||
div
|
||||
[ css
|
||||
[ borderRadius (pct 50)
|
||||
, height (px 50)
|
||||
, width (px 50)
|
||||
, Css.marginRight (Css.px 20)
|
||||
, backgroundColor Colors.navy
|
||||
, displayFlex
|
||||
, Css.flexShrink Css.zero
|
||||
, alignItems center
|
||||
, justifyContent center
|
||||
]
|
||||
]
|
||||
[ UiIcon.bulb
|
||||
|> NriSvg.withColor Colors.mustard
|
||||
|> NriSvg.withWidth (Css.px 32)
|
||||
|> NriSvg.withHeight (Css.px 32)
|
||||
|> NriSvg.toHtml
|
||||
]
|
||||
|
||||
Success ->
|
||||
UiIcon.checkmarkInCircle
|
||||
|> NriSvg.withColor Colors.green
|
||||
|> NriSvg.withWidth iconSize
|
||||
|> NriSvg.withHeight iconSize
|
||||
|> NriSvg.withCss [ marginRight, Css.flexShrink Css.zero ]
|
||||
|> NriSvg.withLabel "Success"
|
||||
|> NriSvg.toHtml
|
||||
|
||||
Custom { icon } ->
|
||||
icon
|
||||
|> NriSvg.withWidth iconSize
|
||||
|> NriSvg.withHeight iconSize
|
||||
|> NriSvg.withCss [ marginRight, Css.flexShrink Css.zero ]
|
||||
|> NriSvg.toHtml
|
||||
|
||||
|
||||
|
||||
-- Role
|
||||
|
||||
|
||||
type Role
|
||||
= AlertRole
|
||||
| AlertDialog
|
||||
|
||||
|
||||
getRoleAttribute : Maybe Role -> List (Html.Attribute msg)
|
||||
getRoleAttribute role =
|
||||
case role of
|
||||
Just AlertRole ->
|
||||
[ Role.alert ]
|
||||
|
||||
Just AlertDialog ->
|
||||
[ Role.alertDialog ]
|
||||
|
||||
Nothing ->
|
||||
[]
|
||||
|
||||
|
||||
|
||||
-- Style overrides
|
||||
|
||||
|
||||
styleOverrides : Style
|
||||
styleOverrides =
|
||||
Css.Global.descendants
|
||||
[ Css.Global.a
|
||||
[ textDecoration none
|
||||
, color Colors.azure
|
||||
, borderBottom3 (px 1) solid Colors.azure
|
||||
, visited [ color Colors.azure ]
|
||||
]
|
||||
, -- 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.p
|
||||
[ margin zero
|
||||
, fontSize (px 13)
|
||||
, Fonts.baseFont
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
|
||||
-- Dismiss buttons
|
||||
|
||||
|
||||
tinyDismissButton : msg -> Html msg
|
||||
tinyDismissButton msg =
|
||||
Nri.Ui.styled div
|
||||
"dismiss-button-container"
|
||||
[]
|
||||
[]
|
||||
[ ClickableSvg.button "Dismiss message"
|
||||
UiIcon.x
|
||||
[ ClickableSvg.onClick msg
|
||||
, ClickableSvg.exactWidth 16
|
||||
, ClickableSvg.exactHeight 16
|
||||
, ClickableSvg.css
|
||||
[ Css.verticalAlign Css.middle
|
||||
, Css.marginLeft (Css.px 5)
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
largeDismissButton : msg -> Html msg
|
||||
largeDismissButton msg =
|
||||
Nri.Ui.styled div
|
||||
"dismiss-button-container"
|
||||
[ padding2 Css.zero (px 20)
|
||||
]
|
||||
[]
|
||||
[ ClickableSvg.button "Dismiss message"
|
||||
UiIcon.x
|
||||
[ ClickableSvg.onClick msg
|
||||
, ClickableSvg.exactWidth 16
|
||||
, ClickableSvg.exactHeight 16
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
bannerDismissButton : msg -> Html msg
|
||||
bannerDismissButton msg =
|
||||
Nri.Ui.styled div
|
||||
"dismiss-button-container"
|
||||
[ padding2 (px 30) (px 40) ]
|
||||
[]
|
||||
[ ClickableSvg.button "Dismiss banner"
|
||||
UiIcon.x
|
||||
[ ClickableSvg.onClick msg
|
||||
, ClickableSvg.exactWidth 16
|
||||
, ClickableSvg.exactHeight 16
|
||||
]
|
||||
]
|
@ -53,7 +53,6 @@ import Accessibility.Styled.Widget as Widget
|
||||
import Browser
|
||||
import Browser.Dom as Dom
|
||||
import Browser.Events
|
||||
import Color.Transparent as Transparent
|
||||
import Css exposing (..)
|
||||
import Css.Transitions
|
||||
import Html.Styled as Root
|
||||
@ -65,6 +64,7 @@ import Nri.Ui.Fonts.V1 as Fonts
|
||||
import Nri.Ui.SpriteSheet
|
||||
import Nri.Ui.Svg.V1
|
||||
import Task
|
||||
import TransparentColor as Transparent
|
||||
|
||||
|
||||
{-| -}
|
||||
|
@ -165,7 +165,6 @@ import Accessibility.Styled.Widget as Widget
|
||||
import Browser
|
||||
import Browser.Dom as Dom
|
||||
import Browser.Events
|
||||
import Color.Transparent as Transparent
|
||||
import Css exposing (..)
|
||||
import Css.Transitions
|
||||
import Html.Styled as Root
|
||||
@ -180,6 +179,7 @@ import Nri.Ui.Html.Attributes.V2 as ExtraAttributes
|
||||
import Nri.Ui.SpriteSheet
|
||||
import Nri.Ui.Svg.V1
|
||||
import Task
|
||||
import TransparentColor as Transparent
|
||||
|
||||
|
||||
{-| -}
|
||||
|
@ -1,246 +0,0 @@
|
||||
module Nri.Ui.Modal.V3 exposing
|
||||
( Model
|
||||
, info
|
||||
, warning
|
||||
)
|
||||
|
||||
{-| Changes from V2:
|
||||
|
||||
- Add assets for close button
|
||||
|
||||
@docs Model
|
||||
@docs info
|
||||
@docs warning
|
||||
|
||||
-}
|
||||
|
||||
import Accessibility.Styled as Html exposing (..)
|
||||
import Accessibility.Styled.Role as Role
|
||||
import Accessibility.Styled.Widget as Widget
|
||||
import Css
|
||||
import Css.Global exposing (Snippet, body, children, descendants, everything, selector)
|
||||
import Html.Styled
|
||||
import Html.Styled.Events exposing (onClick)
|
||||
import Nri.Ui
|
||||
import Nri.Ui.AssetPath exposing (Asset(..))
|
||||
import Nri.Ui.Colors.Extra
|
||||
import Nri.Ui.Colors.V1
|
||||
import Nri.Ui.Fonts.V1 as Fonts
|
||||
import Nri.Ui.Icon.V3 as Icon
|
||||
|
||||
|
||||
{-|
|
||||
|
||||
- `onDismiss`: If `Nothing`, the modal will not be dismissable
|
||||
- `visibleTitle`: If `False`, the title will still be used for screen readers
|
||||
- `content`: This will be placed in a `width:100%` div in the main area of the modal
|
||||
- `footerContent`: The optional items here will be stacked below the main content area and center-aligned.
|
||||
Commonly you will either give a list of Nri.Ui.Buttons,
|
||||
or an empty list.
|
||||
|
||||
-}
|
||||
type alias Model msg =
|
||||
{ title : String
|
||||
, visibleTitle : Bool
|
||||
, content : Html msg
|
||||
, footerContent : List (Html msg)
|
||||
, onDismiss : Maybe msg
|
||||
, width : Maybe Int
|
||||
}
|
||||
|
||||
|
||||
type alias Assets r =
|
||||
{ r | icons_xBlue_svg : Asset }
|
||||
|
||||
|
||||
type ModalType
|
||||
= Info
|
||||
| Warning
|
||||
|
||||
|
||||
{-| -}
|
||||
info : Assets r -> Model msg -> Html msg
|
||||
info assets =
|
||||
view assets Info
|
||||
|
||||
|
||||
{-| -}
|
||||
warning : Assets r -> Model msg -> Html msg
|
||||
warning assets =
|
||||
view assets Warning
|
||||
|
||||
|
||||
view : Assets r -> ModalType -> Model msg -> Html msg
|
||||
view assets modalType { title, visibleTitle, content, onDismiss, footerContent, width } =
|
||||
Nri.Ui.styled div
|
||||
"modal-backdrop-container"
|
||||
((case modalType of
|
||||
Info ->
|
||||
Css.backgroundColor (Nri.Ui.Colors.Extra.withAlpha 0.9 Nri.Ui.Colors.V1.navy)
|
||||
|
||||
Warning ->
|
||||
Css.backgroundColor (Nri.Ui.Colors.Extra.withAlpha 0.9 Nri.Ui.Colors.V1.gray20)
|
||||
)
|
||||
:: [ Css.height (Css.vh 100)
|
||||
, Css.left Css.zero
|
||||
, Css.overflow Css.hidden
|
||||
, Css.position Css.fixed
|
||||
, Css.top Css.zero
|
||||
, Css.width (Css.pct 100)
|
||||
, Css.zIndex (Css.int 200)
|
||||
, Css.displayFlex
|
||||
, Css.alignItems Css.center
|
||||
, Css.justifyContent Css.center
|
||||
]
|
||||
)
|
||||
[ Role.dialog
|
||||
, Widget.label title
|
||||
, Widget.modal True
|
||||
]
|
||||
[ Nri.Ui.styled Html.Styled.div
|
||||
"modal-click-catcher"
|
||||
[ Css.bottom Css.zero
|
||||
, Css.left Css.zero
|
||||
, Css.position Css.absolute
|
||||
, Css.right Css.zero
|
||||
, Css.top Css.zero
|
||||
]
|
||||
(case onDismiss of
|
||||
Nothing ->
|
||||
[]
|
||||
|
||||
Just msg ->
|
||||
[ onClick msg ]
|
||||
)
|
||||
[]
|
||||
, Nri.Ui.styled div
|
||||
"modal-container"
|
||||
[ Css.width (Css.px 600)
|
||||
, Css.maxHeight <| Css.calc (Css.vh 100) Css.minus (Css.px 100)
|
||||
, Css.padding4 (Css.px 40) Css.zero (Css.px 40) Css.zero
|
||||
, Css.margin2 (Css.px 75) Css.auto
|
||||
, Css.backgroundColor Nri.Ui.Colors.V1.white
|
||||
, Css.borderRadius (Css.px 20)
|
||||
, Css.property "box-shadow" "0 1px 10px 0 rgba(0, 0, 0, 0.35)"
|
||||
, Css.position Css.relative -- required for closeButtonContainer
|
||||
, Css.displayFlex
|
||||
, Css.alignItems Css.center
|
||||
, Css.flexDirection Css.column
|
||||
, Css.flexWrap Css.noWrap
|
||||
, Fonts.baseFont
|
||||
]
|
||||
[]
|
||||
[ -- This global <style> node sets overflow to hidden on the body element,
|
||||
-- thereby preventing the page from scrolling behind the backdrop when the modal is
|
||||
-- open (and this node is present on the page).
|
||||
Css.Global.global
|
||||
[ Css.Global.body
|
||||
[ Css.overflow Css.hidden ]
|
||||
]
|
||||
, case onDismiss of
|
||||
Just msg ->
|
||||
closeButton assets msg
|
||||
|
||||
Nothing ->
|
||||
text ""
|
||||
, if visibleTitle then
|
||||
viewHeader modalType title
|
||||
|
||||
else
|
||||
text ""
|
||||
, viewContent modalType content
|
||||
, viewFooter footerContent
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
closeButton : Assets r -> msg -> Html msg
|
||||
closeButton assets msg =
|
||||
Nri.Ui.styled div
|
||||
"close-button-container"
|
||||
[ Css.position Css.absolute
|
||||
, Css.top Css.zero
|
||||
, Css.right Css.zero
|
||||
, Css.padding (Css.px 25)
|
||||
]
|
||||
[]
|
||||
[ Icon.button
|
||||
{ alt = "Close"
|
||||
, msg = msg
|
||||
, icon = Icon.close assets
|
||||
, disabled = False
|
||||
, size = Icon.Medium
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
viewHeader : ModalType -> String -> Html msg
|
||||
viewHeader modalType title =
|
||||
Nri.Ui.styled Html.h3
|
||||
"modal-header"
|
||||
((case modalType of
|
||||
Info ->
|
||||
Css.color Nri.Ui.Colors.V1.navy
|
||||
|
||||
Warning ->
|
||||
Css.color Nri.Ui.Colors.V1.red
|
||||
)
|
||||
:: [ Css.fontWeight (Css.int 700)
|
||||
, Css.lineHeight (Css.px 27)
|
||||
, Css.margin2 Css.zero (Css.px 49)
|
||||
, Css.fontSize (Css.px 20)
|
||||
, Fonts.baseFont
|
||||
, Css.textAlign Css.center
|
||||
]
|
||||
)
|
||||
[]
|
||||
[ Html.text title
|
||||
]
|
||||
|
||||
|
||||
viewContent : ModalType -> Html msg -> Html msg
|
||||
viewContent modalType content =
|
||||
Nri.Ui.styled div
|
||||
"modal-content"
|
||||
[ Css.overflowY Css.auto
|
||||
, Css.padding2 (Css.px 30) (Css.px 40)
|
||||
, Css.width (Css.pct 100)
|
||||
, Css.minHeight (Css.px 150)
|
||||
, Css.boxSizing Css.borderBox
|
||||
]
|
||||
[]
|
||||
[ content ]
|
||||
|
||||
|
||||
viewFooter : List (Html msg) -> Html msg
|
||||
viewFooter footerContent =
|
||||
case footerContent of
|
||||
[] ->
|
||||
Html.text ""
|
||||
|
||||
_ ->
|
||||
Nri.Ui.styled div
|
||||
"modal-footer"
|
||||
[ Css.alignItems Css.center
|
||||
, Css.displayFlex
|
||||
, Css.flexDirection Css.column
|
||||
, Css.flexGrow (Css.int 2)
|
||||
, Css.flexWrap Css.noWrap
|
||||
, Css.margin4 (Css.px 20) Css.zero Css.zero Css.zero
|
||||
, Css.width (Css.pct 100)
|
||||
]
|
||||
[]
|
||||
(List.map
|
||||
(\x ->
|
||||
Nri.Ui.styled div
|
||||
"modal-footer-item"
|
||||
[ Css.margin4 (Css.px 10) Css.zero Css.zero Css.zero
|
||||
, Css.firstChild
|
||||
[ Css.margin Css.zero
|
||||
]
|
||||
]
|
||||
[]
|
||||
[ x ]
|
||||
)
|
||||
footerContent
|
||||
)
|
@ -17,8 +17,8 @@ import Html.Styled as Html exposing (Html)
|
||||
import Html.Styled.Attributes as Attributes
|
||||
import Http
|
||||
import Nri.Ui.Button.V10 as Button
|
||||
import Nri.Ui.Heading.V2 as Heading
|
||||
import Nri.Ui.Html.V3 exposing (viewIf)
|
||||
import Nri.Ui.Text.V2 as Text
|
||||
|
||||
|
||||
{-| The default page information is for the button
|
||||
@ -201,8 +201,8 @@ view : Config msg -> Html msg
|
||||
view config =
|
||||
viewContainer
|
||||
[ viewEmoji [ Html.text config.emoji ]
|
||||
, Text.heading [ Html.text config.title ]
|
||||
, Text.tagline [ Html.text config.subtitle ]
|
||||
, Heading.h1 [] [ Html.text config.title ]
|
||||
, Heading.h2 [] [ Html.text config.subtitle ]
|
||||
, viewButton
|
||||
[ viewExit config ]
|
||||
, viewIf
|
||||
|
@ -1,276 +0,0 @@
|
||||
module Nri.Ui.SegmentedControl.V11 exposing
|
||||
( Option, view
|
||||
, Radio, viewRadioGroup
|
||||
, Width(..)
|
||||
)
|
||||
|
||||
{-| Changes from V10:
|
||||
|
||||
- change selection using left/right arrow keys
|
||||
- only currently-selected or first control is tabbable
|
||||
- tabpanel is tabbable
|
||||
- Uses TabsInternal under the hood
|
||||
- `viewSelect` renamed to `viewRadioGroup`, `SelectOption` renamed to `Radio`
|
||||
- `viewRadioGroup` uses native HTML radio input internally
|
||||
|
||||
@docs Option, view
|
||||
@docs Radio, viewRadioGroup
|
||||
@docs Width
|
||||
|
||||
-}
|
||||
|
||||
import Accessibility.Styled exposing (..)
|
||||
import Accessibility.Styled.Aria as Aria
|
||||
import Accessibility.Styled.Role as Role
|
||||
import Accessibility.Styled.Style as Style
|
||||
import Accessibility.Styled.Widget as Widget
|
||||
import Css exposing (..)
|
||||
import EventExtras
|
||||
import Html.Styled
|
||||
import Html.Styled.Attributes as Attributes exposing (css, href)
|
||||
import Html.Styled.Events as Events
|
||||
import Json.Encode as Encode
|
||||
import Nri.Ui
|
||||
import Nri.Ui.Colors.Extra exposing (withAlpha)
|
||||
import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.Fonts.V1 as Fonts
|
||||
import Nri.Ui.Html.Attributes.V2 as AttributesExtra
|
||||
import Nri.Ui.Svg.V1 as Svg exposing (Svg)
|
||||
import Nri.Ui.Util exposing (dashify)
|
||||
import TabsInternal
|
||||
|
||||
|
||||
{-| -}
|
||||
type Width
|
||||
= FitContent
|
||||
| FillContainer
|
||||
|
||||
|
||||
{-| -}
|
||||
type alias Radio value msg =
|
||||
{ value : value
|
||||
, label : String
|
||||
, attributes : List (Attribute msg)
|
||||
, icon : Maybe Svg
|
||||
}
|
||||
|
||||
|
||||
{-| Creates a set of radio buttons styled to look like a segmented control.
|
||||
|
||||
- `onSelect`: the message to produce when an option is selected (clicked) by the user
|
||||
- `toString`: function to get the radio value as a string
|
||||
- `options`: the list of options available
|
||||
- `selected`: if present, the value of the currently-selected option
|
||||
- `width`: how to size the segmented control
|
||||
- `legend`:
|
||||
- value read to screenreader users to explain the radio group's purpose <https://dequeuniversity.com/rules/axe/3.3/radiogroup?application=axeAPI>
|
||||
- after lowercasing & dashifying, this value is used to group the radio buttons together
|
||||
|
||||
-}
|
||||
viewRadioGroup :
|
||||
{ onSelect : a -> msg
|
||||
, toString : a -> String
|
||||
, options : List (Radio a msg)
|
||||
, selected : Maybe a
|
||||
, width : Width
|
||||
, legend : String
|
||||
}
|
||||
-> Html msg
|
||||
viewRadioGroup config =
|
||||
let
|
||||
viewRadio option =
|
||||
let
|
||||
isSelected =
|
||||
Just option.value == config.selected
|
||||
in
|
||||
labelAfter
|
||||
[ css
|
||||
-- ensure that the focus state is visible, even
|
||||
-- though the radio button that technically has focus
|
||||
-- is not
|
||||
(Css.pseudoClass "focus-within"
|
||||
[ Css.property "outline-style" "auto" ]
|
||||
:: styles config.width isSelected
|
||||
)
|
||||
]
|
||||
(div [] [ viewIcon option.icon, text option.label ])
|
||||
(radio name (config.toString option.value) isSelected <|
|
||||
(Events.onCheck (\_ -> config.onSelect option.value)
|
||||
:: css [ Css.opacity Css.zero ]
|
||||
:: Attributes.attribute "data-nri-checked"
|
||||
(if isSelected then
|
||||
"true"
|
||||
|
||||
else
|
||||
"false"
|
||||
)
|
||||
:: Style.invisible
|
||||
)
|
||||
)
|
||||
|
||||
name =
|
||||
dashify (String.toLower config.legend)
|
||||
|
||||
legendId =
|
||||
"legend-" ++ name
|
||||
in
|
||||
div
|
||||
[ Role.radioGroup
|
||||
, Aria.labelledBy legendId
|
||||
, css [ displayFlex, cursor pointer ]
|
||||
]
|
||||
(p (Attributes.id legendId :: Style.invisible) [ text config.legend ]
|
||||
:: List.map viewRadio config.options
|
||||
)
|
||||
|
||||
|
||||
{-| -}
|
||||
type alias Option value msg =
|
||||
{ value : value
|
||||
, label : String
|
||||
, attributes : List (Attribute msg)
|
||||
, icon : Maybe Svg
|
||||
, content : Html msg
|
||||
}
|
||||
|
||||
|
||||
{-|
|
||||
|
||||
- `onSelect` : the message to produce when an option is selected by the user
|
||||
- `onFocus` : the message to focus an element by id string
|
||||
- `toString` : function to get the option value as a string
|
||||
- `options`: the list of options available
|
||||
- `selected`: the value of the currently-selected option
|
||||
- `width`: how to size the segmented control
|
||||
- `toUrl`: a optional function that takes a `route` and returns the URL of that route. You should always use pass a `toUrl` function when the segmented control options correspond to routes in your SPA.
|
||||
|
||||
-}
|
||||
view :
|
||||
{ onSelect : a -> msg
|
||||
, onFocus : String -> msg
|
||||
, toString : a -> String
|
||||
, options : List (Option a msg)
|
||||
, selected : a
|
||||
, width : Width
|
||||
, toUrl : Maybe (a -> String)
|
||||
}
|
||||
-> Html msg
|
||||
view config =
|
||||
let
|
||||
toInternalTab : Option a msg -> TabsInternal.Tab a msg
|
||||
toInternalTab option =
|
||||
{ id = option.value
|
||||
, idString = config.toString option.value
|
||||
, tabAttributes = option.attributes
|
||||
, tabView = [ viewIcon option.icon, text option.label ]
|
||||
, panelView = option.content
|
||||
, spaHref = Maybe.map (\toUrl -> toUrl option.value) config.toUrl
|
||||
}
|
||||
|
||||
{ tabList, tabPanels } =
|
||||
TabsInternal.views
|
||||
{ onSelect = config.onSelect
|
||||
, onFocus = config.onFocus
|
||||
, selected = config.selected
|
||||
, tabs = List.map toInternalTab config.options
|
||||
, tabListStyles = [ displayFlex, cursor pointer, marginBottom (px 10) ]
|
||||
, tabStyles = styles config.width
|
||||
}
|
||||
in
|
||||
div []
|
||||
[ tabList
|
||||
, tabPanels
|
||||
]
|
||||
|
||||
|
||||
viewIcon : Maybe Svg.Svg -> Html msg
|
||||
viewIcon icon =
|
||||
case icon of
|
||||
Nothing ->
|
||||
text ""
|
||||
|
||||
Just svg ->
|
||||
svg
|
||||
|> Svg.withWidth (px 18)
|
||||
|> Svg.withHeight (px 18)
|
||||
|> Svg.withCss
|
||||
[ display inlineBlock
|
||||
, verticalAlign textTop
|
||||
, lineHeight (px 15)
|
||||
, marginRight (px 8)
|
||||
]
|
||||
|> Svg.toHtml
|
||||
|
||||
|
||||
styles : Width -> Bool -> List Style
|
||||
styles width isSelected =
|
||||
[ sharedSegmentStyles
|
||||
, if isSelected then
|
||||
focusedSegmentStyles
|
||||
|
||||
else
|
||||
unFocusedSegmentStyles
|
||||
, case width of
|
||||
FitContent ->
|
||||
Css.batch []
|
||||
|
||||
FillContainer ->
|
||||
expandingTabStyles
|
||||
]
|
||||
|
||||
|
||||
sharedSegmentStyles : Style
|
||||
sharedSegmentStyles =
|
||||
[ padding2 (px 6) (px 20)
|
||||
, height (px 45)
|
||||
, Fonts.baseFont
|
||||
, fontSize (px 15)
|
||||
, fontWeight bold
|
||||
, lineHeight (px 30)
|
||||
, margin zero
|
||||
, firstOfType
|
||||
[ borderTopLeftRadius (px 8)
|
||||
, borderBottomLeftRadius (px 8)
|
||||
, borderLeft3 (px 1) solid Colors.azure
|
||||
]
|
||||
, lastOfType
|
||||
[ borderTopRightRadius (px 8)
|
||||
, borderBottomRightRadius (px 8)
|
||||
]
|
||||
, border3 (px 1) solid Colors.azure
|
||||
, borderLeft (px 0)
|
||||
, boxSizing borderBox
|
||||
, cursor pointer
|
||||
, property "transition" "background-color 0.2s, color 0.2s, box-shadow 0.2s, border 0.2s, border-width 0s"
|
||||
, textDecoration none
|
||||
, hover [ textDecoration none ]
|
||||
, focus [ textDecoration none ]
|
||||
]
|
||||
|> Css.batch
|
||||
|
||||
|
||||
focusedSegmentStyles : Style
|
||||
focusedSegmentStyles =
|
||||
[ backgroundColor Colors.glacier
|
||||
, boxShadow5 inset zero (px 3) zero (withAlpha 0.2 Colors.gray20)
|
||||
, color Colors.navy
|
||||
]
|
||||
|> Css.batch
|
||||
|
||||
|
||||
unFocusedSegmentStyles : Style
|
||||
unFocusedSegmentStyles =
|
||||
[ backgroundColor Colors.white
|
||||
, boxShadow5 inset zero (px -2) zero Colors.azure
|
||||
, color Colors.azure
|
||||
, hover [ backgroundColor Colors.frost ]
|
||||
]
|
||||
|> Css.batch
|
||||
|
||||
|
||||
expandingTabStyles : Style
|
||||
expandingTabStyles =
|
||||
[ flexGrow (int 1)
|
||||
, textAlign center
|
||||
]
|
||||
|> Css.batch
|
@ -1,300 +0,0 @@
|
||||
module Nri.Ui.SegmentedControl.V12 exposing
|
||||
( Option, view
|
||||
, Radio, viewRadioGroup
|
||||
, Positioning(..), Width(..)
|
||||
)
|
||||
|
||||
{-| Changes from V11:
|
||||
|
||||
- allow HTML in labels
|
||||
- [use idString instead of toString](https://github.com/NoRedInk/noredink-ui/issues/575)
|
||||
- allow control to be centered
|
||||
|
||||
@docs Option, view
|
||||
@docs Radio, viewRadioGroup
|
||||
@docs Positioning, Width
|
||||
|
||||
-}
|
||||
|
||||
import Accessibility.Styled exposing (..)
|
||||
import Accessibility.Styled.Aria as Aria
|
||||
import Accessibility.Styled.Role as Role
|
||||
import Accessibility.Styled.Style as Style
|
||||
import Accessibility.Styled.Widget as Widget
|
||||
import Css exposing (..)
|
||||
import EventExtras
|
||||
import Html.Styled
|
||||
import Html.Styled.Attributes as Attributes exposing (css, href)
|
||||
import Html.Styled.Events as Events
|
||||
import Json.Encode as Encode
|
||||
import Nri.Ui
|
||||
import Nri.Ui.Colors.Extra exposing (withAlpha)
|
||||
import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.Fonts.V1 as Fonts
|
||||
import Nri.Ui.Html.Attributes.V2 as AttributesExtra
|
||||
import Nri.Ui.Svg.V1 as Svg exposing (Svg)
|
||||
import Nri.Ui.Util exposing (dashify)
|
||||
import TabsInternal
|
||||
|
||||
|
||||
{-| -}
|
||||
type Positioning
|
||||
= Left Width
|
||||
| Center
|
||||
|
||||
|
||||
{-| -}
|
||||
type Width
|
||||
= FitContent
|
||||
| FillContainer
|
||||
|
||||
|
||||
{-| -}
|
||||
type alias Radio value msg =
|
||||
{ value : value
|
||||
, idString : String
|
||||
, label : Html msg
|
||||
, attributes : List (Attribute msg)
|
||||
, icon : Maybe Svg
|
||||
}
|
||||
|
||||
|
||||
{-| Creates a set of radio buttons styled to look like a segmented control.
|
||||
|
||||
- `onSelect`: the message to produce when an option is selected (clicked) by the user
|
||||
- `toString`: function to get the radio value as a string
|
||||
- `options`: the list of options available
|
||||
- `selected`: if present, the value of the currently-selected option
|
||||
- `positioning`: how to position and size the segmented control
|
||||
- `legend`:
|
||||
- value read to screenreader users to explain the radio group's purpose <https://dequeuniversity.com/rules/axe/3.3/radiogroup?application=axeAPI>
|
||||
- after lowercasing & dashifying, this value is used to group the radio buttons together
|
||||
|
||||
-}
|
||||
viewRadioGroup :
|
||||
{ onSelect : a -> msg
|
||||
, options : List (Radio a msg)
|
||||
, selected : Maybe a
|
||||
, positioning : Positioning
|
||||
, legend : String
|
||||
}
|
||||
-> Html msg
|
||||
viewRadioGroup config =
|
||||
let
|
||||
viewRadio option =
|
||||
let
|
||||
isSelected =
|
||||
Just option.value == config.selected
|
||||
in
|
||||
Html.Styled.label
|
||||
[ css
|
||||
-- ensure that the focus state is visible, even
|
||||
-- though the radio button that technically has focus
|
||||
-- is not
|
||||
(Css.pseudoClass "focus-within"
|
||||
[ Css.property "outline-style" "auto" ]
|
||||
:: styles config.positioning isSelected
|
||||
)
|
||||
]
|
||||
[ radio name option.idString isSelected <|
|
||||
(Events.onCheck (\_ -> config.onSelect option.value)
|
||||
:: css [ Css.opacity Css.zero ]
|
||||
:: Attributes.attribute "data-nri-checked"
|
||||
(if isSelected then
|
||||
"true"
|
||||
|
||||
else
|
||||
"false"
|
||||
)
|
||||
:: Style.invisible
|
||||
)
|
||||
, div [] [ viewIcon option.icon, option.label ]
|
||||
]
|
||||
|
||||
name =
|
||||
dashify (String.toLower config.legend)
|
||||
|
||||
legendId =
|
||||
"legend-" ++ name
|
||||
in
|
||||
div
|
||||
[ Role.radioGroup
|
||||
, Aria.labelledBy legendId
|
||||
, css
|
||||
[ displayFlex
|
||||
, cursor pointer
|
||||
, case config.positioning of
|
||||
Left _ ->
|
||||
justifyContent flexStart
|
||||
|
||||
Center ->
|
||||
justifyContent center
|
||||
]
|
||||
]
|
||||
(p (Attributes.id legendId :: Style.invisible) [ text config.legend ]
|
||||
:: List.map viewRadio config.options
|
||||
)
|
||||
|
||||
|
||||
{-| -}
|
||||
type alias Option value msg =
|
||||
{ value : value
|
||||
, idString : String
|
||||
, label : Html msg
|
||||
, attributes : List (Attribute msg)
|
||||
, icon : Maybe Svg
|
||||
, content : Html msg
|
||||
}
|
||||
|
||||
|
||||
{-|
|
||||
|
||||
- `onSelect` : the message to produce when an option is selected by the user
|
||||
- `onFocus` : the message to focus an element by id string
|
||||
- `options`: the list of options available
|
||||
- `selected`: the value of the currently-selected option
|
||||
- `positioning`: how to position and size the segmented control
|
||||
- `toUrl`: a optional function that takes a `route` and returns the URL of that route. You should always use pass a `toUrl` function when the segmented control options correspond to routes in your SPA.
|
||||
|
||||
-}
|
||||
view :
|
||||
{ onSelect : a -> msg
|
||||
, onFocus : String -> msg
|
||||
, options : List (Option a msg)
|
||||
, selected : a
|
||||
, positioning : Positioning
|
||||
, toUrl : Maybe (a -> String)
|
||||
}
|
||||
-> Html msg
|
||||
view config =
|
||||
let
|
||||
toInternalTab : Option a msg -> TabsInternal.Tab a msg
|
||||
toInternalTab option =
|
||||
{ id = option.value
|
||||
, idString = option.idString
|
||||
, tabAttributes = option.attributes
|
||||
, tabView = [ viewIcon option.icon, option.label ]
|
||||
, panelView = option.content
|
||||
, spaHref = Maybe.map (\toUrl -> toUrl option.value) config.toUrl
|
||||
}
|
||||
|
||||
{ tabList, tabPanels } =
|
||||
TabsInternal.views
|
||||
{ onSelect = config.onSelect
|
||||
, onFocus = config.onFocus
|
||||
, selected = config.selected
|
||||
, tabs = List.map toInternalTab config.options
|
||||
, tabListStyles =
|
||||
[ displayFlex
|
||||
, cursor pointer
|
||||
, marginBottom (px 10)
|
||||
, case config.positioning of
|
||||
Left _ ->
|
||||
justifyContent flexStart
|
||||
|
||||
Center ->
|
||||
justifyContent center
|
||||
]
|
||||
, tabStyles = styles config.positioning
|
||||
}
|
||||
in
|
||||
div []
|
||||
[ tabList
|
||||
, tabPanels
|
||||
]
|
||||
|
||||
|
||||
viewIcon : Maybe Svg.Svg -> Html msg
|
||||
viewIcon icon =
|
||||
case icon of
|
||||
Nothing ->
|
||||
text ""
|
||||
|
||||
Just svg ->
|
||||
svg
|
||||
|> Svg.withWidth (px 18)
|
||||
|> Svg.withHeight (px 18)
|
||||
|> Svg.withCss
|
||||
[ display inlineBlock
|
||||
, verticalAlign textTop
|
||||
, lineHeight (px 15)
|
||||
, marginRight (px 8)
|
||||
]
|
||||
|> Svg.toHtml
|
||||
|
||||
|
||||
styles : Positioning -> Bool -> List Style
|
||||
styles positioning isSelected =
|
||||
[ sharedSegmentStyles
|
||||
, if isSelected then
|
||||
focusedSegmentStyles
|
||||
|
||||
else
|
||||
unFocusedSegmentStyles
|
||||
, case positioning of
|
||||
Left FitContent ->
|
||||
Css.batch []
|
||||
|
||||
Left FillContainer ->
|
||||
expandingTabStyles
|
||||
|
||||
Center ->
|
||||
Css.batch []
|
||||
]
|
||||
|
||||
|
||||
sharedSegmentStyles : Style
|
||||
sharedSegmentStyles =
|
||||
[ padding2 (px 6) (px 20)
|
||||
, height (px 45)
|
||||
, Fonts.baseFont
|
||||
, fontSize (px 15)
|
||||
, fontWeight bold
|
||||
, lineHeight (px 30)
|
||||
, margin zero
|
||||
, firstOfType
|
||||
[ borderTopLeftRadius (px 8)
|
||||
, borderBottomLeftRadius (px 8)
|
||||
, borderLeft3 (px 1) solid Colors.azure
|
||||
]
|
||||
, lastOfType
|
||||
[ borderTopRightRadius (px 8)
|
||||
, borderBottomRightRadius (px 8)
|
||||
]
|
||||
, border3 (px 1) solid Colors.azure
|
||||
, borderLeft (px 0)
|
||||
, boxSizing borderBox
|
||||
, cursor pointer
|
||||
, property "transition" "background-color 0.2s, color 0.2s, box-shadow 0.2s, border 0.2s, border-width 0s"
|
||||
, textDecoration none
|
||||
, hover [ textDecoration none ]
|
||||
, focus [ textDecoration none ]
|
||||
]
|
||||
|> Css.batch
|
||||
|
||||
|
||||
focusedSegmentStyles : Style
|
||||
focusedSegmentStyles =
|
||||
[ backgroundColor Colors.glacier
|
||||
, boxShadow5 inset zero (px 3) zero (withAlpha 0.2 Colors.gray20)
|
||||
, color Colors.navy
|
||||
]
|
||||
|> Css.batch
|
||||
|
||||
|
||||
unFocusedSegmentStyles : Style
|
||||
unFocusedSegmentStyles =
|
||||
[ backgroundColor Colors.white
|
||||
, boxShadow5 inset zero (px -2) zero Colors.azure
|
||||
, color Colors.azure
|
||||
, hover [ backgroundColor Colors.frost ]
|
||||
]
|
||||
|> Css.batch
|
||||
|
||||
|
||||
expandingTabStyles : Style
|
||||
expandingTabStyles =
|
||||
[ flexGrow (int 1)
|
||||
, textAlign center
|
||||
]
|
||||
|> Css.batch
|
@ -1,312 +0,0 @@
|
||||
module Nri.Ui.SegmentedControl.V13 exposing
|
||||
( Option, view
|
||||
, Radio, viewRadioGroup
|
||||
, Positioning(..), Width(..)
|
||||
)
|
||||
|
||||
{-| Post-release patches:
|
||||
|
||||
- Fixes <https://github.com/NoRedInk/noredink-ui/issues/608>
|
||||
|
||||
Changes from V12:
|
||||
|
||||
- Adds tooltip support
|
||||
- combine onFocus and onSelect into focusAndSelect msg handler (for tooltips)
|
||||
|
||||
@docs Option, view
|
||||
@docs Radio, viewRadioGroup
|
||||
@docs Positioning, Width
|
||||
|
||||
-}
|
||||
|
||||
import Accessibility.Styled exposing (..)
|
||||
import Accessibility.Styled.Aria as Aria
|
||||
import Accessibility.Styled.Role as Role
|
||||
import Accessibility.Styled.Style as Style
|
||||
import Accessibility.Styled.Widget as Widget
|
||||
import Css exposing (..)
|
||||
import EventExtras
|
||||
import Html.Styled
|
||||
import Html.Styled.Attributes as Attributes exposing (css, href)
|
||||
import Html.Styled.Events as Events
|
||||
import Json.Encode as Encode
|
||||
import Nri.Ui
|
||||
import Nri.Ui.Colors.Extra exposing (withAlpha)
|
||||
import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.Fonts.V1 as Fonts
|
||||
import Nri.Ui.Html.Attributes.V2 as AttributesExtra
|
||||
import Nri.Ui.Svg.V1 as Svg exposing (Svg)
|
||||
import Nri.Ui.Tooltip.V2 as Tooltip
|
||||
import Nri.Ui.Util exposing (dashify)
|
||||
import TabsInternal.V2 as TabsInternal
|
||||
|
||||
|
||||
{-| -}
|
||||
type Positioning
|
||||
= Left Width
|
||||
| Center
|
||||
|
||||
|
||||
{-| -}
|
||||
type Width
|
||||
= FitContent
|
||||
| FillContainer
|
||||
|
||||
|
||||
{-| -}
|
||||
type alias Radio value msg =
|
||||
{ value : value
|
||||
, idString : String
|
||||
, label : Html msg
|
||||
, attributes : List (Attribute msg)
|
||||
, icon : Maybe Svg
|
||||
}
|
||||
|
||||
|
||||
{-| Creates a set of radio buttons styled to look like a segmented control.
|
||||
|
||||
- `onSelect`: the message to produce when an option is selected (clicked) by the user
|
||||
- `toString`: function to get the radio value as a string
|
||||
- `options`: the list of options available
|
||||
- `selected`: if present, the value of the currently-selected option
|
||||
- `positioning`: how to position and size the segmented control
|
||||
- `legend`:
|
||||
- value read to screenreader users to explain the radio group's purpose <https://dequeuniversity.com/rules/axe/3.3/radiogroup?application=axeAPI>
|
||||
- after lowercasing & dashifying, this value is used to group the radio buttons together
|
||||
|
||||
-}
|
||||
viewRadioGroup :
|
||||
{ onSelect : a -> msg
|
||||
, options : List (Radio a msg)
|
||||
, selected : Maybe a
|
||||
, positioning : Positioning
|
||||
, legend : String
|
||||
}
|
||||
-> Html msg
|
||||
viewRadioGroup config =
|
||||
let
|
||||
numOptions =
|
||||
List.length config.options
|
||||
|
||||
viewRadio index option =
|
||||
let
|
||||
isSelected =
|
||||
Just option.value == config.selected
|
||||
in
|
||||
Html.Styled.label
|
||||
[ css
|
||||
-- ensure that the focus state is visible, even
|
||||
-- though the radio button that technically has focus
|
||||
-- is not
|
||||
(Css.pseudoClass "focus-within"
|
||||
[ Css.property "outline-style" "auto" ]
|
||||
:: styles config.positioning numOptions index isSelected
|
||||
)
|
||||
]
|
||||
[ radio name option.idString isSelected <|
|
||||
(Events.onCheck (\_ -> config.onSelect option.value)
|
||||
:: css [ Css.opacity Css.zero ]
|
||||
:: Attributes.attribute "data-nri-checked"
|
||||
(if isSelected then
|
||||
"true"
|
||||
|
||||
else
|
||||
"false"
|
||||
)
|
||||
:: Style.invisible
|
||||
)
|
||||
, div [] [ viewIcon option.icon, option.label ]
|
||||
]
|
||||
|
||||
name =
|
||||
dashify (String.toLower config.legend)
|
||||
|
||||
legendId =
|
||||
"legend-" ++ name
|
||||
in
|
||||
div
|
||||
[ Role.radioGroup
|
||||
, Aria.labelledBy legendId
|
||||
, css
|
||||
[ displayFlex
|
||||
, cursor pointer
|
||||
, case config.positioning of
|
||||
Left _ ->
|
||||
justifyContent flexStart
|
||||
|
||||
Center ->
|
||||
justifyContent center
|
||||
]
|
||||
]
|
||||
(p (Attributes.id legendId :: Style.invisible) [ text config.legend ]
|
||||
:: List.indexedMap viewRadio config.options
|
||||
)
|
||||
|
||||
|
||||
{-| Tooltip defaults: `[Tooltip.smallPadding, Tooltip.onBottom, Tooltip.fitToContent]`
|
||||
-}
|
||||
type alias Option value msg =
|
||||
{ value : value
|
||||
, idString : String
|
||||
, label : Html msg
|
||||
, attributes : List (Attribute msg)
|
||||
, tabTooltip : List (Tooltip.Attribute msg)
|
||||
, icon : Maybe Svg
|
||||
, content : Html msg
|
||||
}
|
||||
|
||||
|
||||
{-|
|
||||
|
||||
- `focusAndSelect` : the message to produce when an option is selected by the user
|
||||
- `options`: the list of options available
|
||||
- `selected`: the value of the currently-selected option
|
||||
- `positioning`: how to position and size the segmented control
|
||||
- `toUrl`: a optional function that takes a `route` and returns the URL of that route. You should always use pass a `toUrl` function when the segmented control options correspond to routes in your SPA.
|
||||
|
||||
-}
|
||||
view :
|
||||
{ focusAndSelect : { select : a, focus : Maybe String } -> msg
|
||||
, options : List (Option a msg)
|
||||
, selected : a
|
||||
, positioning : Positioning
|
||||
, toUrl : Maybe (a -> String)
|
||||
}
|
||||
-> Html msg
|
||||
view config =
|
||||
let
|
||||
toInternalTab : Option a msg -> TabsInternal.Tab a msg
|
||||
toInternalTab option =
|
||||
{ id = option.value
|
||||
, idString = option.idString
|
||||
, tabAttributes = option.attributes
|
||||
, tabTooltip =
|
||||
case config.positioning of
|
||||
Left FillContainer ->
|
||||
Tooltip.containerCss [ Css.width (Css.pct 100) ] :: option.tabTooltip
|
||||
|
||||
_ ->
|
||||
option.tabTooltip
|
||||
, tabView = [ viewIcon option.icon, option.label ]
|
||||
, panelView = option.content
|
||||
, spaHref = Maybe.map (\toUrl -> toUrl option.value) config.toUrl
|
||||
, disabled = False
|
||||
, labelledBy = Nothing
|
||||
}
|
||||
|
||||
{ tabList, tabPanels } =
|
||||
TabsInternal.views
|
||||
{ focusAndSelect = config.focusAndSelect
|
||||
, selected = config.selected
|
||||
, tabs = List.map toInternalTab config.options
|
||||
, tabListStyles =
|
||||
[ displayFlex
|
||||
, cursor pointer
|
||||
, marginBottom (px 10)
|
||||
, case config.positioning of
|
||||
Left _ ->
|
||||
justifyContent flexStart
|
||||
|
||||
Center ->
|
||||
justifyContent center
|
||||
]
|
||||
, tabStyles = styles config.positioning (List.length config.options)
|
||||
}
|
||||
in
|
||||
div []
|
||||
[ tabList
|
||||
, tabPanels
|
||||
]
|
||||
|
||||
|
||||
viewIcon : Maybe Svg.Svg -> Html msg
|
||||
viewIcon icon =
|
||||
case icon of
|
||||
Nothing ->
|
||||
text ""
|
||||
|
||||
Just svg ->
|
||||
svg
|
||||
|> Svg.withWidth (px 18)
|
||||
|> Svg.withHeight (px 18)
|
||||
|> Svg.withCss
|
||||
[ display inlineBlock
|
||||
, verticalAlign textTop
|
||||
, lineHeight (px 15)
|
||||
, marginRight (px 8)
|
||||
]
|
||||
|> Svg.toHtml
|
||||
|
||||
|
||||
styles : Positioning -> Int -> Int -> Bool -> List Style
|
||||
styles positioning numEntries index isSelected =
|
||||
[ sharedSegmentStyles numEntries index
|
||||
, if isSelected then
|
||||
focusedSegmentStyles
|
||||
|
||||
else
|
||||
unFocusedSegmentStyles
|
||||
, Css.batch <|
|
||||
case positioning of
|
||||
Left FillContainer ->
|
||||
[ width (Css.pct 100)
|
||||
, flexGrow (int 1)
|
||||
, textAlign center
|
||||
]
|
||||
|
||||
_ ->
|
||||
[]
|
||||
]
|
||||
|
||||
|
||||
sharedSegmentStyles : Int -> Int -> Style
|
||||
sharedSegmentStyles numEntries index =
|
||||
[ padding2 (px 6) (px 15)
|
||||
, height (px 45)
|
||||
, Fonts.baseFont
|
||||
, fontSize (px 15)
|
||||
, fontWeight bold
|
||||
, lineHeight (px 30)
|
||||
, margin zero
|
||||
, border3 (px 1) solid Colors.azure
|
||||
, boxSizing borderBox
|
||||
, cursor pointer
|
||||
, property "transition" "background-color 0.2s, color 0.2s, box-shadow 0.2s, border 0.2s, border-width 0s"
|
||||
, textDecoration none
|
||||
, hover [ textDecoration none ]
|
||||
, focus [ textDecoration none ]
|
||||
]
|
||||
++ (if index == 0 then
|
||||
[ borderTopLeftRadius (px 8)
|
||||
, borderBottomLeftRadius (px 8)
|
||||
]
|
||||
|
||||
else if index == numEntries - 1 then
|
||||
[ borderTopRightRadius (px 8)
|
||||
, borderBottomRightRadius (px 8)
|
||||
, borderLeft (px 0)
|
||||
]
|
||||
|
||||
else
|
||||
[ borderLeft (px 0) ]
|
||||
)
|
||||
|> Css.batch
|
||||
|
||||
|
||||
focusedSegmentStyles : Style
|
||||
focusedSegmentStyles =
|
||||
[ backgroundColor Colors.glacier
|
||||
, boxShadow5 inset zero (px 3) zero (withAlpha 0.2 Colors.gray20)
|
||||
, color Colors.navy
|
||||
]
|
||||
|> Css.batch
|
||||
|
||||
|
||||
unFocusedSegmentStyles : Style
|
||||
unFocusedSegmentStyles =
|
||||
[ backgroundColor Colors.white
|
||||
, boxShadow5 inset zero (px -2) zero Colors.azure
|
||||
, color Colors.azure
|
||||
, hover [ backgroundColor Colors.frost ]
|
||||
]
|
||||
|> Css.batch
|
@ -6,7 +6,6 @@ module Nri.Ui.Select.V7 exposing (Choice, view)
|
||||
|
||||
-}
|
||||
|
||||
import Color
|
||||
import Css
|
||||
import Dict
|
||||
import Html.Styled as Html exposing (Html)
|
||||
@ -19,6 +18,7 @@ import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.CssVendorPrefix.V1 as VendorPrefixed
|
||||
import Nri.Ui.Fonts.V1 as Fonts
|
||||
import Nri.Ui.Util
|
||||
import SolidColor
|
||||
|
||||
|
||||
{-| A single possible choice.
|
||||
@ -153,7 +153,7 @@ selectArrowsCss : Css.Style
|
||||
selectArrowsCss =
|
||||
let
|
||||
color =
|
||||
Color.toRGBString (ColorsExtra.fromCssColor Colors.azure)
|
||||
SolidColor.toRGBString (ColorsExtra.fromCssColor Colors.azure)
|
||||
in
|
||||
Css.batch
|
||||
[ """<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="12px" height="16px" viewBox="0 0 12 16"><g fill=" """
|
||||
|
@ -17,7 +17,6 @@ import Accessibility.Styled.Aria exposing (labelledBy)
|
||||
import Accessibility.Styled.Role as Role
|
||||
import Accessibility.Styled.Style
|
||||
import Accessibility.Styled.Widget as Widget
|
||||
import Color
|
||||
import Css
|
||||
import Css.Animations
|
||||
import Css.Global
|
||||
@ -31,8 +30,9 @@ import Nri.Ui.Button.V8 as Button
|
||||
import Nri.Ui.Colors.Extra
|
||||
import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.Fonts.V1 as Fonts
|
||||
import Nri.Ui.Heading.V2 as Heading
|
||||
import Nri.Ui.Slide.V1 as Slide exposing (AnimationDirection(..))
|
||||
import Nri.Ui.Text.V2 as Text
|
||||
import SolidColor
|
||||
|
||||
|
||||
{-| -}
|
||||
@ -148,7 +148,7 @@ viewModal config (State { previousPanel }) summary =
|
||||
, panelContainer config.height
|
||||
[ Slide.animateOut direction ]
|
||||
[ viewIcon panelView.icon
|
||||
, Text.subHeading
|
||||
, Heading.h4 []
|
||||
[ span [ Html.Styled.Attributes.id (panelId panelView) ] [ Html.text panelView.title ]
|
||||
]
|
||||
, viewContent panelView.content
|
||||
@ -167,7 +167,7 @@ viewModalContent config summary styles =
|
||||
, panelContainer config.height
|
||||
styles
|
||||
[ viewIcon summary.current.icon
|
||||
, Text.subHeading
|
||||
, Heading.h4 []
|
||||
[ span
|
||||
[ Html.Styled.Attributes.id (panelId summary.current)
|
||||
, css [ Css.margin2 Css.zero (Css.px 21) ]
|
||||
@ -412,7 +412,7 @@ dot type_ =
|
||||
|
||||
animateBackgroundColor color =
|
||||
Nri.Ui.Colors.Extra.fromCssColor color
|
||||
|> Color.toRGBString
|
||||
|> SolidColor.toRGBString
|
||||
|> Css.Animations.property "background-color"
|
||||
in
|
||||
case type_ of
|
||||
|
@ -14,7 +14,6 @@ module Nri.Ui.SortableTable.V2 exposing
|
||||
|
||||
-}
|
||||
|
||||
import Color
|
||||
import Css exposing (..)
|
||||
import Css.Global exposing (Snippet, adjacentSiblings, children, class, descendants, each, everything, media, selector, withClass)
|
||||
import Html.Styled as Html exposing (Html)
|
||||
@ -24,6 +23,7 @@ import Nri.Ui.Colors.Extra
|
||||
import Nri.Ui.Colors.V1
|
||||
import Nri.Ui.CssVendorPrefix.V1 as CssVendorPrefix
|
||||
import Nri.Ui.Table.V5
|
||||
import SolidColor
|
||||
import Svg.Styled as Svg exposing (Svg)
|
||||
import Svg.Styled.Attributes as SvgAttributes
|
||||
|
||||
@ -365,4 +365,4 @@ sortArrow direction active =
|
||||
|
||||
toCssString : Css.Color -> String
|
||||
toCssString =
|
||||
Color.toRGBString << Nri.Ui.Colors.Extra.fromCssColor
|
||||
SolidColor.toRGBString << Nri.Ui.Colors.Extra.fromCssColor
|
||||
|
@ -1,258 +0,0 @@
|
||||
module Nri.Ui.Text.V2 exposing
|
||||
( caption, heading, mediumBody, smallBody, smallBodyGray, subHeading, smallHeading, tagline
|
||||
, ugMediumBody, ugSmallBody
|
||||
, noWidow
|
||||
)
|
||||
|
||||
{-|
|
||||
|
||||
|
||||
## Semantic text types:
|
||||
|
||||
@docs caption, heading, mediumBody, smallBody, smallBodyGray, subHeading, smallHeading, tagline
|
||||
|
||||
|
||||
## User-generated text styles:
|
||||
|
||||
@docs ugMediumBody, ugSmallBody
|
||||
|
||||
|
||||
## Modifying strings to display nicely:
|
||||
|
||||
@docs noWidow
|
||||
|
||||
-}
|
||||
|
||||
import Css exposing (..)
|
||||
import Html.Styled exposing (..)
|
||||
import Html.Styled.Attributes exposing (css)
|
||||
import Nri.Ui.Colors.V1 exposing (..)
|
||||
import Nri.Ui.Fonts.V1 as Fonts
|
||||
|
||||
|
||||
{-| This is a Page Heading.
|
||||
-}
|
||||
heading : List (Html msg) -> Html msg
|
||||
heading content =
|
||||
h1
|
||||
[ css
|
||||
(textStyles
|
||||
++ [ Fonts.baseFont
|
||||
, fontSize (px 30)
|
||||
, color navy
|
||||
, lineHeight (px 40.5)
|
||||
, fontWeight (int 700)
|
||||
, margin zero
|
||||
]
|
||||
)
|
||||
]
|
||||
content
|
||||
|
||||
|
||||
{-| This is a tagline for a page heading.
|
||||
-}
|
||||
tagline : List (Html msg) -> Html msg
|
||||
tagline content =
|
||||
h2
|
||||
[ css
|
||||
(textStyles
|
||||
++ [ Fonts.baseFont
|
||||
, fontSize (px 20)
|
||||
, color gray45
|
||||
, lineHeight (px 27)
|
||||
, fontWeight (int 400)
|
||||
, margin4 (px 5) (px 0) (px 0) (px 0)
|
||||
]
|
||||
)
|
||||
]
|
||||
content
|
||||
|
||||
|
||||
{-| This is a subhead.
|
||||
-}
|
||||
subHeading : List (Html msg) -> Html msg
|
||||
subHeading content =
|
||||
h3
|
||||
[ css
|
||||
(textStyles
|
||||
++ [ Fonts.baseFont
|
||||
, fontSize (px 20)
|
||||
, color navy
|
||||
, lineHeight (px 27)
|
||||
, fontWeight (int 700)
|
||||
, margin4 (px 20) (px 0) (px 10) (px 0)
|
||||
]
|
||||
)
|
||||
]
|
||||
content
|
||||
|
||||
|
||||
{-| This is a small Page Heading.
|
||||
-}
|
||||
smallHeading : List (Html msg) -> Html msg
|
||||
smallHeading content =
|
||||
h4
|
||||
[ css
|
||||
(textStyles
|
||||
++ [ Fonts.baseFont
|
||||
, fontSize (px 16)
|
||||
, color gray20
|
||||
, lineHeight (px 23)
|
||||
, fontWeight (int 700)
|
||||
, margin zero
|
||||
]
|
||||
)
|
||||
]
|
||||
content
|
||||
|
||||
|
||||
{-| This is some medium body copy.
|
||||
-}
|
||||
mediumBody : List (Html msg) -> Html msg
|
||||
mediumBody content =
|
||||
p
|
||||
[ css
|
||||
(textStyles
|
||||
++ [ Fonts.baseFont
|
||||
, fontSize (px 18)
|
||||
, color gray20
|
||||
, lineHeight (px 27)
|
||||
, fontWeight (int 400)
|
||||
, margin4 (px 10) (px 0) (px 0) (px 0)
|
||||
]
|
||||
)
|
||||
]
|
||||
content
|
||||
|
||||
|
||||
{-| This is some small body copy.
|
||||
-}
|
||||
smallBody : List (Html msg) -> Html msg
|
||||
smallBody content =
|
||||
p
|
||||
[ css
|
||||
(textStyles
|
||||
++ [ Fonts.baseFont
|
||||
, fontSize (px 15)
|
||||
, color gray20
|
||||
, lineHeight (px 23)
|
||||
, fontWeight (int 400)
|
||||
, margin4 (px 7) (px 0) (px 0) (px 0)
|
||||
]
|
||||
)
|
||||
]
|
||||
content
|
||||
|
||||
|
||||
{-| This is some small body copy but it's gray.
|
||||
-}
|
||||
smallBodyGray : List (Html msg) -> Html msg
|
||||
smallBodyGray content =
|
||||
p
|
||||
[ css
|
||||
(textStyles
|
||||
++ [ Fonts.baseFont
|
||||
, fontSize (px 15)
|
||||
, color gray45
|
||||
, lineHeight (px 23)
|
||||
, fontWeight (int 400)
|
||||
, margin4 (px 7) (px 0) (px 0) (px 0)
|
||||
]
|
||||
)
|
||||
]
|
||||
content
|
||||
|
||||
|
||||
textStyles =
|
||||
[ padding zero
|
||||
, textAlign left
|
||||
, firstChild
|
||||
[ margin zero
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
{-| This is a little note or caption.
|
||||
-}
|
||||
caption : List (Html msg) -> Html msg
|
||||
caption content =
|
||||
p
|
||||
[ css
|
||||
[ Fonts.baseFont
|
||||
, fontSize (px 13)
|
||||
, color gray45
|
||||
, lineHeight (px 18)
|
||||
, fontWeight (int 400)
|
||||
, margin4 (px 5) (px 0) (px 0) (px 0)
|
||||
]
|
||||
]
|
||||
content
|
||||
|
||||
|
||||
{-| User-generated text.
|
||||
-}
|
||||
ugMediumBody : List (Html msg) -> Html msg
|
||||
ugMediumBody =
|
||||
p
|
||||
[ css
|
||||
[ Fonts.quizFont
|
||||
, fontSize (px 18)
|
||||
, lineHeight (px 30)
|
||||
, whiteSpace preLine
|
||||
, color gray20
|
||||
, margin4 (px 10) (px 0) (px 0) (px 0)
|
||||
, firstChild [ margin zero ]
|
||||
, firstOfType [ margin zero ]
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
{-| User-generated text.
|
||||
-}
|
||||
ugSmallBody : List (Html msg) -> Html msg
|
||||
ugSmallBody =
|
||||
p
|
||||
[ css
|
||||
[ Fonts.quizFont
|
||||
, fontSize (px 16)
|
||||
, lineHeight (px 25)
|
||||
, whiteSpace preLine
|
||||
, color gray20
|
||||
, margin4 (px 7) (px 0) (px 0) (px 0)
|
||||
, firstChild [ margin zero ]
|
||||
, firstOfType [ margin zero ]
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
{-| Eliminate widows (single words on their own line caused by
|
||||
wrapping) by inserting a non-breaking space if there are at least two
|
||||
words.
|
||||
-}
|
||||
noWidow : String -> String
|
||||
noWidow inputs =
|
||||
let
|
||||
-- this value is a unicode non-breaking space since Elm
|
||||
-- doesn't support named character entities
|
||||
nbsp =
|
||||
"\u{00A0}"
|
||||
|
||||
words =
|
||||
String.split " " inputs
|
||||
|
||||
insertPoint =
|
||||
List.length words - 1
|
||||
in
|
||||
words
|
||||
|> List.indexedMap
|
||||
(\i word ->
|
||||
if i == 0 then
|
||||
word
|
||||
|
||||
else if i == insertPoint && insertPoint > 0 then
|
||||
nbsp ++ word
|
||||
|
||||
else
|
||||
" " ++ word
|
||||
)
|
||||
|> String.join ""
|
@ -1,223 +0,0 @@
|
||||
module Nri.Ui.Text.V4 exposing
|
||||
( caption, mediumBody, smallBody, smallBodyGray
|
||||
, ugMediumBody, ugSmallBody
|
||||
, noWidow
|
||||
)
|
||||
|
||||
{-| Post-release patches
|
||||
|
||||
- adjusts link styles
|
||||
|
||||
Changes from V3:
|
||||
|
||||
- Removes Headings (they now live in Nri.Ui.Heading.V2)
|
||||
|
||||
|
||||
## Understanding spacing
|
||||
|
||||
- All text styles have a specific line-height. This is set so that when text in the given style
|
||||
is long enough to wrap, the spacing between wrapped lines looks good.
|
||||
- No text styles have padding.
|
||||
- **Heading styles** do not have margin. It is up to the caller to add appropriate margin to the layout.
|
||||
- **Paragraph styles** only have bottom margin, but with **:last-child bottom margin set to zero**.
|
||||
This bottom margin is set to look good when multiple paragraphs of the same style follow one another.
|
||||
- If you want content after the paragraph and don't want the margin, put the paragraph in a `div` so that it will be the last-child, which will get rid of the bottom margin.
|
||||
- **User-authored content blocks** preserve line breaks and do not have margin.
|
||||
|
||||
|
||||
## Headings
|
||||
|
||||
Headings now live in Nri.Ui.Heading.V2. Here's a mapping to help with upgrades:
|
||||
|
||||
| Nri.Ui.Text.V3 | Nri.Ui.Heading.V2 |
|
||||
|===================|===================|
|
||||
| Text.heading | Heading.h1 |
|
||||
| Text.tagline | Heading.h2 |
|
||||
| Text.subHeading | Heading.h3 |
|
||||
| Text.smallHeading | Heading.h4 |
|
||||
|
||||
If you look at your new code and go "hmm, those shouldn't be at this level of
|
||||
heading" then you can customize the tag apart from the style using the new
|
||||
API. See the Nri.Ui.Heading.V2 docs for details.
|
||||
|
||||
|
||||
## Paragraph styles
|
||||
|
||||
@docs caption, mediumBody, smallBody, smallBodyGray
|
||||
|
||||
|
||||
## User-authored content blocks:
|
||||
|
||||
@docs ugMediumBody, ugSmallBody
|
||||
|
||||
|
||||
## Modifying strings to display nicely:
|
||||
|
||||
@docs noWidow
|
||||
|
||||
-}
|
||||
|
||||
import Css exposing (..)
|
||||
import Css.Global exposing (a, descendants)
|
||||
import Html.Styled exposing (..)
|
||||
import Html.Styled.Attributes exposing (css)
|
||||
import Nri.Ui.Colors.V1 exposing (..)
|
||||
import Nri.Ui.Fonts.V1 as Fonts
|
||||
|
||||
|
||||
{-| This is some medium body copy.
|
||||
-}
|
||||
mediumBody : List (Html msg) -> Html msg
|
||||
mediumBody content =
|
||||
p
|
||||
[ paragraphStyles
|
||||
{ font = Fonts.baseFont
|
||||
, color = gray20
|
||||
, size = 18
|
||||
, lineHeight = 28
|
||||
, weight = 400
|
||||
, margin = 10
|
||||
}
|
||||
]
|
||||
content
|
||||
|
||||
|
||||
{-| This is some small body copy.
|
||||
-}
|
||||
smallBody : List (Html msg) -> Html msg
|
||||
smallBody content =
|
||||
p
|
||||
[ paragraphStyles
|
||||
{ font = Fonts.baseFont
|
||||
, color = gray20
|
||||
, size = 15
|
||||
, lineHeight = 23
|
||||
, weight = 400
|
||||
, margin = 7
|
||||
}
|
||||
]
|
||||
content
|
||||
|
||||
|
||||
{-| This is some small body copy but it's gray.
|
||||
-}
|
||||
smallBodyGray : List (Html msg) -> Html msg
|
||||
smallBodyGray content =
|
||||
p
|
||||
[ paragraphStyles
|
||||
{ font = Fonts.baseFont
|
||||
, color = gray45
|
||||
, size = 15
|
||||
, lineHeight = 23
|
||||
, weight = 400
|
||||
, margin = 7
|
||||
}
|
||||
]
|
||||
content
|
||||
|
||||
|
||||
paragraphStyles config =
|
||||
css
|
||||
[ config.font
|
||||
, fontSize (px config.size)
|
||||
, color config.color
|
||||
, lineHeight (px config.lineHeight)
|
||||
, fontWeight (int config.weight)
|
||||
, padding zero
|
||||
, textAlign left
|
||||
, margin4 (px 0) (px 0) (px config.margin) (px 0)
|
||||
, Css.Global.descendants
|
||||
[ Css.Global.a
|
||||
[ textDecoration none
|
||||
, color azure
|
||||
, borderBottom3 (px 1) solid azure
|
||||
, visited
|
||||
[ color azure ]
|
||||
]
|
||||
]
|
||||
, lastChild
|
||||
[ margin zero
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
{-| This is a little note or caption.
|
||||
-}
|
||||
caption : List (Html msg) -> Html msg
|
||||
caption content =
|
||||
p
|
||||
[ paragraphStyles
|
||||
{ font = Fonts.baseFont
|
||||
, color = gray45
|
||||
, size = 13
|
||||
, lineHeight = 18
|
||||
, weight = 400
|
||||
, margin = 5
|
||||
}
|
||||
]
|
||||
content
|
||||
|
||||
|
||||
{-| User-generated text.
|
||||
-}
|
||||
ugMediumBody : List (Html msg) -> Html msg
|
||||
ugMediumBody =
|
||||
p
|
||||
[ css
|
||||
[ Fonts.quizFont
|
||||
, fontSize (px 18)
|
||||
, lineHeight (px 30)
|
||||
, whiteSpace preLine
|
||||
, color gray20
|
||||
, margin zero
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
{-| User-generated text.
|
||||
-}
|
||||
ugSmallBody : List (Html msg) -> Html msg
|
||||
ugSmallBody =
|
||||
p
|
||||
[ css
|
||||
[ Fonts.quizFont
|
||||
, fontSize (px 16)
|
||||
, lineHeight (px 25)
|
||||
, whiteSpace preLine
|
||||
, color gray20
|
||||
, margin zero
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
{-| Eliminate widows (single words on their own line caused by
|
||||
wrapping) by inserting a non-breaking space if there are at least two
|
||||
words.
|
||||
-}
|
||||
noWidow : String -> String
|
||||
noWidow inputs =
|
||||
let
|
||||
-- this value is a unicode non-breaking space since Elm
|
||||
-- doesn't support named character entities
|
||||
nbsp =
|
||||
"\u{00A0}"
|
||||
|
||||
words =
|
||||
String.split " " inputs
|
||||
|
||||
insertPoint =
|
||||
List.length words - 1
|
||||
in
|
||||
words
|
||||
|> List.indexedMap
|
||||
(\i word ->
|
||||
if i == 0 then
|
||||
word
|
||||
|
||||
else if i == insertPoint && insertPoint > 0 then
|
||||
nbsp ++ word
|
||||
|
||||
else
|
||||
" " ++ word
|
||||
)
|
||||
|> String.join ""
|
@ -1,77 +0,0 @@
|
||||
module AtomicDesignType exposing
|
||||
( AtomicDesignType(..)
|
||||
, all, sorter, toString
|
||||
)
|
||||
|
||||
{-|
|
||||
|
||||
@docs AtomicDesignType
|
||||
@docs all, sorter, toString
|
||||
|
||||
-}
|
||||
|
||||
import Sort exposing (Sorter)
|
||||
|
||||
|
||||
{-| -}
|
||||
type AtomicDesignType
|
||||
= Atom
|
||||
| Molecule
|
||||
| Organism
|
||||
| Template
|
||||
| Page
|
||||
|
||||
|
||||
{-| -}
|
||||
all : List AtomicDesignType
|
||||
all =
|
||||
[ Atom
|
||||
, Molecule
|
||||
, Organism
|
||||
, Template
|
||||
, Page
|
||||
]
|
||||
|
||||
|
||||
{-| -}
|
||||
sorter : Sorter AtomicDesignType
|
||||
sorter =
|
||||
Sort.by
|
||||
(\v ->
|
||||
case v of
|
||||
Atom ->
|
||||
0
|
||||
|
||||
Molecule ->
|
||||
1
|
||||
|
||||
Organism ->
|
||||
2
|
||||
|
||||
Template ->
|
||||
3
|
||||
|
||||
Page ->
|
||||
4
|
||||
)
|
||||
Sort.increasing
|
||||
|
||||
|
||||
{-| -}
|
||||
toString : AtomicDesignType -> String
|
||||
toString atomicDesignType =
|
||||
case atomicDesignType of
|
||||
Atom ->
|
||||
"Atom"
|
||||
|
||||
Molecule ->
|
||||
"Molecule"
|
||||
|
||||
Organism ->
|
||||
"Organism"
|
||||
|
||||
Template ->
|
||||
"Template"
|
||||
|
||||
Page ->
|
||||
"Page"
|
@ -1,6 +1,5 @@
|
||||
module Example exposing (Example, view, wrapMsg, wrapState)
|
||||
|
||||
import AtomicDesignType exposing (AtomicDesignType)
|
||||
import Category exposing (Category)
|
||||
import Css exposing (..)
|
||||
import Css.Global exposing (a, descendants)
|
||||
@ -21,7 +20,6 @@ type alias Example state msg =
|
||||
, subscriptions : state -> Sub msg
|
||||
, view : state -> List (Html msg)
|
||||
, categories : List Category
|
||||
, atomicDesignType : AtomicDesignType
|
||||
, keyboardSupport : List KeyboardSupport
|
||||
}
|
||||
|
||||
@ -47,7 +45,6 @@ wrapMsg wrapMsg_ unwrapMsg example =
|
||||
, subscriptions = \state -> Sub.map wrapMsg_ (example.subscriptions state)
|
||||
, view = \state -> List.map (Html.map wrapMsg_) (example.view state)
|
||||
, categories = example.categories
|
||||
, atomicDesignType = example.atomicDesignType
|
||||
, keyboardSupport = example.keyboardSupport
|
||||
}
|
||||
|
||||
@ -79,7 +76,6 @@ wrapState wrapState_ unwrapState example =
|
||||
>> Maybe.map example.view
|
||||
>> Maybe.withDefault []
|
||||
, categories = example.categories
|
||||
, atomicDesignType = example.atomicDesignType
|
||||
, keyboardSupport = example.keyboardSupport
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,6 @@ import Examples.DisclosureIndicator as DisclosureIndicator
|
||||
import Examples.Divider as Divider
|
||||
import Examples.Fonts as Fonts
|
||||
import Examples.Heading as Heading
|
||||
import Examples.Icon as Icon
|
||||
import Examples.Loading as Loading
|
||||
import Examples.Logo as Logo
|
||||
import Examples.MasteryIcon as MasteryIcon
|
||||
@ -287,25 +286,6 @@ all =
|
||||
HeadingState childState ->
|
||||
Just childState
|
||||
|
||||
_ ->
|
||||
Nothing
|
||||
)
|
||||
, Icon.example
|
||||
|> Example.wrapMsg IconMsg
|
||||
(\msg ->
|
||||
case msg of
|
||||
IconMsg childMsg ->
|
||||
Just childMsg
|
||||
|
||||
_ ->
|
||||
Nothing
|
||||
)
|
||||
|> Example.wrapState IconState
|
||||
(\msg ->
|
||||
case msg of
|
||||
IconState childState ->
|
||||
Just childState
|
||||
|
||||
_ ->
|
||||
Nothing
|
||||
)
|
||||
@ -782,7 +762,6 @@ type State
|
||||
| DividerState Divider.State
|
||||
| FontsState Fonts.State
|
||||
| HeadingState Heading.State
|
||||
| IconState Icon.State
|
||||
| LoadingState Loading.State
|
||||
| LogoState Logo.State
|
||||
| MasteryIconState MasteryIcon.State
|
||||
@ -823,7 +802,6 @@ type Msg
|
||||
| DividerMsg Divider.Msg
|
||||
| FontsMsg Fonts.Msg
|
||||
| HeadingMsg Heading.Msg
|
||||
| IconMsg Icon.Msg
|
||||
| LoadingMsg Loading.Msg
|
||||
| LogoMsg Logo.Msg
|
||||
| MasteryIconMsg MasteryIcon.Msg
|
||||
|
@ -11,7 +11,6 @@ module Examples.Accordion exposing
|
||||
-}
|
||||
|
||||
import Accessibility.Styled as Html exposing (Html)
|
||||
import AtomicDesignType exposing (AtomicDesignType(..))
|
||||
import Browser.Dom as Dom
|
||||
import Category exposing (Category(..))
|
||||
import Css exposing (..)
|
||||
@ -26,7 +25,6 @@ import Nri.Ui.DisclosureIndicator.V2 as DisclosureIndicator
|
||||
import Nri.Ui.Fonts.V1 as Fonts
|
||||
import Nri.Ui.Heading.V2 as Heading
|
||||
import Nri.Ui.Svg.V1 as Svg
|
||||
import Nri.Ui.Text.V4 as Text
|
||||
import Nri.Ui.UiIcon.V1 as UiIcon
|
||||
import Set exposing (Set)
|
||||
import Task
|
||||
@ -42,7 +40,6 @@ example =
|
||||
, subscriptions = \_ -> Sub.none
|
||||
, view = view
|
||||
, categories = [ Layout ]
|
||||
, atomicDesignType = Molecule
|
||||
, keyboardSupport =
|
||||
[ { keys = [ Arrow KeyboardSupport.Up ]
|
||||
, result = "Moves the focus to the previous accordion header button (wraps focus to the last header button)"
|
||||
|
@ -6,13 +6,11 @@ module Examples.AssignmentIcon exposing (example, State, Msg)
|
||||
|
||||
-}
|
||||
|
||||
import AtomicDesignType exposing (AtomicDesignType(..))
|
||||
import Category exposing (Category(..))
|
||||
import Example exposing (Example)
|
||||
import Examples.IconExamples as IconExamples
|
||||
import KeyboardSupport exposing (Direction(..), Key(..))
|
||||
import Nri.Ui.AssignmentIcon.V2 as AssignmentIcon
|
||||
import Nri.Ui.Icon.V5 as Icon
|
||||
|
||||
|
||||
{-| -}
|
||||
@ -31,7 +29,6 @@ example =
|
||||
{ name = "AssignmentIcon"
|
||||
, version = 2
|
||||
, categories = [ Icons ]
|
||||
, atomicDesignType = Atom
|
||||
, keyboardSupport = []
|
||||
, state = ()
|
||||
, update = \_ state -> ( state, Cmd.none )
|
||||
|
@ -6,7 +6,6 @@ module Examples.Button exposing (Msg, State, example)
|
||||
|
||||
-}
|
||||
|
||||
import AtomicDesignType exposing (AtomicDesignType(..))
|
||||
import Category exposing (Category(..))
|
||||
import Css exposing (middle, verticalAlign)
|
||||
import Debug.Control as Control exposing (Control)
|
||||
@ -30,7 +29,6 @@ example =
|
||||
, subscriptions = \_ -> Sub.none
|
||||
, view = \state -> [ viewButtonExamples state ]
|
||||
, categories = [ Buttons ]
|
||||
, atomicDesignType = Atom
|
||||
, keyboardSupport = []
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,6 @@ module Examples.Callout exposing (example, State, Msg)
|
||||
-}
|
||||
|
||||
import Accessibility.Styled exposing (text)
|
||||
import AtomicDesignType exposing (AtomicDesignType(..))
|
||||
import Category exposing (Category(..))
|
||||
import Css
|
||||
import Example exposing (Example)
|
||||
@ -33,7 +32,6 @@ example =
|
||||
{ name = "Callout"
|
||||
, version = 1
|
||||
, categories = [ Messaging ]
|
||||
, atomicDesignType = Molecule
|
||||
, keyboardSupport = []
|
||||
, state = ()
|
||||
, update = \_ state -> ( state, Cmd.none )
|
||||
|
@ -6,7 +6,6 @@ module Examples.Checkbox exposing (Msg, State, example)
|
||||
|
||||
-}
|
||||
|
||||
import AtomicDesignType exposing (AtomicDesignType(..))
|
||||
import Category exposing (Category(..))
|
||||
import Css
|
||||
import Example exposing (Example)
|
||||
@ -17,7 +16,6 @@ import Nri.Ui.Checkbox.V5 as Checkbox
|
||||
import Nri.Ui.Data.PremiumLevel as PremiumLevel exposing (PremiumLevel(..))
|
||||
import Nri.Ui.PremiumCheckbox.V6 as PremiumCheckbox
|
||||
import Set exposing (Set)
|
||||
import Sort.Set
|
||||
|
||||
|
||||
{-| -}
|
||||
@ -51,7 +49,6 @@ example =
|
||||
, viewPremiumCheckboxes state
|
||||
]
|
||||
, categories = [ Inputs ]
|
||||
, atomicDesignType = Molecule
|
||||
, keyboardSupport =
|
||||
[ { keys = [ Space ]
|
||||
, result = "Select or deselect the checkbox (may cause page scroll)"
|
||||
|
@ -6,9 +6,7 @@ module Examples.ClickableSvg exposing (Msg, State, example)
|
||||
|
||||
-}
|
||||
|
||||
import AtomicDesignType exposing (AtomicDesignType(..))
|
||||
import Category exposing (Category(..))
|
||||
import Color exposing (Color)
|
||||
import Css
|
||||
import Debug.Control as Control exposing (Control)
|
||||
import EventExtras
|
||||
@ -34,7 +32,6 @@ example =
|
||||
{ name = "ClickableSvg"
|
||||
, version = 2
|
||||
, categories = [ Buttons, Icons ]
|
||||
, atomicDesignType = Molecule
|
||||
, keyboardSupport = []
|
||||
, state = init
|
||||
, update = update
|
||||
|
@ -6,7 +6,6 @@ module Examples.ClickableText exposing (Msg, State, example)
|
||||
|
||||
-}
|
||||
|
||||
import AtomicDesignType exposing (AtomicDesignType(..))
|
||||
import Category exposing (Category(..))
|
||||
import Css exposing (middle, verticalAlign)
|
||||
import Debug.Control as Control exposing (Control)
|
||||
@ -35,7 +34,6 @@ example =
|
||||
, subscriptions = \_ -> Sub.none
|
||||
, view = \state -> [ viewExamples state ]
|
||||
, categories = [ Buttons ]
|
||||
, atomicDesignType = Molecule
|
||||
, keyboardSupport = []
|
||||
}
|
||||
|
||||
|
@ -6,9 +6,7 @@ module Examples.Colors exposing (example, State, Msg)
|
||||
|
||||
-}
|
||||
|
||||
import AtomicDesignType exposing (AtomicDesignType(..))
|
||||
import Category exposing (Category(..))
|
||||
import Color exposing (highContrast)
|
||||
import Css
|
||||
import Example exposing (Example)
|
||||
import Html.Styled as Html
|
||||
@ -17,6 +15,7 @@ import KeyboardSupport exposing (Direction(..), Key(..))
|
||||
import Nri.Ui.Colors.Extra
|
||||
import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.Heading.V2 as Heading
|
||||
import SolidColor exposing (highContrast)
|
||||
|
||||
|
||||
type alias ColorExample =
|
||||
@ -38,7 +37,6 @@ example =
|
||||
{ name = "Colors"
|
||||
, version = 1
|
||||
, categories = [ Colors ]
|
||||
, atomicDesignType = Atom
|
||||
, keyboardSupport = []
|
||||
, state = ()
|
||||
, update = \_ state -> ( state, Cmd.none )
|
||||
|
@ -6,7 +6,6 @@ module Examples.Confetti exposing (example, State, Msg)
|
||||
|
||||
-}
|
||||
|
||||
import AtomicDesignType exposing (AtomicDesignType(..))
|
||||
import Browser.Events
|
||||
import Category exposing (Category(..))
|
||||
import Css exposing (Color)
|
||||
@ -25,7 +24,6 @@ example =
|
||||
{ name = "Confetti"
|
||||
, version = 2
|
||||
, categories = [ Animations ]
|
||||
, atomicDesignType = Molecule
|
||||
, keyboardSupport = []
|
||||
, state = Confetti.init 700
|
||||
, update = update
|
||||
|
@ -6,7 +6,6 @@ module Examples.DisclosureIndicator exposing (Msg, State, example)
|
||||
|
||||
-}
|
||||
|
||||
import AtomicDesignType exposing (AtomicDesignType(..))
|
||||
import Category exposing (Category(..))
|
||||
import Css
|
||||
import Example exposing (Example)
|
||||
@ -17,7 +16,7 @@ import KeyboardSupport exposing (Direction(..), Key(..))
|
||||
import Nri.Ui.Button.V10 as Button
|
||||
import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.DisclosureIndicator.V2 as DisclosureIndicator
|
||||
import Nri.Ui.Text.V2 as Text
|
||||
import Nri.Ui.Text.V5 as Text
|
||||
|
||||
|
||||
{-| -}
|
||||
@ -33,14 +32,13 @@ example =
|
||||
{ name = "DisclosureIndicator"
|
||||
, version = 2
|
||||
, categories = [ Widgets ]
|
||||
, atomicDesignType = Atom
|
||||
, keyboardSupport = []
|
||||
, state = init
|
||||
, update = update
|
||||
, subscriptions = \_ -> Sub.none
|
||||
, view =
|
||||
\state ->
|
||||
[ Text.smallBodyGray [ Html.text "The disclosure indicator is only the caret. It is NOT a button -- you must create a button or clickabletext yourself!" ]
|
||||
[ Text.smallBodyGray [] [ Html.text "The disclosure indicator is only the caret. It is NOT a button -- you must create a button or clickabletext yourself!" ]
|
||||
, Html.div [ css [ Css.displayFlex, Css.padding (Css.px 8) ] ]
|
||||
[ Button.button "Toggle large indicator"
|
||||
[ Button.onClick ToggleLarge, Button.small, Button.secondary ]
|
||||
|
@ -6,7 +6,6 @@ module Examples.Divider exposing (Msg, State, example)
|
||||
|
||||
-}
|
||||
|
||||
import AtomicDesignType exposing (AtomicDesignType(..))
|
||||
import Category exposing (Category(..))
|
||||
import Css
|
||||
import Example exposing (Example)
|
||||
@ -32,7 +31,6 @@ example =
|
||||
{ name = "Divider"
|
||||
, version = 2
|
||||
, categories = [ Layout ]
|
||||
, atomicDesignType = Atom
|
||||
, keyboardSupport = []
|
||||
, state = {}
|
||||
, update = \_ state -> ( state, Cmd.none )
|
||||
|
@ -6,7 +6,6 @@ module Examples.Fonts exposing (example, State, Msg)
|
||||
|
||||
-}
|
||||
|
||||
import AtomicDesignType exposing (AtomicDesignType(..))
|
||||
import Category exposing (Category(..))
|
||||
import Example exposing (Example)
|
||||
import Html.Styled as Html
|
||||
@ -32,7 +31,6 @@ example =
|
||||
{ name = "Fonts"
|
||||
, version = 1
|
||||
, categories = [ Text ]
|
||||
, atomicDesignType = Atom
|
||||
, keyboardSupport = []
|
||||
, state = ()
|
||||
, update = \_ state -> ( state, Cmd.none )
|
||||
|
@ -6,7 +6,6 @@ module Examples.Heading exposing (example, State, Msg)
|
||||
|
||||
-}
|
||||
|
||||
import AtomicDesignType exposing (AtomicDesignType(..))
|
||||
import Category exposing (Category(..))
|
||||
import Css
|
||||
import Example exposing (Example)
|
||||
@ -32,7 +31,6 @@ example =
|
||||
{ name = "Heading"
|
||||
, version = 2
|
||||
, categories = [ Text, Layout ]
|
||||
, atomicDesignType = Atom
|
||||
, keyboardSupport = []
|
||||
, state = ()
|
||||
, update = \_ state -> ( state, Cmd.none )
|
||||
|
@ -1,114 +0,0 @@
|
||||
module Examples.Icon exposing (example, State, Msg)
|
||||
|
||||
{-|
|
||||
|
||||
@docs example, State, Msg
|
||||
|
||||
-}
|
||||
|
||||
import AtomicDesignType exposing (AtomicDesignType(..))
|
||||
import Category exposing (Category(..))
|
||||
import Css
|
||||
import Css.Global
|
||||
import Example exposing (Example)
|
||||
import Html.Styled as Html exposing (Html)
|
||||
import Html.Styled.Attributes exposing (css)
|
||||
import KeyboardSupport exposing (Direction(..), Key(..))
|
||||
import Nri.Ui.AssetPath as AssetPath exposing (Asset(..))
|
||||
import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.Heading.V2 as Heading
|
||||
import Nri.Ui.Icon.V5 as Icon
|
||||
import Nri.Ui.Text.V4 as Text
|
||||
|
||||
|
||||
{-| -}
|
||||
type alias State =
|
||||
()
|
||||
|
||||
|
||||
{-| -}
|
||||
type alias Msg =
|
||||
()
|
||||
|
||||
|
||||
{-| -}
|
||||
example : Example State Msg
|
||||
example =
|
||||
{ name = "Icon"
|
||||
, version = 5
|
||||
, categories = [ Icons ]
|
||||
, atomicDesignType = Atom
|
||||
, keyboardSupport = []
|
||||
, state = ()
|
||||
, update = \_ state -> ( state, Cmd.none )
|
||||
, subscriptions = \_ -> Sub.none
|
||||
, view =
|
||||
\_ ->
|
||||
[ viewLarge "Bulbs and Tips"
|
||||
[ deprecatedIcon { icon = Icon.lightBulb { hint_png = Asset "assets/images/hint.png" }, background = Colors.frost, alt = "LightBulb" }
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
viewLarge :
|
||||
String
|
||||
-> List ( String, Html msg )
|
||||
-> Html msg
|
||||
viewLarge headerText icons =
|
||||
Html.section [ css [ Css.marginTop (Css.px 16) ] ]
|
||||
[ Heading.h2 [] [ Html.text headerText ]
|
||||
, Html.div [ css [ Css.displayFlex, Css.flexWrap Css.wrap ] ]
|
||||
(List.map viewIcon icons)
|
||||
]
|
||||
|
||||
|
||||
viewIcon : ( String, Html msg ) -> Html msg
|
||||
viewIcon ( name, assignmentIcon ) =
|
||||
Html.div
|
||||
[ css
|
||||
[ Css.margin (Css.px 10)
|
||||
, Css.width (Css.px 160)
|
||||
, Css.boxShadow4 (Css.px 10) (Css.px 5) (Css.px 5) Colors.navy
|
||||
, Css.displayFlex
|
||||
, Css.flexDirection Css.column
|
||||
, Css.alignItems Css.center
|
||||
, Css.justifyContent Css.flexStart
|
||||
]
|
||||
]
|
||||
[ Html.div
|
||||
[ css
|
||||
[ Css.height (Css.px 80)
|
||||
, Css.width (Css.px 80)
|
||||
, Css.margin (Css.px 10)
|
||||
, Css.color Colors.green
|
||||
]
|
||||
]
|
||||
[ assignmentIcon
|
||||
]
|
||||
, Text.mediumBody [ Html.text name ]
|
||||
]
|
||||
|
||||
|
||||
deprecatedIcon : { alt : String, background : Css.Color, icon : Icon.IconType } -> ( String, Html msg )
|
||||
deprecatedIcon { alt, background, icon } =
|
||||
( alt
|
||||
, Html.div
|
||||
[ css
|
||||
[ Css.backgroundColor background
|
||||
, Css.height (Css.px 80)
|
||||
, Css.width (Css.px 80)
|
||||
, Css.displayFlex
|
||||
, Css.alignItems Css.center
|
||||
, Css.justifyContent Css.center
|
||||
, Css.Global.descendants
|
||||
[ Css.Global.img
|
||||
[ Css.maxWidth (Css.pct 100)
|
||||
, Css.maxHeight (Css.pct 100)
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
[ Icon.icon { alt = alt, icon = icon }
|
||||
]
|
||||
)
|
@ -6,7 +6,7 @@ import Html.Styled.Attributes exposing (css, style, title)
|
||||
import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.Heading.V2 as Heading
|
||||
import Nri.Ui.Svg.V1 as Svg
|
||||
import Nri.Ui.Text.V4 as Text
|
||||
import Nri.Ui.Text.V5 as Text
|
||||
|
||||
|
||||
view : String -> List ( String, Svg.Svg ) -> Html msg
|
||||
@ -55,5 +55,5 @@ viewIcon ( name, icon, style ) =
|
||||
]
|
||||
]
|
||||
[ Html.div [ css style ] [ Svg.toHtml icon ]
|
||||
, Text.smallBody [ Html.text name ]
|
||||
, Text.smallBody [] [ Html.text name ]
|
||||
]
|
||||
|
@ -6,7 +6,6 @@ module Examples.Loading exposing (example, State, Msg)
|
||||
|
||||
-}
|
||||
|
||||
import AtomicDesignType exposing (AtomicDesignType(..))
|
||||
import Browser.Events
|
||||
import Category exposing (Category(..))
|
||||
import Css
|
||||
@ -21,7 +20,7 @@ import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.Heading.V2 as Heading
|
||||
import Nri.Ui.Loading.V1 as Loading
|
||||
import Nri.Ui.Svg.V1 as Svg
|
||||
import Nri.Ui.Text.V4 as Text
|
||||
import Nri.Ui.Text.V5 as Text
|
||||
|
||||
|
||||
{-| -}
|
||||
@ -91,7 +90,6 @@ example =
|
||||
{ name = "Loading"
|
||||
, version = 1
|
||||
, categories = [ Pages ]
|
||||
, atomicDesignType = Page
|
||||
, keyboardSupport = []
|
||||
, state = init
|
||||
, update = update
|
||||
@ -115,7 +113,7 @@ example =
|
||||
[ Loading.spinningPencil
|
||||
|> Svg.withColor Colors.blue
|
||||
|> Svg.toHtml
|
||||
, Text.caption [ Html.text "By default, the spinningPencil is white. Showing as blue for visibility." ]
|
||||
, Text.caption [] [ Html.text "By default, the spinningPencil is white. Showing as blue for visibility." ]
|
||||
, Loading.spinningDots
|
||||
|> Svg.toHtml
|
||||
]
|
||||
|
@ -6,7 +6,6 @@ module Examples.Logo exposing (example, State, Msg)
|
||||
|
||||
-}
|
||||
|
||||
import AtomicDesignType exposing (AtomicDesignType(..))
|
||||
import Category exposing (Category(..))
|
||||
import Css
|
||||
import Example exposing (Example)
|
||||
@ -32,7 +31,6 @@ example =
|
||||
{ name = "Logo"
|
||||
, version = 1
|
||||
, categories = [ Icons ]
|
||||
, atomicDesignType = Atom
|
||||
, keyboardSupport = []
|
||||
, state = ()
|
||||
, update = \_ state -> ( state, Cmd.none )
|
||||
|
@ -6,7 +6,6 @@ module Examples.MasteryIcon exposing (example, State, Msg)
|
||||
|
||||
-}
|
||||
|
||||
import AtomicDesignType exposing (AtomicDesignType(..))
|
||||
import Category exposing (Category(..))
|
||||
import Example exposing (Example)
|
||||
import Examples.IconExamples as IconExamples
|
||||
@ -31,7 +30,6 @@ example =
|
||||
{ name = "MasteryIcon"
|
||||
, version = 1
|
||||
, categories = [ Icons ]
|
||||
, atomicDesignType = Atom
|
||||
, keyboardSupport = []
|
||||
, state = ()
|
||||
, update = \_ state -> ( state, Cmd.none )
|
||||
|
@ -7,7 +7,6 @@ module Examples.Menu exposing (Msg, State, example)
|
||||
-}
|
||||
|
||||
import Accessibility.Styled as Html exposing (..)
|
||||
import AtomicDesignType exposing (AtomicDesignType(..))
|
||||
import Browser.Dom as Dom
|
||||
import Category exposing (Category(..))
|
||||
import Css
|
||||
@ -35,7 +34,6 @@ example =
|
||||
, update = update
|
||||
, subscriptions = \_ -> Sub.none
|
||||
, categories = [ Widgets ]
|
||||
, atomicDesignType = Molecule
|
||||
, keyboardSupport =
|
||||
[ { keys = [ Space ], result = "Opens the menu" }
|
||||
, { keys = [ Enter ], result = "Opens the menu" }
|
||||
|
@ -1,13 +1,11 @@
|
||||
module Examples.Message exposing (Msg, State, example)
|
||||
|
||||
import Accessibility.Styled as Html exposing (..)
|
||||
import AtomicDesignType exposing (AtomicDesignType(..))
|
||||
import Category exposing (Category(..))
|
||||
import CommonControls
|
||||
import Css exposing (..)
|
||||
import Debug.Control as Control exposing (Control)
|
||||
import Example exposing (Example)
|
||||
import Html.Styled exposing (styled)
|
||||
import Html.Styled.Attributes as Attributes exposing (css, href)
|
||||
import KeyboardSupport exposing (Direction(..), Key(..))
|
||||
import Nri.Ui.Colors.V1 as Colors
|
||||
@ -181,7 +179,6 @@ example =
|
||||
{ name = "Message"
|
||||
, version = 3
|
||||
, categories = [ Messaging ]
|
||||
, atomicDesignType = Molecule
|
||||
, keyboardSupport = []
|
||||
, state = init
|
||||
, update = update
|
||||
|
@ -7,7 +7,6 @@ module Examples.Modal exposing (Msg, State, example)
|
||||
-}
|
||||
|
||||
import Accessibility.Styled as Html exposing (Html, div, h3, h4, p, span, text)
|
||||
import AtomicDesignType exposing (AtomicDesignType(..))
|
||||
import Browser.Dom as Dom
|
||||
import Category exposing (Category(..))
|
||||
import Css exposing (..)
|
||||
@ -22,7 +21,7 @@ import Nri.Ui.ClickableText.V3 as ClickableText
|
||||
import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.FocusTrap.V1 as FocusTrap
|
||||
import Nri.Ui.Modal.V11 as Modal
|
||||
import Nri.Ui.Text.V4 as Text
|
||||
import Nri.Ui.Text.V5 as Text
|
||||
import Task
|
||||
|
||||
|
||||
@ -111,7 +110,6 @@ example =
|
||||
{ name = "Modal"
|
||||
, version = 11
|
||||
, categories = [ Modals ]
|
||||
, atomicDesignType = Organism
|
||||
, keyboardSupport =
|
||||
[ { keys = [ KeyboardSupport.Tab ]
|
||||
, result = "Moves focus to the next button within the modal or wraps back to the first element within the modal."
|
||||
@ -230,7 +228,7 @@ launchModalButton settings =
|
||||
|
||||
viewModalContent : String -> Html msg
|
||||
viewModalContent content =
|
||||
Text.mediumBody [ span [ css [ whiteSpace preLine ] ] [ text content ] ]
|
||||
Text.mediumBody [] [ span [ css [ whiteSpace preLine ] ] [ text content ] ]
|
||||
|
||||
|
||||
continueButtonId : String
|
||||
|
@ -6,7 +6,6 @@ module Examples.Page exposing (example, State, Msg)
|
||||
|
||||
-}
|
||||
|
||||
import AtomicDesignType exposing (AtomicDesignType(..))
|
||||
import Category exposing (Category(..))
|
||||
import CommonControls
|
||||
import Css
|
||||
@ -59,7 +58,6 @@ example =
|
||||
{ name = "Page"
|
||||
, version = 3
|
||||
, categories = [ Pages ]
|
||||
, atomicDesignType = Page
|
||||
, keyboardSupport = []
|
||||
, state = { httpError = CommonControls.httpError, recoveryText = initRecoveryText }
|
||||
, update = update
|
||||
|
@ -6,7 +6,6 @@ module Examples.Pennant exposing (example, State, Msg)
|
||||
|
||||
-}
|
||||
|
||||
import AtomicDesignType exposing (AtomicDesignType(..))
|
||||
import Category exposing (Category(..))
|
||||
import Css exposing (..)
|
||||
import Example exposing (Example)
|
||||
@ -35,7 +34,6 @@ example =
|
||||
{ name = "Pennant"
|
||||
, version = 2
|
||||
, categories = [ Icons ]
|
||||
, atomicDesignType = Atom
|
||||
, keyboardSupport = []
|
||||
, state = ()
|
||||
, update = \_ state -> ( state, Cmd.none )
|
||||
|
@ -10,7 +10,6 @@ module Examples.RadioButton exposing
|
||||
|
||||
-}
|
||||
|
||||
import AtomicDesignType exposing (AtomicDesignType(..))
|
||||
import Category exposing (Category(..))
|
||||
import Css exposing (..)
|
||||
import Debug.Control as Control exposing (Control)
|
||||
@ -37,7 +36,6 @@ example =
|
||||
, subscriptions = subscriptions
|
||||
, view = view
|
||||
, categories = [ Layout ]
|
||||
, atomicDesignType = Atom
|
||||
, keyboardSupport =
|
||||
[ { keys = [ Arrow Left ]
|
||||
, result = "Move the focus & select the radio button to the left"
|
||||
|
@ -11,7 +11,6 @@ module Examples.SegmentedControl exposing
|
||||
-}
|
||||
|
||||
import Accessibility.Styled as Html exposing (Html)
|
||||
import AtomicDesignType exposing (AtomicDesignType(..))
|
||||
import Browser.Dom as Dom
|
||||
import Category exposing (Category(..))
|
||||
import Css
|
||||
@ -69,7 +68,6 @@ example =
|
||||
}
|
||||
]
|
||||
, categories = [ Widgets, Layout ]
|
||||
, atomicDesignType = Molecule
|
||||
, keyboardSupport =
|
||||
[ { keys = [ KeyboardSupport.Tab ]
|
||||
, result = "Move focus to the currently-selected Control's content"
|
||||
|
@ -6,7 +6,6 @@ module Examples.Select exposing (Msg, State, example)
|
||||
|
||||
-}
|
||||
|
||||
import AtomicDesignType exposing (AtomicDesignType(..))
|
||||
import Category exposing (Category(..))
|
||||
import Css
|
||||
import Example exposing (Example)
|
||||
@ -26,7 +25,6 @@ example =
|
||||
, update = update
|
||||
, subscriptions = \_ -> Sub.none
|
||||
, categories = [ Inputs ]
|
||||
, atomicDesignType = Molecule
|
||||
, keyboardSupport = []
|
||||
, view =
|
||||
\state ->
|
||||
|
@ -7,7 +7,6 @@ module Examples.Slide exposing (Msg, State, example)
|
||||
-}
|
||||
|
||||
import Accessibility.Styled as Html
|
||||
import AtomicDesignType exposing (AtomicDesignType(..))
|
||||
import Category exposing (Category(..))
|
||||
import Css
|
||||
import Example exposing (Example)
|
||||
@ -39,7 +38,6 @@ example =
|
||||
{ name = "Slide"
|
||||
, version = 1
|
||||
, categories = [ Animations ]
|
||||
, atomicDesignType = Atom
|
||||
, keyboardSupport = []
|
||||
, state = init
|
||||
, update = update
|
||||
|
@ -7,7 +7,6 @@ module Examples.SlideModal exposing (Msg, State, example)
|
||||
-}
|
||||
|
||||
import Accessibility.Styled as Html exposing (Html, div, h3, p, text)
|
||||
import AtomicDesignType exposing (AtomicDesignType(..))
|
||||
import Category exposing (Category(..))
|
||||
import Css
|
||||
import Example exposing (Example)
|
||||
@ -37,7 +36,6 @@ example =
|
||||
{ name = "SlideModal"
|
||||
, version = 2
|
||||
, categories = [ Modals ]
|
||||
, atomicDesignType = Organism
|
||||
, keyboardSupport = []
|
||||
, state = init
|
||||
, update = update
|
||||
|
@ -6,7 +6,6 @@ module Examples.SortableTable exposing (Msg, State, example)
|
||||
|
||||
-}
|
||||
|
||||
import AtomicDesignType exposing (AtomicDesignType(..))
|
||||
import Category exposing (Category(..))
|
||||
import Example exposing (Example)
|
||||
import Html.Styled as Html
|
||||
@ -38,7 +37,6 @@ example =
|
||||
{ name = "SortableTable"
|
||||
, version = 2
|
||||
, categories = [ Tables, Layout ]
|
||||
, atomicDesignType = Molecule
|
||||
, keyboardSupport = []
|
||||
, state = init
|
||||
, update = update
|
||||
|
@ -6,9 +6,7 @@ module Examples.Svg exposing (Msg, State, example)
|
||||
|
||||
-}
|
||||
|
||||
import AtomicDesignType exposing (AtomicDesignType(..))
|
||||
import Category exposing (Category(..))
|
||||
import Color exposing (Color)
|
||||
import Css
|
||||
import Example exposing (Example)
|
||||
import Examples.IconExamples as IconExamples
|
||||
@ -22,6 +20,7 @@ import Nri.Ui.Heading.V2 as Heading
|
||||
import Nri.Ui.Select.V7 as Select
|
||||
import Nri.Ui.Svg.V1 as Svg
|
||||
import Nri.Ui.UiIcon.V1 as UiIcon
|
||||
import SolidColor exposing (SolidColor)
|
||||
|
||||
|
||||
{-| -}
|
||||
@ -30,7 +29,6 @@ example =
|
||||
{ name = "Svg"
|
||||
, version = 1
|
||||
, categories = [ Icons ]
|
||||
, atomicDesignType = Atom
|
||||
, keyboardSupport = []
|
||||
, state = init
|
||||
, update = update
|
||||
@ -55,8 +53,8 @@ viewSettings state =
|
||||
[ Html.text "Color: "
|
||||
, Html.input
|
||||
[ Attributes.type_ "color"
|
||||
, Attributes.value (Color.toHex state.color)
|
||||
, Events.onInput (SetColor << Color.fromHex)
|
||||
, Attributes.value (SolidColor.toHex state.color)
|
||||
, Events.onInput (SetColor << SolidColor.fromHex)
|
||||
]
|
||||
[]
|
||||
]
|
||||
@ -97,7 +95,7 @@ viewResults : State -> Html.Html Msg
|
||||
viewResults state =
|
||||
let
|
||||
( red, green, blue ) =
|
||||
Color.toRGB state.color
|
||||
SolidColor.toRGB state.color
|
||||
in
|
||||
Html.div [ Attributes.css [ Css.displayFlex ] ]
|
||||
[ Html.pre
|
||||
@ -141,7 +139,7 @@ viewResults state =
|
||||
|
||||
{-| -}
|
||||
type alias State =
|
||||
{ color : Color
|
||||
{ color : SolidColor
|
||||
, width : Float
|
||||
, height : Float
|
||||
, label : String
|
||||
@ -160,7 +158,7 @@ init =
|
||||
|
||||
{-| -}
|
||||
type Msg
|
||||
= SetColor (Result String Color)
|
||||
= SetColor (Result String SolidColor)
|
||||
| SetWidth (Maybe Float)
|
||||
| SetHeight (Maybe Float)
|
||||
| SetLabel String
|
||||
|
@ -6,7 +6,6 @@ module Examples.Switch exposing (Msg, State, example)
|
||||
|
||||
-}
|
||||
|
||||
import AtomicDesignType
|
||||
import Category
|
||||
import Example exposing (Example)
|
||||
import Html.Styled as Html
|
||||
@ -68,6 +67,5 @@ example =
|
||||
]
|
||||
]
|
||||
, categories = [ Category.Inputs ]
|
||||
, atomicDesignType = AtomicDesignType.Atom
|
||||
, keyboardSupport = [{- TODO -}]
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ module Examples.Table exposing (Msg, State, example)
|
||||
|
||||
-}
|
||||
|
||||
import AtomicDesignType exposing (AtomicDesignType(..))
|
||||
import Category exposing (Category(..))
|
||||
import Css exposing (..)
|
||||
import Example exposing (Example)
|
||||
@ -37,7 +36,6 @@ example =
|
||||
, update = \_ state -> ( state, Cmd.none )
|
||||
, subscriptions = \_ -> Sub.none
|
||||
, categories = [ Tables, Layout ]
|
||||
, atomicDesignType = Molecule
|
||||
, keyboardSupport = []
|
||||
, view =
|
||||
\() ->
|
||||
|
@ -10,7 +10,6 @@ module Examples.Tabs exposing
|
||||
|
||||
-}
|
||||
|
||||
import AtomicDesignType exposing (AtomicDesignType(..))
|
||||
import Browser.Dom as Dom
|
||||
import Category exposing (Category(..))
|
||||
import Css
|
||||
@ -123,7 +122,6 @@ example =
|
||||
{ name = "Tabs"
|
||||
, version = 7
|
||||
, categories = [ Layout ]
|
||||
, atomicDesignType = Molecule
|
||||
, keyboardSupport =
|
||||
[ { keys = [ KeyboardSupport.Tab ]
|
||||
, result = "Move focus to the currently-selected Tab's tab panel"
|
||||
|
@ -6,7 +6,6 @@ module Examples.Text exposing (example, State, Msg)
|
||||
|
||||
-}
|
||||
|
||||
import AtomicDesignType exposing (AtomicDesignType(..))
|
||||
import Category exposing (Category(..))
|
||||
import Css
|
||||
import Example exposing (Example)
|
||||
@ -33,7 +32,6 @@ example =
|
||||
{ name = "Text"
|
||||
, version = 5
|
||||
, categories = [ Text ]
|
||||
, atomicDesignType = Atom
|
||||
, keyboardSupport = []
|
||||
, state = ()
|
||||
, update = \_ state -> ( state, Cmd.none )
|
||||
|
@ -6,7 +6,6 @@ module Examples.Text.Writing exposing (example, State, Msg)
|
||||
|
||||
-}
|
||||
|
||||
import AtomicDesignType exposing (AtomicDesignType(..))
|
||||
import Category exposing (Category(..))
|
||||
import Example exposing (Example)
|
||||
import Html.Styled exposing (text)
|
||||
@ -29,7 +28,6 @@ example =
|
||||
{ name = "Text.Writing"
|
||||
, version = 1
|
||||
, categories = [ Text ]
|
||||
, atomicDesignType = Atom
|
||||
, keyboardSupport = []
|
||||
, state = ()
|
||||
, update = \_ state -> ( state, Cmd.none )
|
||||
|
@ -6,7 +6,6 @@ module Examples.TextArea exposing (Msg, State, example)
|
||||
|
||||
-}
|
||||
|
||||
import AtomicDesignType exposing (AtomicDesignType(..))
|
||||
import Category exposing (Category(..))
|
||||
import Dict exposing (Dict)
|
||||
import Example exposing (Example)
|
||||
@ -44,7 +43,6 @@ example =
|
||||
, update = update
|
||||
, subscriptions = \_ -> Sub.none
|
||||
, categories = [ Inputs ]
|
||||
, atomicDesignType = Molecule
|
||||
, keyboardSupport = []
|
||||
, view =
|
||||
\state ->
|
||||
|
@ -7,7 +7,6 @@ module Examples.TextInput exposing (Msg, State, example)
|
||||
-}
|
||||
|
||||
import Accessibility.Styled as Html exposing (..)
|
||||
import AtomicDesignType exposing (AtomicDesignType(..))
|
||||
import Category exposing (Category(..))
|
||||
import Css exposing (..)
|
||||
import Debug.Control as Control exposing (Control)
|
||||
@ -55,7 +54,6 @@ example =
|
||||
{ name = "TextInput"
|
||||
, version = 6
|
||||
, categories = [ Inputs ]
|
||||
, atomicDesignType = Molecule
|
||||
, keyboardSupport = []
|
||||
, state = init
|
||||
, update = update
|
||||
|
@ -7,19 +7,18 @@ module Examples.Tooltip exposing (example, State, Msg)
|
||||
-}
|
||||
|
||||
import Accessibility.Styled as Html exposing (Html)
|
||||
import AtomicDesignType exposing (AtomicDesignType(..))
|
||||
import Category exposing (Category(..))
|
||||
import Css
|
||||
import Debug.Control as Control exposing (Control)
|
||||
import Example exposing (Example)
|
||||
import Html.Styled.Attributes as Attributes exposing (css, href)
|
||||
import KeyboardSupport exposing (Direction(..), Key(..))
|
||||
import Nri.Ui.ClickableSvg.V1 as ClickableSvg
|
||||
import Nri.Ui.ClickableSvg.V2 as ClickableSvg
|
||||
import Nri.Ui.ClickableText.V3 as ClickableText
|
||||
import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.Heading.V2 as Heading
|
||||
import Nri.Ui.Svg.V1 as Svg
|
||||
import Nri.Ui.Text.V4 as Text
|
||||
import Nri.Ui.Text.V5 as Text
|
||||
import Nri.Ui.Tooltip.V2 as Tooltip
|
||||
import Nri.Ui.UiIcon.V1 as UiIcon
|
||||
|
||||
@ -29,7 +28,6 @@ example =
|
||||
{ name = "Tooltip"
|
||||
, version = 2
|
||||
, categories = [ Widgets ]
|
||||
, atomicDesignType = Molecule
|
||||
, keyboardSupport = []
|
||||
, state = init
|
||||
, update = update
|
||||
@ -79,7 +77,7 @@ update msg model =
|
||||
view : State -> List (Html Msg)
|
||||
view model =
|
||||
[ Heading.h3 [] [ Html.text "Using the Tooltip module" ]
|
||||
, Text.mediumBody
|
||||
, Text.mediumBody []
|
||||
[ Html.text "Label the Tooltip as either being the "
|
||||
, viewPrimaryLabelTooltip model.openTooltip
|
||||
, Html.text " or the "
|
||||
|
@ -6,7 +6,6 @@ module Examples.UiIcon exposing (example, State, Msg)
|
||||
|
||||
-}
|
||||
|
||||
import AtomicDesignType exposing (AtomicDesignType(..))
|
||||
import Category exposing (Category(..))
|
||||
import Example exposing (Example)
|
||||
import Examples.IconExamples as IconExamples
|
||||
@ -30,7 +29,6 @@ example =
|
||||
{ name = "UiIcon"
|
||||
, version = 1
|
||||
, categories = List.singleton Icons
|
||||
, atomicDesignType = Atom
|
||||
, keyboardSupport = []
|
||||
, state = ()
|
||||
, update = \_ state -> ( state, Cmd.none )
|
||||
|
@ -1,7 +1,6 @@
|
||||
module Main exposing (init, main)
|
||||
|
||||
import Accessibility.Styled as Html exposing (Html, img, text)
|
||||
import AtomicDesignType exposing (AtomicDesignType)
|
||||
import Browser exposing (Document, UrlRequest(..))
|
||||
import Browser.Dom
|
||||
import Browser.Navigation exposing (Key)
|
||||
@ -40,7 +39,6 @@ type alias Model =
|
||||
{ -- Global UI
|
||||
route : Route
|
||||
, moduleStates : Dict String (Example Examples.State Examples.Msg)
|
||||
, atomicDesignTypes : Set AtomicDesignType
|
||||
, navigationKey : Key
|
||||
}
|
||||
|
||||
@ -51,7 +49,6 @@ init () url key =
|
||||
, moduleStates =
|
||||
Dict.fromList
|
||||
(List.map (\example -> ( example.name, example )) Examples.all)
|
||||
, atomicDesignTypes = Set.fromList AtomicDesignType.sorter AtomicDesignType.all
|
||||
, navigationKey = key
|
||||
}
|
||||
, Cmd.none
|
||||
@ -62,7 +59,6 @@ type Msg
|
||||
= UpdateModuleStates String Examples.Msg
|
||||
| OnUrlRequest Browser.UrlRequest
|
||||
| OnUrlChange Url
|
||||
| ToggleAtomicDesignType AtomicDesignType Bool
|
||||
| SkipToMainContent
|
||||
| NoOp
|
||||
|
||||
@ -99,21 +95,6 @@ update action model =
|
||||
OnUrlChange route ->
|
||||
( { model | route = Routes.fromLocation route }, Cmd.none )
|
||||
|
||||
ToggleAtomicDesignType atomicDesignType isOpen ->
|
||||
( { model
|
||||
| atomicDesignTypes =
|
||||
(if isOpen then
|
||||
Set.insert
|
||||
|
||||
else
|
||||
Set.remove
|
||||
)
|
||||
atomicDesignType
|
||||
model.atomicDesignTypes
|
||||
}
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
SkipToMainContent ->
|
||||
( model
|
||||
, Task.attempt (\_ -> NoOp) (Browser.Dom.focus "maincontent")
|
||||
@ -141,12 +122,7 @@ view_ : Model -> Html Msg
|
||||
view_ model =
|
||||
let
|
||||
examples filterBy =
|
||||
List.filter
|
||||
(\m ->
|
||||
filterBy m
|
||||
&& Set.memberOf model.atomicDesignTypes m.atomicDesignType
|
||||
)
|
||||
(Dict.values model.moduleStates)
|
||||
List.filter (\m -> filterBy m) (Dict.values model.moduleStates)
|
||||
|
||||
mainContentHeader heading =
|
||||
Heading.h1
|
||||
@ -163,7 +139,7 @@ view_ model =
|
||||
, minHeight (vh 100)
|
||||
]
|
||||
]
|
||||
[ navigation model.route model.atomicDesignTypes
|
||||
[ navigation model.route
|
||||
, Html.main_ [ css [ flexGrow (int 1), sectionStyles ] ]
|
||||
(case model.route of
|
||||
Routes.Doodad doodad ->
|
||||
@ -209,8 +185,8 @@ view_ model =
|
||||
]
|
||||
|
||||
|
||||
navigation : Route -> Set AtomicDesignType -> Html Msg
|
||||
navigation route openAtomicDesignTypes =
|
||||
navigation : Route -> Html Msg
|
||||
navigation route =
|
||||
let
|
||||
isActive category =
|
||||
case route of
|
||||
@ -297,28 +273,9 @@ navigation route openAtomicDesignTypes =
|
||||
[ css [ margin4 zero zero (px 40) zero, padding zero ]
|
||||
, id "categories"
|
||||
]
|
||||
, Html.fieldset []
|
||||
(Html.legend [] [ text "Atomic Design type" ]
|
||||
:: List.map (checkAtomicDesignType openAtomicDesignTypes) AtomicDesignType.all
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
checkAtomicDesignType : Set AtomicDesignType -> AtomicDesignType -> Html Msg
|
||||
checkAtomicDesignType openAtomicDesignTypes atomicDesignType =
|
||||
let
|
||||
isChecked =
|
||||
Set.memberOf openAtomicDesignTypes atomicDesignType
|
||||
|
||||
name =
|
||||
AtomicDesignType.toString atomicDesignType
|
||||
in
|
||||
Html.labelAfter [ css [ display block ] ] (text name) <|
|
||||
Html.checkbox name
|
||||
(Just isChecked)
|
||||
[ Events.onCheck (ToggleAtomicDesignType atomicDesignType) ]
|
||||
|
||||
|
||||
sectionStyles : Css.Style
|
||||
sectionStyles =
|
||||
Css.batch [ margin2 (px 40) zero ]
|
||||
|
@ -7,7 +7,7 @@
|
||||
"elm-version": "0.19.1",
|
||||
"dependencies": {
|
||||
"direct": {
|
||||
"BrianHicks/elm-particle": "1.3.1",
|
||||
"BrianHicks/elm-particle": "1.5.0",
|
||||
"avh4/elm-debug-controls": "2.2.1",
|
||||
"elm/browser": "1.0.2",
|
||||
"elm/core": "1.0.5",
|
||||
@ -19,15 +19,13 @@
|
||||
"elm/regex": "1.0.0",
|
||||
"elm/svg": "1.0.1",
|
||||
"elm/url": "1.0.0",
|
||||
"elm-community/random-extra": "3.1.0",
|
||||
"elm-community/random-extra": "3.2.0",
|
||||
"elm-community/string-extra": "4.0.1",
|
||||
"pablohirafuji/elm-markdown": "2.0.5",
|
||||
"rtfeldman/elm-css": "16.1.0",
|
||||
"rtfeldman/elm-css": "16.1.1",
|
||||
"rtfeldman/elm-sorter-experiment": "2.1.1",
|
||||
"tesk9/accessible-html": "4.0.0",
|
||||
"tesk9/accessible-html-with-css": "2.1.1",
|
||||
"tesk9/modal": "5.0.1",
|
||||
"tesk9/palette": "2.0.0",
|
||||
"tesk9/accessible-html-with-css": "2.2.0",
|
||||
"tesk9/palette": "3.0.1",
|
||||
"wernerdegroot/listzipper": "4.0.0"
|
||||
},
|
||||
"indirect": {
|
||||
@ -38,7 +36,6 @@
|
||||
"elm/virtual-dom": "1.0.2",
|
||||
"justinmimbs/date": "3.2.1",
|
||||
"justinmimbs/time-extra": "1.1.0",
|
||||
"owanturist/elm-union-find": "1.0.0",
|
||||
"rtfeldman/elm-hex": "1.0.0"
|
||||
}
|
||||
},
|
||||
|
@ -1,33 +0,0 @@
|
||||
module Spec.List.Zipper.Extra exposing (fromSpec)
|
||||
|
||||
import Expect exposing (Expectation)
|
||||
import List.Zipper
|
||||
import List.Zipper.Extra
|
||||
import Test exposing (..)
|
||||
|
||||
|
||||
fromSpec : Test
|
||||
fromSpec =
|
||||
let
|
||||
before =
|
||||
[ 1, 2 ]
|
||||
|
||||
current =
|
||||
3
|
||||
|
||||
after =
|
||||
[ 4, 5 ]
|
||||
|
||||
zipper =
|
||||
List.Zipper.Extra.from before current after
|
||||
in
|
||||
describe "List.Zipper.Extra.from before current after"
|
||||
[ test "before" <|
|
||||
\() ->
|
||||
Expect.equal (List.Zipper.before zipper) before
|
||||
, test "current" <|
|
||||
\() ->
|
||||
Expect.equal (List.Zipper.current zipper) current
|
||||
, test "after" <|
|
||||
\() -> Expect.equal (List.Zipper.after zipper) after
|
||||
]
|
@ -1,77 +0,0 @@
|
||||
module Spec.Nri.Ui.SegmentedControl exposing (..)
|
||||
|
||||
import Expect
|
||||
import Html.Attributes as Attributes
|
||||
import Html.Styled
|
||||
import Json.Encode as Encode
|
||||
import Nri.Ui.SegmentedControl.V13 as SegmentedControl
|
||||
import Test exposing (..)
|
||||
import Test.Html.Query as Query
|
||||
import Test.Html.Selector as Selector
|
||||
|
||||
|
||||
spec : Test
|
||||
spec =
|
||||
describe "segmented controls"
|
||||
[ describe "radio groups indicate when they're selected" <|
|
||||
-- nb. this is tested especially since QA is including this
|
||||
-- attribute in their automated tests. Make sure it doesn't break
|
||||
-- them!
|
||||
let
|
||||
selected =
|
||||
"I'm Selected!"
|
||||
|
||||
notSelected =
|
||||
"I'm Not Selected!"
|
||||
|
||||
control =
|
||||
SegmentedControl.viewRadioGroup
|
||||
{ onSelect = identity
|
||||
, options =
|
||||
List.map
|
||||
(\value ->
|
||||
{ value = value
|
||||
, idString = value
|
||||
, label = Html.Styled.text value
|
||||
, attributes = []
|
||||
, icon = Nothing
|
||||
}
|
||||
)
|
||||
[ selected, notSelected ]
|
||||
, selected = Just selected
|
||||
, positioning = SegmentedControl.Left SegmentedControl.FitContent
|
||||
, legend = "A Segmented Control Example"
|
||||
}
|
||||
in
|
||||
[ test "a checked=true attribute is added to the selected control" <|
|
||||
\_ ->
|
||||
Html.Styled.toUnstyled control
|
||||
|> Query.fromHtml
|
||||
|> Query.has
|
||||
[ Selector.all
|
||||
[ Selector.tag "label"
|
||||
, Selector.containing [ Selector.text selected ]
|
||||
, Selector.containing
|
||||
[ Selector.tag "input"
|
||||
, Selector.attribute (Attributes.type_ "radio")
|
||||
, Selector.attribute (Attributes.attribute "data-nri-checked" "true")
|
||||
]
|
||||
]
|
||||
]
|
||||
, test "a checked=false attribute is added to a non-selected control" <|
|
||||
\_ ->
|
||||
Html.Styled.toUnstyled control
|
||||
|> Query.fromHtml
|
||||
|> Query.has
|
||||
[ Selector.all
|
||||
[ Selector.tag "label"
|
||||
, Selector.containing [ Selector.text notSelected ]
|
||||
, Selector.containing
|
||||
[ Selector.tag "input"
|
||||
, Selector.attribute (Attributes.type_ "radio")
|
||||
, Selector.attribute (Attributes.attribute "data-nri-checked" "false")
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
@ -3,14 +3,13 @@
|
||||
"tests": [
|
||||
"Nri.Ui",
|
||||
"Nri.Ui.Accordion.V1",
|
||||
"Nri.Ui.Accordion.V2",
|
||||
"Nri.Ui.Accordion.V3",
|
||||
"Nri.Ui.AssetPath",
|
||||
"Nri.Ui.AssignmentIcon.V2",
|
||||
"Nri.Ui.Button.V10",
|
||||
"Nri.Ui.Button.V8",
|
||||
"Nri.Ui.Callout.V1",
|
||||
"Nri.Ui.Checkbox.V5",
|
||||
"Nri.Ui.ClickableSvg.V1",
|
||||
"Nri.Ui.ClickableSvg.V2",
|
||||
"Nri.Ui.ClickableText.V3",
|
||||
"Nri.Ui.Colors.Extra",
|
||||
@ -26,19 +25,13 @@
|
||||
"Nri.Ui.Heading.V2",
|
||||
"Nri.Ui.Html.Attributes.V2",
|
||||
"Nri.Ui.Html.V3",
|
||||
"Nri.Ui.Icon.V3",
|
||||
"Nri.Ui.Icon.V4",
|
||||
"Nri.Ui.Icon.V5",
|
||||
"Nri.Ui.InputStyles.V2",
|
||||
"Nri.Ui.InputStyles.V3",
|
||||
"Nri.Ui.Loading.V1",
|
||||
"Nri.Ui.Logo.V1",
|
||||
"Nri.Ui.MasteryIcon.V1",
|
||||
"Nri.Ui.Menu.V1",
|
||||
"Nri.Ui.Message.V1",
|
||||
"Nri.Ui.Message.V2",
|
||||
"Nri.Ui.Menu.V2",
|
||||
"Nri.Ui.Message.V3",
|
||||
"Nri.Ui.Modal.V3",
|
||||
"Nri.Ui.Modal.V10",
|
||||
"Nri.Ui.Modal.V11",
|
||||
"Nri.Ui.Page.V3",
|
||||
@ -47,9 +40,6 @@
|
||||
"Nri.Ui.PremiumCheckbox.V6",
|
||||
"Nri.Ui.RadioButton.V1",
|
||||
"Nri.Ui.RadioButton.V2",
|
||||
"Nri.Ui.SegmentedControl.V11",
|
||||
"Nri.Ui.SegmentedControl.V12",
|
||||
"Nri.Ui.SegmentedControl.V13",
|
||||
"Nri.Ui.SegmentedControl.V14",
|
||||
"Nri.Ui.Select.V5",
|
||||
"Nri.Ui.Select.V7",
|
||||
@ -62,8 +52,6 @@
|
||||
"Nri.Ui.Table.V5",
|
||||
"Nri.Ui.Tabs.V6",
|
||||
"Nri.Ui.Tabs.V7",
|
||||
"Nri.Ui.Text.V2",
|
||||
"Nri.Ui.Text.V4",
|
||||
"Nri.Ui.Text.V5",
|
||||
"Nri.Ui.Text.Writing.V1",
|
||||
"Nri.Ui.TextArea.V4",
|
||||
|
Loading…
Reference in New Issue
Block a user