mirror of
https://github.com/NoRedInk/noredink-ui.git
synced 2024-11-27 13:02:42 +03:00
Merge branch 'master' into tessa/remove-modal-v9
This commit is contained in:
commit
cfa65b53b5
1
elm.json
1
elm.json
@ -52,7 +52,6 @@
|
|||||||
"Nri.Ui.Svg.V1",
|
"Nri.Ui.Svg.V1",
|
||||||
"Nri.Ui.Table.V4",
|
"Nri.Ui.Table.V4",
|
||||||
"Nri.Ui.Table.V5",
|
"Nri.Ui.Table.V5",
|
||||||
"Nri.Ui.Tabs.V5",
|
|
||||||
"Nri.Ui.Tabs.V6",
|
"Nri.Ui.Tabs.V6",
|
||||||
"Nri.Ui.Text.V2",
|
"Nri.Ui.Text.V2",
|
||||||
"Nri.Ui.Text.V4",
|
"Nri.Ui.Text.V4",
|
||||||
|
@ -1,348 +0,0 @@
|
|||||||
module Nri.Ui.Tabs.V5 exposing
|
|
||||||
( view
|
|
||||||
, Alignment(..)
|
|
||||||
, Tab, viewTabDefault
|
|
||||||
)
|
|
||||||
|
|
||||||
{-|
|
|
||||||
|
|
||||||
@docs view
|
|
||||||
@docs Alignment
|
|
||||||
@docs Tab, viewTabDefault
|
|
||||||
|
|
||||||
-}
|
|
||||||
|
|
||||||
import Accessibility.Styled.Aria as Aria
|
|
||||||
import Accessibility.Styled.Role as Role
|
|
||||||
import Accessibility.Styled.Widget as Widget
|
|
||||||
import Css exposing (..)
|
|
||||||
import EventExtras
|
|
||||||
import Html.Styled as Html exposing (Attribute, Html)
|
|
||||||
import Html.Styled.Attributes as Attributes
|
|
||||||
import Html.Styled.Events as Events
|
|
||||||
import Json.Decode
|
|
||||||
import List.Zipper exposing (Zipper)
|
|
||||||
import List.Zipper.Extra
|
|
||||||
import Nri.Ui
|
|
||||||
import Nri.Ui.Colors.Extra
|
|
||||||
import Nri.Ui.Colors.V1 as Colors
|
|
||||||
import Nri.Ui.Fonts.V1
|
|
||||||
import Nri.Ui.Html.Attributes.V2 as AttributesExtra
|
|
||||||
|
|
||||||
|
|
||||||
{-| Determines whether tabs are centered or floating to the left or right.
|
|
||||||
-}
|
|
||||||
type Alignment
|
|
||||||
= Left
|
|
||||||
| Center
|
|
||||||
| Right
|
|
||||||
|
|
||||||
|
|
||||||
{-| -}
|
|
||||||
type alias Tab id msg =
|
|
||||||
{ id : id
|
|
||||||
, idString : String
|
|
||||||
, tabView : Html msg
|
|
||||||
, panelView : Html msg
|
|
||||||
, spaHref : Maybe String
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
{-| -}
|
|
||||||
view :
|
|
||||||
{ title : Maybe String
|
|
||||||
, alignment : Alignment
|
|
||||||
, customSpacing : Maybe Float
|
|
||||||
, onSelect : id -> msg
|
|
||||||
, onFocus : String -> msg
|
|
||||||
, selected : id
|
|
||||||
, tabs : List (Tab id msg)
|
|
||||||
}
|
|
||||||
-> Html msg
|
|
||||||
view config =
|
|
||||||
Nri.Ui.styled Html.div
|
|
||||||
(styledName "container")
|
|
||||||
[]
|
|
||||||
[]
|
|
||||||
[ Html.styled Html.div
|
|
||||||
[ Css.displayFlex
|
|
||||||
, Css.alignItems Css.flexEnd
|
|
||||||
, Css.borderBottom (Css.px 1)
|
|
||||||
, Css.borderBottomStyle Css.solid
|
|
||||||
, Css.borderBottomColor Colors.navy
|
|
||||||
, Nri.Ui.Fonts.V1.baseFont
|
|
||||||
]
|
|
||||||
[]
|
|
||||||
[ config.title
|
|
||||||
|> Maybe.map viewTitle
|
|
||||||
|> Maybe.withDefault (Html.text "")
|
|
||||||
, Html.styled Html.div
|
|
||||||
(stylesTabsAligned config.alignment)
|
|
||||||
[ Role.tabList
|
|
||||||
]
|
|
||||||
(List.map
|
|
||||||
(viewTab_
|
|
||||||
{ customSpacing = config.customSpacing
|
|
||||||
, onSelect = config.onSelect
|
|
||||||
, onFocus = config.onFocus
|
|
||||||
, tabs = config.tabs
|
|
||||||
, selected = config.selected
|
|
||||||
}
|
|
||||||
)
|
|
||||||
config.tabs
|
|
||||||
)
|
|
||||||
]
|
|
||||||
, Html.div []
|
|
||||||
(List.map
|
|
||||||
(\tab ->
|
|
||||||
Html.div
|
|
||||||
([ Role.tabPanel
|
|
||||||
, Aria.labelledBy (tabToId tab.idString)
|
|
||||||
, Attributes.id (tabToBodyId tab.idString)
|
|
||||||
]
|
|
||||||
++ (if tab.id /= config.selected then
|
|
||||||
[ Attributes.css [ Css.display none ]
|
|
||||||
, Widget.hidden True
|
|
||||||
]
|
|
||||||
|
|
||||||
else
|
|
||||||
[ Widget.hidden False ]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
[ tab.panelView ]
|
|
||||||
)
|
|
||||||
config.tabs
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
{-| -}
|
|
||||||
viewTabDefault : String -> Html msg
|
|
||||||
viewTabDefault title =
|
|
||||||
Html.div
|
|
||||||
[ Attributes.css
|
|
||||||
[ Css.padding4 (Css.px 14) (Css.px 20) (Css.px 12) (Css.px 20)
|
|
||||||
]
|
|
||||||
]
|
|
||||||
[ Html.text title ]
|
|
||||||
|
|
||||||
|
|
||||||
viewTitle : String -> Html msg
|
|
||||||
viewTitle title =
|
|
||||||
Html.styled Html.h1
|
|
||||||
[ Css.flexGrow (Css.int 2)
|
|
||||||
, Css.fontSize (Css.px 30)
|
|
||||||
, Css.fontWeight Css.bold
|
|
||||||
, Css.margin Css.zero
|
|
||||||
, Css.marginTop (Css.px 5)
|
|
||||||
, Css.marginBottom (Css.px 10)
|
|
||||||
, Css.color Colors.navy
|
|
||||||
, Css.width (Css.px 430)
|
|
||||||
]
|
|
||||||
[]
|
|
||||||
[ Html.text title ]
|
|
||||||
|
|
||||||
|
|
||||||
viewTab_ :
|
|
||||||
{ onSelect : id -> msg
|
|
||||||
, onFocus : String -> msg
|
|
||||||
, tabs : List (Tab id msg)
|
|
||||||
, selected : id
|
|
||||||
, customSpacing : Maybe Float
|
|
||||||
}
|
|
||||||
-> Tab id msg
|
|
||||||
-> Html msg
|
|
||||||
viewTab_ { onSelect, onFocus, tabs, selected, customSpacing } tab =
|
|
||||||
let
|
|
||||||
isSelected =
|
|
||||||
selected == tab.id
|
|
||||||
|
|
||||||
tabIndex =
|
|
||||||
-- From recommendation at https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/Tab_Role#Best_practices
|
|
||||||
if isSelected then
|
|
||||||
0
|
|
||||||
|
|
||||||
else
|
|
||||||
-1
|
|
||||||
|
|
||||||
( tag, tagSpecificAttributes ) =
|
|
||||||
case tab.spaHref of
|
|
||||||
Just href ->
|
|
||||||
( Html.a
|
|
||||||
, [ if isSelected then
|
|
||||||
Aria.currentPage
|
|
||||||
|
|
||||||
else
|
|
||||||
AttributesExtra.none
|
|
||||||
, Attributes.href href
|
|
||||||
, EventExtras.onClickPreventDefaultForLinkWithHref (onSelect tab.id)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
Nothing ->
|
|
||||||
( Html.button
|
|
||||||
, [ Events.onClick (onSelect tab.id)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
in
|
|
||||||
Html.styled tag
|
|
||||||
(tabStyles isSelected customSpacing)
|
|
||||||
(tagSpecificAttributes
|
|
||||||
++ [ Attributes.tabindex tabIndex
|
|
||||||
, Widget.selected isSelected
|
|
||||||
, Role.tab
|
|
||||||
, Attributes.id (tabToId tab.idString)
|
|
||||||
, Events.onFocus (onSelect tab.id)
|
|
||||||
, Events.on "keyup"
|
|
||||||
(Json.Decode.andThen (keyEvents onFocus tabs tab) Events.keyCode)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
[ tab.tabView
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
keyEvents : (String -> msg) -> List (Tab id msg) -> Tab id msg -> Int -> Json.Decode.Decoder msg
|
|
||||||
keyEvents onFocus tabs thisTab keyCode =
|
|
||||||
let
|
|
||||||
findAdjacentTab tab acc =
|
|
||||||
case acc of
|
|
||||||
( _, Just _ ) ->
|
|
||||||
acc
|
|
||||||
|
|
||||||
( True, Nothing ) ->
|
|
||||||
( True, Just (tabToId tab.idString) )
|
|
||||||
|
|
||||||
( False, Nothing ) ->
|
|
||||||
( tab.id == thisTab.id, Nothing )
|
|
||||||
|
|
||||||
nextTab =
|
|
||||||
List.foldl findAdjacentTab ( False, Nothing ) tabs
|
|
||||||
|> Tuple.second
|
|
||||||
|
|
||||||
previousTab =
|
|
||||||
List.foldr findAdjacentTab ( False, Nothing ) tabs
|
|
||||||
|> Tuple.second
|
|
||||||
in
|
|
||||||
case keyCode of
|
|
||||||
39 ->
|
|
||||||
-- Right
|
|
||||||
case nextTab of
|
|
||||||
Just next ->
|
|
||||||
Json.Decode.succeed (onFocus next)
|
|
||||||
|
|
||||||
Nothing ->
|
|
||||||
Json.Decode.fail "No next tab"
|
|
||||||
|
|
||||||
37 ->
|
|
||||||
-- Left
|
|
||||||
case previousTab of
|
|
||||||
Just previous ->
|
|
||||||
Json.Decode.succeed (onFocus previous)
|
|
||||||
|
|
||||||
Nothing ->
|
|
||||||
Json.Decode.fail "No previous tab"
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
Json.Decode.fail "Upsupported key event"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- HELP
|
|
||||||
|
|
||||||
|
|
||||||
tabToId : String -> String
|
|
||||||
tabToId tab =
|
|
||||||
String.replace " " "-" tab
|
|
||||||
|
|
||||||
|
|
||||||
tabToBodyId : String -> String
|
|
||||||
tabToBodyId tab =
|
|
||||||
"tab-body-" ++ tabToId tab
|
|
||||||
|
|
||||||
|
|
||||||
styledName : String -> String
|
|
||||||
styledName suffix =
|
|
||||||
"Nri-Ui-Tabs__" ++ suffix
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- STYLES
|
|
||||||
|
|
||||||
|
|
||||||
stylesTabsAligned : Alignment -> List Style
|
|
||||||
stylesTabsAligned alignment =
|
|
||||||
let
|
|
||||||
alignmentStyles =
|
|
||||||
case alignment of
|
|
||||||
Left ->
|
|
||||||
Css.justifyContent Css.flexStart
|
|
||||||
|
|
||||||
Center ->
|
|
||||||
Css.justifyContent Css.center
|
|
||||||
|
|
||||||
Right ->
|
|
||||||
Css.justifyContent Css.flexEnd
|
|
||||||
in
|
|
||||||
alignmentStyles
|
|
||||||
:: [ Css.margin Css.zero
|
|
||||||
, Css.fontSize (Css.px 19)
|
|
||||||
, Css.displayFlex
|
|
||||||
, Css.flexGrow (Css.int 1)
|
|
||||||
, Css.padding Css.zero
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
tabStyles : Bool -> Maybe Float -> List Style
|
|
||||||
tabStyles isSelected customSpacing =
|
|
||||||
let
|
|
||||||
stylesDynamic =
|
|
||||||
if isSelected then
|
|
||||||
[ Css.backgroundColor Colors.white
|
|
||||||
, Css.borderBottom (Css.px 1)
|
|
||||||
, Css.borderBottomStyle Css.solid
|
|
||||||
, Css.borderBottomColor Colors.white
|
|
||||||
]
|
|
||||||
|
|
||||||
else
|
|
||||||
[ Css.backgroundColor Colors.frost
|
|
||||||
, Css.backgroundImage <|
|
|
||||||
Css.linearGradient2 Css.toTop
|
|
||||||
(Css.stop2 (Nri.Ui.Colors.Extra.withAlpha 0.25 Colors.azure) (Css.pct 0))
|
|
||||||
(Css.stop2 (Nri.Ui.Colors.Extra.withAlpha 0 Colors.azure) (Css.pct 25))
|
|
||||||
[ Css.stop2 (Nri.Ui.Colors.Extra.withAlpha 0 Colors.azure) (Css.pct 100) ]
|
|
||||||
]
|
|
||||||
|
|
||||||
baseStyles =
|
|
||||||
[ Css.color Colors.navy
|
|
||||||
, Css.position Css.relative
|
|
||||||
, Css.textDecoration Css.none
|
|
||||||
, Css.property "background" "none"
|
|
||||||
, Css.fontFamily Css.inherit
|
|
||||||
, Css.fontSize Css.inherit
|
|
||||||
, Css.cursor Css.pointer
|
|
||||||
, Css.border zero
|
|
||||||
]
|
|
||||||
|
|
||||||
stylesTab =
|
|
||||||
[ Css.display Css.inlineBlock
|
|
||||||
, Css.borderTopLeftRadius (Css.px 10)
|
|
||||||
, Css.borderTopRightRadius (Css.px 10)
|
|
||||||
, Css.border3 (Css.px 1) Css.solid Colors.navy
|
|
||||||
, Css.marginTop Css.zero
|
|
||||||
, Css.marginRight Css.zero
|
|
||||||
, Css.marginLeft (Css.px (Maybe.withDefault 10 customSpacing))
|
|
||||||
, Css.padding2 (Css.px 1) (Css.px 6)
|
|
||||||
, Css.marginBottom (Css.px -1)
|
|
||||||
, Css.cursor Css.pointer
|
|
||||||
, Css.firstChild [ Css.marginLeft Css.zero ]
|
|
||||||
, property "transition" "background-color 0.2s"
|
|
||||||
, property "transition" "border-color 0.2s"
|
|
||||||
, hover
|
|
||||||
[ backgroundColor Colors.white
|
|
||||||
, borderTopColor Colors.azure
|
|
||||||
, borderRightColor Colors.azure
|
|
||||||
, borderLeftColor Colors.azure
|
|
||||||
]
|
|
||||||
]
|
|
||||||
in
|
|
||||||
baseStyles ++ stylesTab ++ stylesDynamic
|
|
@ -48,7 +48,6 @@
|
|||||||
"Nri.Ui.Svg.V1",
|
"Nri.Ui.Svg.V1",
|
||||||
"Nri.Ui.Table.V4",
|
"Nri.Ui.Table.V4",
|
||||||
"Nri.Ui.Table.V5",
|
"Nri.Ui.Table.V5",
|
||||||
"Nri.Ui.Tabs.V5",
|
|
||||||
"Nri.Ui.Tabs.V6",
|
"Nri.Ui.Tabs.V6",
|
||||||
"Nri.Ui.Text.V2",
|
"Nri.Ui.Text.V2",
|
||||||
"Nri.Ui.Text.V4",
|
"Nri.Ui.Text.V4",
|
||||||
|
Loading…
Reference in New Issue
Block a user