mirror of
https://github.com/NoRedInk/noredink-ui.git
synced 2024-12-20 04:01:40 +03:00
Copy over v8 to v9
This commit is contained in:
parent
9c8e4e15a4
commit
b5fc602a49
335
src/Nri/Ui/SegmentedControl/V9.elm
Normal file
335
src/Nri/Ui/SegmentedControl/V9.elm
Normal file
@ -0,0 +1,335 @@
|
|||||||
|
module Nri.Ui.SegmentedControl.V9 exposing (Config, Option, Width(..), view, viewSpa, ToggleConfig, viewToggle, viewOptionalSelectToggle)
|
||||||
|
|
||||||
|
{-|
|
||||||
|
|
||||||
|
@docs Config, Option, Width, view, viewSpa, ToggleConfig, viewToggle, viewOptionalSelectToggle
|
||||||
|
|
||||||
|
Changes from V7:
|
||||||
|
|
||||||
|
- remove dependence on Nri.Ui.Icon.V5
|
||||||
|
- fix icons overlowing the segmented control
|
||||||
|
|
||||||
|
-}
|
||||||
|
|
||||||
|
import Accessibility.Styled exposing (..)
|
||||||
|
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
|
||||||
|
import Html.Styled.Attributes as Attr exposing (css, href)
|
||||||
|
import Html.Styled.Events as Events
|
||||||
|
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.Svg.V1 as Svg exposing (Svg)
|
||||||
|
import Nri.Ui.Util exposing (dashify)
|
||||||
|
|
||||||
|
|
||||||
|
{-|
|
||||||
|
|
||||||
|
- `onClick` : the message to produce when an option is selected (clicked) by the user
|
||||||
|
- `options`: the list of options available
|
||||||
|
- `selected`: the value of the currently-selected option
|
||||||
|
- `width`: how to size the segmented control
|
||||||
|
- `content`: the panel content for the selected option
|
||||||
|
|
||||||
|
-}
|
||||||
|
type alias Config a msg =
|
||||||
|
{ onClick : a -> msg
|
||||||
|
, options : List (Option a)
|
||||||
|
, selected : a
|
||||||
|
, width : Width
|
||||||
|
, content : Html msg
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{-| Same shape as Config but without the content
|
||||||
|
-}
|
||||||
|
type alias ToggleConfig a msg =
|
||||||
|
{ onClick : a -> msg
|
||||||
|
, options : List (Option a)
|
||||||
|
, selected : a
|
||||||
|
, width : Width
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{-| Same shape as ToggleConfig but with an optional selected. This would ideally
|
||||||
|
be the same as ToggleConfig but we as Zambonis don't have time in the ticket to
|
||||||
|
also upgrade all existing uses of viewToggle. Katie is mentally noting this as a
|
||||||
|
good hackday clean up but if you find it and fix it first, that's great too!
|
||||||
|
-}
|
||||||
|
type alias ToggleConfigWithOptionalSelection a msg =
|
||||||
|
{ onClick : a -> msg
|
||||||
|
, options : List (Option a)
|
||||||
|
, selected : Maybe a
|
||||||
|
, width : Width
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{-| -}
|
||||||
|
type alias Option a =
|
||||||
|
{ value : a
|
||||||
|
, icon : Maybe Svg
|
||||||
|
, label : String
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{-| -}
|
||||||
|
type Width
|
||||||
|
= FitContent
|
||||||
|
| FillContainer
|
||||||
|
|
||||||
|
|
||||||
|
{-| -}
|
||||||
|
view : Config a msg -> Html msg
|
||||||
|
view config =
|
||||||
|
viewHelper Nothing config
|
||||||
|
|
||||||
|
|
||||||
|
{-| Creates a segmented control that supports SPA navigation.
|
||||||
|
You should always use this instead of `view` when building a SPA
|
||||||
|
and the segmented control options correspond to routes in the SPA.
|
||||||
|
|
||||||
|
The first parameter is a function that takes a `route` and returns the URL of that route.
|
||||||
|
|
||||||
|
-}
|
||||||
|
viewSpa : (route -> String) -> Config route msg -> Html msg
|
||||||
|
viewSpa toUrl config =
|
||||||
|
viewHelper (Just toUrl) config
|
||||||
|
|
||||||
|
|
||||||
|
{-| Creates _just the toggle_ when need the ui element itself and not a page control
|
||||||
|
-}
|
||||||
|
viewToggle : ToggleConfig a msg -> Html msg
|
||||||
|
viewToggle config =
|
||||||
|
tabList
|
||||||
|
[ css
|
||||||
|
[ displayFlex
|
||||||
|
, cursor pointer
|
||||||
|
]
|
||||||
|
]
|
||||||
|
(List.map
|
||||||
|
(viewTab
|
||||||
|
{ onClick = config.onClick
|
||||||
|
, selected = Just config.selected
|
||||||
|
, width = config.width
|
||||||
|
, selectedAttribute = Widget.selected True
|
||||||
|
, maybeToUrl = Nothing
|
||||||
|
}
|
||||||
|
)
|
||||||
|
config.options
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
{-| Creates _just the toggle_ when need the ui element itself and not a page
|
||||||
|
control. Since this element is used for a selection and not for page navigation,
|
||||||
|
it seems reasonable to handle nothing being selected. Additionally, this feels
|
||||||
|
like under the hood it should be radio buttons or something that denotes
|
||||||
|
selection instead of buttons. Again, Katie is mentally noting this clean up for
|
||||||
|
hackday but if your heart sees fit, update if you'd like!
|
||||||
|
-}
|
||||||
|
viewOptionalSelectToggle : ToggleConfigWithOptionalSelection a msg -> Html msg
|
||||||
|
viewOptionalSelectToggle config =
|
||||||
|
tabList
|
||||||
|
[ css
|
||||||
|
[ displayFlex
|
||||||
|
, cursor pointer
|
||||||
|
]
|
||||||
|
]
|
||||||
|
(List.map
|
||||||
|
(viewTab
|
||||||
|
{ onClick = config.onClick
|
||||||
|
, selected = config.selected
|
||||||
|
, width = config.width
|
||||||
|
, selectedAttribute = Widget.selected True
|
||||||
|
, maybeToUrl = Nothing
|
||||||
|
}
|
||||||
|
)
|
||||||
|
config.options
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
viewHelper : Maybe (a -> String) -> Config a msg -> Html msg
|
||||||
|
viewHelper maybeToUrl config =
|
||||||
|
let
|
||||||
|
selected =
|
||||||
|
config.options
|
||||||
|
|> List.filter (\o -> o.value == config.selected)
|
||||||
|
|> List.head
|
||||||
|
in
|
||||||
|
div []
|
||||||
|
[ tabList
|
||||||
|
[ css
|
||||||
|
[ displayFlex
|
||||||
|
, cursor pointer
|
||||||
|
]
|
||||||
|
]
|
||||||
|
(List.map
|
||||||
|
(viewTab
|
||||||
|
{ onClick = config.onClick
|
||||||
|
, selected = Just config.selected
|
||||||
|
, width = config.width
|
||||||
|
, selectedAttribute = Aria.currentPage
|
||||||
|
, maybeToUrl = maybeToUrl
|
||||||
|
}
|
||||||
|
)
|
||||||
|
config.options
|
||||||
|
)
|
||||||
|
, tabPanel
|
||||||
|
(List.filterMap identity
|
||||||
|
[ Maybe.map (Aria.labelledBy << tabIdFor) selected
|
||||||
|
, Just <| css [ paddingTop (px 10) ]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
[ config.content
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
tabIdFor : Option a -> String
|
||||||
|
tabIdFor option =
|
||||||
|
"Nri-Ui-SegmentedControl-Tab-" ++ dashify option.label
|
||||||
|
|
||||||
|
|
||||||
|
panelIdFor : Option a -> String
|
||||||
|
panelIdFor option =
|
||||||
|
"Nri-Ui-SegmentedControl-Panel-" ++ dashify option.label
|
||||||
|
|
||||||
|
|
||||||
|
viewTab :
|
||||||
|
{ onClick : a -> msg
|
||||||
|
, selected : Maybe a
|
||||||
|
, width : Width
|
||||||
|
, selectedAttribute : Attribute msg
|
||||||
|
, maybeToUrl : Maybe (a -> String)
|
||||||
|
}
|
||||||
|
-> Option a
|
||||||
|
-> Html msg
|
||||||
|
viewTab config option =
|
||||||
|
let
|
||||||
|
idValue =
|
||||||
|
tabIdFor option
|
||||||
|
|
||||||
|
element attrs children =
|
||||||
|
case config.maybeToUrl of
|
||||||
|
Nothing ->
|
||||||
|
-- This is for a non-SPA view
|
||||||
|
button
|
||||||
|
(Events.onClick (config.onClick option.value)
|
||||||
|
:: attrs
|
||||||
|
)
|
||||||
|
children
|
||||||
|
|
||||||
|
Just toUrl ->
|
||||||
|
-- This is a for a SPA view
|
||||||
|
Html.Styled.a
|
||||||
|
(href (toUrl option.value)
|
||||||
|
:: EventExtras.onClickPreventDefaultForLinkWithHref
|
||||||
|
(config.onClick option.value)
|
||||||
|
:: attrs
|
||||||
|
)
|
||||||
|
children
|
||||||
|
in
|
||||||
|
element
|
||||||
|
(List.concat
|
||||||
|
[ [ Attr.id idValue
|
||||||
|
, Role.tab
|
||||||
|
, css sharedTabStyles
|
||||||
|
]
|
||||||
|
, if Just option.value == config.selected then
|
||||||
|
[ css focusedTabStyles
|
||||||
|
, config.selectedAttribute
|
||||||
|
]
|
||||||
|
|
||||||
|
else
|
||||||
|
[ css unFocusedTabStyles ]
|
||||||
|
, case config.width of
|
||||||
|
FitContent ->
|
||||||
|
[]
|
||||||
|
|
||||||
|
FillContainer ->
|
||||||
|
[ css expandingTabStyles ]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
[ case option.icon of
|
||||||
|
Nothing ->
|
||||||
|
text ""
|
||||||
|
|
||||||
|
Just svg ->
|
||||||
|
span
|
||||||
|
[ css
|
||||||
|
[ maxWidth (px 18)
|
||||||
|
, width (px 18)
|
||||||
|
, maxHeight (px 18)
|
||||||
|
, height (px 18)
|
||||||
|
, display inlineBlock
|
||||||
|
, verticalAlign textTop
|
||||||
|
, lineHeight (px 15)
|
||||||
|
, marginRight (px 8)
|
||||||
|
]
|
||||||
|
]
|
||||||
|
[ Svg.toHtml svg ]
|
||||||
|
, text option.label
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
sharedTabStyles : List Style
|
||||||
|
sharedTabStyles =
|
||||||
|
[ 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
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
focusedTabStyles : List Style
|
||||||
|
focusedTabStyles =
|
||||||
|
[ backgroundColor Colors.glacier
|
||||||
|
, boxShadow5 inset zero (px 3) zero (withAlpha 0.2 Colors.gray20)
|
||||||
|
, color Colors.navy
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
unFocusedTabStyles : List Style
|
||||||
|
unFocusedTabStyles =
|
||||||
|
[ backgroundColor Colors.white
|
||||||
|
, boxShadow5 inset zero (px -2) zero Colors.azure
|
||||||
|
, color Colors.azure
|
||||||
|
, hover
|
||||||
|
[ backgroundColor Colors.frost
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
expandingTabStyles : List Style
|
||||||
|
expandingTabStyles =
|
||||||
|
[ flexGrow (int 1)
|
||||||
|
, textAlign center
|
||||||
|
]
|
Loading…
Reference in New Issue
Block a user