mirror of
https://github.com/NoRedInk/noredink-ui.git
synced 2024-11-13 07:48:26 +03:00
Merge remote-tracking branch 'origin/master' into premium-checkbox-v6
This commit is contained in:
commit
8041530e72
2
Makefile
2
Makefile
@ -31,7 +31,7 @@ styleguide-app/bundle.js: lib/index.js styleguide-app/manifest.js styleguide-app
|
||||
npx browserify --entry styleguide-app/manifest.js --outfile styleguide-app/bundle.js
|
||||
|
||||
styleguide-app/elm.js: styleguide-app/bundle.js $(shell find src styleguide-app -type f -name '*.elm')
|
||||
cd styleguide-app; npx elm make Main.elm --output=$(@F)
|
||||
cd styleguide-app && npx elm make Main.elm --output=$(@F)
|
||||
|
||||
# for publishing styleguide
|
||||
|
||||
|
4
elm.json
4
elm.json
@ -3,7 +3,7 @@
|
||||
"name": "NoRedInk/noredink-ui",
|
||||
"summary": "UI Widgets we use at NRI",
|
||||
"license": "BSD-3-Clause",
|
||||
"version": "6.24.1",
|
||||
"version": "6.25.0",
|
||||
"exposed-modules": [
|
||||
"Nri.Ui.Alert.V2",
|
||||
"Nri.Ui.Alert.V3",
|
||||
@ -21,6 +21,7 @@
|
||||
"Nri.Ui.Button.V6",
|
||||
"Nri.Ui.Button.V7",
|
||||
"Nri.Ui.Button.V8",
|
||||
"Nri.Ui.Button.V9",
|
||||
"Nri.Ui.Checkbox.V3",
|
||||
"Nri.Ui.Checkbox.V4",
|
||||
"Nri.Ui.Checkbox.V5",
|
||||
@ -33,6 +34,7 @@
|
||||
"Nri.Ui.Dropdown.V2",
|
||||
"Nri.Ui.Effects.V1",
|
||||
"Nri.Ui.Fonts.V1",
|
||||
"Nri.Ui.Heading.V1",
|
||||
"Nri.Ui.Html.Attributes.V2",
|
||||
"Nri.Ui.Html.V3",
|
||||
"Nri.Ui.Icon.V3",
|
||||
|
1977
package-lock.json
generated
1977
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -27,8 +27,9 @@
|
||||
"homepage": "https://github.com/NoRedInk/NoRedInk-ui#readme",
|
||||
"devDependencies": {
|
||||
"browserify": "16.2.3",
|
||||
"elm": "0.19.0",
|
||||
"elm": "^0.19.0-no-deps",
|
||||
"elm-format": "0.8.1",
|
||||
"elm-test": "0.19.0-rev6"
|
||||
"elm-test": "0.19.0-rev6",
|
||||
"request": "^2.88.0"
|
||||
}
|
||||
}
|
||||
|
934
src/Nri/Ui/Button/V9.elm
Normal file
934
src/Nri/Ui/Button/V9.elm
Normal file
@ -0,0 +1,934 @@
|
||||
module Nri.Ui.Button.V9 exposing
|
||||
( button, link
|
||||
, Attribute
|
||||
, icon, custom
|
||||
, onClick
|
||||
, linkSpa, linkExternal, linkWithMethod, linkWithTracking, linkExternalWithTracking
|
||||
, small, medium, large
|
||||
, exactWidth, unboundedWidth, fillContainerWidth
|
||||
, primary, secondary, danger, premium
|
||||
, enabled, unfulfilled, disabled, error, loading, success
|
||||
, delete
|
||||
, toggleButton
|
||||
)
|
||||
|
||||
{-|
|
||||
|
||||
|
||||
# Changes from V8:
|
||||
|
||||
- Changes API to be attribute-based, rather than config-based
|
||||
|
||||
|
||||
# Create a button or link
|
||||
|
||||
@docs button, link
|
||||
@docs Attribute
|
||||
@docs icon, custom
|
||||
|
||||
|
||||
## Behavior
|
||||
|
||||
@docs onClick
|
||||
@docs linkSpa, linkExternal, linkWithMethod, linkWithTracking, linkExternalWithTracking
|
||||
|
||||
|
||||
## Sizing
|
||||
|
||||
@docs small, medium, large
|
||||
@docs exactWidth, unboundedWidth, fillContainerWidth
|
||||
|
||||
|
||||
## Change the color scheme
|
||||
|
||||
@docs primary, secondary, danger, premium
|
||||
|
||||
|
||||
## Change the state (buttons only)
|
||||
|
||||
@docs enabled, unfulfilled, disabled, error, loading, success
|
||||
|
||||
|
||||
# Commonly-used buttons
|
||||
|
||||
@docs delete
|
||||
@docs toggleButton
|
||||
|
||||
-}
|
||||
|
||||
import Accessibility.Styled as Html exposing (Attribute, Html)
|
||||
import Accessibility.Styled.Role as Role
|
||||
import Accessibility.Styled.Widget as Widget
|
||||
import Css exposing (Style)
|
||||
import Css.Global
|
||||
import EventExtras.Styled as EventExtras
|
||||
import Html as RootHtml
|
||||
import Html.Styled as Styled
|
||||
import Html.Styled.Attributes as Attributes
|
||||
import Html.Styled.Events as Events
|
||||
import Json.Decode
|
||||
import Markdown.Block
|
||||
import Markdown.Inline
|
||||
import Nri.Ui
|
||||
import Nri.Ui.AssetPath as AssetPath exposing (Asset)
|
||||
import Nri.Ui.Colors.Extra as ColorsExtra
|
||||
import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.Fonts.V1
|
||||
import Nri.Ui.Html.Attributes.V2 as AttributesExtra
|
||||
import Nri.Ui.Svg.V1 as NriSvg exposing (Svg)
|
||||
import Svg
|
||||
import Svg.Attributes
|
||||
|
||||
|
||||
styledName : String -> String
|
||||
styledName suffix =
|
||||
"Nri-Ui-Button-V9-" ++ suffix
|
||||
|
||||
|
||||
{-|
|
||||
|
||||
Button.button "My great button!"
|
||||
[ Button.onClick ()
|
||||
, Button.enabled
|
||||
]
|
||||
|
||||
By default, the button is enabled, Medium sized, with primary colors, and an unbounded width.
|
||||
|
||||
-}
|
||||
button : String -> List (Attribute msg) -> Html msg
|
||||
button name attributes =
|
||||
(label name :: attributes)
|
||||
|> List.foldl (\(Attribute attribute) b -> attribute b) build
|
||||
|> renderButton
|
||||
|
||||
|
||||
{-|
|
||||
|
||||
Button.link "My great link!"
|
||||
[ Button.href "My href"
|
||||
, Button.secondary
|
||||
]
|
||||
|
||||
By default, the link is Medium sized, with primary colors, and an unbounded width.
|
||||
|
||||
-}
|
||||
link : String -> List (Attribute msg) -> Html msg
|
||||
link name attributes =
|
||||
(label name :: attributes)
|
||||
|> List.foldl (\(Attribute attribute) l -> attribute l) build
|
||||
|> renderLink
|
||||
|
||||
|
||||
{-| -}
|
||||
label : String -> Attribute msg
|
||||
label label_ =
|
||||
set (\attributes -> { attributes | label = label_ })
|
||||
|
||||
|
||||
{-| -}
|
||||
icon : Svg -> Attribute msg
|
||||
icon icon_ =
|
||||
set (\attributes -> { attributes | icon = Just icon_ })
|
||||
|
||||
|
||||
{-| -}
|
||||
custom : List (Html.Attribute msg) -> Attribute msg
|
||||
custom attributes =
|
||||
set
|
||||
(\config ->
|
||||
{ config
|
||||
| customAttributes = List.append config.customAttributes attributes
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
|
||||
-- LINKING, CLICKING, and TRACKING BEHAVIOR
|
||||
|
||||
|
||||
{-| -}
|
||||
onClick : msg -> Attribute msg
|
||||
onClick msg =
|
||||
set (\attributes -> { attributes | onClick = Just msg })
|
||||
|
||||
|
||||
type Link
|
||||
= Default
|
||||
| WithTracking
|
||||
| SinglePageApp
|
||||
| WithMethod String
|
||||
| External
|
||||
| ExternalWithTracking
|
||||
|
||||
|
||||
{-| Use this link for routing within a single page app.
|
||||
|
||||
This will make a normal <a> tag, but change the Events.onClick behavior to avoid reloading the page.
|
||||
|
||||
See <https://github.com/elm-lang/html/issues/110> for details on this implementation.
|
||||
|
||||
-}
|
||||
linkSpa : String -> Attribute msg
|
||||
linkSpa url =
|
||||
set
|
||||
(\attributes ->
|
||||
{ attributes
|
||||
| linkType = SinglePageApp
|
||||
, url = url
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
{-| Wrap some text so it looks like a button, but actually is wrapped in an anchor to
|
||||
some url, and it's an HTTP request (Rails includes JS to make this use the given HTTP method)
|
||||
-}
|
||||
linkWithMethod : { method : String, url : String } -> Attribute msg
|
||||
linkWithMethod { method, url } =
|
||||
set
|
||||
(\attributes ->
|
||||
{ attributes
|
||||
| linkType = WithMethod method
|
||||
, url = url
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
{-| Wrap some text so it looks like a button, but actually is wrapped in an anchor to some url.
|
||||
This should only take in messages that result in a Msg that triggers Analytics.trackAndRedirect.
|
||||
For buttons that trigger other effects on the page, please use Nri.Button.button instead.
|
||||
-}
|
||||
linkWithTracking : { track : msg, url : String } -> Attribute msg
|
||||
linkWithTracking { track, url } =
|
||||
set
|
||||
(\attributes ->
|
||||
{ attributes
|
||||
| linkType = WithTracking
|
||||
, url = url
|
||||
, onClick = Just track
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
{-| Wrap some text so it looks like a button, but actually is wrapped in an anchor to
|
||||
some url and have it open to an external site
|
||||
-}
|
||||
linkExternal : String -> Attribute msg
|
||||
linkExternal url =
|
||||
set
|
||||
(\attributes ->
|
||||
{ attributes
|
||||
| linkType = External
|
||||
, url = url
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
{-| Wrap some text so it looks like a button, but actually is wrapped in an anchor to some url and have it open to an external site.
|
||||
-}
|
||||
linkExternalWithTracking : { track : msg, url : String } -> Attribute msg
|
||||
linkExternalWithTracking { track, url } =
|
||||
set
|
||||
(\attributes ->
|
||||
{ attributes
|
||||
| linkType = ExternalWithTracking
|
||||
, url = url
|
||||
, onClick = Just track
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
|
||||
-- BUTTON SIZING
|
||||
|
||||
|
||||
{-| -}
|
||||
small : Attribute msg
|
||||
small =
|
||||
set (\attributes -> { attributes | size = Small })
|
||||
|
||||
|
||||
{-| -}
|
||||
medium : Attribute msg
|
||||
medium =
|
||||
set (\attributes -> { attributes | size = Medium })
|
||||
|
||||
|
||||
{-| -}
|
||||
large : Attribute msg
|
||||
large =
|
||||
set (\attributes -> { attributes | size = Large })
|
||||
|
||||
|
||||
|
||||
-- BUTTON WIDTH
|
||||
|
||||
|
||||
type ButtonWidth
|
||||
= WidthExact Int
|
||||
| WidthUnbounded
|
||||
| WidthFillContainer
|
||||
|
||||
|
||||
{-| Sizes for buttons and links that have button classes
|
||||
-}
|
||||
type ButtonSize
|
||||
= Small
|
||||
| Medium
|
||||
| Large
|
||||
|
||||
|
||||
{-| Define a size in `px` for the button's total width.
|
||||
-}
|
||||
exactWidth : Int -> Attribute msg
|
||||
exactWidth inPx =
|
||||
set (\attributes -> { attributes | width = WidthExact inPx })
|
||||
|
||||
|
||||
{-| Leave the maxiumum width unbounded (there is a minimum width).
|
||||
-}
|
||||
unboundedWidth : Attribute msg
|
||||
unboundedWidth =
|
||||
set (\attributes -> { attributes | width = WidthUnbounded })
|
||||
|
||||
|
||||
{-| -}
|
||||
fillContainerWidth : Attribute msg
|
||||
fillContainerWidth =
|
||||
set (\attributes -> { attributes | width = WidthFillContainer })
|
||||
|
||||
|
||||
|
||||
-- COLOR SCHEMES
|
||||
|
||||
|
||||
{-| -}
|
||||
primary : Attribute msg
|
||||
primary =
|
||||
set
|
||||
(\attributes ->
|
||||
{ attributes | style = primaryColors }
|
||||
)
|
||||
|
||||
|
||||
{-| -}
|
||||
secondary : Attribute msg
|
||||
secondary =
|
||||
set
|
||||
(\attributes ->
|
||||
{ attributes | style = secondaryColors }
|
||||
)
|
||||
|
||||
|
||||
{-| -}
|
||||
danger : Attribute msg
|
||||
danger =
|
||||
set
|
||||
(\attributes ->
|
||||
{ attributes
|
||||
| style =
|
||||
{ background = Colors.red
|
||||
, hover = Colors.redDark
|
||||
, text = Colors.white
|
||||
, border = Nothing
|
||||
, shadow = Colors.redDark
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
{-| -}
|
||||
premium : Attribute msg
|
||||
premium =
|
||||
set
|
||||
(\attributes ->
|
||||
{ attributes
|
||||
| style =
|
||||
{ background = Colors.yellow
|
||||
, hover = Colors.ochre
|
||||
, text = Colors.navy
|
||||
, border = Nothing
|
||||
, shadow = Colors.ochre
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
|
||||
-- BUTTON STATE
|
||||
|
||||
|
||||
type ButtonState
|
||||
= Enabled
|
||||
| Unfulfilled
|
||||
| Disabled
|
||||
| Error
|
||||
| Loading
|
||||
| Success
|
||||
|
||||
|
||||
{-| -}
|
||||
enabled : Attribute msg
|
||||
enabled =
|
||||
set (\attributes -> { attributes | state = Enabled })
|
||||
|
||||
|
||||
{-| Shows inactive styles.
|
||||
-}
|
||||
unfulfilled : Attribute msg
|
||||
unfulfilled =
|
||||
set (\attributes -> { attributes | state = Unfulfilled })
|
||||
|
||||
|
||||
{-| Shows inactive styling. If a button, this attribute will disable it.
|
||||
-}
|
||||
disabled : Attribute msg
|
||||
disabled =
|
||||
set (\attributes -> { attributes | state = Disabled })
|
||||
|
||||
|
||||
{-| Shows error styling. If a button, this attribute will disable it.
|
||||
-}
|
||||
error : Attribute msg
|
||||
error =
|
||||
set (\attributes -> { attributes | state = Error })
|
||||
|
||||
|
||||
{-| Shows loading styling. If a button, this attribute will disable it.
|
||||
-}
|
||||
loading : Attribute msg
|
||||
loading =
|
||||
set (\attributes -> { attributes | state = Loading })
|
||||
|
||||
|
||||
{-| Shows success styling. If a button, this attribute will disable it.
|
||||
-}
|
||||
success : Attribute msg
|
||||
success =
|
||||
set (\attributes -> { attributes | state = Success })
|
||||
|
||||
|
||||
{-| -}
|
||||
type Attribute msg
|
||||
= Attribute (ButtonOrLink msg -> ButtonOrLink msg)
|
||||
|
||||
|
||||
|
||||
-- INTERNALS
|
||||
|
||||
|
||||
set :
|
||||
(ButtonOrLinkAttributes msg -> ButtonOrLinkAttributes msg)
|
||||
-> Attribute msg
|
||||
set with =
|
||||
Attribute (\(ButtonOrLink config) -> ButtonOrLink (with config))
|
||||
|
||||
|
||||
build : ButtonOrLink msg
|
||||
build =
|
||||
ButtonOrLink
|
||||
{ onClick = Nothing
|
||||
, url = "#"
|
||||
, linkType = Default
|
||||
, size = Medium
|
||||
, style = primaryColors
|
||||
, width = WidthUnbounded
|
||||
, label = ""
|
||||
, state = Enabled
|
||||
, icon = Nothing
|
||||
, customAttributes = []
|
||||
}
|
||||
|
||||
|
||||
type ButtonOrLink msg
|
||||
= ButtonOrLink (ButtonOrLinkAttributes msg)
|
||||
|
||||
|
||||
type alias ButtonOrLinkAttributes msg =
|
||||
{ onClick : Maybe msg
|
||||
, url : String
|
||||
, linkType : Link
|
||||
, size : ButtonSize
|
||||
, style : ColorPalette
|
||||
, width : ButtonWidth
|
||||
, label : String
|
||||
, state : ButtonState
|
||||
, icon : Maybe Svg
|
||||
, customAttributes : List (Html.Attribute msg)
|
||||
}
|
||||
|
||||
|
||||
renderButton : ButtonOrLink msg -> Html msg
|
||||
renderButton ((ButtonOrLink config) as button_) =
|
||||
let
|
||||
buttonStyle_ =
|
||||
getColorPalette button_
|
||||
|
||||
isDisabled =
|
||||
case config.state of
|
||||
Enabled ->
|
||||
False
|
||||
|
||||
Disabled ->
|
||||
True
|
||||
|
||||
Error ->
|
||||
True
|
||||
|
||||
Unfulfilled ->
|
||||
False
|
||||
|
||||
Loading ->
|
||||
True
|
||||
|
||||
Success ->
|
||||
True
|
||||
in
|
||||
Nri.Ui.styled Html.button
|
||||
(styledName "customButton")
|
||||
[ buttonStyles config.size config.width buttonStyle_ ]
|
||||
((Maybe.map Events.onClick config.onClick
|
||||
|> Maybe.withDefault AttributesExtra.none
|
||||
)
|
||||
:: Attributes.disabled isDisabled
|
||||
:: Attributes.type_ "button"
|
||||
:: config.customAttributes
|
||||
)
|
||||
[ viewLabel config.icon config.label ]
|
||||
|
||||
|
||||
renderLink : ButtonOrLink msg -> Html msg
|
||||
renderLink ((ButtonOrLink config) as link_) =
|
||||
let
|
||||
colorPalette =
|
||||
getColorPalette link_
|
||||
|
||||
linkBase linkFunctionName extraAttrs =
|
||||
Nri.Ui.styled Styled.a
|
||||
(styledName linkFunctionName)
|
||||
[ buttonStyles config.size config.width colorPalette ]
|
||||
(Attributes.href config.url :: extraAttrs)
|
||||
[ viewLabel config.icon config.label ]
|
||||
in
|
||||
case config.linkType of
|
||||
Default ->
|
||||
linkBase "link"
|
||||
(Attributes.target "_self" :: config.customAttributes)
|
||||
|
||||
SinglePageApp ->
|
||||
linkBase "linkSpa"
|
||||
((Maybe.map EventExtras.onClickPreventDefaultForLinkWithHref config.onClick
|
||||
|> Maybe.withDefault AttributesExtra.none
|
||||
)
|
||||
:: config.customAttributes
|
||||
)
|
||||
|
||||
WithMethod method ->
|
||||
linkBase "linkWithMethod"
|
||||
(Attributes.attribute "data-method" method
|
||||
:: config.customAttributes
|
||||
)
|
||||
|
||||
WithTracking ->
|
||||
linkBase
|
||||
"linkWithTracking"
|
||||
((Maybe.map
|
||||
(\msg ->
|
||||
Events.preventDefaultOn "click"
|
||||
(Json.Decode.succeed ( msg, True ))
|
||||
)
|
||||
config.onClick
|
||||
|> Maybe.withDefault AttributesExtra.none
|
||||
)
|
||||
:: config.customAttributes
|
||||
)
|
||||
|
||||
External ->
|
||||
linkBase "linkExternal"
|
||||
(Attributes.target "_blank" :: config.customAttributes)
|
||||
|
||||
ExternalWithTracking ->
|
||||
linkBase "linkExternalWithTracking"
|
||||
(List.append
|
||||
[ Attributes.target "_blank"
|
||||
, Maybe.map EventExtras.onClickForLinkWithHref config.onClick
|
||||
|> Maybe.withDefault AttributesExtra.none
|
||||
]
|
||||
config.customAttributes
|
||||
)
|
||||
|
||||
|
||||
|
||||
-- DELETE BUTTON
|
||||
|
||||
|
||||
{-| A delete button (blue X)
|
||||
-}
|
||||
delete : { label : String, onClick : msg } -> Html msg
|
||||
delete config =
|
||||
Nri.Ui.styled Html.button
|
||||
(styledName "delete")
|
||||
[ Css.display Css.inlineBlock
|
||||
, Css.backgroundRepeat Css.noRepeat
|
||||
, Css.backgroundColor Css.transparent
|
||||
, Css.backgroundPosition Css.center
|
||||
, Css.backgroundSize Css.contain
|
||||
, Css.border Css.zero
|
||||
, Css.width (Css.px 15)
|
||||
, Css.height (Css.px 15)
|
||||
, Css.padding Css.zero
|
||||
, Css.margin2 Css.zero (Css.px 6)
|
||||
, Css.cursor Css.pointer
|
||||
, Css.color Colors.azure
|
||||
]
|
||||
[ Events.onClick config.onClick
|
||||
, Attributes.type_ "button"
|
||||
, -- reference: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_button_role#Labeling_buttons
|
||||
Widget.label config.label
|
||||
]
|
||||
[ Svg.svg [ Svg.Attributes.viewBox "0 0 25 25" ]
|
||||
[ Svg.title [] [ RootHtml.text "Delete" ]
|
||||
, Svg.path
|
||||
[ Svg.Attributes.fill "#146aff" -- TODO: this should be azure, but css colors aren't extractable afaik
|
||||
, Svg.Attributes.d "M1.067 6.015c-1.423-1.422-1.423-3.526 0-4.948 1.422-1.423 3.526-1.423 4.948 0l6.371 6.37 6.371-6.37c1.422-1.423 3.783-1.423 5.176 0 1.423 1.422 1.423 3.782 0 5.176l-6.37 6.37 6.37 6.372c1.423 1.422 1.423 3.526 0 4.948-1.422 1.423-3.526 1.423-4.948 0l-6.371-6.37-6.371 6.37c-1.422 1.423-3.783 1.423-5.176 0-1.423-1.422-1.423-3.782 0-5.176l6.37-6.143-6.37-6.599z"
|
||||
]
|
||||
[]
|
||||
]
|
||||
|> Styled.fromUnstyled
|
||||
]
|
||||
|
||||
|
||||
|
||||
-- TOGGLE BUTTON
|
||||
|
||||
|
||||
{-| A button that can be toggled into a pressed state and back again.
|
||||
-}
|
||||
toggleButton :
|
||||
{ label : String
|
||||
, onSelect : msg
|
||||
, onDeselect : msg
|
||||
, pressed : Bool
|
||||
}
|
||||
-> Html msg
|
||||
toggleButton config =
|
||||
let
|
||||
toggledStyles =
|
||||
if config.pressed then
|
||||
Css.batch
|
||||
[ Css.color Colors.gray20
|
||||
, Css.backgroundColor Colors.glacier
|
||||
, Css.boxShadow5 Css.inset Css.zero (Css.px 3) Css.zero (ColorsExtra.withAlpha 0.2 Colors.gray20)
|
||||
, Css.border3 (Css.px 1) Css.solid Colors.azure
|
||||
, Css.fontWeight Css.bold
|
||||
]
|
||||
|
||||
else
|
||||
Css.batch
|
||||
[]
|
||||
in
|
||||
Nri.Ui.styled Html.button
|
||||
(styledName "toggleButton")
|
||||
[ buttonStyles Medium WidthUnbounded secondaryColors
|
||||
, toggledStyles
|
||||
]
|
||||
[ Events.onClick
|
||||
(if config.pressed then
|
||||
config.onDeselect
|
||||
|
||||
else
|
||||
config.onSelect
|
||||
)
|
||||
, Widget.pressed <| Just config.pressed
|
||||
|
||||
-- reference: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_button_role#Labeling_buttons
|
||||
, Role.button
|
||||
|
||||
-- Note: setting type: 'button' removes the default behavior of submit
|
||||
-- equivalent to preventDefaultBehavior = false
|
||||
-- https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#attr-name
|
||||
, Attributes.type_ "button"
|
||||
]
|
||||
[ viewLabel Nothing config.label ]
|
||||
|
||||
|
||||
buttonStyles : ButtonSize -> ButtonWidth -> ColorPalette -> Style
|
||||
buttonStyles size width colors =
|
||||
Css.batch
|
||||
[ buttonStyle
|
||||
, sizeStyle size width
|
||||
, colorStyle colors
|
||||
]
|
||||
|
||||
|
||||
viewLabel : Maybe Svg -> String -> Html msg
|
||||
viewLabel maybeSvg label_ =
|
||||
Nri.Ui.styled Html.span
|
||||
"button-label-span"
|
||||
[ Css.overflow Css.hidden -- Keep scrollbars out of our button
|
||||
, Css.overflowWrap Css.breakWord -- Ensure that words that exceed the button width break instead of disappearing
|
||||
, Css.padding2 (Css.px 2) Css.zero -- Without a bit of bottom padding, text that extends below the baseline, like "g" gets cut off
|
||||
]
|
||||
[]
|
||||
(case maybeSvg of
|
||||
Nothing ->
|
||||
renderMarkdown label_
|
||||
|
||||
Just svg ->
|
||||
NriSvg.toHtml svg :: renderMarkdown label_
|
||||
)
|
||||
|
||||
|
||||
renderMarkdown : String -> List (Html msg)
|
||||
renderMarkdown markdown =
|
||||
case Markdown.Block.parse Nothing markdown of
|
||||
-- It seems to be always first wrapped in a `Paragraph` and never directly a `PlainInline`
|
||||
[ Markdown.Block.Paragraph _ inlines ] ->
|
||||
List.map (Markdown.Inline.toHtml >> Styled.fromUnstyled) inlines
|
||||
|
||||
_ ->
|
||||
[ Html.text markdown ]
|
||||
|
||||
|
||||
|
||||
-- STYLES
|
||||
|
||||
|
||||
buttonStyle : Style
|
||||
buttonStyle =
|
||||
Css.batch
|
||||
[ Css.cursor Css.pointer
|
||||
, -- Specifying the font can and should go away after bootstrap is removed from application.css
|
||||
Nri.Ui.Fonts.V1.baseFont
|
||||
, Css.textOverflow Css.ellipsis
|
||||
, Css.overflow Css.hidden
|
||||
, Css.textDecoration Css.none
|
||||
, Css.backgroundImage Css.none
|
||||
, Css.textShadow Css.none
|
||||
, Css.property "transition" "background-color 0.2s, color 0.2s, box-shadow 0.2s, border 0.2s, border-width 0s"
|
||||
, Css.boxShadow Css.none
|
||||
, Css.border Css.zero
|
||||
, Css.marginBottom Css.zero
|
||||
, Css.hover [ Css.textDecoration Css.none ]
|
||||
, Css.disabled [ Css.cursor Css.notAllowed ]
|
||||
, Css.display Css.inlineFlex
|
||||
, Css.alignItems Css.center
|
||||
, Css.justifyContent Css.center
|
||||
]
|
||||
|
||||
|
||||
|
||||
-- COLORS
|
||||
|
||||
|
||||
type alias ColorPalette =
|
||||
{ background : Css.Color
|
||||
, hover : Css.Color
|
||||
, text : Css.Color
|
||||
, border : Maybe Css.Color
|
||||
, shadow : Css.Color
|
||||
}
|
||||
|
||||
|
||||
primaryColors : ColorPalette
|
||||
primaryColors =
|
||||
{ background = Colors.azure
|
||||
, hover = Colors.azureDark
|
||||
, text = Colors.white
|
||||
, border = Nothing
|
||||
, shadow = Colors.azureDark
|
||||
}
|
||||
|
||||
|
||||
secondaryColors : ColorPalette
|
||||
secondaryColors =
|
||||
{ background = Colors.white
|
||||
, hover = Colors.glacier
|
||||
, text = Colors.azure
|
||||
, border = Just <| Colors.azure
|
||||
, shadow = Colors.azure
|
||||
}
|
||||
|
||||
|
||||
getColorPalette : ButtonOrLink msg -> ColorPalette
|
||||
getColorPalette (ButtonOrLink config) =
|
||||
case config.state of
|
||||
Enabled ->
|
||||
config.style
|
||||
|
||||
Disabled ->
|
||||
{ background = Colors.gray92
|
||||
, hover = Colors.gray92
|
||||
, text = Colors.gray45
|
||||
, border = Nothing
|
||||
, shadow = Colors.gray92
|
||||
}
|
||||
|
||||
Error ->
|
||||
{ background = Colors.purple
|
||||
, hover = Colors.purple
|
||||
, text = Colors.white
|
||||
, border = Nothing
|
||||
, shadow = Colors.purple
|
||||
}
|
||||
|
||||
Unfulfilled ->
|
||||
{ background = Colors.gray92
|
||||
, hover = Colors.gray92
|
||||
, text = Colors.gray45
|
||||
, border = Nothing
|
||||
, shadow = Colors.gray92
|
||||
}
|
||||
|
||||
Loading ->
|
||||
{ background = Colors.glacier
|
||||
, hover = Colors.glacier
|
||||
, text = Colors.navy
|
||||
, border = Nothing
|
||||
, shadow = Colors.glacier
|
||||
}
|
||||
|
||||
Success ->
|
||||
{ background = Colors.greenDark
|
||||
, hover = Colors.greenDark
|
||||
, text = Colors.white
|
||||
, border = Nothing
|
||||
, shadow = Colors.greenDark
|
||||
}
|
||||
|
||||
|
||||
colorStyle : ColorPalette -> Style
|
||||
colorStyle colorPalette =
|
||||
Css.batch
|
||||
[ Css.color colorPalette.text
|
||||
, Css.backgroundColor colorPalette.background
|
||||
, Css.fontWeight (Css.int 700)
|
||||
, Css.textAlign Css.center
|
||||
, case colorPalette.border of
|
||||
Nothing ->
|
||||
Css.borderStyle Css.none
|
||||
|
||||
Just color ->
|
||||
Css.batch
|
||||
[ Css.borderColor color
|
||||
, Css.borderStyle Css.solid
|
||||
]
|
||||
, Css.borderBottomStyle Css.solid
|
||||
, Css.borderBottomColor colorPalette.shadow
|
||||
, Css.fontStyle Css.normal
|
||||
, Css.hover
|
||||
[ Css.color colorPalette.text
|
||||
, Css.backgroundColor colorPalette.hover
|
||||
, Css.disabled [ Css.backgroundColor colorPalette.background ]
|
||||
]
|
||||
, Css.visited [ Css.color colorPalette.text ]
|
||||
]
|
||||
|
||||
|
||||
sizeStyle : ButtonSize -> ButtonWidth -> Style
|
||||
sizeStyle size width =
|
||||
let
|
||||
config =
|
||||
case size of
|
||||
Small ->
|
||||
{ fontSize = 15
|
||||
, height = 36
|
||||
, imageHeight = 15
|
||||
, shadowHeight = 2
|
||||
, minWidth = 75
|
||||
}
|
||||
|
||||
Medium ->
|
||||
{ fontSize = 17
|
||||
, height = 45
|
||||
, imageHeight = 15
|
||||
, shadowHeight = 3
|
||||
, minWidth = 100
|
||||
}
|
||||
|
||||
Large ->
|
||||
{ fontSize = 20
|
||||
, height = 56
|
||||
, imageHeight = 20
|
||||
, shadowHeight = 4
|
||||
, minWidth = 200
|
||||
}
|
||||
|
||||
sizingAttributes =
|
||||
let
|
||||
verticalPaddingPx =
|
||||
2
|
||||
in
|
||||
[ Css.minHeight (Css.px config.height)
|
||||
, Css.paddingTop (Css.px verticalPaddingPx)
|
||||
, Css.paddingBottom (Css.px verticalPaddingPx)
|
||||
]
|
||||
|
||||
widthAttributes =
|
||||
case width of
|
||||
WidthExact pxWidth ->
|
||||
[ Css.maxWidth (Css.pct 100)
|
||||
, Css.width (Css.px <| toFloat pxWidth)
|
||||
, Css.paddingRight (Css.px 4)
|
||||
, Css.paddingLeft (Css.px 4)
|
||||
]
|
||||
|
||||
WidthUnbounded ->
|
||||
[ Css.paddingLeft (Css.px 16)
|
||||
, Css.paddingRight (Css.px 16)
|
||||
, Css.minWidth (Css.px config.minWidth)
|
||||
]
|
||||
|
||||
WidthFillContainer ->
|
||||
[ Css.paddingLeft (Css.px 16)
|
||||
, Css.paddingRight (Css.px 16)
|
||||
, Css.minWidth (Css.px config.minWidth)
|
||||
, Css.width (Css.pct 100)
|
||||
]
|
||||
|
||||
lineHeightPx =
|
||||
case size of
|
||||
Small ->
|
||||
15
|
||||
|
||||
Medium ->
|
||||
19
|
||||
|
||||
Large ->
|
||||
22
|
||||
in
|
||||
Css.batch
|
||||
[ Css.fontSize (Css.px config.fontSize)
|
||||
, Css.borderRadius (Css.px 8)
|
||||
, Css.lineHeight (Css.px lineHeightPx)
|
||||
, Css.boxSizing Css.borderBox
|
||||
, Css.borderWidth (Css.px 1)
|
||||
, Css.borderBottomWidth (Css.px config.shadowHeight)
|
||||
, Css.batch sizingAttributes
|
||||
, Css.batch widthAttributes
|
||||
, Css.Global.descendants
|
||||
[ Css.Global.img
|
||||
[ Css.height (Css.px config.imageHeight)
|
||||
, Css.marginRight (Css.px <| config.imageHeight / 6)
|
||||
, Css.position Css.relative
|
||||
, Css.bottom (Css.px 2)
|
||||
, Css.verticalAlign Css.middle
|
||||
]
|
||||
, Css.Global.svg
|
||||
[ Css.height (Css.px config.imageHeight) |> Css.important
|
||||
, Css.width (Css.px config.imageHeight) |> Css.important
|
||||
, Css.marginRight (Css.px <| config.imageHeight / 6)
|
||||
, Css.position Css.relative
|
||||
, Css.bottom (Css.px 2)
|
||||
, Css.verticalAlign Css.middle
|
||||
]
|
||||
, Css.Global.svg
|
||||
[ Css.important <| Css.height (Css.px config.imageHeight)
|
||||
, Css.important <| Css.width Css.auto
|
||||
, Css.maxWidth (Css.px (config.imageHeight * 1.25))
|
||||
, Css.paddingRight (Css.px <| config.imageHeight / 6)
|
||||
, Css.position Css.relative
|
||||
, Css.bottom (Css.px 2)
|
||||
, Css.verticalAlign Css.middle
|
||||
]
|
||||
]
|
||||
]
|
190
src/Nri/Ui/Heading/V1.elm
Normal file
190
src/Nri/Ui/Heading/V1.elm
Normal file
@ -0,0 +1,190 @@
|
||||
module Nri.Ui.Heading.V1 exposing
|
||||
( Heading, heading
|
||||
, withVisualLevel, VisualLevel(..)
|
||||
, withDocumentLevel, DocumentLevel(..)
|
||||
, view
|
||||
)
|
||||
|
||||
{-| Headings such as you'd find in Nri.Ui.Text.V3, but customization options for
|
||||
accessibility.
|
||||
|
||||
@docs Heading, heading
|
||||
|
||||
@docs withVisualLevel, VisualLevel
|
||||
|
||||
@docs withDocumentLevel, DocumentLevel
|
||||
|
||||
@docs view
|
||||
|
||||
-}
|
||||
|
||||
import Css exposing (..)
|
||||
import Html.Styled exposing (..)
|
||||
import Html.Styled.Attributes exposing (css)
|
||||
import Nri.Ui.Colors.V1 exposing (..)
|
||||
import Nri.Ui.Fonts.V1 as Fonts
|
||||
|
||||
|
||||
{-| -}
|
||||
type Heading msg
|
||||
= Heading (List (Html msg)) VisualLevel DocumentLevel
|
||||
|
||||
|
||||
{-| start a heading. Render it with `view`.
|
||||
-}
|
||||
heading : List (Html msg) -> Heading msg
|
||||
heading content =
|
||||
Heading content Top H1
|
||||
|
||||
|
||||
|
||||
-- VISUAL LEVEL
|
||||
|
||||
|
||||
{-| -}
|
||||
type VisualLevel
|
||||
= Top
|
||||
| Tagline
|
||||
| Subhead
|
||||
| Small
|
||||
|
||||
|
||||
{-| Customize how the heading looks. The visual hierarchy should go Top ->
|
||||
Tagline -> Subhead -> Small.
|
||||
|
||||
heading [ text "Hello, World!" ]
|
||||
|> withVisualLevel Top
|
||||
|> view
|
||||
|
||||
-}
|
||||
withVisualLevel : VisualLevel -> Heading msg -> Heading msg
|
||||
withVisualLevel visualLevel (Heading content _ documentLevel) =
|
||||
Heading content visualLevel documentLevel
|
||||
|
||||
|
||||
getStyles : VisualLevel -> Style
|
||||
getStyles visualLevel =
|
||||
case visualLevel of
|
||||
Top ->
|
||||
headingStyles
|
||||
{ font = Fonts.baseFont
|
||||
, color = navy
|
||||
, size = 30
|
||||
, lineHeight = 38
|
||||
, weight = 700
|
||||
}
|
||||
|
||||
Tagline ->
|
||||
headingStyles
|
||||
{ font = Fonts.baseFont
|
||||
, color = gray45
|
||||
, size = 20
|
||||
, lineHeight = 30
|
||||
, weight = 400
|
||||
}
|
||||
|
||||
Subhead ->
|
||||
headingStyles
|
||||
{ font = Fonts.baseFont
|
||||
, color = navy
|
||||
, size = 20
|
||||
, lineHeight = 26
|
||||
, weight = 700
|
||||
}
|
||||
|
||||
Small ->
|
||||
Css.batch
|
||||
[ headingStyles
|
||||
{ font = Fonts.baseFont
|
||||
, color = gray20
|
||||
, size = 16
|
||||
, lineHeight = 21
|
||||
, weight = 700
|
||||
}
|
||||
, letterSpacing (px -0.13)
|
||||
]
|
||||
|
||||
|
||||
headingStyles :
|
||||
{ color : Color
|
||||
, font : Style
|
||||
, lineHeight : Float
|
||||
, size : Float
|
||||
, weight : Int
|
||||
}
|
||||
-> Style
|
||||
headingStyles config =
|
||||
Css.batch
|
||||
[ config.font
|
||||
, fontSize (px config.size)
|
||||
, color config.color
|
||||
, lineHeight (px config.lineHeight)
|
||||
, fontWeight (int config.weight)
|
||||
, padding zero
|
||||
, textAlign left
|
||||
, margin zero
|
||||
]
|
||||
|
||||
|
||||
|
||||
-- DOCUMENT LEVEL
|
||||
|
||||
|
||||
{-| -}
|
||||
type DocumentLevel
|
||||
= H1
|
||||
| H2
|
||||
| H3
|
||||
| H4
|
||||
| H5
|
||||
| H6
|
||||
|
||||
|
||||
{-| Customize the document level of the heading. For accessibility reasons, you
|
||||
should have exactly one H1, and only increase the level by one. You can use a
|
||||
tool like [axe](https://www.deque.com/axe/) to check this.
|
||||
|
||||
heading [ text "Hello, World!" ]
|
||||
|> withDocumentLevel H1
|
||||
|> view
|
||||
|
||||
-}
|
||||
withDocumentLevel : DocumentLevel -> Heading msg -> Heading msg
|
||||
withDocumentLevel documentLevel (Heading content visualLevel _) =
|
||||
Heading content visualLevel documentLevel
|
||||
|
||||
|
||||
getTag : DocumentLevel -> (List (Attribute msg) -> List (Html msg) -> Html msg)
|
||||
getTag documentLevel =
|
||||
case documentLevel of
|
||||
H1 ->
|
||||
h1
|
||||
|
||||
H2 ->
|
||||
h2
|
||||
|
||||
H3 ->
|
||||
h3
|
||||
|
||||
H4 ->
|
||||
h4
|
||||
|
||||
H5 ->
|
||||
h5
|
||||
|
||||
H6 ->
|
||||
h6
|
||||
|
||||
|
||||
|
||||
-- VIEW
|
||||
|
||||
|
||||
{-| render a Heading to Html. See the other docs in this module for
|
||||
customizations.
|
||||
-}
|
||||
view : Heading msg -> Html msg
|
||||
view (Heading content visualLevel documentLevel) =
|
||||
getTag documentLevel
|
||||
[ css [ getStyles visualLevel ] ]
|
||||
content
|
@ -24,6 +24,9 @@ module Nri.Ui.Text.V3 exposing
|
||||
|
||||
## Heading styles
|
||||
|
||||
Please use `Nri.Ui.Heading.V1` instead of these in new code. If you're here to
|
||||
make a new Text version, please remove them.
|
||||
|
||||
@docs heading, subHeading, smallHeading, tagline
|
||||
|
||||
|
||||
@ -48,73 +51,47 @@ import Html.Styled exposing (..)
|
||||
import Html.Styled.Attributes exposing (css)
|
||||
import Nri.Ui.Colors.V1 exposing (..)
|
||||
import Nri.Ui.Fonts.V1 as Fonts
|
||||
import Nri.Ui.Heading.V1 as Heading
|
||||
|
||||
|
||||
{-| This is a Page Heading.
|
||||
-}
|
||||
heading : List (Html msg) -> Html msg
|
||||
heading content =
|
||||
h1
|
||||
[ headingStyles
|
||||
{ font = Fonts.baseFont
|
||||
, color = navy
|
||||
, size = 30
|
||||
, lineHeight = 38
|
||||
, weight = 700
|
||||
}
|
||||
]
|
||||
content
|
||||
Heading.heading content
|
||||
|> Heading.withVisualLevel Heading.Top
|
||||
|> Heading.withDocumentLevel Heading.H1
|
||||
|> Heading.view
|
||||
|
||||
|
||||
{-| This is a tagline for a page heading.
|
||||
-}
|
||||
tagline : List (Html msg) -> Html msg
|
||||
tagline content =
|
||||
h2
|
||||
[ headingStyles
|
||||
{ font = Fonts.baseFont
|
||||
, color = gray45
|
||||
, size = 20
|
||||
, lineHeight = 30
|
||||
, weight = 400
|
||||
}
|
||||
]
|
||||
content
|
||||
Heading.heading content
|
||||
|> Heading.withVisualLevel Heading.Tagline
|
||||
|> Heading.withDocumentLevel Heading.H2
|
||||
|> Heading.view
|
||||
|
||||
|
||||
{-| This is a subhead.
|
||||
-}
|
||||
subHeading : List (Html msg) -> Html msg
|
||||
subHeading content =
|
||||
h3
|
||||
[ headingStyles
|
||||
{ font = Fonts.baseFont
|
||||
, color = navy
|
||||
, size = 20
|
||||
, lineHeight = 26
|
||||
, weight = 700
|
||||
}
|
||||
]
|
||||
content
|
||||
Heading.heading content
|
||||
|> Heading.withVisualLevel Heading.Subhead
|
||||
|> Heading.withDocumentLevel Heading.H3
|
||||
|> Heading.view
|
||||
|
||||
|
||||
{-| This is a small Page Heading.
|
||||
-}
|
||||
smallHeading : List (Html msg) -> Html msg
|
||||
smallHeading content =
|
||||
h4
|
||||
[ headingStyles
|
||||
{ font = Fonts.baseFont
|
||||
, color = gray20
|
||||
, size = 16
|
||||
, lineHeight = 21
|
||||
, weight = 700
|
||||
}
|
||||
, css
|
||||
[ letterSpacing (px -0.13)
|
||||
]
|
||||
]
|
||||
content
|
||||
Heading.heading content
|
||||
|> Heading.withVisualLevel Heading.Small
|
||||
|> Heading.withDocumentLevel Heading.H4
|
||||
|> Heading.view
|
||||
|
||||
|
||||
{-| This is some medium body copy.
|
||||
@ -168,19 +145,6 @@ smallBodyGray content =
|
||||
content
|
||||
|
||||
|
||||
headingStyles config =
|
||||
css
|
||||
[ config.font
|
||||
, fontSize (px config.size)
|
||||
, color config.color
|
||||
, lineHeight (px config.lineHeight)
|
||||
, fontWeight (int config.weight)
|
||||
, padding zero
|
||||
, textAlign left
|
||||
, margin zero
|
||||
]
|
||||
|
||||
|
||||
paragraphStyles config =
|
||||
css
|
||||
[ config.font
|
||||
|
@ -1,7 +1,9 @@
|
||||
module Examples.Button exposing (Msg, State, example, init, update)
|
||||
|
||||
{- \
|
||||
@docs Msg, State, example, init, update,
|
||||
{-|
|
||||
|
||||
@docs Msg, State, example, init, update
|
||||
|
||||
-}
|
||||
|
||||
import Css exposing (middle, verticalAlign)
|
||||
@ -11,20 +13,15 @@ import Html.Styled exposing (..)
|
||||
import Html.Styled.Attributes exposing (css, id)
|
||||
import ModuleExample as ModuleExample exposing (Category(..), ModuleExample, ModuleMessages)
|
||||
import Nri.Ui.AssetPath exposing (Asset)
|
||||
import Nri.Ui.Button.V8 as Button
|
||||
import Nri.Ui.Button.V9 as Button
|
||||
import Nri.Ui.Icon.V5 as Icon
|
||||
import Nri.Ui.Svg.V1 as NriSvg exposing (Svg)
|
||||
import Nri.Ui.Text.V3 as Text
|
||||
|
||||
|
||||
{-| -}
|
||||
type Msg
|
||||
= SetState State
|
||||
|
||||
|
||||
{-| -}
|
||||
type State
|
||||
= State (Control Model)
|
||||
type State parentMsg
|
||||
= State (Control (Model parentMsg))
|
||||
|
||||
|
||||
{-| -}
|
||||
@ -35,96 +32,107 @@ type ButtonType
|
||||
|
||||
{-| -}
|
||||
example :
|
||||
(String -> ModuleMessages Msg parentMsg)
|
||||
-> State
|
||||
(String -> ModuleMessages (Msg parentMsg) parentMsg)
|
||||
-> State parentMsg
|
||||
-> ModuleExample parentMsg
|
||||
example unnamedMessages state =
|
||||
let
|
||||
messages =
|
||||
unnamedMessages "ButtonExample"
|
||||
in
|
||||
{ name = "Nri.Ui.Button.V8"
|
||||
{ name = "Nri.Ui.Button.V9"
|
||||
, category = Buttons
|
||||
, content =
|
||||
[ viewButtonExamples messages state ]
|
||||
, content = [ viewButtonExamples messages state ]
|
||||
}
|
||||
|
||||
|
||||
{-| -}
|
||||
init : { r | performance : String, lock : String } -> State
|
||||
init assets =
|
||||
Control.record Model
|
||||
|> Control.field "label" (Control.string "Button")
|
||||
|> Control.field "icon"
|
||||
(Control.maybe False <|
|
||||
Control.choice
|
||||
( "Performance"
|
||||
, Icon.performance assets
|
||||
|> Icon.decorativeIcon
|
||||
|> NriSvg.fromHtml
|
||||
|> Control.value
|
||||
)
|
||||
[ ( "Lock"
|
||||
, Icon.lock assets
|
||||
|> Icon.decorativeIcon
|
||||
|> NriSvg.fromHtml
|
||||
|> Control.value
|
||||
)
|
||||
]
|
||||
)
|
||||
|> Control.field "width"
|
||||
(Control.choice
|
||||
( "Nri.Ui.Button.V7.WidthExact 120", Control.value <| Button.WidthExact 120 )
|
||||
[ ( "Nri.Ui.Button.V7.WidthExact 70", Control.value <| Button.WidthExact 70 )
|
||||
, ( "Nri.Ui.Button.V7.WidthUnbounded", Control.value <| Button.WidthUnbounded )
|
||||
, ( "Nri.Ui.Button.V7.WidthFillContainer", Control.value <| Button.WidthFillContainer )
|
||||
]
|
||||
)
|
||||
|> Control.field "button type"
|
||||
(Control.choice
|
||||
( "Nri.Ui.Button.V7.button", Control.value Button )
|
||||
[ ( "Nri.Ui.Button.V7.link", Control.value Link )
|
||||
]
|
||||
)
|
||||
|> Control.field "state (button only)"
|
||||
(Control.choice
|
||||
( Debug.toString Button.Enabled, Control.value Button.Enabled )
|
||||
(List.map (\x -> ( Debug.toString x, Control.value x ))
|
||||
[ Button.Disabled
|
||||
, Button.Error
|
||||
, Button.Unfulfilled
|
||||
, Button.Loading
|
||||
, Button.Success
|
||||
]
|
||||
)
|
||||
)
|
||||
|> State
|
||||
type Msg parentMsg
|
||||
= SetState (State parentMsg)
|
||||
| NoOp
|
||||
|
||||
|
||||
{-| -}
|
||||
update : Msg -> State -> ( State, Cmd Msg )
|
||||
update : Msg msg -> State msg -> ( State msg, Cmd (Msg msg) )
|
||||
update msg state =
|
||||
case msg of
|
||||
SetState newState ->
|
||||
( newState, Cmd.none )
|
||||
|
||||
NoOp ->
|
||||
( state, Cmd.none )
|
||||
|
||||
|
||||
|
||||
-- INTERNAL
|
||||
|
||||
|
||||
type alias Model =
|
||||
type alias Model msg =
|
||||
{ label : String
|
||||
, icon : Maybe Svg
|
||||
, width : Button.ButtonWidth
|
||||
, buttonType : ButtonType
|
||||
, state : Button.ButtonState
|
||||
, width : Button.Attribute msg
|
||||
, state : Button.Attribute msg
|
||||
}
|
||||
|
||||
|
||||
{-| -}
|
||||
init : { r | performance : String, lock : String } -> State msg
|
||||
init assets =
|
||||
Control.record Model
|
||||
|> Control.field "label" (Control.string "Label")
|
||||
|> Control.field "icon" (iconChoice assets)
|
||||
|> Control.field "button type"
|
||||
(Control.choice
|
||||
( "button", Control.value Button )
|
||||
[ ( "link", Control.value Link )
|
||||
]
|
||||
)
|
||||
|> Control.field "width"
|
||||
(Control.choice
|
||||
( "exactWidth 120", Control.value (Button.exactWidth 120) )
|
||||
[ ( "exactWidth 70", Control.value (Button.exactWidth 70) )
|
||||
, ( "unboundedWidth", Control.value Button.unboundedWidth )
|
||||
, ( "fillContainerWidth", Control.value Button.fillContainerWidth )
|
||||
]
|
||||
)
|
||||
|> Control.field "state (button only)"
|
||||
(Control.choice
|
||||
( "enabled", Control.value Button.enabled )
|
||||
[ ( "disabled", Control.value Button.disabled )
|
||||
, ( "error", Control.value Button.error )
|
||||
, ( "unfulfilled", Control.value Button.unfulfilled )
|
||||
, ( "loading", Control.value Button.loading )
|
||||
, ( "success", Control.value Button.success )
|
||||
]
|
||||
)
|
||||
|> State
|
||||
|
||||
|
||||
iconChoice : { r | performance : String, lock : String } -> Control.Control (Maybe Svg)
|
||||
iconChoice assets =
|
||||
Control.choice
|
||||
( "Nothing", Control.value Nothing )
|
||||
[ ( "Just Performance"
|
||||
, Icon.performance assets
|
||||
|> Icon.decorativeIcon
|
||||
|> NriSvg.fromHtml
|
||||
|> Just
|
||||
|> Control.value
|
||||
)
|
||||
, ( "Just Lock"
|
||||
, Icon.lock assets
|
||||
|> Icon.decorativeIcon
|
||||
|> NriSvg.fromHtml
|
||||
|> Just
|
||||
|> Control.value
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
viewButtonExamples :
|
||||
ModuleMessages Msg parentMsg
|
||||
-> State
|
||||
ModuleMessages (Msg parentMsg) parentMsg
|
||||
-> State parentMsg
|
||||
-> Html parentMsg
|
||||
viewButtonExamples messages (State control) =
|
||||
let
|
||||
@ -139,80 +147,73 @@ viewButtonExamples messages (State control) =
|
||||
{ label = "Delete Something"
|
||||
, onClick = messages.showItWorked "delete"
|
||||
}
|
||||
, Button.linkExternalWithTracking
|
||||
(messages.showItWorked "linkExternalWithTracking clicked")
|
||||
{ size = Button.Medium
|
||||
, style = Button.Secondary
|
||||
, width = Button.WidthUnbounded
|
||||
, label = "linkExternalWithTracking"
|
||||
, icon = Nothing
|
||||
, url = "#"
|
||||
}
|
||||
, Button.link "linkExternalWithTracking"
|
||||
[ Button.unboundedWidth
|
||||
, Button.secondary
|
||||
, Button.linkExternalWithTracking
|
||||
{ url = "#"
|
||||
, track = messages.showItWorked "linkExternalWithTracking clicked"
|
||||
}
|
||||
]
|
||||
]
|
||||
|> div []
|
||||
|
||||
|
||||
sizes : List Button.ButtonSize
|
||||
sizes =
|
||||
[ Button.Small
|
||||
, Button.Medium
|
||||
, Button.Large
|
||||
]
|
||||
|
||||
|
||||
allStyles : List Button.ButtonStyle
|
||||
allStyles =
|
||||
[ Button.Primary
|
||||
, Button.Secondary
|
||||
, Button.Danger
|
||||
, Button.Premium
|
||||
]
|
||||
|
||||
|
||||
buttons :
|
||||
ModuleMessages Msg parentMsg
|
||||
-> Model
|
||||
ModuleMessages (Msg parentMsg) parentMsg
|
||||
-> Model parentMsg
|
||||
-> Html parentMsg
|
||||
buttons messages model =
|
||||
let
|
||||
exampleRow style =
|
||||
sizes =
|
||||
[ ( Button.small, "small" )
|
||||
, ( Button.medium, "medium" )
|
||||
, ( Button.large, "large" )
|
||||
]
|
||||
|
||||
styles =
|
||||
[ ( Button.primary, "primary" )
|
||||
, ( Button.secondary, "secondary" )
|
||||
, ( Button.danger, "danger" )
|
||||
, ( Button.premium, "premium" )
|
||||
]
|
||||
|
||||
exampleRow ( style, styleName ) =
|
||||
List.concat
|
||||
[ [ td
|
||||
[ css
|
||||
[ verticalAlign middle
|
||||
]
|
||||
]
|
||||
[ text <| Debug.toString style ]
|
||||
[ text styleName ]
|
||||
]
|
||||
, sizes
|
||||
|> List.map (exampleCell style)
|
||||
, List.map (Tuple.first >> exampleCell style) sizes
|
||||
]
|
||||
|> tr []
|
||||
|
||||
exampleCell style size =
|
||||
(case model.buttonType of
|
||||
buttonOrLink =
|
||||
case model.buttonType of
|
||||
Link ->
|
||||
Button.link
|
||||
{ size = size
|
||||
, style = style
|
||||
, label = model.label
|
||||
, icon = model.icon
|
||||
, url = ""
|
||||
, width = model.width
|
||||
}
|
||||
|
||||
Button ->
|
||||
Button.button
|
||||
{ size = size
|
||||
, style = style
|
||||
, onClick = messages.showItWorked (Debug.toString ( style, size ))
|
||||
, width = model.width
|
||||
}
|
||||
{ label = model.label
|
||||
, icon = model.icon
|
||||
, state = model.state
|
||||
}
|
||||
)
|
||||
|
||||
exampleCell setStyle setSize =
|
||||
buttonOrLink model.label
|
||||
[ setSize
|
||||
, setStyle
|
||||
, model.width
|
||||
, model.state
|
||||
, Button.custom [ Html.Styled.Attributes.class "styleguide-button" ]
|
||||
, Button.onClick (messages.showItWorked "Button clicked!")
|
||||
, case model.icon of
|
||||
Just icon ->
|
||||
Button.icon icon
|
||||
|
||||
Nothing ->
|
||||
Button.custom []
|
||||
]
|
||||
|> List.singleton
|
||||
|> td
|
||||
[ css
|
||||
@ -223,16 +224,15 @@ buttons messages model =
|
||||
in
|
||||
List.concat
|
||||
[ [ sizes
|
||||
|> List.map (\size -> th [] [ text <| Debug.toString size ])
|
||||
|> List.map (\( _, sizeName ) -> th [] [ text sizeName ])
|
||||
|> (\cells -> tr [] (th [] [] :: cells))
|
||||
]
|
||||
, allStyles
|
||||
|> List.map exampleRow
|
||||
, List.map exampleRow styles
|
||||
]
|
||||
|> table []
|
||||
|
||||
|
||||
toggleButtons : ModuleMessages Msg parentMsg -> Html parentMsg
|
||||
toggleButtons : ModuleMessages (Msg parentMsg) parentMsg -> Html parentMsg
|
||||
toggleButtons messages =
|
||||
div []
|
||||
[ Headings.h3 [ text "Button toggle" ]
|
||||
|
@ -11,8 +11,10 @@ import Html.Styled as Html
|
||||
import Html.Styled.Attributes exposing (css)
|
||||
import Html.Styled.Events exposing (onClick)
|
||||
import ModuleExample as ModuleExample exposing (Category(..), ModuleExample)
|
||||
import Nri.Ui.Button.V8 as Button
|
||||
import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.DisclosureIndicator.V2 as DisclosureIndicator
|
||||
import Nri.Ui.Fonts.V1 as Fonts
|
||||
import Nri.Ui.Text.V2 as Text
|
||||
|
||||
|
||||
{-| -}
|
||||
@ -28,61 +30,36 @@ example parentMessage state =
|
||||
{ name = "Nri.Ui.DisclosureIndicator.V2"
|
||||
, category = Widgets
|
||||
, content =
|
||||
[ viewExample ToggleLarge
|
||||
state.largeState
|
||||
(Css.px 17)
|
||||
(DisclosureIndicator.large [ Css.marginRight (Css.px 10) ] state.largeState)
|
||||
("DisclosureIndicator.large [ Css.marginRight (Css.px 10) ] True"
|
||||
++ "\nI'm a 17px caret icon."
|
||||
)
|
||||
, viewExample ToggleMedium
|
||||
state.mediumState
|
||||
(Css.px 15)
|
||||
(DisclosureIndicator.medium [ Css.paddingRight (Css.px 8) ] state.mediumState)
|
||||
("DisclosureIndicator.medium [ Css.paddingRight (Css.px 8) ] True"
|
||||
++ "\nI'm a 15px caret icon."
|
||||
)
|
||||
[ Text.smallBodyGray [ Html.text "The disclosure indicator is only the caret. It is NOT a button -- you must create a button or clickabletext yourself!" ]
|
||||
, Html.div [ css [ Css.displayFlex, Css.padding (Css.px 8) ] ]
|
||||
[ toggleButton ToggleLarge "Toggle large indicator"
|
||||
, toggleButton ToggleMedium "Toggle medium indicator"
|
||||
]
|
||||
, Html.div [ css [ Css.displayFlex, Css.alignItems Css.center, Css.marginBottom (Css.px 8) ] ]
|
||||
[ DisclosureIndicator.large [ Css.marginRight (Css.px 10) ] state.largeState
|
||||
, Html.text "I'm a 17px caret icon."
|
||||
]
|
||||
, Html.div [ css [ Css.displayFlex, Css.alignItems Css.center, Css.marginBottom (Css.px 8) ] ]
|
||||
[ DisclosureIndicator.medium [ Css.paddingRight (Css.px 8) ] state.mediumState
|
||||
, Html.text "I'm a 15px caret icon."
|
||||
]
|
||||
]
|
||||
|> List.map (Html.map parentMessage)
|
||||
}
|
||||
|
||||
|
||||
viewExample : msg -> Bool -> Css.Px -> Html.Html msg -> String -> Html.Html msg
|
||||
viewExample toggle isOpen fontSize disclosureView disclosureCode =
|
||||
Html.div [ css [ Css.margin2 (Css.px 16) Css.zero ] ]
|
||||
[ Html.div []
|
||||
[ Html.button
|
||||
[ css
|
||||
[ Css.displayFlex
|
||||
, Css.alignItems Css.center
|
||||
, Css.borderStyle Css.none
|
||||
, Css.outline Css.none
|
||||
, Fonts.baseFont
|
||||
, Css.fontSize fontSize
|
||||
]
|
||||
, onClick toggle
|
||||
]
|
||||
[ disclosureView
|
||||
, Html.text "Toggle me!"
|
||||
]
|
||||
]
|
||||
, if isOpen then
|
||||
code disclosureCode
|
||||
|
||||
else
|
||||
Html.text ""
|
||||
]
|
||||
|
||||
|
||||
code : String -> Html.Html msg
|
||||
code copy =
|
||||
Html.code
|
||||
[ css
|
||||
[ Css.fontSize (Css.px 12)
|
||||
, Css.whiteSpace Css.pre
|
||||
]
|
||||
]
|
||||
[ Html.text copy ]
|
||||
toggleButton : msg -> String -> Html.Html msg
|
||||
toggleButton msg label =
|
||||
Button.button
|
||||
{ onClick = msg
|
||||
, size = Button.Small
|
||||
, style = Button.Secondary
|
||||
, width = Button.WidthUnbounded
|
||||
}
|
||||
{ label = label
|
||||
, state = Button.Enabled
|
||||
, icon = Nothing
|
||||
}
|
||||
|
||||
|
||||
{-| -}
|
||||
|
@ -8,13 +8,14 @@ module Examples.Text exposing (example)
|
||||
|
||||
import Html.Styled as Html
|
||||
import ModuleExample as ModuleExample exposing (Category(..), ModuleExample)
|
||||
import Nri.Ui.Heading.V1 as Heading exposing (DocumentLevel(..), VisualLevel(..), heading, withDocumentLevel, withVisualLevel)
|
||||
import Nri.Ui.Text.V3 as Text
|
||||
|
||||
|
||||
{-| -}
|
||||
example : ModuleExample msg
|
||||
example =
|
||||
{ name = "Nri.Ui.Text.V3"
|
||||
{ name = "Nri.Ui.Text.V3 (with headers from Nri.Ui.Heading.V1)"
|
||||
, category = Text
|
||||
, content =
|
||||
let
|
||||
@ -26,10 +27,22 @@ example =
|
||||
"""
|
||||
in
|
||||
[ Html.text "NOTE: When using these styles, please read the documentation in the Elm module about \"Understanding spacing\""
|
||||
, Text.heading [ Html.text "This is the main page heading." ]
|
||||
, Text.tagline [ Html.text "This is a tagline" ]
|
||||
, Text.subHeading [ Html.text "This is a subHeading" ]
|
||||
, Text.smallHeading [ Html.text "This is a smallHeading" ]
|
||||
, heading [ Html.text "This is the main page heading." ]
|
||||
|> withVisualLevel Top
|
||||
|> withDocumentLevel H1
|
||||
|> Heading.view
|
||||
, heading [ Html.text "This is a tagline" ]
|
||||
|> withVisualLevel Tagline
|
||||
|> withDocumentLevel H2
|
||||
|> Heading.view
|
||||
, heading [ Html.text "This is a subHeading" ]
|
||||
|> withVisualLevel Subhead
|
||||
|> withDocumentLevel H3
|
||||
|> Heading.view
|
||||
, heading [ Html.text "This is a smallHeading" ]
|
||||
|> withVisualLevel Small
|
||||
|> withDocumentLevel H4
|
||||
|> Heading.view
|
||||
, Html.hr [] []
|
||||
, Text.heading [ Html.text "Paragraph styles" ]
|
||||
, Text.mediumBody [ Html.text <| "This is a mediumBody. " ++ longerBody ]
|
||||
|
@ -31,7 +31,7 @@ import Url exposing (Url)
|
||||
|
||||
|
||||
type alias ModuleStates =
|
||||
{ buttonExampleState : Examples.Button.State
|
||||
{ buttonExampleState : Examples.Button.State Msg
|
||||
, bannerAlertExampleState : Examples.BannerAlert.State
|
||||
, clickableTextExampleState : Examples.ClickableText.State
|
||||
, checkboxExampleState : Examples.Checkbox.State
|
||||
@ -72,7 +72,7 @@ init =
|
||||
|
||||
|
||||
type Msg
|
||||
= ButtonExampleMsg Examples.Button.Msg
|
||||
= ButtonExampleMsg (Examples.Button.Msg Msg)
|
||||
| BannerAlertExampleMsg Examples.BannerAlert.Msg
|
||||
| ClickableTextExampleMsg Examples.ClickableText.Msg
|
||||
| CheckboxExampleMsg Examples.Checkbox.Msg
|
||||
|
Loading…
Reference in New Issue
Block a user