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
, width, height
, disabled
, withBorder
, primary, secondary, danger, dangerSecondary
, custom, css
, withTooltipAbove, withTooltipBelow
)
@ -42,6 +44,9 @@ module Nri.Ui.ClickableSvg.V1 exposing
## Customization
@docs withBorder
@docs primary, secondary, danger, dangerSecondary
@docs custom, css
@ -53,7 +58,7 @@ module Nri.Ui.ClickableSvg.V1 exposing
import Accessibility.Styled.Widget as Widget
import ClickableAttributes exposing (ClickableAttributes)
import Css exposing (Style)
import Css exposing (Color, Style)
import Html.Styled as Html exposing (Html)
import Html.Styled.Attributes as Attributes
import Nri.Ui.Colors.V1 as Colors
@ -184,6 +189,98 @@ disabled disabled_ =
-- 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.
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 =
withTooltip Tooltip.OnTop
{-| -}
{-| DEPRECATED: prefer to use the Tooltip module directly.
-}
withTooltipBelow : { id : String, isOpen : Bool, onShow : Bool -> msg } -> Attribute msg
withTooltipBelow =
withTooltip Tooltip.OnBottom
@ -294,6 +393,8 @@ build label icon =
, customAttributes = []
, customStyles = []
, tooltip = Nothing
, hasBorder = False
, theme = Secondary
}
@ -311,6 +412,8 @@ type alias ButtonOrLinkAttributes msg =
, customAttributes : List (Html.Attribute msg)
, customStyles : List Style
, tooltip : Maybe (TooltipSettings msg)
, hasBorder : Bool
, theme : Theme
}
@ -326,10 +429,7 @@ renderButton ((ButtonOrLink config) as button_) =
++ ClickableAttributes.toButtonAttributes config.clickableAttributes
++ config.customAttributes
)
[ config.icon
|> Svg.withWidth config.width
|> Svg.withHeight config.height
|> Svg.toHtml
[ renderIcon config
]
|> showTooltip config.label config.tooltip
@ -363,49 +463,136 @@ renderLink ((ButtonOrLink config) as link_) =
)
++ config.customAttributes
)
[ config.icon
|> Svg.withWidth config.width
|> Svg.withHeight config.height
|> Svg.toHtml
[ renderIcon config
]
|> 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 config =
[ -- Colors, text decoration, cursor
Css.backgroundColor Css.transparent
, Css.textDecoration Css.none
, if config.disabled then
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
]
]
let
( { main_, mainHovered, background, backgroundHovered }, cursor ) =
if config.disabled then
( disabledTheme, Css.notAllowed )
else
Css.batch
[ Css.color Colors.azure
, Css.visited [ Css.color Colors.azure ]
, Css.hover
[ Css.textDecoration Css.none
, Css.color Colors.azureDark
, Css.cursor Css.pointer
]
]
else
( applyTheme config.theme, Css.pointer )
in
[ Css.property "transition"
"background-color 0.2s, color 0.2s, border-width 0s, border-color 0.2s"
-- Colors, text decoration, cursor
, 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
, Css.margin Css.zero
, Css.padding Css.zero
, Css.borderWidth Css.zero
, Css.textAlign Css.center
, 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
, Css.boxSizing Css.contentBox
, Css.lineHeight (Css.num 1)
, Css.display Css.inlineBlock
, Css.boxSizing Css.borderBox
, Css.width config.width
, 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 Color exposing (Color)
import Css
import Debug.Control as Control exposing (Control)
import Example exposing (Example)
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.Events as Events
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.Heading.V2 as Heading
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
@ -38,84 +40,121 @@ example =
, subscriptions = \_ -> Sub.none
, view =
\state ->
[ viewExample "ClickableSvg.button \"Back\" UiIcon.arrowLeft [ ClickableSvg.onClick OnClickMsg ]" <|
ClickableSvg.button "Back"
UiIcon.arrowLeft
[ ClickableSvg.onClick (ShowItWorked "You clicked the back button!") ]
, viewExample "ClickableSvg.link \"Back\" UiIcon.arrowLeft [ ClickableSvg.linkSpa \"some_link\" ]" <|
ClickableSvg.link "Back" UiIcon.arrowLeft [ ClickableSvg.linkSpa "some_link" ]
, 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 ]
let
( icon, attributes ) =
applySettings state.settings
in
[ Html.fromUnstyled (Control.view SetControls state.settings)
, viewExampleTable icon attributes
, viewExample
"""
ClickableSvg.button "Go to tutorial"
UiIcon.footsteps
[ ClickableSvg.width (Css.px 30)
, ClickableSvg.height (Css.px 30)
, ClickableSvg.onClick (ShowItWorked "You clicked the tutorials button!")
, ClickableSvg.custom [ Attributes.id "clickable-svg-customized-example-id" ]
, ClickableSvg.css [ Css.border3 (Css.px 1) Css.dashed Colors.azure ]
]
"""
<|
ClickableSvg.button "Go to tutorial"
UiIcon.footsteps
[ ClickableSvg.width (Css.px 30)
, ClickableSvg.height (Css.px 30)
, ClickableSvg.onClick (ShowItWorked "You clicked the tutorials button!")
, ClickableSvg.custom [ Attributes.id "clickable-svg-customized-example-id" ]
, ClickableSvg.css [ Css.border3 (Css.px 1) Css.dashed Colors.azure ]
]
, 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 }
Tooltip.view
{ trigger =
\\attrs ->
ClickableSvg.button "Preview"
UiIcon.preview
[ ClickableSvg.width (Css.px 20)
, ClickableSvg.height (Css.px 20)
, ClickableSvg.onClick (ShowItWorked "You clicked the preview button!")
, ClickableSvg.custom attrs
]
, id = "preview-tooltip"
}
[ Tooltip.plaintext "Preview"
, Tooltip.primaryLabel
, Tooltip.onHover SetPreviewTooltip
, Tooltip.open state.tooltipPreview
, Tooltip.smallPadding
, Tooltip.fitToContent
]
"""
<|
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
}
]
, viewExample
"""
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 }
]
"""
<|
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
}
Tooltip.view
{ trigger =
\attrs ->
ClickableSvg.button "Preview"
UiIcon.preview
[ ClickableSvg.width (Css.px 20)
, ClickableSvg.height (Css.px 20)
, ClickableSvg.onClick (ShowItWorked "You clicked the preview button!")
, ClickableSvg.custom attrs
]
, id = "preview-tooltip"
}
[ Tooltip.plaintext "Preview"
, Tooltip.primaryLabel
, Tooltip.onHover SetPreviewTooltip
, Tooltip.open state.tooltipPreview
, Tooltip.smallPadding
, Tooltip.fitToContent
]
]
}
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 code html =
Html.div
@ -140,6 +179,7 @@ viewCode renderStrategy =
type alias State =
{ tooltipPreview : Bool
, tooltipShareTo : Bool
, settings : Control (Settings Msg)
}
@ -148,6 +188,7 @@ init : State
init =
{ tooltipPreview = False
, tooltipShareTo = False
, settings = initSettings
}
@ -156,6 +197,7 @@ type Msg
= ShowItWorked String
| SetPreviewTooltip Bool
| SetShareTooltip Bool
| SetControls (Control (Settings Msg))
{-| -}
@ -174,3 +216,54 @@ update msg state =
SetShareTooltip bool ->
( { 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))