mirror of
https://github.com/NoRedInk/noredink-ui.git
synced 2024-12-18 03:01:41 +03:00
Merge pull request #74 from NoRedInk/ink__update-checkbox
Ink update checkbox
This commit is contained in:
commit
3d44b3f1be
@ -16,6 +16,7 @@
|
|||||||
"Nri.Ui.Colors.Extra",
|
"Nri.Ui.Colors.Extra",
|
||||||
"Nri.Ui.Checkbox.V1",
|
"Nri.Ui.Checkbox.V1",
|
||||||
"Nri.Ui.Checkbox.V2",
|
"Nri.Ui.Checkbox.V2",
|
||||||
|
"Nri.Ui.Checkbox.V3",
|
||||||
"Nri.Ui.Divider.V1",
|
"Nri.Ui.Divider.V1",
|
||||||
"Nri.Ui.Dropdown.V1",
|
"Nri.Ui.Dropdown.V1",
|
||||||
"Nri.Ui.Effects.V1",
|
"Nri.Ui.Effects.V1",
|
||||||
|
295
src/Nri/Ui/Checkbox/V3.elm
Normal file
295
src/Nri/Ui/Checkbox/V3.elm
Normal file
@ -0,0 +1,295 @@
|
|||||||
|
module Nri.Ui.Checkbox.V3
|
||||||
|
exposing
|
||||||
|
( IsSelected(..)
|
||||||
|
, Model
|
||||||
|
, Theme(..)
|
||||||
|
, view
|
||||||
|
, viewWithLabel
|
||||||
|
)
|
||||||
|
|
||||||
|
{-|
|
||||||
|
|
||||||
|
@docs Model, Theme, IsSelected
|
||||||
|
|
||||||
|
@docs view, viewWithLabel
|
||||||
|
|
||||||
|
-}
|
||||||
|
|
||||||
|
import Accessibility.Styled as Html
|
||||||
|
import Accessibility.Styled.Aria as Aria
|
||||||
|
import Accessibility.Styled.Style
|
||||||
|
import Accessibility.Styled.Widget as Widget
|
||||||
|
import Css exposing (..)
|
||||||
|
import Css.Foreign exposing (Snippet, children, descendants, everything, selector)
|
||||||
|
import Html.Events exposing (defaultOptions)
|
||||||
|
import Html.Styled
|
||||||
|
import Html.Styled.Attributes as Attributes exposing (css)
|
||||||
|
import Html.Styled.Events as Events
|
||||||
|
import Json.Decode
|
||||||
|
import Nri.Ui.AssetPath exposing (Asset(..))
|
||||||
|
import Nri.Ui.AssetPath.Css
|
||||||
|
import Nri.Ui.Fonts.V1 as Fonts
|
||||||
|
import Nri.Ui.Html.Attributes.V2 as ExtraAttributes
|
||||||
|
import Nri.Ui.Html.V2 as HtmlExtra
|
||||||
|
|
||||||
|
|
||||||
|
{-| -}
|
||||||
|
type alias Model msg =
|
||||||
|
{ identifier : String
|
||||||
|
, label : String
|
||||||
|
, setterMsg : Bool -> msg
|
||||||
|
, selected : IsSelected
|
||||||
|
, disabled : Bool
|
||||||
|
, theme : Theme
|
||||||
|
, noOpMsg : msg
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{-|
|
||||||
|
|
||||||
|
= Selected -- Checked (rendered with a checkmark)
|
||||||
|
| NotSelected -- Not Checked (rendered blank)
|
||||||
|
| PartiallySelected -- Indeterminate (rendered dash)
|
||||||
|
|
||||||
|
-}
|
||||||
|
type IsSelected
|
||||||
|
= Selected
|
||||||
|
| NotSelected
|
||||||
|
| PartiallySelected
|
||||||
|
|
||||||
|
|
||||||
|
{-| -}
|
||||||
|
type Theme
|
||||||
|
= Square
|
||||||
|
| LockOnInside
|
||||||
|
|
||||||
|
|
||||||
|
selectedToMaybe : IsSelected -> Maybe Bool
|
||||||
|
selectedToMaybe selected =
|
||||||
|
case selected of
|
||||||
|
Selected ->
|
||||||
|
Just True
|
||||||
|
|
||||||
|
NotSelected ->
|
||||||
|
Just False
|
||||||
|
|
||||||
|
PartiallySelected ->
|
||||||
|
Nothing
|
||||||
|
|
||||||
|
|
||||||
|
{-| Shows a checkbox (the label is only used for accessibility hints)
|
||||||
|
-}
|
||||||
|
view : Assets a -> Model msg -> Html.Html msg
|
||||||
|
view assets model =
|
||||||
|
buildCheckbox assets [] model <|
|
||||||
|
Html.span [ Accessibility.Styled.Style.invisible ]
|
||||||
|
[ Html.text model.label ]
|
||||||
|
|
||||||
|
|
||||||
|
{-| Shows a checkbox and its label text
|
||||||
|
-}
|
||||||
|
viewWithLabel : Assets a -> Model msg -> Html.Html msg
|
||||||
|
viewWithLabel assets model =
|
||||||
|
buildCheckbox assets [] model <|
|
||||||
|
Html.span [] [ Html.text model.label ]
|
||||||
|
|
||||||
|
|
||||||
|
buildCheckbox : Assets a -> List String -> Model msg -> Html.Html msg -> Html.Html msg
|
||||||
|
buildCheckbox assets modifierClasses model labelContent =
|
||||||
|
let
|
||||||
|
toClassList =
|
||||||
|
List.map (\a -> ( "checkbox-" ++ a, True )) >> Attributes.classList
|
||||||
|
in
|
||||||
|
viewCheckbox model <|
|
||||||
|
case model.theme of
|
||||||
|
Square ->
|
||||||
|
{ containerClasses = toClassList (modifierClasses ++ [ "SquareClass" ])
|
||||||
|
, labelStyles =
|
||||||
|
squareLabelStyles model <|
|
||||||
|
case model.selected of
|
||||||
|
Selected ->
|
||||||
|
assets.checkboxChecked_svg
|
||||||
|
|
||||||
|
NotSelected ->
|
||||||
|
assets.checkboxUnchecked_svg
|
||||||
|
|
||||||
|
PartiallySelected ->
|
||||||
|
assets.checkboxCheckedPartially_svg
|
||||||
|
, labelClasses = labelClass model.selected
|
||||||
|
, labelContent = labelContent
|
||||||
|
}
|
||||||
|
|
||||||
|
LockOnInside ->
|
||||||
|
{ containerClasses = toClassList (modifierClasses ++ [ "LockOnInsideClass" ])
|
||||||
|
, labelStyles = lockLabelStyles model assets.checkboxLockOnInside_svg
|
||||||
|
, labelClasses = labelClass model.selected
|
||||||
|
, labelContent = labelContent
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
squareLabelStyles : { b | disabled : Bool } -> Asset -> Html.Styled.Attribute msg
|
||||||
|
squareLabelStyles model image =
|
||||||
|
let
|
||||||
|
baseStyles =
|
||||||
|
[ positioning
|
||||||
|
, textStyle
|
||||||
|
, outline none
|
||||||
|
, addIcon image
|
||||||
|
]
|
||||||
|
in
|
||||||
|
css
|
||||||
|
(if model.disabled then
|
||||||
|
[ cursor auto, opacity (num 0.4) ] ++ baseStyles
|
||||||
|
else
|
||||||
|
[ cursor pointer ] ++ baseStyles
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
lockLabelStyles : { b | disabled : Bool } -> Asset -> Html.Styled.Attribute msg
|
||||||
|
lockLabelStyles model image =
|
||||||
|
let
|
||||||
|
baseStyles =
|
||||||
|
[ positioning
|
||||||
|
, textStyle
|
||||||
|
, outline none
|
||||||
|
, addIcon image
|
||||||
|
]
|
||||||
|
in
|
||||||
|
css
|
||||||
|
(if model.disabled then
|
||||||
|
[ cursor auto, opacity (num 0.4) ] ++ baseStyles
|
||||||
|
else
|
||||||
|
[ cursor pointer ] ++ baseStyles
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
positioning : Style
|
||||||
|
positioning =
|
||||||
|
batch
|
||||||
|
[ display inlineBlock
|
||||||
|
, padding4 (px 13) zero (px 13) (px 35)
|
||||||
|
, verticalAlign middle
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
textStyle : Style
|
||||||
|
textStyle =
|
||||||
|
batch
|
||||||
|
[ Fonts.baseFont
|
||||||
|
, fontSize (px 16)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
addIcon : Asset -> Style
|
||||||
|
addIcon icon =
|
||||||
|
batch
|
||||||
|
[ backgroundImage icon
|
||||||
|
, backgroundRepeat noRepeat
|
||||||
|
, backgroundSize (px 24)
|
||||||
|
, property "background-position" "left center"
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
labelClass : IsSelected -> Html.Styled.Attribute msg
|
||||||
|
labelClass isSelected =
|
||||||
|
case isSelected of
|
||||||
|
Selected ->
|
||||||
|
Attributes.classList
|
||||||
|
[ ( "checkbox-Label", True )
|
||||||
|
, ( "checkbox-Checked", True )
|
||||||
|
]
|
||||||
|
|
||||||
|
NotSelected ->
|
||||||
|
Attributes.classList
|
||||||
|
[ ( "checkbox-Label", True )
|
||||||
|
, ( "checkbox-Unchecked", True )
|
||||||
|
]
|
||||||
|
|
||||||
|
PartiallySelected ->
|
||||||
|
Attributes.classList
|
||||||
|
[ ( "checkbox-Label", True )
|
||||||
|
, ( "checkbox-Indeterminate", True )
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
viewCheckbox :
|
||||||
|
Model msg
|
||||||
|
->
|
||||||
|
{ containerClasses : Html.Attribute msg
|
||||||
|
, labelStyles : Html.Attribute msg
|
||||||
|
, labelClasses : Html.Attribute msg
|
||||||
|
, labelContent : Html.Html msg
|
||||||
|
}
|
||||||
|
-> Html.Html msg
|
||||||
|
viewCheckbox model config =
|
||||||
|
Html.Styled.span
|
||||||
|
[ css
|
||||||
|
[ display block
|
||||||
|
, height inherit
|
||||||
|
, descendants [ Css.Foreign.input [ display none ] ]
|
||||||
|
]
|
||||||
|
, config.containerClasses
|
||||||
|
, Attributes.id <| model.identifier ++ "-container"
|
||||||
|
, -- This is necessary to prevent event propagation.
|
||||||
|
-- See https://github.com/elm-lang/html/issues/96
|
||||||
|
Attributes.map (always model.noOpMsg) <|
|
||||||
|
Events.onWithOptions "click"
|
||||||
|
{ defaultOptions | stopPropagation = True }
|
||||||
|
(Json.Decode.succeed "stop click propagation")
|
||||||
|
]
|
||||||
|
[ Html.checkbox model.identifier
|
||||||
|
(selectedToMaybe model.selected)
|
||||||
|
[ Widget.label model.label
|
||||||
|
, Events.onCheck model.setterMsg
|
||||||
|
, Attributes.id model.identifier
|
||||||
|
, Attributes.disabled model.disabled
|
||||||
|
]
|
||||||
|
, viewLabel model config.labelContent config.labelClasses config.labelStyles
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
viewLabel : Model msg -> Html.Html msg -> Html.Attribute msg -> Html.Attribute msg -> Html.Html msg
|
||||||
|
viewLabel model content class theme =
|
||||||
|
Html.Styled.label
|
||||||
|
[ Attributes.for model.identifier
|
||||||
|
, Aria.controls model.identifier
|
||||||
|
, Widget.disabled model.disabled
|
||||||
|
, Widget.checked (selectedToMaybe model.selected)
|
||||||
|
, if not model.disabled then
|
||||||
|
Attributes.tabindex 0
|
||||||
|
else
|
||||||
|
ExtraAttributes.none
|
||||||
|
, if not model.disabled then
|
||||||
|
--TODO: the accessibility keyboard module might make this a tad more readable.
|
||||||
|
HtmlExtra.onKeyUp
|
||||||
|
{ defaultOptions | preventDefault = True }
|
||||||
|
(\keyCode ->
|
||||||
|
-- 32 is the space bar, 13 is enter
|
||||||
|
if (keyCode == 32 || keyCode == 13) && not model.disabled then
|
||||||
|
Just <| model.setterMsg (Maybe.map not (selectedToMaybe model.selected) |> Maybe.withDefault True)
|
||||||
|
else
|
||||||
|
Nothing
|
||||||
|
)
|
||||||
|
else
|
||||||
|
ExtraAttributes.none
|
||||||
|
, class
|
||||||
|
, theme
|
||||||
|
]
|
||||||
|
[ content ]
|
||||||
|
|
||||||
|
|
||||||
|
{-| The assets used in this module.
|
||||||
|
-}
|
||||||
|
type alias Assets r =
|
||||||
|
{ r
|
||||||
|
| checkboxUnchecked_svg : Asset
|
||||||
|
, checkboxChecked_svg : Asset
|
||||||
|
, checkboxCheckedPartially_svg : Asset
|
||||||
|
, checkboxLockOnInside_svg : Asset
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
backgroundImage : Asset -> Style
|
||||||
|
backgroundImage =
|
||||||
|
Nri.Ui.AssetPath.Css.url
|
||||||
|
>> property "background-image"
|
40
src/Nri/Ui/Html/Attributes/V2.elm
Normal file
40
src/Nri/Ui/Html/Attributes/V2.elm
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
module Nri.Ui.Html.Attributes.V2 exposing (includeIf, none)
|
||||||
|
|
||||||
|
{-| Extras for working with Html.Attributes.
|
||||||
|
|
||||||
|
This is the new version of Nri.Ui.Html.Attributes.Extra.
|
||||||
|
|
||||||
|
@docs none, includeIf
|
||||||
|
|
||||||
|
-}
|
||||||
|
|
||||||
|
import Html.Styled exposing (Attribute)
|
||||||
|
import Html.Styled.Attributes as Attributes
|
||||||
|
import Json.Encode as Encode
|
||||||
|
|
||||||
|
|
||||||
|
{-| Represents an attribute with no semantic meaning, useful for conditionals.
|
||||||
|
|
||||||
|
This is implemented such that whenever Html.Attributes.Extra.none is encountered
|
||||||
|
by VirtualDom it will set a meaningless property on the element object itself to
|
||||||
|
null:
|
||||||
|
|
||||||
|
domNode['Html.Attributes.Extra.none'] = null
|
||||||
|
|
||||||
|
It's totally safe and lets us clean up conditional and maybe attributes
|
||||||
|
|
||||||
|
-}
|
||||||
|
none : Attribute msg
|
||||||
|
none =
|
||||||
|
Attributes.property "Html.Attributes.Extra.none" Encode.null
|
||||||
|
|
||||||
|
|
||||||
|
{-| conditionally include an attribute. Useful for CSS classes generated with
|
||||||
|
`UniqueClass`!
|
||||||
|
-}
|
||||||
|
includeIf : Bool -> Attribute msg -> Attribute msg
|
||||||
|
includeIf cond attr =
|
||||||
|
if cond then
|
||||||
|
attr
|
||||||
|
else
|
||||||
|
none
|
143
src/Nri/Ui/Html/V2.elm
Normal file
143
src/Nri/Ui/Html/V2.elm
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
module Nri.Ui.Html.V2 exposing (..)
|
||||||
|
|
||||||
|
{-| For all utils involving HTML. New version of Nri.Ui.Html.Extra.
|
||||||
|
|
||||||
|
@docs role, noOpHref, noOpHrefUrl
|
||||||
|
|
||||||
|
@docs onEsc, onEnter, onKeyUp, onEnterAndSpace
|
||||||
|
|
||||||
|
@docs textFromList, oxfordifyWithHtml, nbsp
|
||||||
|
|
||||||
|
-}
|
||||||
|
|
||||||
|
import Char
|
||||||
|
import Html.Styled as Html exposing (Attribute, Html, span, text)
|
||||||
|
import Html.Styled.Attributes exposing (..)
|
||||||
|
import Html.Styled.Events exposing (..)
|
||||||
|
import Json.Decode
|
||||||
|
|
||||||
|
|
||||||
|
{-| Convenience for defining role attributes, e.g. <div role="tabpanel">
|
||||||
|
-}
|
||||||
|
role : String -> Attribute msg
|
||||||
|
role =
|
||||||
|
attribute "role"
|
||||||
|
|
||||||
|
|
||||||
|
{-| -}
|
||||||
|
noOpHref : Attribute a
|
||||||
|
noOpHref =
|
||||||
|
href noOpHrefUrl
|
||||||
|
|
||||||
|
|
||||||
|
{-| This is a better choice for a no-op than "#" because "#" changes your
|
||||||
|
location bar. See <http://stackoverflow.com/a/20676911> for more details.
|
||||||
|
-}
|
||||||
|
noOpHrefUrl : String
|
||||||
|
noOpHrefUrl =
|
||||||
|
"javascript:void(0)"
|
||||||
|
|
||||||
|
|
||||||
|
{-| -}
|
||||||
|
onEsc : a -> a -> Attribute a
|
||||||
|
onEsc onEscAction onOtherKey =
|
||||||
|
on "keyup"
|
||||||
|
(Json.Decode.map
|
||||||
|
(\keyCode ->
|
||||||
|
if keyCode == 27 then
|
||||||
|
onEscAction
|
||||||
|
else
|
||||||
|
onOtherKey
|
||||||
|
)
|
||||||
|
keyCode
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
{-| -}
|
||||||
|
onEnter : a -> Attribute a
|
||||||
|
onEnter onEnterAction =
|
||||||
|
onKeyUp defaultOptions
|
||||||
|
(\keyCode ->
|
||||||
|
if keyCode == 13 then
|
||||||
|
Just onEnterAction
|
||||||
|
else
|
||||||
|
Nothing
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
{-| "Buttons" should trigger on Enter and on Space.
|
||||||
|
-}
|
||||||
|
onEnterAndSpace : msg -> Attribute msg
|
||||||
|
onEnterAndSpace msg =
|
||||||
|
onKeyUp defaultOptions
|
||||||
|
(\keyCode ->
|
||||||
|
if keyCode == 13 || keyCode == 32 then
|
||||||
|
Just msg
|
||||||
|
else
|
||||||
|
Nothing
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
{-| Convert a keycode into a message on keyup
|
||||||
|
-}
|
||||||
|
onKeyUp : Options -> (Int -> Maybe a) -> Attribute a
|
||||||
|
onKeyUp options toMaybeMsg =
|
||||||
|
onWithOptions "keyup" options <|
|
||||||
|
Json.Decode.andThen
|
||||||
|
(\keyCode ->
|
||||||
|
keyCode
|
||||||
|
|> toMaybeMsg
|
||||||
|
|> Maybe.map Json.Decode.succeed
|
||||||
|
|> Maybe.withDefault (Json.Decode.fail (toString keyCode))
|
||||||
|
)
|
||||||
|
keyCode
|
||||||
|
|
||||||
|
|
||||||
|
{-| Takes a list of strings, joins them with a space and returns it as a Html.text.
|
||||||
|
textFromList ["Hello", "World"] == text [ String.join " " ["Hello", "World" ] ]
|
||||||
|
-}
|
||||||
|
textFromList : List String -> Html msg
|
||||||
|
textFromList =
|
||||||
|
String.join " " >> text
|
||||||
|
|
||||||
|
|
||||||
|
{-| -}
|
||||||
|
oxfordifyWithHtml : String -> String -> List (Html msg) -> List (Html msg)
|
||||||
|
oxfordifyWithHtml pre post items =
|
||||||
|
let
|
||||||
|
textSpan string =
|
||||||
|
span [] [ text string ]
|
||||||
|
|
||||||
|
final centrals =
|
||||||
|
[ textSpan pre ] ++ centrals ++ [ textSpan post ]
|
||||||
|
in
|
||||||
|
case items of
|
||||||
|
[] ->
|
||||||
|
[]
|
||||||
|
|
||||||
|
[ single ] ->
|
||||||
|
final [ single ]
|
||||||
|
|
||||||
|
[ first, second ] ->
|
||||||
|
final [ first, textSpan " and ", second ]
|
||||||
|
|
||||||
|
many ->
|
||||||
|
let
|
||||||
|
beforeAnd =
|
||||||
|
List.take (List.length many - 1) many
|
||||||
|
|
||||||
|
afterAnd =
|
||||||
|
List.drop (List.length many - 1) many
|
||||||
|
|> List.head
|
||||||
|
|> Maybe.withDefault (textSpan "")
|
||||||
|
in
|
||||||
|
final (List.intersperse (textSpan ", ") beforeAnd ++ [ textSpan ", and ", afterAnd ])
|
||||||
|
|
||||||
|
|
||||||
|
{-| Workaround for `Html.text " "` not working in elm.
|
||||||
|
-}
|
||||||
|
nbsp : Html msg
|
||||||
|
nbsp =
|
||||||
|
Char.fromCode 160
|
||||||
|
|> String.fromChar
|
||||||
|
|> Html.text
|
114
src/Nri/Ui/PremiumCheckbox/V1.elm
Normal file
114
src/Nri/Ui/PremiumCheckbox/V1.elm
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
module Nri.Ui.PremiumCheckbox.V1 exposing (PremiumConfig, premium)
|
||||||
|
|
||||||
|
{-|
|
||||||
|
|
||||||
|
@docs PremiumConfig, premium
|
||||||
|
|
||||||
|
-}
|
||||||
|
|
||||||
|
import Accessibility.Styled as Html
|
||||||
|
import Css exposing (..)
|
||||||
|
import Html.Styled.Attributes as Attributes exposing (css)
|
||||||
|
import Nri.Ui.AssetPath exposing (Asset(..))
|
||||||
|
import Nri.Ui.AssetPath.Css
|
||||||
|
import Nri.Ui.Checkbox.V3 as Checkbox
|
||||||
|
import Nri.Ui.Data.PremiumLevel as PremiumLevel exposing (PremiumLevel(..))
|
||||||
|
|
||||||
|
|
||||||
|
{-|
|
||||||
|
|
||||||
|
- `onChange`: A message for when the user toggles the checkbox
|
||||||
|
- `onLockedClick`: A message for when the user clicks a checkbox they don't have PremiumLevel for.
|
||||||
|
If you get this message, you should show an `Nri.Ui.Premium.Model.view`
|
||||||
|
|
||||||
|
-}
|
||||||
|
type alias PremiumConfig msg =
|
||||||
|
{ label : String
|
||||||
|
, id : String
|
||||||
|
, selected : Checkbox.IsSelected
|
||||||
|
, disabled : Bool
|
||||||
|
, teacherPremiumLevel : PremiumLevel
|
||||||
|
, contentPremiumLevel : PremiumLevel
|
||||||
|
, showFlagWhenLocked : Bool
|
||||||
|
, onChange : Bool -> msg
|
||||||
|
, onLockedClick : msg
|
||||||
|
, noOpMsg : msg
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{-| A checkbox that should be used for premium content
|
||||||
|
|
||||||
|
This checkbox is locked when the premium level of the content is greater than the premium level of the teacher
|
||||||
|
|
||||||
|
-}
|
||||||
|
premium : Assets a -> PremiumConfig msg -> Html.Html msg
|
||||||
|
premium assets config =
|
||||||
|
let
|
||||||
|
isLocked =
|
||||||
|
not <|
|
||||||
|
PremiumLevel.allowedFor
|
||||||
|
config.contentPremiumLevel
|
||||||
|
config.teacherPremiumLevel
|
||||||
|
in
|
||||||
|
Html.div
|
||||||
|
[ css
|
||||||
|
[ displayFlex
|
||||||
|
, alignItems center
|
||||||
|
]
|
||||||
|
]
|
||||||
|
[ Checkbox.viewWithLabel assets
|
||||||
|
{ identifier = config.id
|
||||||
|
, label = config.label
|
||||||
|
, setterMsg =
|
||||||
|
if isLocked then
|
||||||
|
\_ -> config.onLockedClick
|
||||||
|
else
|
||||||
|
config.onChange
|
||||||
|
, selected = config.selected
|
||||||
|
, disabled = config.disabled
|
||||||
|
, theme =
|
||||||
|
if isLocked then
|
||||||
|
Checkbox.LockOnInside
|
||||||
|
else
|
||||||
|
Checkbox.Square
|
||||||
|
, noOpMsg = config.noOpMsg
|
||||||
|
}
|
||||||
|
, if
|
||||||
|
(isLocked && config.showFlagWhenLocked)
|
||||||
|
|| (not isLocked && config.contentPremiumLevel /= Free)
|
||||||
|
then
|
||||||
|
Html.div
|
||||||
|
[ Attributes.class "checkbox-PremiumClass"
|
||||||
|
, css
|
||||||
|
[ property "content" "''"
|
||||||
|
, display inlineBlock
|
||||||
|
, width (px 26)
|
||||||
|
, height (px 24)
|
||||||
|
, marginLeft (px 8)
|
||||||
|
, backgroundImage assets.iconPremiumFlag_svg
|
||||||
|
, backgroundRepeat noRepeat
|
||||||
|
, backgroundPosition center
|
||||||
|
]
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
else
|
||||||
|
Html.text ""
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
{-| The assets used in this module.
|
||||||
|
-}
|
||||||
|
type alias Assets r =
|
||||||
|
{ r
|
||||||
|
| checkboxUnchecked_svg : Asset
|
||||||
|
, checkboxChecked_svg : Asset
|
||||||
|
, checkboxCheckedPartially_svg : Asset
|
||||||
|
, checkboxLockOnInside_svg : Asset
|
||||||
|
, iconPremiumFlag_svg : Asset
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
backgroundImage : Asset -> Style
|
||||||
|
backgroundImage =
|
||||||
|
Nri.Ui.AssetPath.Css.url
|
||||||
|
>> property "background-image"
|
@ -9,6 +9,10 @@ type alias Assets =
|
|||||||
, attention_svg : Asset
|
, attention_svg : Asset
|
||||||
, bulb : String
|
, bulb : String
|
||||||
, calendar : String
|
, calendar : String
|
||||||
|
, checkboxCheckedPartially_svg : Asset
|
||||||
|
, checkboxChecked_svg : Asset
|
||||||
|
, checkboxLockOnInside_svg : Asset
|
||||||
|
, checkboxUnchecked_svg : Asset
|
||||||
, checkmark : String
|
, checkmark : String
|
||||||
, class : String
|
, class : String
|
||||||
, clever : String
|
, clever : String
|
||||||
@ -32,6 +36,7 @@ type alias Assets =
|
|||||||
, iconCalendar_svg : Asset
|
, iconCalendar_svg : Asset
|
||||||
, iconCheck_png : Asset
|
, iconCheck_png : Asset
|
||||||
, iconFlag_png : Asset
|
, iconFlag_png : Asset
|
||||||
|
, iconPremiumFlag_svg : Asset
|
||||||
, icons_arrowDownBlue_svg : Asset
|
, icons_arrowDownBlue_svg : Asset
|
||||||
, icons_arrowRightBlue_svg : Asset
|
, icons_arrowRightBlue_svg : Asset
|
||||||
, icons_clockRed_svg : Asset
|
, icons_clockRed_svg : Asset
|
||||||
@ -60,6 +65,8 @@ type alias Assets =
|
|||||||
, premiumLock_svg : Asset
|
, premiumLock_svg : Asset
|
||||||
, preview : String
|
, preview : String
|
||||||
, quiz : String
|
, quiz : String
|
||||||
|
, rating : String
|
||||||
|
, revising : String
|
||||||
, seemore : String
|
, seemore : String
|
||||||
, share : String
|
, share : String
|
||||||
, skip : String
|
, skip : String
|
||||||
@ -69,6 +76,7 @@ type alias Assets =
|
|||||||
, speedometer : String
|
, speedometer : String
|
||||||
, squiggly_png : Asset
|
, squiggly_png : Asset
|
||||||
, startingOffBadge_png : Asset
|
, startingOffBadge_png : Asset
|
||||||
|
, submitting : String
|
||||||
, teach_assignments_copyWhite_svg : Asset
|
, teach_assignments_copyWhite_svg : Asset
|
||||||
, twitterBlue_svg : Asset
|
, twitterBlue_svg : Asset
|
||||||
, unarchiveBlue2x_png : Asset
|
, unarchiveBlue2x_png : Asset
|
||||||
@ -76,9 +84,6 @@ type alias Assets =
|
|||||||
, writingcycle : String
|
, writingcycle : String
|
||||||
, x : String
|
, x : String
|
||||||
, xWhite_svg : Asset
|
, xWhite_svg : Asset
|
||||||
, submitting : String
|
|
||||||
, rating : String
|
|
||||||
, revising : String
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -89,6 +94,10 @@ assets =
|
|||||||
, attention_svg = Asset "assets/images/attention.svg"
|
, attention_svg = Asset "assets/images/attention.svg"
|
||||||
, bulb = "icon-bulb"
|
, bulb = "icon-bulb"
|
||||||
, calendar = "icon-calendar"
|
, calendar = "icon-calendar"
|
||||||
|
, checkboxCheckedPartially_svg = Asset "assets/images/checkbox_checkedPartially.svg"
|
||||||
|
, checkboxChecked_svg = Asset "assets/images/checkbox_checked.svg"
|
||||||
|
, checkboxLockOnInside_svg = Asset "assets/images/checkbox_lock_on_inside.svg"
|
||||||
|
, checkboxUnchecked_svg = Asset "assets/images/checkbox_unchecked.svg"
|
||||||
, checkmark = "icon-checkmark"
|
, checkmark = "icon-checkmark"
|
||||||
, class = "icon-class"
|
, class = "icon-class"
|
||||||
, clever = "icon-clever"
|
, clever = "icon-clever"
|
||||||
@ -112,6 +121,7 @@ assets =
|
|||||||
, iconCalendar_svg = Asset "assets/images/icon-calendar.svg"
|
, iconCalendar_svg = Asset "assets/images/icon-calendar.svg"
|
||||||
, iconCheck_png = Asset "assets/images/icon-check.png"
|
, iconCheck_png = Asset "assets/images/icon-check.png"
|
||||||
, iconFlag_png = Asset "assets/images/icon-flag.png"
|
, iconFlag_png = Asset "assets/images/icon-flag.png"
|
||||||
|
, iconPremiumFlag_svg = Asset "assets/images/icon_premium_flag.svg"
|
||||||
, icons_arrowDownBlue_svg = Asset "assets/images/arrow-down-blue.svg"
|
, icons_arrowDownBlue_svg = Asset "assets/images/arrow-down-blue.svg"
|
||||||
, icons_arrowRightBlue_svg = Asset "assets/images/arrow-right-blue.svg"
|
, icons_arrowRightBlue_svg = Asset "assets/images/arrow-right-blue.svg"
|
||||||
, icons_clockRed_svg = Asset "assets/images/clock-red.svg"
|
, icons_clockRed_svg = Asset "assets/images/clock-red.svg"
|
||||||
@ -139,6 +149,8 @@ assets =
|
|||||||
, premiumLock_svg = Asset "assets/images/premium-lock.svg"
|
, premiumLock_svg = Asset "assets/images/premium-lock.svg"
|
||||||
, preview = "icon-preview"
|
, preview = "icon-preview"
|
||||||
, quiz = "icon-quiz"
|
, quiz = "icon-quiz"
|
||||||
|
, rating = "icon-rating"
|
||||||
|
, revising = "icon-revising"
|
||||||
, seemore = "icon-seemore"
|
, seemore = "icon-seemore"
|
||||||
, share = "icon-share"
|
, share = "icon-share"
|
||||||
, skip = "icon-skip"
|
, skip = "icon-skip"
|
||||||
@ -148,6 +160,7 @@ assets =
|
|||||||
, speedometer = "icon-speedometer"
|
, speedometer = "icon-speedometer"
|
||||||
, squiggly_png = Asset "assets/images/squiggly.png"
|
, squiggly_png = Asset "assets/images/squiggly.png"
|
||||||
, startingOffBadge_png = Asset "assets/images/starting-off-badge.png"
|
, startingOffBadge_png = Asset "assets/images/starting-off-badge.png"
|
||||||
|
, submitting = "icon-submitting"
|
||||||
, teach_assignments_copyWhite_svg = Asset "assets/images/copy-white.svg"
|
, teach_assignments_copyWhite_svg = Asset "assets/images/copy-white.svg"
|
||||||
, twitterBlue_svg = Asset "assets/images/twitter-blue.svg"
|
, twitterBlue_svg = Asset "assets/images/twitter-blue.svg"
|
||||||
, unarchiveBlue2x_png = Asset "assets/images/unarchive-blue_2x.png"
|
, unarchiveBlue2x_png = Asset "assets/images/unarchive-blue_2x.png"
|
||||||
@ -155,7 +168,4 @@ assets =
|
|||||||
, writingcycle = "icon-writingcycle"
|
, writingcycle = "icon-writingcycle"
|
||||||
, x = "icon-x"
|
, x = "icon-x"
|
||||||
, xWhite_svg = Asset "assets/images/x-white.svg"
|
, xWhite_svg = Asset "assets/images/x-white.svg"
|
||||||
, submitting = "icon-submitting"
|
|
||||||
, rating = "icon-rating"
|
|
||||||
, revising = "icon-revising"
|
|
||||||
}
|
}
|
||||||
|
199
styleguide-app/Examples/Checkbox.elm
Normal file
199
styleguide-app/Examples/Checkbox.elm
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
module Examples.Checkbox exposing (Msg, State, example, init, update)
|
||||||
|
|
||||||
|
{-|
|
||||||
|
|
||||||
|
@docs Msg, State, example, init, update,
|
||||||
|
|
||||||
|
-}
|
||||||
|
|
||||||
|
import Assets exposing (assets)
|
||||||
|
import Debug.Control as Control exposing (Control)
|
||||||
|
import Html.Styled as Html exposing (..)
|
||||||
|
import ModuleExample as ModuleExample exposing (Category(..), ModuleExample)
|
||||||
|
import Nri.Ui.Checkbox.V3 as Checkbox
|
||||||
|
import Nri.Ui.Data.PremiumLevel as PremiumLevel exposing (PremiumLevel(..))
|
||||||
|
import Nri.Ui.PremiumCheckbox.V1 as PremiumCheckbox
|
||||||
|
import Set exposing (Set)
|
||||||
|
|
||||||
|
|
||||||
|
{-| -}
|
||||||
|
type Msg
|
||||||
|
= ToggleCheck Id Bool
|
||||||
|
| SetPremiumControl (Control PremiumExampleConfig)
|
||||||
|
| NoOp
|
||||||
|
|
||||||
|
|
||||||
|
{-| -}
|
||||||
|
type alias State =
|
||||||
|
{ isChecked : Set String
|
||||||
|
, premiumControl : Control PremiumExampleConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{-| -}
|
||||||
|
example : (Msg -> msg) -> State -> ModuleExample msg
|
||||||
|
example parentMessage state =
|
||||||
|
{ filename = "Nri/Checkbox.elm"
|
||||||
|
, category = Inputs
|
||||||
|
, content =
|
||||||
|
[ viewInteractableCheckbox "styleguide-checkbox-interactable" state
|
||||||
|
, viewIndeterminateCheckbox "styleguide-checkbox-indeterminate" state
|
||||||
|
, viewLockedOnInsideCheckbox "styleguide-locked-on-inside-checkbox" state
|
||||||
|
, viewDisabledCheckbox "styleguide-checkbox-disabled" state
|
||||||
|
, h3 [] [ text "Premium Checkboxes" ]
|
||||||
|
, Control.view SetPremiumControl state.premiumControl
|
||||||
|
|> Html.fromUnstyled
|
||||||
|
, viewPremiumCheckboxes state
|
||||||
|
]
|
||||||
|
|> List.map (Html.toUnstyled << Html.map parentMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{-| -}
|
||||||
|
init : State
|
||||||
|
init =
|
||||||
|
{ isChecked = Set.empty
|
||||||
|
, premiumControl =
|
||||||
|
Control.record PremiumExampleConfig
|
||||||
|
|> Control.field "disabled" (Control.bool False)
|
||||||
|
|> Control.field "teacherPremiumLevel"
|
||||||
|
(Control.choice
|
||||||
|
[ ( "Free", Control.value PremiumLevel.Free )
|
||||||
|
, ( "Premium", Control.value PremiumLevel.Premium )
|
||||||
|
, ( "Premium (with writing)", Control.value PremiumLevel.PremiumWithWriting )
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|> Control.field "showFlagWhenLocked" (Control.bool True)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{-| -}
|
||||||
|
update : Msg -> State -> ( State, Cmd Msg )
|
||||||
|
update msg state =
|
||||||
|
case msg of
|
||||||
|
ToggleCheck id checked ->
|
||||||
|
let
|
||||||
|
isChecked =
|
||||||
|
if checked then
|
||||||
|
Set.insert id state.isChecked
|
||||||
|
else
|
||||||
|
Set.remove id state.isChecked
|
||||||
|
in
|
||||||
|
( { state | isChecked = isChecked }, Cmd.none )
|
||||||
|
|
||||||
|
SetPremiumControl premiumControl ->
|
||||||
|
( { state | premiumControl = premiumControl }, Cmd.none )
|
||||||
|
|
||||||
|
NoOp ->
|
||||||
|
( state, Cmd.none )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- INTERNAL
|
||||||
|
|
||||||
|
|
||||||
|
type alias PremiumExampleConfig =
|
||||||
|
{ disabled : Bool
|
||||||
|
, teacherPremiumLevel : PremiumLevel
|
||||||
|
, showFlagWhenLocked : Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
viewInteractableCheckbox : Id -> State -> Html Msg
|
||||||
|
viewInteractableCheckbox id state =
|
||||||
|
Checkbox.viewWithLabel
|
||||||
|
assets
|
||||||
|
{ identifier = id
|
||||||
|
, label = "This is an interactable checkbox!"
|
||||||
|
, setterMsg = ToggleCheck id
|
||||||
|
, selected = isSelected id state
|
||||||
|
, disabled = False
|
||||||
|
, theme = Checkbox.Square
|
||||||
|
, noOpMsg = NoOp
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
viewIndeterminateCheckbox : Id -> State -> Html Msg
|
||||||
|
viewIndeterminateCheckbox id state =
|
||||||
|
Checkbox.viewWithLabel
|
||||||
|
assets
|
||||||
|
{ identifier = id
|
||||||
|
, label = "This Checkbox is set in an indeterminate state"
|
||||||
|
, setterMsg = ToggleCheck id
|
||||||
|
, selected = Checkbox.PartiallySelected
|
||||||
|
, disabled = True
|
||||||
|
, theme = Checkbox.Square
|
||||||
|
, noOpMsg = NoOp
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
viewLockedOnInsideCheckbox : Id -> State -> Html Msg
|
||||||
|
viewLockedOnInsideCheckbox id state =
|
||||||
|
Checkbox.viewWithLabel
|
||||||
|
assets
|
||||||
|
{ identifier = id
|
||||||
|
, label = "I'm a locked Checkbox on the inside"
|
||||||
|
, setterMsg = ToggleCheck id
|
||||||
|
, selected = Checkbox.NotSelected
|
||||||
|
, disabled = True
|
||||||
|
, theme = Checkbox.LockOnInside
|
||||||
|
, noOpMsg = NoOp
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
viewDisabledCheckbox : Id -> State -> Html Msg
|
||||||
|
viewDisabledCheckbox id state =
|
||||||
|
Checkbox.viewWithLabel
|
||||||
|
assets
|
||||||
|
{ identifier = id
|
||||||
|
, label = "Disabled theme"
|
||||||
|
, setterMsg = ToggleCheck id
|
||||||
|
, selected = isSelected id state
|
||||||
|
, disabled = True
|
||||||
|
, theme = Checkbox.Square
|
||||||
|
, noOpMsg = NoOp
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
viewPremiumCheckboxes : State -> Html Msg
|
||||||
|
viewPremiumCheckboxes state =
|
||||||
|
let
|
||||||
|
config =
|
||||||
|
Control.currentValue state.premiumControl
|
||||||
|
|
||||||
|
checkbox label premiumLevel =
|
||||||
|
PremiumCheckbox.premium
|
||||||
|
assets
|
||||||
|
{ label = label
|
||||||
|
, id = "premium-checkbox-" ++ label
|
||||||
|
, selected =
|
||||||
|
if Set.member label state.isChecked then
|
||||||
|
Checkbox.Selected
|
||||||
|
else
|
||||||
|
Checkbox.NotSelected
|
||||||
|
, disabled = config.disabled
|
||||||
|
, teacherPremiumLevel = config.teacherPremiumLevel
|
||||||
|
, contentPremiumLevel = premiumLevel
|
||||||
|
, showFlagWhenLocked = config.showFlagWhenLocked
|
||||||
|
, onChange = ToggleCheck label
|
||||||
|
, onLockedClick = NoOp
|
||||||
|
, noOpMsg = NoOp
|
||||||
|
}
|
||||||
|
in
|
||||||
|
Html.div []
|
||||||
|
[ checkbox "Identify Adjectives 1 (Free)" PremiumLevel.Free
|
||||||
|
, checkbox "Identify Adjectives 2 (Premium)" PremiumLevel.Premium
|
||||||
|
, checkbox "Revising Wordy Phrases 1 (Writing)" PremiumLevel.PremiumWithWriting
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
type alias Id =
|
||||||
|
String
|
||||||
|
|
||||||
|
|
||||||
|
isSelected : Id -> State -> Checkbox.IsSelected
|
||||||
|
isSelected id state =
|
||||||
|
if Set.member id state.isChecked then
|
||||||
|
Checkbox.Selected
|
||||||
|
else
|
||||||
|
Checkbox.NotSelected
|
@ -10,7 +10,8 @@ import Dict exposing (Dict)
|
|||||||
import Html
|
import Html
|
||||||
import Html.Styled
|
import Html.Styled
|
||||||
import ModuleExample as ModuleExample exposing (Category(..), ModuleExample)
|
import ModuleExample as ModuleExample exposing (Category(..), ModuleExample)
|
||||||
import Nri.Ui.Checkbox.V2 as Checkbox
|
import Nri.Ui.AssetPath exposing (Asset(..))
|
||||||
|
import Nri.Ui.Checkbox.V3 as Checkbox
|
||||||
import Nri.Ui.Text.V2 as Text
|
import Nri.Ui.Text.V2 as Text
|
||||||
import Nri.Ui.TextArea.V3 as TextArea
|
import Nri.Ui.TextArea.V3 as TextArea
|
||||||
|
|
||||||
@ -27,9 +28,9 @@ type Msg
|
|||||||
{-| -}
|
{-| -}
|
||||||
type alias State =
|
type alias State =
|
||||||
{ textValues : Dict Int String
|
{ textValues : Dict Int String
|
||||||
, showLabel : Bool
|
, showLabel : Checkbox.IsSelected
|
||||||
, isInError : Bool
|
, isInError : Checkbox.IsSelected
|
||||||
, autoResize : Bool
|
, autoResize : Checkbox.IsSelected
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -42,77 +43,80 @@ example parentMessage state =
|
|||||||
[ Text.heading [ Html.Styled.text "Textarea controls" ]
|
[ Text.heading [ Html.Styled.text "Textarea controls" ]
|
||||||
|> Html.Styled.toUnstyled
|
|> Html.Styled.toUnstyled
|
||||||
, Html.div []
|
, Html.div []
|
||||||
[ Checkbox.viewWithLabel
|
[ Checkbox.viewWithLabel assets
|
||||||
{ identifier = "show-textarea-label"
|
{ identifier = "show-textarea-label"
|
||||||
, label = "Show Label"
|
, label = "Show Label"
|
||||||
, setterMsg = ToggleLabel
|
, setterMsg = ToggleLabel
|
||||||
, isChecked = Just state.showLabel
|
, selected = state.showLabel
|
||||||
, disabled = False
|
, disabled = False
|
||||||
, theme = Checkbox.Square Checkbox.Default
|
, theme = Checkbox.Square
|
||||||
, noOpMsg = NoOp
|
, noOpMsg = NoOp
|
||||||
}
|
}
|
||||||
, Checkbox.viewWithLabel
|
|> Html.Styled.toUnstyled
|
||||||
|
, Checkbox.viewWithLabel assets
|
||||||
{ identifier = "textarea-autoresize"
|
{ identifier = "textarea-autoresize"
|
||||||
, label = "Autoresize"
|
, label = "Autoresize"
|
||||||
, setterMsg = ToggleAutoResize
|
, setterMsg = ToggleAutoResize
|
||||||
, isChecked = Just state.autoResize
|
, selected = state.autoResize
|
||||||
, disabled = False
|
, disabled = False
|
||||||
, theme = Checkbox.Square Checkbox.Default
|
, theme = Checkbox.Square
|
||||||
, noOpMsg = NoOp
|
, noOpMsg = NoOp
|
||||||
}
|
}
|
||||||
, Checkbox.viewWithLabel
|
|> Html.Styled.toUnstyled
|
||||||
|
, Checkbox.viewWithLabel assets
|
||||||
{ identifier = "textarea-isInError"
|
{ identifier = "textarea-isInError"
|
||||||
, label = "Show Error State"
|
, label = "Show Error State"
|
||||||
, setterMsg = ToggleErrorState
|
, setterMsg = ToggleErrorState
|
||||||
, isChecked = Just state.isInError
|
, selected = state.isInError
|
||||||
, disabled = False
|
, disabled = False
|
||||||
, theme = Checkbox.Square Checkbox.Default
|
, theme = Checkbox.Square
|
||||||
, noOpMsg = NoOp
|
, noOpMsg = NoOp
|
||||||
}
|
}
|
||||||
|
|> Html.Styled.toUnstyled
|
||||||
]
|
]
|
||||||
, TextArea.view
|
, TextArea.view
|
||||||
{ value = Maybe.withDefault "" <| Dict.get 1 state.textValues
|
{ value = Maybe.withDefault "" <| Dict.get 1 state.textValues
|
||||||
, autofocus = False
|
, autofocus = False
|
||||||
, onInput = InputGiven 1
|
, onInput = InputGiven 1
|
||||||
, isInError = state.isInError
|
, isInError = state.isInError == Checkbox.Selected
|
||||||
, label = "TextArea.view"
|
, label = "TextArea.view"
|
||||||
, height =
|
, height =
|
||||||
if state.autoResize then
|
if state.autoResize == Checkbox.Selected then
|
||||||
TextArea.AutoResize TextArea.SingleLine
|
TextArea.AutoResize TextArea.SingleLine
|
||||||
else
|
else
|
||||||
TextArea.Fixed
|
TextArea.Fixed
|
||||||
, placeholder = "Placeholder"
|
, placeholder = "Placeholder"
|
||||||
, showLabel = state.showLabel
|
, showLabel = state.showLabel == Checkbox.Selected
|
||||||
}
|
}
|
||||||
|> Html.Styled.toUnstyled
|
|> Html.Styled.toUnstyled
|
||||||
, TextArea.writing
|
, TextArea.writing
|
||||||
{ value = Maybe.withDefault "" <| Dict.get 2 state.textValues
|
{ value = Maybe.withDefault "" <| Dict.get 2 state.textValues
|
||||||
, autofocus = False
|
, autofocus = False
|
||||||
, onInput = InputGiven 2
|
, onInput = InputGiven 2
|
||||||
, isInError = state.isInError
|
, isInError = state.isInError == Checkbox.Selected
|
||||||
, label = "TextArea.writing"
|
, label = "TextArea.writing"
|
||||||
, height =
|
, height =
|
||||||
if state.autoResize then
|
if state.autoResize == Checkbox.Selected then
|
||||||
TextArea.AutoResize TextArea.DefaultHeight
|
TextArea.AutoResize TextArea.DefaultHeight
|
||||||
else
|
else
|
||||||
TextArea.Fixed
|
TextArea.Fixed
|
||||||
, placeholder = "Placeholder"
|
, placeholder = "Placeholder"
|
||||||
, showLabel = state.showLabel
|
, showLabel = state.showLabel == Checkbox.Selected
|
||||||
}
|
}
|
||||||
|> Html.Styled.toUnstyled
|
|> Html.Styled.toUnstyled
|
||||||
, TextArea.contentCreation
|
, TextArea.contentCreation
|
||||||
{ value = Maybe.withDefault "" <| Dict.get 3 state.textValues
|
{ value = Maybe.withDefault "" <| Dict.get 3 state.textValues
|
||||||
, autofocus = False
|
, autofocus = False
|
||||||
, onInput = InputGiven 3
|
, onInput = InputGiven 3
|
||||||
, isInError = state.isInError
|
, isInError = state.isInError == Checkbox.Selected
|
||||||
, label = "TextArea.contentCreation"
|
, label = "TextArea.contentCreation"
|
||||||
, height =
|
, height =
|
||||||
if state.autoResize then
|
if state.autoResize == Checkbox.Selected then
|
||||||
TextArea.AutoResize TextArea.DefaultHeight
|
TextArea.AutoResize TextArea.DefaultHeight
|
||||||
else
|
else
|
||||||
TextArea.Fixed
|
TextArea.Fixed
|
||||||
, placeholder = "Placeholder"
|
, placeholder = "Placeholder"
|
||||||
, showLabel = state.showLabel
|
, showLabel = state.showLabel == Checkbox.Selected
|
||||||
}
|
}
|
||||||
|> Html.Styled.toUnstyled
|
|> Html.Styled.toUnstyled
|
||||||
]
|
]
|
||||||
@ -124,15 +128,22 @@ example parentMessage state =
|
|||||||
init : State
|
init : State
|
||||||
init =
|
init =
|
||||||
{ textValues = Dict.empty
|
{ textValues = Dict.empty
|
||||||
, showLabel = True
|
, showLabel = Checkbox.Selected
|
||||||
, isInError = False
|
, isInError = Checkbox.NotSelected
|
||||||
, autoResize = False
|
, autoResize = Checkbox.NotSelected
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
{-| -}
|
{-| -}
|
||||||
update : Msg -> State -> ( State, Cmd Msg )
|
update : Msg -> State -> ( State, Cmd Msg )
|
||||||
update msg state =
|
update msg state =
|
||||||
|
let
|
||||||
|
toggle bool =
|
||||||
|
if bool then
|
||||||
|
Checkbox.Selected
|
||||||
|
else
|
||||||
|
Checkbox.NotSelected
|
||||||
|
in
|
||||||
case msg of
|
case msg of
|
||||||
InputGiven id newValue ->
|
InputGiven id newValue ->
|
||||||
( { state | textValues = Dict.insert id newValue state.textValues }
|
( { state | textValues = Dict.insert id newValue state.textValues }
|
||||||
@ -140,17 +151,17 @@ update msg state =
|
|||||||
)
|
)
|
||||||
|
|
||||||
ToggleLabel bool ->
|
ToggleLabel bool ->
|
||||||
( { state | showLabel = bool }
|
( { state | showLabel = toggle bool }
|
||||||
, Cmd.none
|
, Cmd.none
|
||||||
)
|
)
|
||||||
|
|
||||||
ToggleErrorState bool ->
|
ToggleErrorState bool ->
|
||||||
( { state | isInError = bool }
|
( { state | isInError = toggle bool }
|
||||||
, Cmd.none
|
, Cmd.none
|
||||||
)
|
)
|
||||||
|
|
||||||
ToggleAutoResize bool ->
|
ToggleAutoResize bool ->
|
||||||
( { state | autoResize = bool }
|
( { state | autoResize = toggle bool }
|
||||||
, Cmd.none
|
, Cmd.none
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -164,3 +175,15 @@ update msg state =
|
|||||||
|
|
||||||
type alias Id =
|
type alias Id =
|
||||||
Int
|
Int
|
||||||
|
|
||||||
|
|
||||||
|
assets =
|
||||||
|
{ checkboxUnchecked_svg = Asset "checkboxUnchecked_svg"
|
||||||
|
, checkboxChecked_svg = Asset "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOEAAADhCAMAAAAJbSJIAAAAb1BMVEX///9DoEc2nDt1tXcoly601bU5nD1nr2o/n0Pw9vAzmzhAn0QxmjYumTMqmDAsmTK92r6UxJacyJ1YqFu52LvC3MOQwpIZlCGLwI3M4s2u0q+q0KueyaCkzKX1+fXq8+rh7uHW6NeDvIVztHVssW4Cx6cUAAACZUlEQVR4nO3Zi1LTYBRF4dOSQkqt4AURL6Di+z+jQWVsadJcen5z9pm1HiDZ3yQzSSZmRERERERERERERERERERERERERERERERElKj7uQeUbrVdzz2hbKuqrlMTV9VikZr4BExN/ANMTHwGpiX+AyYl7gJTEveBCYkvgemIh8BkxDZgKmI7sCEuH+ae5lMXcLF8nHuaT93As7mn+QRQPYDqAVQPoHoA1QOoHkD1AKoHUD2A6gFUD6B6ANUDqB5A9QCqB1A9gOoBVA+gegDVA6geQPUAqgfw/3Rd7MhRgLcXhY4cBHhTLc7LEIMA755mFCFGAhYhBgF+f57hTowGdCcGAX672jm1KzEI8GG7WZQhRgFebvZP70YMAlwvNy8HOBGjAOsDoBMxCPB+U7eNcCAGAa7bgQ7EIECzi/OOIScSwwBLEQMByxBDAUsQgwHNfjoTwwG9iQGBDXHpRwwJ9CQGBZo9OhHDAr2IgYFmZw7E0EAPYnDg6cTwwFOJAsDTiBJAsx+TiSLAhng5jSgDnEoUAk4jSgGnEMWA44lywLFEQaDZxxFESeAYoiiwIb4aRpQFDiUKA80+DCBKA4cQxYFmb3qI8sCGuD1GTAA8TkwBPEKs33U9MbWAZm+7iC3/xiWBR4hZgCOJikCzL1f9MmngCKIq0Ozr6+TAhjjkKioDBxG1gWaf+25UdWAvUR/YQ8wANPvU9aqdBXiEmAXYScwD7PjozQRsJeYCthCzAQ+I+YBm76vkwD1iTuAOMSvQ7LpKDvxLzAz8TcwNNLu5TQ40u5t7ABERERERERERERERERFN7hdTvSHAHrHcMgAAAABJRU5ErkJggg=="
|
||||||
|
, checkboxCheckedPartially_svg = Asset "checkboxCheckedPartially_svg"
|
||||||
|
, iconPremiumUnlocked_png = Asset "iconPremiumUnlocked_png"
|
||||||
|
, iconPremiumLocked_png = Asset "iconPremiumLocked_png"
|
||||||
|
, checkboxLockOnInside_svg = Asset "checkboxLockOnInside_svg"
|
||||||
|
, iconPremiumKey_png = Asset "iconPremiumKey_png"
|
||||||
|
, iconPremiumFlag_svg = Asset "iconPremiumFlag_svg"
|
||||||
|
}
|
||||||
|
@ -3,6 +3,7 @@ module NriModules exposing (ModuleStates, Msg, init, nriThemedModules, styles, s
|
|||||||
import Assets exposing (assets)
|
import Assets exposing (assets)
|
||||||
import DEPRECATED.Css.File exposing (Stylesheet, compile, stylesheet)
|
import DEPRECATED.Css.File exposing (Stylesheet, compile, stylesheet)
|
||||||
import Examples.Button
|
import Examples.Button
|
||||||
|
import Examples.Checkbox
|
||||||
import Examples.Colors
|
import Examples.Colors
|
||||||
import Examples.Dropdown
|
import Examples.Dropdown
|
||||||
import Examples.Fonts
|
import Examples.Fonts
|
||||||
@ -31,6 +32,7 @@ import String.Extra
|
|||||||
|
|
||||||
type alias ModuleStates =
|
type alias ModuleStates =
|
||||||
{ buttonExampleState : Examples.Button.State
|
{ buttonExampleState : Examples.Button.State
|
||||||
|
, checkboxExampleState : Examples.Checkbox.State
|
||||||
, dropdownState : Examples.Dropdown.State Examples.Dropdown.Value
|
, dropdownState : Examples.Dropdown.State Examples.Dropdown.Value
|
||||||
, segmentedControlState : Examples.SegmentedControl.State
|
, segmentedControlState : Examples.SegmentedControl.State
|
||||||
, selectState : Examples.Select.State Examples.Select.Value
|
, selectState : Examples.Select.State Examples.Select.Value
|
||||||
@ -43,6 +45,7 @@ type alias ModuleStates =
|
|||||||
init : ModuleStates
|
init : ModuleStates
|
||||||
init =
|
init =
|
||||||
{ buttonExampleState = Examples.Button.init assets
|
{ buttonExampleState = Examples.Button.init assets
|
||||||
|
, checkboxExampleState = Examples.Checkbox.init
|
||||||
, dropdownState = Examples.Dropdown.init
|
, dropdownState = Examples.Dropdown.init
|
||||||
, segmentedControlState = Examples.SegmentedControl.init
|
, segmentedControlState = Examples.SegmentedControl.init
|
||||||
, selectState = Examples.Select.init
|
, selectState = Examples.Select.init
|
||||||
@ -54,6 +57,7 @@ init =
|
|||||||
|
|
||||||
type Msg
|
type Msg
|
||||||
= ButtonExampleMsg Examples.Button.Msg
|
= ButtonExampleMsg Examples.Button.Msg
|
||||||
|
| CheckboxExampleMsg Examples.Checkbox.Msg
|
||||||
| DropdownMsg Examples.Dropdown.Msg
|
| DropdownMsg Examples.Dropdown.Msg
|
||||||
| SegmentedControlMsg Examples.SegmentedControl.Msg
|
| SegmentedControlMsg Examples.SegmentedControl.Msg
|
||||||
| SelectMsg Examples.Select.Msg
|
| SelectMsg Examples.Select.Msg
|
||||||
@ -76,6 +80,13 @@ update msg moduleStates =
|
|||||||
, Cmd.map ButtonExampleMsg cmd
|
, Cmd.map ButtonExampleMsg cmd
|
||||||
)
|
)
|
||||||
|
|
||||||
|
CheckboxExampleMsg msg ->
|
||||||
|
let
|
||||||
|
( checkboxExampleState, cmd ) =
|
||||||
|
Examples.Checkbox.update msg moduleStates.checkboxExampleState
|
||||||
|
in
|
||||||
|
( { moduleStates | checkboxExampleState = checkboxExampleState }, Cmd.map CheckboxExampleMsg cmd )
|
||||||
|
|
||||||
DropdownMsg msg ->
|
DropdownMsg msg ->
|
||||||
let
|
let
|
||||||
( dropdownState, cmd ) =
|
( dropdownState, cmd ) =
|
||||||
@ -162,6 +173,7 @@ container width children =
|
|||||||
nriThemedModules : ModuleStates -> List (ModuleExample Msg)
|
nriThemedModules : ModuleStates -> List (ModuleExample Msg)
|
||||||
nriThemedModules model =
|
nriThemedModules model =
|
||||||
[ Examples.Button.example assets (exampleMessages ButtonExampleMsg) model.buttonExampleState
|
[ Examples.Button.example assets (exampleMessages ButtonExampleMsg) model.buttonExampleState
|
||||||
|
, Examples.Checkbox.example CheckboxExampleMsg model.checkboxExampleState
|
||||||
, Examples.Dropdown.example DropdownMsg model.dropdownState
|
, Examples.Dropdown.example DropdownMsg model.dropdownState
|
||||||
, Examples.Icon.example
|
, Examples.Icon.example
|
||||||
, Examples.Page.example NoOp
|
, Examples.Page.example NoOp
|
||||||
|
3
styleguide-app/assets/images/checkbox_checked.svg
Normal file
3
styleguide-app/assets/images/checkbox_checked.svg
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:915d0521f5bc23c22b393b71da2cd18503077fed13bec61822ce2900b96b0813
|
||||||
|
size 2051
|
@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:4e5ceec4a07abd78ea907ec309cae33f146360d908c834b75c1b0e5fb28f5838
|
||||||
|
size 3002
|
3
styleguide-app/assets/images/checkbox_lock_on_inside.svg
Normal file
3
styleguide-app/assets/images/checkbox_lock_on_inside.svg
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:d45df2ba55c45815fd7df437ec023b49542c46428548d344480c253abf5b2062
|
||||||
|
size 4341
|
3
styleguide-app/assets/images/checkbox_unchecked.svg
Normal file
3
styleguide-app/assets/images/checkbox_unchecked.svg
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:2e10d667be9d7f32ef27714c59a1743796872127eb33870075b013458e6c58c8
|
||||||
|
size 1282
|
3
styleguide-app/assets/images/icon_premium_flag.svg
Normal file
3
styleguide-app/assets/images/icon_premium_flag.svg
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:3bcb4a83ca4e7c4739748a81d5f56fbedf9d9925fe1db8e9fe2e4312c3fa21d2
|
||||||
|
size 1069
|
180
tests/Spec/Nri/Ui/PremiumCheckbox/V1.elm
Normal file
180
tests/Spec/Nri/Ui/PremiumCheckbox/V1.elm
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
module Spec.Nri.Ui.PremiumCheckbox.V1 exposing (spec)
|
||||||
|
|
||||||
|
import Html.Styled
|
||||||
|
import Nri.Ui.AssetPath exposing (Asset(Asset))
|
||||||
|
import Nri.Ui.Checkbox.V3 exposing (IsSelected(..))
|
||||||
|
import Nri.Ui.Data.PremiumLevel exposing (PremiumLevel(..))
|
||||||
|
import Nri.Ui.PremiumCheckbox.V1 as PremiumCheckbox
|
||||||
|
import Test exposing (..)
|
||||||
|
import Test.Html.Query as Query
|
||||||
|
import Test.Html.Selector as Selector
|
||||||
|
|
||||||
|
|
||||||
|
premiumView config =
|
||||||
|
PremiumCheckbox.premium assets
|
||||||
|
{ label = config.label
|
||||||
|
, id = "id"
|
||||||
|
, selected = config.selected
|
||||||
|
, disabled = config.disabled
|
||||||
|
, teacherPremiumLevel = config.teacherPremiumLevel
|
||||||
|
, contentPremiumLevel = config.contentPremiumLevel
|
||||||
|
, showFlagWhenLocked = config.showFlagWhenLocked
|
||||||
|
, onChange = \_ -> ()
|
||||||
|
, onLockedClick = ()
|
||||||
|
, noOpMsg = ()
|
||||||
|
}
|
||||||
|
|> Html.Styled.toUnstyled
|
||||||
|
|> Query.fromHtml
|
||||||
|
|
||||||
|
|
||||||
|
spec : Test
|
||||||
|
spec =
|
||||||
|
describe "Nri.Ui.PremiumCheckbox.V1"
|
||||||
|
[ describe "premium"
|
||||||
|
[ test "displays the label" <|
|
||||||
|
\() ->
|
||||||
|
premiumView
|
||||||
|
{ label = "i am label"
|
||||||
|
, selected = Selected
|
||||||
|
, disabled = False
|
||||||
|
, teacherPremiumLevel = Free
|
||||||
|
, contentPremiumLevel = Free
|
||||||
|
, showFlagWhenLocked = True
|
||||||
|
}
|
||||||
|
|> Query.has [ Selector.text "i am label" ]
|
||||||
|
, test "appears selected when Selected is passed in" <|
|
||||||
|
\() ->
|
||||||
|
premiumView
|
||||||
|
{ label = "i am label"
|
||||||
|
, selected = Selected
|
||||||
|
, disabled = False
|
||||||
|
, teacherPremiumLevel = Free
|
||||||
|
, contentPremiumLevel = Free
|
||||||
|
, showFlagWhenLocked = True
|
||||||
|
}
|
||||||
|
|> Query.has [ Selector.class "checkbox-Checked" ]
|
||||||
|
, test "appears unselected when NotSelected is passed in" <|
|
||||||
|
\() ->
|
||||||
|
premiumView
|
||||||
|
{ label = "i am label"
|
||||||
|
, selected = NotSelected
|
||||||
|
, disabled = False
|
||||||
|
, teacherPremiumLevel = Free
|
||||||
|
, contentPremiumLevel = Free
|
||||||
|
, showFlagWhenLocked = True
|
||||||
|
}
|
||||||
|
|> Query.has [ Selector.class "checkbox-Unchecked" ]
|
||||||
|
, test "appears partially selected when PartiallySelected is passed in" <|
|
||||||
|
\() ->
|
||||||
|
premiumView
|
||||||
|
{ label = "i am label"
|
||||||
|
, selected = PartiallySelected
|
||||||
|
, disabled = False
|
||||||
|
, teacherPremiumLevel = Free
|
||||||
|
, contentPremiumLevel = Free
|
||||||
|
, showFlagWhenLocked = True
|
||||||
|
}
|
||||||
|
|> Query.has [ Selector.class "checkbox-Indeterminate" ]
|
||||||
|
, test "appears locked when teacherPremiumLevel < contentPremiumLevel" <|
|
||||||
|
\() ->
|
||||||
|
premiumView
|
||||||
|
{ label = "i am label"
|
||||||
|
, selected = PartiallySelected
|
||||||
|
, disabled = False
|
||||||
|
, teacherPremiumLevel = Free
|
||||||
|
, contentPremiumLevel = Premium
|
||||||
|
, showFlagWhenLocked = True
|
||||||
|
}
|
||||||
|
|> Query.has [ Selector.class "checkbox-LockOnInsideClass" ]
|
||||||
|
, test "appears unlocked when teacherPremiumLevel >= contentPremiumLevel" <|
|
||||||
|
\() ->
|
||||||
|
premiumView
|
||||||
|
{ label = "i am label"
|
||||||
|
, selected = PartiallySelected
|
||||||
|
, disabled = False
|
||||||
|
, teacherPremiumLevel = Premium
|
||||||
|
, contentPremiumLevel = Premium
|
||||||
|
, showFlagWhenLocked = True
|
||||||
|
}
|
||||||
|
|> Query.hasNot [ Selector.class "checkbox-LockOnInsideClass" ]
|
||||||
|
, test "appears with P flag when teacherPremiumLevel >= contentPremiumLevel" <|
|
||||||
|
\() ->
|
||||||
|
premiumView
|
||||||
|
{ label = "i am label"
|
||||||
|
, selected = PartiallySelected
|
||||||
|
, disabled = False
|
||||||
|
, teacherPremiumLevel = Premium
|
||||||
|
, contentPremiumLevel = Premium
|
||||||
|
, showFlagWhenLocked = False
|
||||||
|
}
|
||||||
|
|> Query.has [ Selector.class "checkbox-PremiumClass" ]
|
||||||
|
, test "does not appear with P flag when teacherPremiumLevel < contentPremiumLevel and showFlagWhenLocked = False" <|
|
||||||
|
\() ->
|
||||||
|
premiumView
|
||||||
|
{ label = "i am label"
|
||||||
|
, selected = PartiallySelected
|
||||||
|
, disabled = False
|
||||||
|
, teacherPremiumLevel = Free
|
||||||
|
, contentPremiumLevel = Premium
|
||||||
|
, showFlagWhenLocked = False
|
||||||
|
}
|
||||||
|
|> Query.hasNot [ Selector.class "checkbox-PremiumClass" ]
|
||||||
|
, test "appears with P flag for Premium content when teacherPremiumLevel < contentPremiumLevel and showFlagWhenLocked = True" <|
|
||||||
|
\() ->
|
||||||
|
premiumView
|
||||||
|
{ label = "i am label"
|
||||||
|
, selected = PartiallySelected
|
||||||
|
, disabled = False
|
||||||
|
, teacherPremiumLevel = Free
|
||||||
|
, contentPremiumLevel = Premium
|
||||||
|
, showFlagWhenLocked = True
|
||||||
|
}
|
||||||
|
|> Query.has [ Selector.class "checkbox-PremiumClass" ]
|
||||||
|
, test "never shows P flag for nonPremium content" <|
|
||||||
|
\() ->
|
||||||
|
premiumView
|
||||||
|
{ label = "i am label"
|
||||||
|
, selected = PartiallySelected
|
||||||
|
, disabled = False
|
||||||
|
, teacherPremiumLevel = Free
|
||||||
|
, contentPremiumLevel = Free
|
||||||
|
, showFlagWhenLocked = True
|
||||||
|
}
|
||||||
|
|> Query.hasNot [ Selector.class "checkbox-PremiumClass" ]
|
||||||
|
, test "is not disabled when disabled = False and the checkbox is unlocked" <|
|
||||||
|
\() ->
|
||||||
|
premiumView
|
||||||
|
{ label = "i am label"
|
||||||
|
, selected = PartiallySelected
|
||||||
|
, disabled = False
|
||||||
|
, teacherPremiumLevel = Free
|
||||||
|
, contentPremiumLevel = Free
|
||||||
|
, showFlagWhenLocked = True
|
||||||
|
}
|
||||||
|
|> Query.has [ Selector.disabled False ]
|
||||||
|
, test "is disabled when disabled = True and the checkbox is unlocked" <|
|
||||||
|
\() ->
|
||||||
|
premiumView
|
||||||
|
{ label = "i am label"
|
||||||
|
, selected = PartiallySelected
|
||||||
|
, disabled = True
|
||||||
|
, teacherPremiumLevel = Free
|
||||||
|
, contentPremiumLevel = Free
|
||||||
|
, showFlagWhenLocked = True
|
||||||
|
}
|
||||||
|
|> Query.has [ Selector.disabled True ]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
assets =
|
||||||
|
{ checkboxUnchecked_svg = Asset "checkboxUnchecked reference"
|
||||||
|
, checkboxChecked_svg = Asset "checkboxChecked reference"
|
||||||
|
, checkboxCheckedPartially_svg = Asset "checkboxCheckedPartially reference"
|
||||||
|
, iconPremiumUnlocked_png = Asset "iconPremiumUnlocked reference"
|
||||||
|
, iconCheck_png = Asset "iconCheck reference"
|
||||||
|
, iconPremiumLocked_png = Asset "iconPremiumLocked reference"
|
||||||
|
, checkboxLockOnInside_svg = Asset "checkboxLockOnInside reference"
|
||||||
|
, iconPremiumKey_png = Asset "iconPremiumKey reference"
|
||||||
|
, iconPremiumFlag_svg = Asset "iconPremiumFlag reference"
|
||||||
|
}
|
@ -14,6 +14,7 @@
|
|||||||
"elm-community/elm-test": "4.0.0 <= v < 5.0.0",
|
"elm-community/elm-test": "4.0.0 <= v < 5.0.0",
|
||||||
"elm-lang/core": "5.1.1 <= v < 6.0.0",
|
"elm-lang/core": "5.1.1 <= v < 6.0.0",
|
||||||
"elm-lang/html": "2.0.0 <= v < 3.0.0",
|
"elm-lang/html": "2.0.0 <= v < 3.0.0",
|
||||||
|
"elm-lang/svg": "2.0.0 <= v < 3.0.0",
|
||||||
"pablohirafuji/elm-markdown": "2.0.4 <= v < 3.0.0",
|
"pablohirafuji/elm-markdown": "2.0.4 <= v < 3.0.0",
|
||||||
"rtfeldman/elm-css": "13.1.1 <= v < 14.0.0",
|
"rtfeldman/elm-css": "13.1.1 <= v < 14.0.0",
|
||||||
"rtfeldman/elm-css-helpers": "2.1.0 <= v < 3.0.0",
|
"rtfeldman/elm-css-helpers": "2.1.0 <= v < 3.0.0",
|
||||||
|
Loading…
Reference in New Issue
Block a user