Merge pull request #614 from NoRedInk/kraken/clickable-svg-with-border

Kraken/clickable svg with border
This commit is contained in:
Tessa 2020-09-28 16:47:53 -07:00 committed by GitHub
commit 710a2a1bcb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 388 additions and 108 deletions

View File

@ -5,6 +5,8 @@ module Nri.Ui.ClickableSvg.V1 exposing
, href, linkSpa, linkExternal, linkWithMethod, linkWithTracking, linkExternalWithTracking , href, linkSpa, linkExternal, linkWithMethod, linkWithTracking, linkExternalWithTracking
, width, height , width, height
, disabled , disabled
, withBorder
, primary, secondary, danger, dangerSecondary
, custom, css , custom, css
, withTooltipAbove, withTooltipBelow , withTooltipAbove, withTooltipBelow
) )
@ -42,6 +44,9 @@ module Nri.Ui.ClickableSvg.V1 exposing
## Customization ## Customization
@docs withBorder
@docs primary, secondary, danger, dangerSecondary
@docs custom, css @docs custom, css
@ -53,7 +58,7 @@ module Nri.Ui.ClickableSvg.V1 exposing
import Accessibility.Styled.Widget as Widget import Accessibility.Styled.Widget as Widget
import ClickableAttributes exposing (ClickableAttributes) import ClickableAttributes exposing (ClickableAttributes)
import Css exposing (Style) import Css exposing (Color, Style)
import Html.Styled as Html exposing (Html) import Html.Styled as Html exposing (Html)
import Html.Styled.Attributes as Attributes import Html.Styled.Attributes as Attributes
import Nri.Ui.Colors.V1 as Colors import Nri.Ui.Colors.V1 as Colors
@ -184,6 +189,98 @@ disabled disabled_ =
-- CUSTOMIZATION -- 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. {-| Use this helper to add custom attributes.
Do NOT use this helper to add css styles, as they may not be applied the way Do NOT use this helper to add css styles, as they may not be applied the way
@ -259,13 +356,15 @@ withTooltip position { id, isOpen, onShow } =
) )
{-| -} {-| DEPRECATED: prefer to use the Tooltip module directly.
-}
withTooltipAbove : { id : String, isOpen : Bool, onShow : Bool -> msg } -> Attribute msg withTooltipAbove : { id : String, isOpen : Bool, onShow : Bool -> msg } -> Attribute msg
withTooltipAbove = withTooltipAbove =
withTooltip Tooltip.OnTop withTooltip Tooltip.OnTop
{-| -} {-| DEPRECATED: prefer to use the Tooltip module directly.
-}
withTooltipBelow : { id : String, isOpen : Bool, onShow : Bool -> msg } -> Attribute msg withTooltipBelow : { id : String, isOpen : Bool, onShow : Bool -> msg } -> Attribute msg
withTooltipBelow = withTooltipBelow =
withTooltip Tooltip.OnBottom withTooltip Tooltip.OnBottom
@ -294,6 +393,8 @@ build label icon =
, customAttributes = [] , customAttributes = []
, customStyles = [] , customStyles = []
, tooltip = Nothing , tooltip = Nothing
, hasBorder = False
, theme = Secondary
} }
@ -311,6 +412,8 @@ type alias ButtonOrLinkAttributes msg =
, customAttributes : List (Html.Attribute msg) , customAttributes : List (Html.Attribute msg)
, customStyles : List Style , customStyles : List Style
, tooltip : Maybe (TooltipSettings msg) , tooltip : Maybe (TooltipSettings msg)
, hasBorder : Bool
, theme : Theme
} }
@ -326,10 +429,7 @@ renderButton ((ButtonOrLink config) as button_) =
++ ClickableAttributes.toButtonAttributes config.clickableAttributes ++ ClickableAttributes.toButtonAttributes config.clickableAttributes
++ config.customAttributes ++ config.customAttributes
) )
[ config.icon [ renderIcon config
|> Svg.withWidth config.width
|> Svg.withHeight config.height
|> Svg.toHtml
] ]
|> showTooltip config.label config.tooltip |> showTooltip config.label config.tooltip
@ -363,49 +463,136 @@ renderLink ((ButtonOrLink config) as link_) =
) )
++ config.customAttributes ++ config.customAttributes
) )
[ config.icon [ renderIcon config
|> Svg.withWidth config.width
|> Svg.withHeight config.height
|> Svg.toHtml
] ]
|> showTooltip config.label config.tooltip |> 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 : ButtonOrLinkAttributes msg -> List Style
buttonOrLinkStyles config = buttonOrLinkStyles config =
[ -- Colors, text decoration, cursor let
Css.backgroundColor Css.transparent ( { main_, mainHovered, background, backgroundHovered }, cursor ) =
, Css.textDecoration Css.none if config.disabled then
, if config.disabled then ( disabledTheme, Css.notAllowed )
Css.batch
[ Css.color Colors.gray75
, Css.visited [ Css.color Colors.gray75 ]
, Css.hover
[ Css.textDecoration Css.none
, Css.color Colors.gray75
, Css.cursor Css.notAllowed
]
]
else else
Css.batch ( applyTheme config.theme, Css.pointer )
[ Css.color Colors.azure in
, Css.visited [ Css.color Colors.azure ] [ Css.property "transition"
, Css.hover "background-color 0.2s, color 0.2s, border-width 0s, border-color 0.2s"
[ Css.textDecoration Css.none
, Css.color Colors.azureDark -- Colors, text decoration, cursor
, Css.cursor Css.pointer , 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 -- Margins, borders, padding
, Css.margin Css.zero , Css.margin Css.zero
, Css.padding Css.zero , Css.textAlign Css.center
, Css.borderWidth Css.zero , 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 -- Sizing
, Css.boxSizing Css.contentBox , Css.display Css.inlineBlock
, Css.lineHeight (Css.num 1) , Css.boxSizing Css.borderBox
, Css.width config.width , Css.width config.width
, Css.height config.height , 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

View File

@ -10,9 +10,10 @@ import AtomicDesignType exposing (AtomicDesignType(..))
import Category exposing (Category(..)) import Category exposing (Category(..))
import Color exposing (Color) import Color exposing (Color)
import Css import Css
import Debug.Control as Control exposing (Control)
import Example exposing (Example) import Example exposing (Example)
import Examples.IconExamples as IconExamples import Examples.IconExamples as IconExamples
import Html.Styled as Html import Html.Styled as Html exposing (Html)
import Html.Styled.Attributes as Attributes import Html.Styled.Attributes as Attributes
import Html.Styled.Events as Events import Html.Styled.Events as Events
import KeyboardSupport exposing (Direction(..), Key(..)) import KeyboardSupport exposing (Direction(..), Key(..))
@ -21,7 +22,8 @@ import Nri.Ui.Colors.Extra exposing (fromCssColor, toCssColor)
import Nri.Ui.Colors.V1 as Colors import Nri.Ui.Colors.V1 as Colors
import Nri.Ui.Heading.V2 as Heading import Nri.Ui.Heading.V2 as Heading
import Nri.Ui.Select.V7 as Select import Nri.Ui.Select.V7 as Select
import Nri.Ui.Svg.V1 as Svg import Nri.Ui.Svg.V1 as Svg exposing (Svg)
import Nri.Ui.Tooltip.V2 as Tooltip
import Nri.Ui.UiIcon.V1 as UiIcon import Nri.Ui.UiIcon.V1 as UiIcon
@ -38,84 +40,121 @@ example =
, subscriptions = \_ -> Sub.none , subscriptions = \_ -> Sub.none
, view = , view =
\state -> \state ->
[ viewExample "ClickableSvg.button \"Back\" UiIcon.arrowLeft [ ClickableSvg.onClick OnClickMsg ]" <| let
ClickableSvg.button "Back" ( icon, attributes ) =
UiIcon.arrowLeft applySettings state.settings
[ ClickableSvg.onClick (ShowItWorked "You clicked the back button!") ] in
, viewExample "ClickableSvg.link \"Back\" UiIcon.arrowLeft [ ClickableSvg.linkSpa \"some_link\" ]" <| [ Html.fromUnstyled (Control.view SetControls state.settings)
ClickableSvg.link "Back" UiIcon.arrowLeft [ ClickableSvg.linkSpa "some_link" ] , viewExampleTable icon attributes
, viewExample "ClickableSvg.button \"Disabled\" UiIcon.arrowLeft [ ClickableSvg.disabled True ]" <|
ClickableSvg.button "Disabled" UiIcon.arrowLeft [ ClickableSvg.disabled True ]
, viewExample "ClickableSvg.link \"Disabled\" UiIcon.arrowLeft [ ClickableSvg.disabled True ]" <|
ClickableSvg.link "Disabled" UiIcon.arrowLeft [ ClickableSvg.disabled True ]
, viewExample , viewExample
""" """
ClickableSvg.button "Go to tutorial" Tooltip.view
UiIcon.footsteps { trigger =
[ ClickableSvg.width (Css.px 30) \\attrs ->
, ClickableSvg.height (Css.px 30) ClickableSvg.button "Preview"
, ClickableSvg.onClick (ShowItWorked "You clicked the tutorials button!") UiIcon.preview
, ClickableSvg.custom [ Attributes.id "clickable-svg-customized-example-id" ] [ ClickableSvg.width (Css.px 20)
, ClickableSvg.css [ Css.border3 (Css.px 1) Css.dashed Colors.azure ] , ClickableSvg.height (Css.px 20)
] , ClickableSvg.onClick (ShowItWorked "You clicked the preview button!")
""" , ClickableSvg.custom attrs
<| ]
ClickableSvg.button "Go to tutorial" , id = "preview-tooltip"
UiIcon.footsteps }
[ ClickableSvg.width (Css.px 30) [ Tooltip.plaintext "Preview"
, ClickableSvg.height (Css.px 30) , Tooltip.primaryLabel
, ClickableSvg.onClick (ShowItWorked "You clicked the tutorials button!") , Tooltip.onHover SetPreviewTooltip
, ClickableSvg.custom [ Attributes.id "clickable-svg-customized-example-id" ] , Tooltip.open state.tooltipPreview
, ClickableSvg.css [ Css.border3 (Css.px 1) Css.dashed Colors.azure ] , Tooltip.smallPadding
] , Tooltip.fitToContent
, viewExample
"""
ClickableSvg.button "Preview"
UiIcon.preview
[ ClickableSvg.width (Css.px 20)
, ClickableSvg.height (Css.px 20)
, ClickableSvg.onClick (ShowItWorked "You clicked the preview button!")
, ClickableSvg.withTooltipAbove { id = "preview", isOpen = state.tooltipPreview, onShow = SetPreviewTooltip }
] ]
""" """
<| <|
ClickableSvg.button "Preview" Tooltip.view
UiIcon.preview { trigger =
[ ClickableSvg.width (Css.px 20) \attrs ->
, ClickableSvg.height (Css.px 20) ClickableSvg.button "Preview"
, ClickableSvg.onClick (ShowItWorked "You clicked the preview button!") UiIcon.preview
, ClickableSvg.withTooltipAbove [ ClickableSvg.width (Css.px 20)
{ id = "preview" , ClickableSvg.height (Css.px 20)
, isOpen = state.tooltipPreview , ClickableSvg.onClick (ShowItWorked "You clicked the preview button!")
, onShow = SetPreviewTooltip , ClickableSvg.custom attrs
} ]
] , id = "preview-tooltip"
, viewExample }
""" [ Tooltip.plaintext "Preview"
ClickableSvg.button "Share" , Tooltip.primaryLabel
UiIcon.share , Tooltip.onHover SetPreviewTooltip
[ ClickableSvg.width (Css.px 20) , Tooltip.open state.tooltipPreview
, ClickableSvg.height (Css.px 20) , Tooltip.smallPadding
, ClickableSvg.onClick (ShowItWorked "You clicked the Share button!") , Tooltip.fitToContent
, ClickableSvg.withTooltipBelow { id = "share", isOpen = state.tooltipShareTo, onShow = SetShareTooltip }
]
"""
<|
ClickableSvg.button "Share"
UiIcon.share
[ ClickableSvg.width (Css.px 20)
, ClickableSvg.height (Css.px 20)
, ClickableSvg.onClick (ShowItWorked "You clicked the share button!")
, ClickableSvg.withTooltipBelow
{ id = "share"
, isOpen = state.tooltipShareTo
, onShow = SetShareTooltip
}
] ]
] ]
} }
viewExampleTable : Svg -> List (ClickableSvg.Attribute Msg) -> Html Msg
viewExampleTable icon attributes =
let
viewExampleRow index ( themeName, theme ) =
Html.tr []
[ cell index [ Html.text themeName ]
, cell index [ buttonExample (theme :: attributes) ]
, cell index [ linkExample (theme :: attributes) ]
, cell index [ buttonExample (ClickableSvg.withBorder :: theme :: attributes) ]
, cell index [ linkExample (ClickableSvg.withBorder :: theme :: attributes) ]
]
cell index =
Html.td
[ Attributes.css
[ if modBy 2 index == 0 then
Css.backgroundColor Colors.gray96
else
Css.backgroundColor Colors.white
, Css.padding (Css.px 10)
]
]
buttonExample attributes_ =
ClickableSvg.button "Button example"
icon
(ClickableSvg.onClick (ShowItWorked "You clicked the back button!")
:: attributes_
)
linkExample attributes_ =
ClickableSvg.link "Link example"
icon
(ClickableSvg.linkSpa "some_link" :: attributes_)
in
Html.table []
[ Html.thead []
[ Html.tr []
[ Html.th [] [ Html.text "theme" ]
, Html.th [ Attributes.colspan 2 ] [ Html.text "" ]
, Html.th [ Attributes.colspan 2 ] [ Html.text "withBorder" ]
]
]
, Html.tbody [] <|
List.indexedMap viewExampleRow
[ ( "primary", ClickableSvg.primary )
, ( "secondary", ClickableSvg.secondary )
, ( "danger", ClickableSvg.danger )
, ( "dangerSecondary", ClickableSvg.dangerSecondary )
]
, Html.tfoot []
[ Html.tr []
[ Html.td [] [ Html.text "" ]
, Html.td [] [ Html.text "button" ]
, Html.td [] [ Html.text "link" ]
, Html.td [] [ Html.text "button" ]
, Html.td [] [ Html.text "link" ]
]
]
]
viewExample : String -> Html.Html msg -> Html.Html msg viewExample : String -> Html.Html msg -> Html.Html msg
viewExample code html = viewExample code html =
Html.div Html.div
@ -140,6 +179,7 @@ viewCode renderStrategy =
type alias State = type alias State =
{ tooltipPreview : Bool { tooltipPreview : Bool
, tooltipShareTo : Bool , tooltipShareTo : Bool
, settings : Control (Settings Msg)
} }
@ -148,6 +188,7 @@ init : State
init = init =
{ tooltipPreview = False { tooltipPreview = False
, tooltipShareTo = False , tooltipShareTo = False
, settings = initSettings
} }
@ -156,6 +197,7 @@ type Msg
= ShowItWorked String = ShowItWorked String
| SetPreviewTooltip Bool | SetPreviewTooltip Bool
| SetShareTooltip Bool | SetShareTooltip Bool
| SetControls (Control (Settings Msg))
{-| -} {-| -}
@ -174,3 +216,54 @@ update msg state =
SetShareTooltip bool -> SetShareTooltip bool ->
( { state | tooltipShareTo = bool }, Cmd.none ) ( { state | tooltipShareTo = bool }, Cmd.none )
SetControls settings ->
( { state | settings = settings }, Cmd.none )
type alias Settings msg =
{ icon : Svg
, disabled : ClickableSvg.Attribute msg
, width : ClickableSvg.Attribute msg
, height : ClickableSvg.Attribute msg
}
applySettings : Control (Settings msg) -> ( Svg, List (ClickableSvg.Attribute msg) )
applySettings settings =
let
{ icon, disabled, width, height } =
Control.currentValue settings
in
( icon, [ disabled, width, height ] )
initSettings : Control (Settings msg)
initSettings =
Control.record Settings
|> Control.field "icon"
(Control.choice
[ ( "arrowLeft", Control.value UiIcon.arrowLeft )
, ( "unarchive", Control.value UiIcon.unarchive )
, ( "share", Control.value UiIcon.share )
, ( "preview", Control.value UiIcon.preview )
, ( "skip", Control.value UiIcon.skip )
, ( "copyToClipboard", Control.value UiIcon.copyToClipboard )
, ( "gift", Control.value UiIcon.gift )
, ( "home", Control.value UiIcon.home )
, ( "library", Control.value UiIcon.library )
, ( "searchInCicle", Control.value UiIcon.searchInCicle )
]
)
|> Control.field "disabled"
(Control.map ClickableSvg.disabled (Control.bool False))
|> Control.field "width"
(Control.map (Css.px >> ClickableSvg.width) (controlNumber 30))
|> Control.field "height"
(Control.map (Css.px >> ClickableSvg.height) (controlNumber 30))
controlNumber : Float -> Control Float
controlNumber default =
Control.map (String.toFloat >> Maybe.withDefault default)
(Control.string (String.fromFloat default))