Merge pull request #419 from NoRedInk/tessa/add-width-label-helpers

Tessa/add width label helpers
This commit is contained in:
Tessa 2019-12-05 12:43:06 -08:00 committed by GitHub
commit 42d3c57a8a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 282 additions and 13 deletions

View File

@ -1,42 +1,102 @@
module Nri.Ui.Svg.V1 exposing
( Svg
, withColor
, withColor, withLabel, withWidth, withHeight
, fromHtml, toHtml
)
{-|
@docs Svg
@docs withColor
@docs withColor, withLabel, withWidth, withHeight
@docs fromHtml, toHtml
-}
import Accessibility.Styled.Widget as Widget
import Css exposing (Color)
import Html.Styled as Html exposing (Html)
import Html.Styled.Attributes as Attributes
{-| -}
{-| Opaque type describing a non-interactable Html element.
-}
type Svg
= Svg (Html Never)
{-| -}
withColor : Color -> Svg -> Svg
withColor color (Svg svg) =
Svg (Html.span [ Attributes.css [ Css.color color ] ] [ svg ])
= Svg
{ icon : Html Never
, color : Maybe Color
, width : Maybe Css.Px
, height : Maybe Css.Px
, label : Maybe String
}
{-| Tag html as being an svg.
-}
fromHtml : Html Never -> Svg
fromHtml =
fromHtml icon =
Svg
{ icon = icon
, color = Nothing
, height = Nothing
, width = Nothing
, label = Nothing
}
{-| -}
withColor : Color -> Svg -> Svg
withColor color (Svg record) =
Svg { record | color = Just color }
{-| Add a string aria-label property to the element.
See [Using the aria-label attribute](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_aria-label_attribute) for
guidelines of when and how to use this attribute.
-}
withLabel : String -> Svg -> Svg
withLabel label (Svg record) =
Svg { record | label = Just label }
{-| -}
withWidth : Css.Px -> Svg -> Svg
withWidth width (Svg record) =
Svg { record | width = Just width }
{-| -}
withHeight : Css.Px -> Svg -> Svg
withHeight height (Svg record) =
Svg { record | height = Just height }
{-| Render an svg.
-}
toHtml : Svg -> Html msg
toHtml (Svg svg) =
Html.map never svg
toHtml (Svg record) =
let
css =
List.filterMap identity
[ Maybe.map Css.color record.color
, Maybe.map Css.width record.width
, Maybe.map Css.height record.height
]
attributes =
List.filterMap identity
[ if List.isEmpty css then
Nothing
else
Just (Attributes.css (Css.display Css.inlineBlock :: css))
, Maybe.map Widget.label record.label
]
in
case attributes of
[] ->
Html.map never record.icon
_ ->
Html.div attributes [ Html.map never record.icon ]

View File

@ -0,0 +1,195 @@
module Examples.Svg exposing
( Msg
, State
, example
, init
, update
)
{-|
@docs Msg
@docs State
@docs example
@docs init
@docs update
-}
import Color exposing (Color)
import Css
import Examples.IconExamples as IconExamples
import Html.Styled as Html
import Html.Styled.Attributes as Attributes
import Html.Styled.Events as Events
import ModuleExample exposing (Category(..), ModuleExample)
import Nri.Ui.Colors.Extra exposing (fromCssColor, toCssColor)
import Nri.Ui.Colors.V1 as Colors
import Nri.Ui.Heading.V2 as Heading
import Nri.Ui.Select.V6 as Select
import Nri.Ui.Svg.V1 as Svg
import Nri.Ui.UiIcon.V1 as UiIcon
{-| -}
example : (Msg -> msg) -> State -> ModuleExample msg
example parentMessage state =
{ name = "Nri.Ui.Svg.V1"
, category = Icons
, content =
[ viewSettings state
|> Html.map parentMessage
, viewResults state
]
}
viewSettings : State -> Html.Html Msg
viewSettings state =
Html.div
[ Attributes.css
[ Css.displayFlex
, Css.justifyContent Css.spaceBetween
]
]
[ Html.label []
[ Html.text "Color: "
, Html.input
[ Attributes.type_ "color"
, Attributes.value (Color.toHex state.color)
, Events.onInput (SetColor << Color.fromHex)
]
[]
]
, Html.label []
[ Html.text "Width: "
, Html.input
[ Attributes.type_ "range"
, Attributes.min "0"
, Attributes.max "200"
, Attributes.value (String.fromFloat state.width)
, Events.onInput (SetWidth << String.toFloat)
]
[]
]
, Html.label []
[ Html.text "Height: "
, Html.input
[ Attributes.type_ "range"
, Attributes.min "0"
, Attributes.max "200"
, Attributes.value (String.fromFloat state.height)
, Events.onInput (SetHeight << String.toFloat)
]
[]
]
, Html.label []
[ Html.text "Aria-label: "
, Html.input
[ Attributes.value state.label
, Events.onInput SetLabel
]
[]
]
]
viewResults : State -> Html.Html msg
viewResults state =
let
( red, green, blue ) =
Color.toRGB state.color
in
Html.div [ Attributes.css [ Css.displayFlex ] ]
[ Html.pre
[ Attributes.css
[ Css.width (Css.px 400)
, Css.marginRight (Css.px 20)
]
]
[ [ "color : Css.Color"
, "color ="
, " Css.rgb " ++ String.fromFloat red ++ " " ++ String.fromFloat green ++ " " ++ String.fromFloat blue
, ""
, ""
, "renderedSvg : Svg "
, "renderedSvg = "
, " UiIcon.newspaper"
, " |> Svg.withColor color"
, " |> Svg.withWidth (Css.px " ++ String.fromFloat state.width ++ ")"
, " |> Svg.withHeight (Css.px " ++ String.fromFloat state.height ++ ")"
, " |> Svg.withLabel \"" ++ state.label ++ "\""
, " |> Svg.toHtml"
]
|> String.join "\n"
|> Html.text
]
, Html.div
[ Attributes.css
[ Css.backgroundColor Colors.gray92
, Css.flexGrow (Css.int 2)
]
]
[ UiIcon.newspaper
|> Svg.withColor (toCssColor state.color)
|> Svg.withWidth (Css.px state.width)
|> Svg.withHeight (Css.px state.height)
|> Svg.withLabel state.label
|> Svg.toHtml
]
]
{-| -}
type alias State =
{ color : Color
, width : Float
, height : Float
, label : String
}
{-| -}
init : State
init =
{ color = fromCssColor Colors.blue
, width = 30
, height = 30
, label = "Newspaper"
}
{-| -}
type Msg
= SetColor (Result String Color)
| SetWidth (Maybe Float)
| SetHeight (Maybe Float)
| SetLabel String
{-| -}
update : Msg -> State -> ( State, Cmd Msg )
update msg state =
case msg of
SetColor (Ok color) ->
( { state | color = color }
, Cmd.none
)
SetColor (Err err) ->
( state, Cmd.none )
SetWidth (Just width) ->
( { state | width = width }, Cmd.none )
SetWidth Nothing ->
( state, Cmd.none )
SetHeight (Just height) ->
( { state | height = height }, Cmd.none )
SetHeight Nothing ->
( state, Cmd.none )
SetLabel label ->
( { state | label = label }, Cmd.none )

View File

@ -24,6 +24,7 @@ import Examples.Select
import Examples.Slide
import Examples.SlideModal
import Examples.SortableTable
import Examples.Svg
import Examples.Table
import Examples.Tabs
import Examples.Text
@ -55,6 +56,7 @@ type alias ModuleStates =
, slideModalExampleState : Examples.SlideModal.State
, slideExampleState : Examples.Slide.State
, sortableTableState : Examples.SortableTable.State
, svgState : Examples.Svg.State
, tabsExampleState : Examples.Tabs.Tab
, tooltipExampleState : Examples.Tooltip.State
}
@ -78,6 +80,7 @@ init =
, slideModalExampleState = Examples.SlideModal.init
, slideExampleState = Examples.Slide.init
, sortableTableState = Examples.SortableTable.init
, svgState = Examples.Svg.init
, tabsExampleState = Examples.Tabs.First
, tooltipExampleState = Examples.Tooltip.init
}
@ -101,6 +104,7 @@ type Msg
| SlideModalExampleMsg Examples.SlideModal.Msg
| SlideExampleMsg Examples.Slide.Msg
| SortableTableMsg Examples.SortableTable.Msg
| SvgMsg Examples.Svg.Msg
| TabsExampleMsg Examples.Tabs.Tab
| TooltipExampleMsg Examples.Tooltip.Msg
| NoOp
@ -184,6 +188,15 @@ update outsideMsg moduleStates =
in
( moduleStates, Cmd.none )
SvgMsg msg ->
let
( svgState, cmd ) =
Examples.Svg.update msg moduleStates.svgState
in
( { moduleStates | svgState = svgState }
, Cmd.map SvgMsg cmd
)
TableExampleMsg msg ->
let
( tableExampleState, cmd ) =
@ -321,6 +334,7 @@ nriThemedModules model =
, Examples.Slide.example SlideExampleMsg model.slideExampleState
, Examples.SlideModal.example SlideModalExampleMsg model.slideModalExampleState
, Examples.SortableTable.example SortableTableMsg model.sortableTableState
, Examples.Svg.example SvgMsg model.svgState
, Examples.Table.example TableExampleMsg model.tableExampleState
, Examples.Tabs.example TabsExampleMsg model.tabsExampleState
, Examples.Text.example