mirror of
https://github.com/NoRedInk/noredink-ui.git
synced 2024-11-27 13:02:42 +03:00
Merge remote-tracking branch 'origin/master' into bat/remove-old-components-18
This commit is contained in:
commit
3f38576338
1
elm.json
1
elm.json
@ -27,6 +27,7 @@
|
||||
"Nri.Ui.Divider.V2",
|
||||
"Nri.Ui.Effects.V1",
|
||||
"Nri.Ui.WhenFocusLeaves.V1",
|
||||
"Nri.Ui.FocusRing.V1",
|
||||
"Nri.Ui.FocusTrap.V1",
|
||||
"Nri.Ui.Fonts.V1",
|
||||
"Nri.Ui.Heading.V3",
|
||||
|
@ -99,6 +99,7 @@ import Markdown.Inline
|
||||
import Nri.Ui
|
||||
import Nri.Ui.Colors.Extra as ColorsExtra
|
||||
import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.FocusRing.V1 as FocusRing
|
||||
import Nri.Ui.Fonts.V1
|
||||
import Nri.Ui.Html.Attributes.V2 as ExtraAttributes
|
||||
import Nri.Ui.MediaQuery.V1 as MediaQuery
|
||||
@ -605,10 +606,14 @@ renderButton ((ButtonOrLink config) as button_) =
|
||||
in
|
||||
Nri.Ui.styled Html.button
|
||||
(styledName "customButton")
|
||||
[ buttonStyles config.size config.width buttonStyle_ config.customStyles ]
|
||||
[ buttonStyles config.size config.width buttonStyle_ config.customStyles
|
||||
, Css.pseudoClass "focus-visible"
|
||||
[ Css.outline Css.none, FocusRing.boxShadows [] ]
|
||||
]
|
||||
(ClickableAttributes.toButtonAttributes config.clickableAttributes
|
||||
++ Attributes.disabled (isDisabled config.state)
|
||||
:: Attributes.type_ "button"
|
||||
:: Attributes.class FocusRing.customClass
|
||||
:: config.customAttributes
|
||||
)
|
||||
[ viewLabel config.size config.icon config.label ]
|
||||
@ -629,8 +634,14 @@ renderLink ((ButtonOrLink config) as link_) =
|
||||
in
|
||||
Nri.Ui.styled Styled.a
|
||||
(styledName linkFunctionName)
|
||||
[ buttonStyles config.size config.width colorPalette config.customStyles ]
|
||||
(attributes ++ config.customAttributes)
|
||||
[ buttonStyles config.size config.width colorPalette config.customStyles
|
||||
, Css.pseudoClass "focus-visible"
|
||||
[ Css.outline Css.none, FocusRing.boxShadows [] ]
|
||||
]
|
||||
(Attributes.class FocusRing.customClass
|
||||
:: attributes
|
||||
++ config.customAttributes
|
||||
)
|
||||
[ viewLabel config.size config.icon config.label ]
|
||||
|
||||
|
||||
@ -689,19 +700,34 @@ toggleButton :
|
||||
-> Html msg
|
||||
toggleButton config =
|
||||
let
|
||||
pressedShadowColor =
|
||||
ColorsExtra.withAlpha 0.2 Colors.gray20
|
||||
|
||||
toggledBoxShadow =
|
||||
"inset 0 3px 0 "
|
||||
++ ColorsExtra.toCssString pressedShadowColor
|
||||
|
||||
toggledStyles =
|
||||
if config.pressed then
|
||||
Css.batch
|
||||
[ Css.color Colors.navy
|
||||
, Css.backgroundColor Colors.glacier
|
||||
, Css.boxShadow5 Css.inset Css.zero (Css.px 3) Css.zero (ColorsExtra.withAlpha 0.2 Colors.gray20)
|
||||
, Css.boxShadow5 Css.inset Css.zero (Css.px 3) Css.zero pressedShadowColor
|
||||
, Css.pseudoClass "focus-visible"
|
||||
[ Css.outline Css.none
|
||||
, FocusRing.boxShadows [ toggledBoxShadow ]
|
||||
]
|
||||
, Css.border3 (Css.px 1) Css.solid Colors.azure
|
||||
, Css.fontWeight Css.bold
|
||||
]
|
||||
|
||||
else
|
||||
Css.batch
|
||||
[]
|
||||
[ Css.pseudoClass "focus-visible"
|
||||
[ Css.outline Css.none
|
||||
, FocusRing.boxShadows []
|
||||
]
|
||||
]
|
||||
in
|
||||
Nri.Ui.styled Html.button
|
||||
(styledName "toggleButton")
|
||||
@ -729,6 +755,7 @@ toggleButton config =
|
||||
-- equivalent to preventDefaultBehavior = false
|
||||
-- https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#attr-name
|
||||
, Attributes.type_ "button"
|
||||
, Attributes.class FocusRing.customClass
|
||||
]
|
||||
[ viewLabel Medium Nothing config.label ]
|
||||
|
||||
|
@ -41,6 +41,7 @@ import Html.Styled.Attributes as Attributes exposing (css)
|
||||
import Html.Styled.Events as Events
|
||||
import Json.Decode
|
||||
import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.FocusRing.V1 as FocusRing
|
||||
import Nri.Ui.Fonts.V1 as Fonts
|
||||
import Nri.Ui.Svg.V1 exposing (Svg)
|
||||
|
||||
@ -165,13 +166,17 @@ checkboxContainer model =
|
||||
, marginLeft (px -4)
|
||||
, pseudoClass "focus-within"
|
||||
[ Css.Global.descendants
|
||||
[ Css.Global.class "checkbox-icon-container"
|
||||
[ borderColor (rgb 0 95 204)
|
||||
]
|
||||
[ Css.Global.class "checkbox-icon-container" FocusRing.styles
|
||||
]
|
||||
]
|
||||
, Css.Global.descendants
|
||||
[ Css.Global.input [ position absolute, top (calc (pct 50) minus (px 10)), left (px 10) ]
|
||||
[ Css.Global.input
|
||||
[ position absolute
|
||||
, top (calc (pct 50) minus (px 10))
|
||||
, left (px 10)
|
||||
, outline none |> Css.important
|
||||
, boxShadow none |> Css.important
|
||||
]
|
||||
]
|
||||
]
|
||||
, Attributes.id (model.identifier ++ "-container")
|
||||
@ -298,7 +303,7 @@ viewIcon styles icon =
|
||||
[ position absolute
|
||||
, left zero
|
||||
, top (calc (pct 50) minus (px 18))
|
||||
, border3 (px 2) solid transparent
|
||||
, margin (px 2)
|
||||
, padding (px 2)
|
||||
, borderRadius (px 3)
|
||||
, height (Css.px 27)
|
||||
|
@ -73,6 +73,7 @@ import Css.Media
|
||||
import Html.Styled as Html exposing (Html)
|
||||
import Html.Styled.Attributes as Attributes
|
||||
import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.FocusRing.V1 as FocusRing
|
||||
import Nri.Ui.Html.Attributes.V2 as ExtraAttributes
|
||||
import Nri.Ui.MediaQuery.V1 as MediaQuery
|
||||
import Nri.Ui.Svg.V1 as Svg exposing (Svg)
|
||||
@ -521,6 +522,7 @@ renderButton ((ButtonOrLink config) as button_) =
|
||||
in
|
||||
Html.button
|
||||
([ Attributes.class "Nri-Ui-Clickable-Svg-V1__button"
|
||||
, Attributes.class FocusRing.customClass
|
||||
, Attributes.type_ "button"
|
||||
, Attributes.css (buttonOrLinkStyles config theme ++ config.customStyles)
|
||||
, Attributes.disabled config.disabled
|
||||
@ -551,6 +553,7 @@ renderLink ((ButtonOrLink config) as link_) =
|
||||
in
|
||||
Html.a
|
||||
([ Attributes.class ("Nri-Ui-Clickable-Svg-" ++ linkFunctionName)
|
||||
, Attributes.class FocusRing.customClass
|
||||
, Attributes.css (buttonOrLinkStyles config theme ++ config.customStyles)
|
||||
, Aria.disabled config.disabled
|
||||
, Aria.label config.label
|
||||
@ -700,6 +703,15 @@ buttonOrLinkStyles config { main_, mainHovered, background, backgroundHovered, b
|
||||
, Css.boxSizing Css.borderBox
|
||||
, Css.width (Css.px (Maybe.withDefault (getSize config.size) config.width))
|
||||
, Css.height (Css.px (Maybe.withDefault (getSize config.size) config.height))
|
||||
|
||||
-- Focus
|
||||
, Css.pseudoClass "focus-visible"
|
||||
(if config.hasBorder then
|
||||
[ Css.outline Css.none, FocusRing.boxShadows [] ]
|
||||
|
||||
else
|
||||
FocusRing.styles
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
|
@ -96,6 +96,7 @@ import Html.Styled as Html exposing (..)
|
||||
import Html.Styled.Attributes as Attributes
|
||||
import Nri.Ui
|
||||
import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.FocusRing.V1 as FocusRing
|
||||
import Nri.Ui.Fonts.V1
|
||||
import Nri.Ui.Html.Attributes.V2 as ExtraAttributes
|
||||
import Nri.Ui.MediaQuery.V1 as MediaQuery
|
||||
@ -479,8 +480,8 @@ defaults =
|
||||
, size = Medium
|
||||
, label = ""
|
||||
, icon = Nothing
|
||||
, customAttributes = []
|
||||
, customStyles = []
|
||||
, customAttributes = [ Attributes.class FocusRing.customClass ]
|
||||
, customStyles = [ Css.pseudoClass "focus-visible" (Css.borderRadius (Css.px 4) :: FocusRing.tightStyles) ]
|
||||
}
|
||||
|
||||
|
||||
|
@ -17,6 +17,7 @@ module Nri.Ui.Colors.Extra exposing
|
||||
|
||||
import Css
|
||||
import SolidColor exposing (SolidColor)
|
||||
import TransparentColor
|
||||
|
||||
|
||||
{-| -}
|
||||
@ -50,5 +51,11 @@ withAlpha alpha { red, green, blue } =
|
||||
|
||||
{-| -}
|
||||
toCssString : Css.Color -> String
|
||||
toCssString =
|
||||
SolidColor.toRGBString << fromCssColor
|
||||
toCssString color =
|
||||
TransparentColor.fromRGBA
|
||||
{ red = toFloat color.red
|
||||
, green = toFloat color.green
|
||||
, blue = toFloat color.blue
|
||||
, alpha = TransparentColor.customOpacity color.alpha
|
||||
}
|
||||
|> TransparentColor.toRGBAString
|
||||
|
166
src/Nri/Ui/FocusRing/V1.elm
Normal file
166
src/Nri/Ui/FocusRing/V1.elm
Normal file
@ -0,0 +1,166 @@
|
||||
module Nri.Ui.FocusRing.V1 exposing
|
||||
( forKeyboardUsers, forMouseUsers
|
||||
, styles, tightStyles
|
||||
, boxShadows, outerBoxShadow, insetBoxShadow
|
||||
, customClass
|
||||
, outerColor, innerColor
|
||||
)
|
||||
|
||||
{-|
|
||||
|
||||
@docs forKeyboardUsers, forMouseUsers
|
||||
@docs styles, tightStyles
|
||||
@docs boxShadows, outerBoxShadow, insetBoxShadow
|
||||
@docs customClass
|
||||
@docs outerColor, innerColor
|
||||
|
||||
-}
|
||||
|
||||
import Css exposing (Color)
|
||||
import Css.Global exposing (Snippet)
|
||||
import Nri.Ui.Colors.Extra exposing (toCssString)
|
||||
import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.InputStyles.V3 as InputStyles exposing (focusedErrorInputBoxShadow, focusedInputBoxShadow)
|
||||
|
||||
|
||||
{-| When :focus-visible, add the two-tone focus ring.
|
||||
|
||||
Hides default focus ring from elements that are tagged as having a custom focus ring.
|
||||
|
||||
-}
|
||||
forKeyboardUsers : List Css.Global.Snippet
|
||||
forKeyboardUsers =
|
||||
[ Css.Global.class customClass [ Css.outline Css.none ]
|
||||
, Css.Global.selector (":not(." ++ customClass ++ "):focus-visible") styles
|
||||
, Css.Global.selector "p a:focus-visible" [ Css.important (Css.batch tightStyles) ]
|
||||
, Css.Global.class InputStyles.inputClass
|
||||
[ Css.pseudoClass "focus-visible"
|
||||
[ boxShadows [ focusedInputBoxShadow ]
|
||||
|> Css.important
|
||||
, Css.Global.withClass "error"
|
||||
[ boxShadows [ focusedErrorInputBoxShadow ]
|
||||
|> Css.important
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
{-| -}
|
||||
forMouseUsers : List Snippet
|
||||
forMouseUsers =
|
||||
[ Css.Global.everything [ Css.outline Css.none ]
|
||||
, Css.Global.selector ":focus-within .checkbox-icon-container"
|
||||
[ Css.important (Css.boxShadow Css.none)
|
||||
]
|
||||
, Css.Global.selector ":focus-within .Nri-RadioButton-RadioButtonIcon"
|
||||
[ Css.important (Css.boxShadow Css.none)
|
||||
]
|
||||
, Css.Global.selector ".nri-ui-input:focus"
|
||||
[ applyBoxShadows [ InputStyles.focusedInputBoxShadow ]
|
||||
]
|
||||
, Css.Global.selector ".switch-track"
|
||||
[ Css.important (Css.boxShadow Css.none)
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
{-| Add this class to remove global focus styles. Only do this
|
||||
if you'll be adding the two-tone focus ring styles another way.
|
||||
-}
|
||||
customClass : String
|
||||
customClass =
|
||||
"custom-focus-ring"
|
||||
|
||||
|
||||
{-| A two-tone focus ring that will be visually apparent for any background/element combination.
|
||||
|
||||
NOTE: use `boxShadows` instead if your focusable element:
|
||||
|
||||
- already has a box shadow
|
||||
- has an explicit border radius set
|
||||
|
||||
-}
|
||||
styles : List Css.Style
|
||||
styles =
|
||||
[ boxShadows []
|
||||
, Css.outline Css.none
|
||||
, Css.borderRadius (Css.px 4)
|
||||
]
|
||||
|
||||
|
||||
{-|
|
||||
|
||||
focus
|
||||
[ FocusRing.boxShadows [ "inset 0 3px 0 0 " ++ ColorsExtra.toCssString glacier ]
|
||||
, outline none
|
||||
]
|
||||
|
||||
-}
|
||||
boxShadows : List String -> Css.Style
|
||||
boxShadows existingBoxShadows =
|
||||
existingBoxShadows
|
||||
++ [ "0 0 0 3px " ++ innerColorString
|
||||
, "0 0 0 6px " ++ outerColorString
|
||||
]
|
||||
|> applyBoxShadows
|
||||
|
||||
|
||||
{-| Prefer `styles` over tightStyles, except in cases where line spacing/font size will otherwise cause obscured content.
|
||||
-}
|
||||
tightStyles : List Css.Style
|
||||
tightStyles =
|
||||
[ Css.outline Css.none
|
||||
, applyBoxShadows
|
||||
[ "inset 0 0 0 2px " ++ innerColorString
|
||||
, "0 0 0 2px " ++ outerColorString
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
{-| In special cases, we don't use a two-tone focus ring, and an outset focus ring would be obscured.
|
||||
|
||||
Be very sure this is what you need before using this!
|
||||
|
||||
-}
|
||||
insetBoxShadow : Css.Style
|
||||
insetBoxShadow =
|
||||
applyBoxShadows [ "inset 0 0 0 3px " ++ outerColorString ]
|
||||
|
||||
|
||||
{-| In special cases, we don't use a two-tone focus ring.
|
||||
|
||||
Be very sure this is what you need before using this!
|
||||
|
||||
-}
|
||||
outerBoxShadow : Css.Style
|
||||
outerBoxShadow =
|
||||
applyBoxShadows [ "0 0 0 3px " ++ outerColorString ]
|
||||
|
||||
|
||||
applyBoxShadows : List String -> Css.Style
|
||||
applyBoxShadows =
|
||||
-- using `property` due to https://github.com/rtfeldman/elm-css/issues/265
|
||||
String.join "," >> Css.property "box-shadow"
|
||||
|
||||
|
||||
innerColorString : String
|
||||
innerColorString =
|
||||
toCssString innerColor
|
||||
|
||||
|
||||
outerColorString : String
|
||||
outerColorString =
|
||||
toCssString outerColor
|
||||
|
||||
|
||||
{-| -}
|
||||
innerColor : Color
|
||||
innerColor =
|
||||
Colors.white
|
||||
|
||||
|
||||
{-| -}
|
||||
outerColor : Color
|
||||
outerColor =
|
||||
Colors.red
|
@ -1,6 +1,7 @@
|
||||
module Nri.Ui.InputStyles.V3 exposing
|
||||
( label, Theme(..), input
|
||||
, inputPaddingVertical, inputLineHeight, textAreaHeight, writingLineHeight, writingPadding, writingPaddingTop, writingMinHeight, defaultMarginTop
|
||||
, focusedInputBoxShadow, focusedErrorInputBoxShadow, errorClass, inputClass
|
||||
)
|
||||
|
||||
{-| InputStyles used by the TextInput and TextArea widgets.
|
||||
@ -11,6 +12,7 @@ module Nri.Ui.InputStyles.V3 exposing
|
||||
## Shared hardcoded values
|
||||
|
||||
@docs inputPaddingVertical, inputLineHeight, textAreaHeight, writingLineHeight, writingPadding, writingPaddingTop, writingMinHeight, defaultMarginTop
|
||||
@docs focusedInputBoxShadow, focusedErrorInputBoxShadow, errorClass, inputClass
|
||||
|
||||
|
||||
## Changelog
|
||||
@ -23,6 +25,7 @@ module Nri.Ui.InputStyles.V3 exposing
|
||||
|
||||
import Css exposing (..)
|
||||
import Css.Global
|
||||
import Nri.Ui.Colors.Extra as ColorsExtra
|
||||
import Nri.Ui.Colors.V1 exposing (..)
|
||||
import Nri.Ui.Fonts.V1
|
||||
|
||||
@ -116,10 +119,34 @@ defaultMarginTop =
|
||||
9
|
||||
|
||||
|
||||
{-| -}
|
||||
focusedInputBoxShadow : String
|
||||
focusedInputBoxShadow =
|
||||
"inset 0 3px 0 0 " ++ ColorsExtra.toCssString glacier
|
||||
|
||||
|
||||
{-| -}
|
||||
focusedErrorInputBoxShadow : String
|
||||
focusedErrorInputBoxShadow =
|
||||
"inset 0 3px 0 0 " ++ ColorsExtra.toCssString purpleLight
|
||||
|
||||
|
||||
{-| -}
|
||||
inputClass : String
|
||||
inputClass =
|
||||
"nri-input"
|
||||
|
||||
|
||||
{-| -}
|
||||
errorClass : String
|
||||
errorClass =
|
||||
"nri-input-error"
|
||||
|
||||
|
||||
{-| 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 =
|
||||
input : Theme -> Style
|
||||
input theme =
|
||||
let
|
||||
sharedStyles =
|
||||
batch
|
||||
@ -142,20 +169,16 @@ input theme isInError =
|
||||
, focus
|
||||
[ borderColor azure
|
||||
, outline none
|
||||
, boxShadow6 inset zero (px 3) zero zero glacier
|
||||
, property "box-shadow" focusedInputBoxShadow
|
||||
]
|
||||
, if isInError then
|
||||
batch
|
||||
, Css.Global.withClass errorClass
|
||||
[ borderColor purple
|
||||
, boxShadow6 inset zero (px 3) zero zero purpleLight
|
||||
, focus
|
||||
[ borderColor purple
|
||||
, boxShadow6 inset zero (px 3) zero zero purpleLight
|
||||
, focus
|
||||
[ borderColor purple
|
||||
, boxShadow6 inset zero (px 3) zero zero purpleLight
|
||||
]
|
||||
, property "box-shadow" focusedErrorInputBoxShadow
|
||||
]
|
||||
|
||||
else
|
||||
batch []
|
||||
]
|
||||
]
|
||||
in
|
||||
batch
|
||||
@ -191,15 +214,11 @@ input theme isInError =
|
||||
[ backgroundColor azure
|
||||
, color white
|
||||
, borderColor azure
|
||||
, if isInError then
|
||||
batch
|
||||
[ backgroundColor purple
|
||||
, color white
|
||||
, borderColor purple
|
||||
]
|
||||
|
||||
else
|
||||
batch []
|
||||
, Css.Global.withClass errorClass
|
||||
[ backgroundColor purple
|
||||
, color white
|
||||
, borderColor purple
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
@ -57,6 +57,7 @@ import Html.Styled.Attributes as Attributes exposing (class, classList, css)
|
||||
import Html.Styled.Events as Events
|
||||
import Json.Decode
|
||||
import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.FocusRing.V1 as FocusRing
|
||||
import Nri.Ui.Fonts.V1
|
||||
import Nri.Ui.Html.Attributes.V2 as AttributesExtra
|
||||
import Nri.Ui.Html.V3 exposing (viewJust)
|
||||
@ -323,7 +324,11 @@ button attributes title =
|
||||
StandardButton
|
||||
(\menuConfig buttonAttributes ->
|
||||
Html.button
|
||||
([ classList [ ( "ToggleButton", True ), ( "WithBorder", buttonConfig.hasBorder ) ]
|
||||
([ classList
|
||||
[ ( "ToggleButton", True )
|
||||
, ( "WithBorder", buttonConfig.hasBorder )
|
||||
, ( FocusRing.customClass, True )
|
||||
]
|
||||
, css
|
||||
[ Nri.Ui.Fonts.V1.baseFont
|
||||
, fontSize (px 15)
|
||||
@ -334,6 +339,10 @@ button attributes title =
|
||||
, height (pct 100)
|
||||
, fontWeight (int 600)
|
||||
, cursor pointer
|
||||
, pseudoClass "focus-visible"
|
||||
[ outline none
|
||||
, FocusRing.boxShadows []
|
||||
]
|
||||
, if menuConfig.isDisabled then
|
||||
Css.batch
|
||||
[ opacity (num 0.4)
|
||||
|
@ -48,6 +48,7 @@ import Html.Styled.Events exposing (onClick)
|
||||
import InputErrorAndGuidanceInternal exposing (ErrorState, Guidance)
|
||||
import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.Data.PremiumDisplay as PremiumDisplay exposing (PremiumDisplay)
|
||||
import Nri.Ui.FocusRing.V1 as FocusRing
|
||||
import Nri.Ui.Fonts.V1 as Fonts
|
||||
import Nri.Ui.Html.Attributes.V2 as Extra
|
||||
import Nri.Ui.Pennant.V2 as Pennant
|
||||
@ -301,16 +302,15 @@ view { label, name, value, valueToString, selectedValue } attributes =
|
||||
[ Attributes.id (idValue ++ "-container")
|
||||
, css
|
||||
[ position relative
|
||||
, marginLeft (px -4)
|
||||
, Css.paddingLeft (Css.px 40)
|
||||
, Css.marginLeft (Css.px -2)
|
||||
, Css.paddingLeft (Css.px 38)
|
||||
, Css.paddingTop (px 6)
|
||||
, Css.paddingBottom (px 4)
|
||||
, display inlineBlock
|
||||
, pseudoClass "focus-within"
|
||||
[ Css.Global.descendants
|
||||
[ Css.Global.class "Nri-RadioButton-RadioButtonIcon"
|
||||
[ borderColor (rgb 0 95 204)
|
||||
]
|
||||
FocusRing.tightStyles
|
||||
]
|
||||
]
|
||||
, Css.batch config.containerCss
|
||||
@ -339,17 +339,6 @@ view { label, name, value, valueToString, selectedValue } attributes =
|
||||
, top (pct 50)
|
||||
, left (px 4)
|
||||
, opacity zero
|
||||
, pseudoClass "focus"
|
||||
[ Css.Global.adjacentSiblings
|
||||
[ Css.Global.everything
|
||||
[ Css.Global.descendants
|
||||
[ Css.Global.class "Nri-RadioButton-RadioButtonIcon"
|
||||
[ borderColor (rgb 0 95 204)
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
++ List.map (Attributes.map never) config.custom
|
||||
@ -439,8 +428,8 @@ viewLockedButton { idValue, label } config =
|
||||
[ Attributes.id (idValue ++ "-container")
|
||||
, css
|
||||
[ position relative
|
||||
, marginLeft (px -4)
|
||||
, Css.paddingLeft (Css.px 40)
|
||||
, marginLeft (px -2)
|
||||
, Css.paddingLeft (Css.px 38)
|
||||
, Css.paddingTop (px 6)
|
||||
, Css.paddingBottom (px 4)
|
||||
, display inlineBlock
|
||||
@ -542,9 +531,6 @@ radioInputIcon config =
|
||||
iconHeight =
|
||||
26
|
||||
|
||||
borderWidth =
|
||||
2
|
||||
|
||||
iconPadding =
|
||||
2
|
||||
in
|
||||
@ -562,9 +548,8 @@ radioInputIcon config =
|
||||
[]
|
||||
, position absolute
|
||||
, left zero
|
||||
, top (calc (pct 50) Css.minus (Css.px ((iconHeight + borderWidth + iconPadding) / 2)))
|
||||
, top (calc (pct 50) Css.minus (Css.px ((iconHeight - 2 + iconPadding) / 2)))
|
||||
, Css.property "transition" ".3s all"
|
||||
, border3 (px borderWidth) solid transparent
|
||||
, borderRadius (px 50)
|
||||
, padding (px iconPadding)
|
||||
, displayFlex
|
||||
|
@ -26,8 +26,9 @@ import Css exposing (..)
|
||||
import Html.Styled
|
||||
import Html.Styled.Attributes as Attributes exposing (css)
|
||||
import Html.Styled.Events as Events
|
||||
import Nri.Ui.Colors.Extra exposing (withAlpha)
|
||||
import Nri.Ui.Colors.Extra as ColorsExtra
|
||||
import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.FocusRing.V1 as FocusRing
|
||||
import Nri.Ui.Fonts.V1 as Fonts
|
||||
import Nri.Ui.Svg.V1 as Svg exposing (Svg)
|
||||
import Nri.Ui.Tooltip.V3 as Tooltip
|
||||
@ -92,13 +93,8 @@ viewRadioGroup config =
|
||||
inner extraAttrs =
|
||||
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
|
||||
)
|
||||
(styles config.positioning numOptions index isSelected)
|
||||
:: Attributes.class FocusRing.customClass
|
||||
:: extraAttrs
|
||||
)
|
||||
[ radio name option.idString isSelected <|
|
||||
@ -194,7 +190,7 @@ view config =
|
||||
toInternalTab option =
|
||||
{ id = option.value
|
||||
, idString = option.idString
|
||||
, tabAttributes = option.attributes
|
||||
, tabAttributes = Attributes.class FocusRing.customClass :: option.attributes
|
||||
, tabTooltip =
|
||||
case config.positioning of
|
||||
Left FillContainer ->
|
||||
@ -272,6 +268,11 @@ styles positioning numEntries index isSelected =
|
||||
|
||||
_ ->
|
||||
[]
|
||||
, -- ensure that the focus state is visible & looks nice
|
||||
Css.pseudoClass "focus-within"
|
||||
[ FocusRing.boxShadows [ focusedSegmentBoxShadowValue ]
|
||||
, outline none
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
@ -312,12 +313,22 @@ sharedSegmentStyles numEntries index =
|
||||
focusedSegmentStyles : Style
|
||||
focusedSegmentStyles =
|
||||
[ backgroundColor Colors.glacier
|
||||
, boxShadow5 inset zero (px 3) zero (withAlpha 0.2 Colors.gray20)
|
||||
, Css.property "box-shadow" focusedSegmentBoxShadowValue
|
||||
, color Colors.navy
|
||||
]
|
||||
|> Css.batch
|
||||
|
||||
|
||||
focusedSegmentBoxShadowValue : String
|
||||
focusedSegmentBoxShadowValue =
|
||||
let
|
||||
colorStr =
|
||||
ColorsExtra.withAlpha 0.2 Colors.gray20
|
||||
|> ColorsExtra.toCssString
|
||||
in
|
||||
"inset 0 3px 0 " ++ colorStr
|
||||
|
||||
|
||||
unFocusedSegmentStyles : Style
|
||||
unFocusedSegmentStyles =
|
||||
[ backgroundColor Colors.white
|
||||
|
@ -417,7 +417,10 @@ viewSelect config =
|
||||
)
|
||||
, Css.borderBottomWidth (Css.px 3)
|
||||
, Css.borderRadius (Css.px 8)
|
||||
, Css.focus [ Css.borderColor Colors.azure ]
|
||||
, Css.focus
|
||||
[ Css.borderColor Colors.azure
|
||||
, Css.borderRadius (Css.px 8) |> Css.important
|
||||
]
|
||||
|
||||
-- Font and color
|
||||
, Css.color Colors.gray20
|
||||
|
@ -61,6 +61,7 @@ import Nri.Ui.ClickableSvg.V2 as ClickableSvg
|
||||
import Nri.Ui.ClickableText.V3 as ClickableText
|
||||
import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.Data.PremiumDisplay as PremiumDisplay exposing (PremiumDisplay)
|
||||
import Nri.Ui.FocusRing.V1 as FocusRing
|
||||
import Nri.Ui.Fonts.V1 as Fonts
|
||||
import Nri.Ui.Html.Attributes.V2 as ExtraAttributes
|
||||
import Nri.Ui.Html.V3 exposing (viewJust)
|
||||
@ -333,10 +334,17 @@ viewSkipLink onSkip =
|
||||
ClickableText.button "Skip to main content"
|
||||
[ ClickableText.icon UiIcon.arrowPointingRight
|
||||
, ClickableText.small
|
||||
, ClickableText.custom [ Attributes.class FocusRing.customClass ]
|
||||
, ClickableText.css
|
||||
[ Css.pseudoClass "not(:focus)"
|
||||
[ Style.invisibleStyle
|
||||
]
|
||||
, Css.pseudoClass "focus-visible"
|
||||
[ outline none
|
||||
, FocusRing.outerBoxShadow
|
||||
]
|
||||
, Css.padding (Css.px 2)
|
||||
, Css.borderRadius (Css.px 4)
|
||||
]
|
||||
, ClickableText.onClick onSkip
|
||||
]
|
||||
@ -422,7 +430,10 @@ viewSidebarLeaf config extraStyles entryConfig =
|
||||
)
|
||||
++ entryConfig.customStyles
|
||||
)
|
||||
(attributes ++ entryConfig.customAttributes)
|
||||
(Attributes.class FocusRing.customClass
|
||||
:: attributes
|
||||
++ entryConfig.customAttributes
|
||||
)
|
||||
[ viewJust
|
||||
(\icon_ ->
|
||||
icon_
|
||||
@ -446,7 +457,9 @@ viewLockedEntry extraStyles entryConfig =
|
||||
]
|
||||
(case entryConfig.onLockedContent of
|
||||
Just event ->
|
||||
Events.onClick event :: entryConfig.customAttributes
|
||||
Events.onClick event
|
||||
:: Attributes.class FocusRing.customClass
|
||||
:: entryConfig.customAttributes
|
||||
|
||||
Nothing ->
|
||||
entryConfig.customAttributes
|
||||
@ -463,6 +476,7 @@ viewLockedEntry extraStyles entryConfig =
|
||||
sharedEntryStyles : List Style
|
||||
sharedEntryStyles =
|
||||
[ padding2 (px 13) (px 20)
|
||||
, Css.pseudoClass "focus-visible" [ outline none, FocusRing.insetBoxShadow ]
|
||||
, Css.property "word-break" "normal"
|
||||
, Css.property "overflow-wrap" "anywhere"
|
||||
, displayFlex
|
||||
|
@ -42,6 +42,7 @@ import Html.Styled.Attributes as Attributes
|
||||
import Html.Styled.Events as Events
|
||||
import Nri.Ui.Colors.Extra exposing (toCssString)
|
||||
import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.FocusRing.V1 as FocusRing
|
||||
import Nri.Ui.Fonts.V1 as Fonts
|
||||
import Nri.Ui.Html.Attributes.V2 as Extra
|
||||
import Nri.Ui.MediaQuery.V1 as MediaQuery
|
||||
@ -160,9 +161,9 @@ view { label, id } attrs =
|
||||
, Css.position Css.relative
|
||||
, Css.pseudoClass "focus-within"
|
||||
[ Global.descendants
|
||||
[ Global.class "switch-slider"
|
||||
[ stroke Colors.azure
|
||||
, Css.property "stroke-width" "3px"
|
||||
[ Global.class "switch-track"
|
||||
[ FocusRing.boxShadows []
|
||||
, Css.borderRadius (Css.px 16)
|
||||
]
|
||||
]
|
||||
]
|
||||
@ -367,6 +368,7 @@ viewSwitch config =
|
||||
else
|
||||
Css.opacity (Css.num 1)
|
||||
]
|
||||
|> Nri.Ui.Svg.V1.withCustom [ SvgAttributes.class "switch-track" ]
|
||||
|
||||
|
||||
stroke : Color -> Style
|
||||
|
@ -32,6 +32,7 @@ import Html.Styled.Attributes as Attributes
|
||||
import Nri.Ui
|
||||
import Nri.Ui.Colors.Extra exposing (withAlpha)
|
||||
import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.FocusRing.V1 as FocusRing
|
||||
import Nri.Ui.Fonts.V1 as Fonts
|
||||
import Nri.Ui.Tooltip.V3 as Tooltip
|
||||
import TabsInternal.V2 as TabsInternal
|
||||
@ -105,10 +106,23 @@ spaHref url =
|
||||
Attribute (\tab -> { tab | spaHref = Just url })
|
||||
|
||||
|
||||
{-| -}
|
||||
tabAttributes : List (Html.Attribute msg) -> Attribute id msg
|
||||
tabAttributes attrs =
|
||||
Attribute (\tab -> { tab | tabAttributes = tab.tabAttributes ++ attrs })
|
||||
|
||||
|
||||
{-| -}
|
||||
build : { id : id, idString : String } -> List (Attribute id msg) -> Tab id msg
|
||||
build config attributes =
|
||||
Tab (TabsInternal.fromList config (List.map (\(Attribute f) -> f) attributes))
|
||||
Tab
|
||||
(TabsInternal.fromList config
|
||||
(List.map (\(Attribute f) -> f)
|
||||
(tabAttributes [ Attributes.class FocusRing.customClass ]
|
||||
:: attributes
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
{-| Determines whether tabs are centered or floating to the left or right.
|
||||
@ -272,6 +286,10 @@ tabStyles customSpacing index isSelected =
|
||||
, borderRightColor Colors.azure
|
||||
, borderLeftColor Colors.azure
|
||||
]
|
||||
, focus
|
||||
[ FocusRing.outerBoxShadow
|
||||
, outline none
|
||||
]
|
||||
]
|
||||
|
||||
margin =
|
||||
|
@ -117,7 +117,7 @@ view_ theme model =
|
||||
[ Css.display Css.block ]
|
||||
autoresizeAttrs
|
||||
[ Html.styled Html.textarea
|
||||
[ InputStyles.input theme model.isInError
|
||||
[ InputStyles.input theme
|
||||
, Css.boxSizing Css.borderBox
|
||||
, case model.height of
|
||||
AutoResize minimumHeight ->
|
||||
@ -133,7 +133,11 @@ view_ theme model =
|
||||
, Attributes.autofocus model.autofocus
|
||||
, Attributes.placeholder model.placeholder
|
||||
, Attributes.attribute "data-gramm" "false" -- disables grammarly to prevent https://github.com/NoRedInk/NoRedInk/issues/14859
|
||||
, Attributes.class "override-sass-styles"
|
||||
, Attributes.class "override-sass-styles custom-focus-ring"
|
||||
, Attributes.classList
|
||||
[ ( InputStyles.inputClass, True )
|
||||
, ( InputStyles.errorClass, model.isInError )
|
||||
]
|
||||
, Attributes.attribute "aria-invalid" <|
|
||||
if model.isInError then
|
||||
"true"
|
||||
|
@ -808,7 +808,7 @@ view label attributes =
|
||||
++ [ Attributes.id idValue
|
||||
, InputErrorAndGuidanceInternal.describedBy idValue config
|
||||
, Attributes.css
|
||||
[ InputStyles.input config.inputStyle isInError
|
||||
[ InputStyles.input config.inputStyle
|
||||
, if config.inputStyle == InputStyles.Writing then
|
||||
Css.Global.withClass "override-sass-styles"
|
||||
[ textAlign center
|
||||
@ -840,7 +840,11 @@ view label attributes =
|
||||
, maybeAttr (attribute "inputmode") config.inputMode
|
||||
, maybeAttr (attribute "autocomplete") config.autocomplete
|
||||
, maybeAttr onEnter_ eventsAndValues.onEnter
|
||||
, class "override-sass-styles"
|
||||
, class "nri-ui-textinput override-sass-styles custom-focus-ring"
|
||||
, classList
|
||||
[ ( InputStyles.inputClass, True )
|
||||
, ( InputStyles.errorClass, isInError )
|
||||
]
|
||||
, Attributes.attribute "aria-invalid" <|
|
||||
if isInError then
|
||||
"true"
|
||||
|
@ -851,8 +851,18 @@ viewTooltip tooltipId config =
|
||||
, Css.fontWeight (Css.int 600)
|
||||
, Css.color Colors.white
|
||||
, Shadows.high
|
||||
, Global.descendants [ Global.a [ Css.textDecoration Css.underline ] ]
|
||||
, Global.descendants [ Global.a [ Css.color Colors.white ] ]
|
||||
, Global.descendants
|
||||
[ Global.a
|
||||
[ Css.textDecoration Css.underline
|
||||
, Css.color Colors.white
|
||||
, Css.visited [ Css.color Colors.white ]
|
||||
, Css.hover [ Css.color Colors.white ]
|
||||
, Css.pseudoClass "focus-visible"
|
||||
[ Css.outline Css.none
|
||||
, Css.property "box-shadow" "0 0 0 2px #FFF"
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
++ config.tooltipStyleOverrides
|
||||
)
|
||||
|
@ -7,15 +7,17 @@ import Browser.Dom
|
||||
import Browser.Navigation exposing (Key)
|
||||
import Category exposing (Category)
|
||||
import Css exposing (..)
|
||||
import Css.Global
|
||||
import Css.Media exposing (withMedia)
|
||||
import Dict exposing (Dict)
|
||||
import Example exposing (Example)
|
||||
import Examples
|
||||
import Html.Styled.Attributes exposing (..)
|
||||
import Http
|
||||
import InputMethod exposing (InputMethod)
|
||||
import Json.Decode as Decode
|
||||
import Nri.Ui.CssVendorPrefix.V1 as VendorPrefixed
|
||||
import Nri.Ui.Heading.V3 as Heading
|
||||
import Nri.Ui.FocusRing.V1 as FocusRing
|
||||
import Nri.Ui.MediaQuery.V1 exposing (mobile)
|
||||
import Nri.Ui.Page.V3 as Page
|
||||
import Nri.Ui.SideNav.V4 as SideNav
|
||||
@ -40,6 +42,7 @@ type alias Model key =
|
||||
, openTooltip : Maybe TooltipId
|
||||
, navigationKey : key
|
||||
, elliePackageDependencies : Result Http.Error (Dict String String)
|
||||
, inputMethod : InputMethod
|
||||
}
|
||||
|
||||
|
||||
@ -57,6 +60,7 @@ init () url key =
|
||||
, openTooltip = Nothing
|
||||
, navigationKey = key
|
||||
, elliePackageDependencies = Ok Dict.empty
|
||||
, inputMethod = InputMethod.init
|
||||
}
|
||||
, Cmd.batch
|
||||
[ loadPackage
|
||||
@ -80,6 +84,7 @@ type Msg
|
||||
| ToggleTooltip TooltipId Bool
|
||||
| LoadedPackages (Result Http.Error (Dict String String))
|
||||
| Focused (Result Browser.Dom.Error ())
|
||||
| NewInputMethod InputMethod
|
||||
|
||||
|
||||
update : Msg -> Model key -> ( Model key, Effect )
|
||||
@ -181,6 +186,9 @@ update action model =
|
||||
Focused _ ->
|
||||
( model, None )
|
||||
|
||||
NewInputMethod inputMethod ->
|
||||
( { model | inputMethod = inputMethod }, None )
|
||||
|
||||
|
||||
type Effect
|
||||
= GoToRoute Route
|
||||
@ -215,9 +223,12 @@ perform navigationKey effect =
|
||||
|
||||
subscriptions : Model key -> Sub Msg
|
||||
subscriptions model =
|
||||
Dict.values model.moduleStates
|
||||
|> List.map (\example -> Sub.map (UpdateModuleStates example.name) (example.subscriptions example.state))
|
||||
|> Sub.batch
|
||||
Sub.batch
|
||||
[ Dict.values model.moduleStates
|
||||
|> List.map (\example -> Sub.map (UpdateModuleStates example.name) (example.subscriptions example.state))
|
||||
|> Sub.batch
|
||||
, Sub.map NewInputMethod InputMethod.subscriptions
|
||||
]
|
||||
|
||||
|
||||
view : Model key -> Document Msg
|
||||
@ -227,6 +238,7 @@ view model =
|
||||
List.map Html.toUnstyled
|
||||
[ view_
|
||||
, Html.map never Sprite.attach
|
||||
, Css.Global.global (InputMethod.styles model.inputMethod)
|
||||
]
|
||||
in
|
||||
case model.route of
|
||||
|
@ -12,6 +12,7 @@ import KeyboardSupport exposing (KeyboardSupport)
|
||||
import Nri.Ui.ClickableText.V3 as ClickableText
|
||||
import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.Container.V2 as Container
|
||||
import Nri.Ui.FocusRing.V1 as FocusRing
|
||||
|
||||
|
||||
type alias Example state msg =
|
||||
@ -118,6 +119,7 @@ preview_ { navigate, exampleHref } example =
|
||||
[ Css.backgroundColor Colors.glacier
|
||||
, Css.cursor Css.pointer
|
||||
]
|
||||
, Css.pseudoClass "focus-within" [ FocusRing.boxShadows [] ]
|
||||
]
|
||||
, Container.custom [ Events.onClick (navigate example) ]
|
||||
, Container.html
|
||||
@ -125,6 +127,7 @@ preview_ { navigate, exampleHref } example =
|
||||
[ ClickableText.href (exampleHref example)
|
||||
, ClickableText.css [ Css.marginBottom (Css.px 10) ]
|
||||
, ClickableText.nriDescription "doodad-link"
|
||||
, ClickableText.custom [ Attributes.class FocusRing.customClass ]
|
||||
]
|
||||
:: [ Html.div
|
||||
[ Attributes.css
|
||||
|
@ -50,7 +50,7 @@ example =
|
||||
[ Html.div [ css [ Css.position Css.relative ] ]
|
||||
[ Html.textarea
|
||||
[ css
|
||||
[ InputStyles.input Standard False
|
||||
[ InputStyles.input Standard
|
||||
, Css.minHeight (Css.px 100)
|
||||
, Css.maxWidth (Css.px 140)
|
||||
, Css.backgroundColor Colors.white |> Css.important
|
||||
@ -123,6 +123,7 @@ example =
|
||||
, placeholder = "Placeholder"
|
||||
, showLabel = state.showLabel == Checkbox.Selected
|
||||
}
|
||||
, Html.br [ css [ Css.marginBottom (Css.px 10) ] ] []
|
||||
, TextArea.writing
|
||||
{ value = Maybe.withDefault "" <| Dict.get 2 state.textValues
|
||||
, autofocus = False
|
||||
@ -139,6 +140,7 @@ example =
|
||||
, placeholder = "Placeholder"
|
||||
, showLabel = state.showLabel == Checkbox.Selected
|
||||
}
|
||||
, Html.br [ css [ Css.marginBottom (Css.px 10) ] ] []
|
||||
, TextArea.contentCreation
|
||||
{ value = Maybe.withDefault "" <| Dict.get 3 state.textValues
|
||||
, autofocus = False
|
||||
@ -155,6 +157,7 @@ example =
|
||||
, placeholder = "Placeholder"
|
||||
, showLabel = state.showLabel == Checkbox.Selected
|
||||
}
|
||||
, Html.br [ css [ Css.marginBottom (Css.px 10) ] ] []
|
||||
, TextArea.writing
|
||||
{ value = Maybe.withDefault "" <| Dict.get 4 state.textValues
|
||||
, autofocus = False
|
||||
|
99
styleguide-app/InputMethod.elm
Normal file
99
styleguide-app/InputMethod.elm
Normal file
@ -0,0 +1,99 @@
|
||||
module InputMethod exposing (InputMethod(..), init, subscriptions, styles)
|
||||
|
||||
{-| If in the NRI monolith, please see Nri.InputMethod for the equivalent module.
|
||||
|
||||
Utilities for detecting input method and hiding focus rings when
|
||||
appropriate. Inspired by a blog post from [David Gilbertson](https://medium.com/hackernoon/removing-that-ugly-focus-ring-and-keeping-it-too-6c8727fefcd2).
|
||||
|
||||
@docs InputMethod, init, subscriptions, styles
|
||||
|
||||
-}
|
||||
|
||||
import Browser.Events
|
||||
import Css.Global exposing (Snippet)
|
||||
import Json.Decode as Decode exposing (Decoder)
|
||||
import Nri.Ui.FocusRing.V1 as FocusRing
|
||||
|
||||
|
||||
{-| Represents the method of input the user is currently using to
|
||||
iteract with our app.
|
||||
-}
|
||||
type InputMethod
|
||||
= Keyboard
|
||||
| Mouse
|
||||
|
||||
|
||||
{-| Even though most users will probably be using a mouse, setting the initial
|
||||
value to Keyboard makes it so that we display the focus ring when we do care
|
||||
about which element is focused initially for users that rely on the keyboard.
|
||||
|
||||
There dont't seem to be any downsides of doing this for mouse users, since as
|
||||
soon as they interact with the page the input method will be changed.
|
||||
|
||||
-}
|
||||
init : InputMethod
|
||||
init =
|
||||
Keyboard
|
||||
|
||||
|
||||
{-| A subscription of the input method the user is currently using.
|
||||
-}
|
||||
subscriptions : Sub InputMethod
|
||||
subscriptions =
|
||||
Sub.batch
|
||||
[ Browser.Events.onKeyDown
|
||||
(Decode.map2 (\k t -> ( k, t ))
|
||||
(Decode.field "key" Decode.string)
|
||||
(Decode.at [ "target", "tagName" ] Decode.string)
|
||||
|> Decode.andThen
|
||||
(\( key, tagName ) ->
|
||||
case key of
|
||||
"ArrowUp" ->
|
||||
unlessInInput tagName
|
||||
|
||||
"ArrowDown" ->
|
||||
unlessInInput tagName
|
||||
|
||||
"ArrowRight" ->
|
||||
unlessInInput tagName
|
||||
|
||||
"ArrowLeft" ->
|
||||
unlessInInput tagName
|
||||
|
||||
"Tab" ->
|
||||
Decode.succeed Keyboard
|
||||
|
||||
"Escape" ->
|
||||
Decode.succeed Keyboard
|
||||
|
||||
" " ->
|
||||
unlessInInput tagName
|
||||
|
||||
_ ->
|
||||
Decode.fail "Not a navigation key. Discarding event."
|
||||
)
|
||||
)
|
||||
, Browser.Events.onMouseDown (Decode.succeed Mouse)
|
||||
]
|
||||
|
||||
|
||||
unlessInInput : String -> Decoder InputMethod
|
||||
unlessInInput tagName =
|
||||
if tagName == "TEXTAREA" || tagName == "INPUT" then
|
||||
Decode.fail "In an input. Discarding event."
|
||||
|
||||
else
|
||||
Decode.succeed Keyboard
|
||||
|
||||
|
||||
{-| A collection of global styles that will hide or show the focus ring if keyboard
|
||||
navigation is detected from the user.
|
||||
-}
|
||||
styles : InputMethod -> List Snippet
|
||||
styles inputMethod =
|
||||
case inputMethod of
|
||||
Keyboard ->
|
||||
FocusRing.forKeyboardUsers
|
||||
|
||||
Mouse ->
|
||||
FocusRing.forMouseUsers
|
@ -23,6 +23,7 @@
|
||||
"Nri.Ui.Divider.V2",
|
||||
"Nri.Ui.Effects.V1",
|
||||
"Nri.Ui.WhenFocusLeaves.V1",
|
||||
"Nri.Ui.FocusRing.V1",
|
||||
"Nri.Ui.FocusTrap.V1",
|
||||
"Nri.Ui.Fonts.V1",
|
||||
"Nri.Ui.Heading.V3",
|
||||
|
Loading…
Reference in New Issue
Block a user