@@ -1,51 +1,186 @@
1module Element.Input exposing
1module Element.Input exposing
3 , checkbox, defaultCheckbox
4 , checkbox, defaultCheckbox
4− , text, Placeholder, placeholder
6+ , Placeholder, placeholder
5 , username, newPassword, currentPassword, email, search, spellChecked
7 , username, newPassword, currentPassword, email, search, spellChecked
7 , slider, Thumb, thumb, defaultThumb
8 , slider, Thumb, thumb, defaultThumb
8 , radio, radioRow, Option, option, optionWith, OptionState(..)
9 , radio, radioRow, Option, option, optionWith, OptionState(..)
9 , Label, labelAbove, labelBelow, labelLeft, labelRight, labelHidden
10 , Label, labelAbove, labelBelow, labelLeft, labelRight, labelHidden
13−{-|
13+{-| Input elements have a lot of constraints!
15+We want all of our input elements to:
17+ - _Always be accessible_
18+ - _Behave intuitively_
19+ - _Be completely restyleable_
21+While these three goals may seem pretty obvious, Html and CSS have made it surprisingly difficult to achieve!
23+And incredibly difficult for developers to remember all the tricks necessary to make things work. If you've every tried to make a `<textarea>` be the height of it's content or restyle a radio button while maintaining accessibility, you may be familiar.
25+This module is intended to be accessible by default. You shouldn't have to wade through docs, articles, and books to find out [exactly how accessible your html actually is](https://www.powermapper.com/tests/screen-readers/aria/index.html).
30+All Elements can be styled on focus by using [`Element.focusStyle`](Element#focusStyle) to set a global focus style or [`Element.focused`](Element#focused) to set a focus style individually for an element.
15@docs button
37@docs button
42+A checkbox requires you to store a `Bool` in your model.
44+This is also the first input element that has a [`required label`](#Label).
46+ import Element exposing (text)
47+ import Element.Input as Input
50+ = GuacamoleChecked Bool
54+ { onChange = GuacamoleChecked
55+ , icon = Input.defaultCheckbox
56+ , checked = model.guacamole
59+ (text "Do you want Guacamole?")
17@docs checkbox, defaultCheckbox
62@docs checkbox, defaultCheckbox
20−## Text Input
65+# Text
22−@docs text, Placeholder, placeholder
67+@docs text, multiline
24−We can also give a hint about what type of content our text field contains. This will allow things like autofill to work correctly.
69+@docs Placeholder, placeholder
26−@docs username, newPassword, currentPassword, email, search, spellChecked
74+If we want to play nicely with a browser's ability to autofill a form, we need to be able to give it a hint about what we're expecting.
76+The following inputs are very similar to `Input.text`, but they give the browser a hint to allow autofill to work correctly.
78+@docs username, newPassword, currentPassword, email, search, spellChecked
31−@docs multiline
81+# Sliders
83+A slider is great for choosing between a range of numerical values.
34−## Slider
85+ - **thumb** - The icon that you click and drag to change the value.
86+ - **track** - The line behind the thumb denoting where you can slide to.
36@docs slider, Thumb, thumb, defaultThumb
88@docs slider, Thumb, thumb, defaultThumb
39−## Radio Buttons
91+# Radio Selection
93+The fact that we still call this a radio selection is fascinating. I can't remember the last time I actually used an honest-to-goodness button on a radio. Chalk it up along with the floppy disk save icon or the word [Dashboard](https://en.wikipedia.org/wiki/Dashboard).
95+Perhaps a better name would be `Input.chooseOne`, because this allows you to select one of a set of options!
97+Nevertheless, here we are. Here's how you put one together
103+ { onChange = ChooseLunch
104+ , selected = Just model.lunch
105+ , label = Input.labelAbove [] (text "Lunch")
107+ [ Input.option Burrito (text "Burrito")
108+ , Input.option Taco (text "Taco!")
109+ , Input.option Gyro (text "Gyro")
113+**Note** we're using `Input.option`, which will render the default radio icon you're probably used to. If you want compeltely custom styling, use `Input.optionWith`!
41@docs radio, radioRow, Option, option, optionWith, OptionState
115@docs radio, radioRow, Option, option, optionWith, OptionState
120+Every input has a required `Label`.
46@docs Label, labelAbove, labelBelow, labelLeft, labelRight, labelHidden
122@docs Label, labelAbove, labelBelow, labelLeft, labelRight, labelHidden
48−@docs focusedOnLoad
124+
127+You might be wondering where something like `<form>` is.
129+What I've found is that most people who want `<form>` usually want it for the [implicit submission behavior](https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#implicit-submission) or to be clearer, they want to do something when the `Enter` key is pressed.
131+Instead of implicit submission behavior, [try making an `onEnter` event handler like in this Ellie Example](https://ellie-app.com/5X6jBKtxzdpa1). Then everything is explicit!
133+And no one has to look up obtuse html documentation to understand the behavior of their code :).
138+Presently, elm-ui does not expose a replacement for `<input type="file">`; in the meantime, an `Input.button` and `elm/file`'s `File.Select` may meet your needs.
143+You also might be wondering how to disable an input.
145+Disabled inputs can be a little problematic for user experience, and doubly so for accessibility. This is because it's now your priority to inform the user _why_ some field is disabled.
147+If an input is truly disabled, meaning it's not focusable or doesn't send off a `Msg`, you actually lose your ability to help the user out! For those wary about accessibility [this is a big problem.](https://ux.stackexchange.com/questions/103239/should-disabled-elements-be-focusable-for-accessibility-purposes)
149+Here are some alternatives to think about that don't involve explicitly disabling an input.
151+**Disabled Buttons** - Change the `Msg` it fires, the text that is rendered, and optionally set a `Region.description` which will be available to screen readers.
153+ import Element.Input as Input
154+ import Element.Region as Region
159+ [ Background.color blue
162+ Just SaveButtonPressed
164+ text "Save blog post"
169+ [ Background.color grey
170+ , Region.description
171+ "A publish date is required before saving a blogpost."
174+ Just DisabledSaveButtonPressed
179+Consider showing a hint if `DisabledSaveButtonPressed` is sent.
181+For other inputs such as `Input.text`, consider simply rendering it in a normal `paragraph` or `el` if it's not editable.
183+Alternatively, see if it's reasonable to _not_ display an input if you'd normally disable it. Is there an option where it's only visible when it's editable?
@@ -97,13 +232,33 @@ type LabelLocation
100−{-| Every input has a required `label`.
102type Label msg
236type Label msg
103 = Label LabelLocation (List (Attribute msg)) (Element msg)
237 = Label LabelLocation (List (Attribute msg)) (Element msg)
104 | HiddenLabel String
238 | HiddenLabel String
241+isStacked : Label msg -> Bool
108labelRight : List (Attribute msg) -> Element msg -> Label msg
263labelRight : List (Attribute msg) -> Element msg -> Label msg
109labelRight =
264labelRight =
@@ -156,16 +311,26 @@ hiddenLabelAttribute label =
157{-| A standard button.
312{-| A standard button.
159−The `onPress` handler will be fired either `onClick` or when the element is focused and the enter key has been pressed.
314+The `onPress` handler will be fired either `onClick` or when the element is focused and the `Enter` key has been pressed.
316+ import Element exposing (rgb255, text)
317+ import Element.Background as Background
161 import Element.Input as Input
318 import Element.Input as Input
163− Input.button []
320+ blue =
164− { onPress = ClickMsg
321+ Element.rgb255 238 238 238
165− , label = text "My Button"
168−`onPress` takes a `Maybe msg`. If you provide the value `Nothing`, then the button will be disabled.
323+ myButton =
325+ [ Background.color blue
327+ [ Background.color purple ]
329+ { onPress = Just ClickMsg
330+ , label = text "My Button"
333+**Note** If you have an icon button but want it to be accessible, consider adding a [`Region.description`](Element-Region#description), which will describe the button to screen readers.
@@ -204,7 +369,17 @@ button attrs { onPress, label } =
205 Just msg ->
370 Just msg ->
206 Events.onClick msg
371 Events.onClick msg
207− :: onEnter msg
372+ :: onKeyLookup
374+ if code == enter then
377+ else if code == space then
@@ -237,7 +412,14 @@ type alias Checkbox msg =
417+ - **onChange** - The `Msg` to send.
418+ - **icon** - The checkbox icon to show. This can be whatever you'd like, but `Input.defaultCheckbox` is included to get you started.
419+ - **checked** - The current checked state.
420+ - **label** - The [`Label`](#Label) for this checkbox
241checkbox :
423checkbox :
242 List (Attribute msg)
424 List (Attribute msg)
@@ -275,22 +457,24 @@ checkbox attrs { label, icon, checked, onChange } =
278− applyLabel attributes
460+ applyLabel
284− Html.Attributes.attribute "role" "checkbox"
461+ (Internal.Attr (Html.Attributes.attribute "role" "checkbox")
285− , Internal.Attr <|
462+ :: Internal.Attr
286− Html.Attributes.attribute "aria-checked" <|
463+ (Html.Attributes.attribute "aria-checked" <|
287 if checked then
464 if checked then
292− , hiddenLabelAttribute label
470+ :: hiddenLabelAttribute label
293− , Element.centerY
477+ [ Element.centerY
294 , Element.height Element.fill
478 , Element.height Element.fill
295 , Element.width Element.shrink
479 , Element.width Element.shrink
@@ -343,7 +527,9 @@ defaultThumb =
345 { onChange = AdjustValue
529 { onChange = AdjustValue
346− , label = Input.labelAbove [] (text "My Slider Value")
532+ (text "My Slider Value")
347 , min = 0
533 , min = 0
348 , max = 75
534 , max = 75
349 , step = Nothing
535 , step = Nothing
@@ -360,7 +546,7 @@ The slider can be vertical or horizontal depending on the width/height of the sl
360 - `height (px someHeight)` and `width (px someWidth)` where `someHeight` > `someWidth` will also do it.
546 - `height (px someHeight)` and `width (px someWidth)` where `someHeight` > `someWidth` will also do it.
361 - otherwise, the slider will be horizontal.
547 - otherwise, the slider will be horizontal.
363−**Note:** If you want a slider for an `Int` value:
549+**Note** If you want a slider for an `Int` value:
365 - set `step` to be `Just 1`, or some other whole value
551 - set `step` to be `Just 1`, or some other whole value
366 - `value = toFloat model.myInt`
552 - `value = toFloat model.myInt`
@@ -527,7 +713,7 @@ slider attributes input =
527 ("input[type=\"range\"]." ++ className ++ "::-ms-thumb")
713 ("input[type=\"range\"]." ++ className ++ "::-ms-thumb")
528 thumbShadowStyle
714 thumbShadowStyle
530− , Internal.Attr (Html.Attributes.class (className ++ " focusable-parent"))
716+ , Internal.Attr (Html.Attributes.class (className ++ " ui-slide-bar focusable-parent"))
531 , Internal.Attr
717 , Internal.Attr
532 (Html.Events.onInput
718 (Html.Events.onInput
@@ -593,10 +779,10 @@ slider attributes input =
593 -- This is after `attributes` because the thumb should be in front of everything.
779 -- This is after `attributes` because the thumb should be in front of everything.
594 ++ [ Element.behindContent <|
780 ++ [ Element.behindContent <|
595 if vertical then
781 if vertical then
596− viewVerticalThumb factor thumbAttributes trackWidth
782+ viewVerticalThumb factor (Internal.htmlClass "focusable-thumb" :: thumbAttributes) trackWidth
599− viewHorizontalThumb factor thumbAttributes trackHeight
785+ viewHorizontalThumb factor (Internal.htmlClass "focusable-thumb" :: thumbAttributes) trackHeight
602 Element.none
788 Element.none
@@ -677,10 +863,9 @@ textHelper textInput attrs textOptions =
677 defaultTextBoxStyle ++ attrs
863 defaultTextBoxStyle ++ attrs
679 redistributed =
865 redistributed =
680− redistribute withDefaults
866+ redistribute (textInput.type_ == TextArea)
681−
867+ (isStacked textOptions.label)
682− moveUpToCompensateForPadding =
683− calcMoveToCompensateForPadding withDefaults
868+ withDefaults
685 onlySpacing attr =
870 onlySpacing attr =
686 case attr of
871 case attr of
@@ -690,35 +875,46 @@ textHelper textInput attrs textOptions =
693− isHeightContent attr =
878+ getPadding attr =
695− Internal.Height Internal.Content ->
701− bottomPadding attr =
702 case attr of
879 case attr of
703 Internal.StyleClass cls (Internal.PaddingStyle pad t r b l) ->
880 Internal.StyleClass cls (Internal.PaddingStyle pad t r b l) ->
704− Just <|
881+ -- The - 3 is here to prevent accidental triggering of scrollbars
705− Element.paddingEach
882+ -- when things are off by a pixel or two.
883+ -- (or at least when the browser *thinks* it's off by a pixel or two)
706− { top = 0
885+ { top = max 0 (floor (t - 3))
707− , right = 0
886+ , right = max 0 (floor (r - 3))
708− , bottom = b
887+ , bottom = max 0 (floor (b - 3))
709− , left = 0
888+ , left = max 0 (floor (l - 3))
715− heightContent =
894+ heightConstrained =
716 case textInput.type_ of
895 case textInput.type_ of
717 TextInputNode inputType ->
896 TextInputNode inputType ->
720 TextArea ->
899 TextArea ->
901+ |> List.filterMap getHeight
904+ |> Maybe.map isConstrained
905+ |> Maybe.withDefault False
721− List.any isHeightContent withDefaults
908+ withDefaults
909+ |> List.filterMap getPadding
912+ |> Maybe.withDefault
723 inputElement =
919 inputElement =
724 Internal.element
920 Internal.element
@@ -730,47 +926,63 @@ textHelper textInput attrs textOptions =
730 TextArea ->
926 TextArea ->
731 Internal.NodeName "textarea"
927 Internal.NodeName "textarea"
733− ([ value textOptions.text
734− , Internal.Attr (Html.Events.onInput textOptions.onChange)
735− , hiddenLabelAttribute textOptions.label
736− , spellcheck textInput.spellchecked
737− , Maybe.map autofill textInput.autofill
738− |> Maybe.withDefault Internal.NoAttribute
740− ++ (case textInput.type_ of
929+ ((case textInput.type_ of
741− TextInputNode inputType ->
930+ TextInputNode inputType ->
931+ -- Note: Due to a weird edgecase in...Edge...
932+ -- `type` needs to come _before_ `value`
933+ -- More reading: https://github.com/mdgriffith/elm-ui/pull/94/commits/4f493a27001ccc3cf1f2baa82e092c35d3811876
742− [ Internal.Attr (Html.Attributes.type_ inputType)
934+ [ Internal.Attr (Html.Attributes.type_ inputType)
743− , Internal.htmlClass classes.inputText
935+ , Internal.htmlClass classes.inputText
744− , Internal.htmlClass "focusable"
747− TextArea ->
938+ TextArea ->
748− [ Element.clip
939+ [ Element.clip
749− , Element.height Element.fill
940+ , Element.height Element.fill
750− , Internal.htmlClass classes.inputMultiline
941+ , Internal.htmlClass classes.inputMultiline
751− , Element.alpha 0.3
942+ , calcMoveToCompensateForPadding withDefaults
752− , moveUpToCompensateForPadding
943+
944+ -- The only reason we do this padding trick is so that when the user clicks in the padding,
945+ -- that the cursor will reset correctly.
946+ -- This could probably be combined with the above `calcMoveToCompensateForPadding`
947+ , Element.paddingEach parentPadding
948+ , Internal.Attr (Html.Attributes.style "margin" (renderBox (negateBox parentPadding)))
949+ , Internal.Attr (Html.Attributes.style "box-sizing" "content-box")
952+ ++ [ value textOptions.text
953+ , Internal.Attr (Html.Events.onInput textOptions.onChange)
954+ , hiddenLabelAttribute textOptions.label
955+ , spellcheck textInput.spellchecked
956+ , Maybe.map autofill textInput.autofill
957+ |> Maybe.withDefault Internal.NoAttribute
755 ++ redistributed.input
959 ++ redistributed.input
757 (Internal.Unkeyed [])
961 (Internal.Unkeyed [])
759 wrappedInput =
963 wrappedInput =
760− -- if heightContent then
761 case textInput.type_ of
964 case textInput.type_ of
762 TextArea ->
965 TextArea ->
763 -- textarea with height-content means that
966 -- textarea with height-content means that
764− -- the input element is rendered `inFront` and transparently.
967+ -- the input element is rendered `inFront` with a transparent background
765− -- Then the input text is rendered as the actual element.
968+ -- Then the input text is rendered as the space filling element.
766 Internal.element
969 Internal.element
767 Internal.asEl
970 Internal.asEl
768 Internal.div
971 Internal.div
972+ ((if heightConstrained then
973+ (::) Element.scrollbarY
769− ([ Element.width Element.fill
978+ [ Element.width Element.fill
770− , Element.height (Element.px 100)
979+ , if List.any hasFocusStyle withDefaults then
771− , Element.scrollbarY
980+ Internal.NoAttribute
772− , Internal.htmlClass classes.focusedWithin
983+ Internal.htmlClass classes.focusedWithin
984+ , Internal.htmlClass classes.inputMultilineWrapper
774 ++ redistributed.parent
986 ++ redistributed.parent
776 (Internal.Unkeyed
988 (Internal.Unkeyed
@@ -780,25 +992,33 @@ textHelper textInput attrs textOptions =
780 (Element.width Element.fill
992 (Element.width Element.fill
781 :: Element.height Element.fill
993 :: Element.height Element.fill
782 :: Element.inFront inputElement
994 :: Element.inFront inputElement
783− :: Internal.htmlClass classes.inputMultiline
995+ :: Internal.htmlClass classes.inputMultilineParent
784− :: List.concat
996+ :: redistributed.wrapper
785− [ if heightContent then
997+ )
786− redistributed.wrapper
998+ (Internal.Unkeyed
787−
999+ (if textOptions.text == "" then
789− redistributed.wrapper
790− ++ List.filterMap bottomPadding redistributed.parent
791− , case textOptions.placeholder of
1000+ case textOptions.placeholder of
792 Nothing ->
1001 Nothing ->
1002+ -- Without this, firefox will make the text area lose focus
1003+ -- if the input is empty and you mash the keyboard
1004+ [ Element.text "\u{00A0}"
795 Just place ->
1007 Just place ->
796− [ renderPlaceholder place redistributed.cover (textOptions.text == "")
1008+ [ renderPlaceholder place
1010+ (textOptions.text == "")
1014+ [ Internal.unstyled
1015+ (Html.span [ Html.Attributes.class classes.inputMultilineFiller ]
1016+ -- We append a non-breaking space to the end of the content so that newlines don't get chomped.
1017+ [ Html.text (textOptions.text ++ "\u{00A0}")
801− [ Internal.unstyled (Html.text textOptions.text) ]
@@ -808,6 +1028,12 @@ textHelper textInput attrs textOptions =
808 Internal.asEl
1028 Internal.asEl
809 Internal.div
1029 Internal.div
810 (Element.width Element.fill
1030 (Element.width Element.fill
1031+ :: (if List.any hasFocusStyle withDefaults then
1032+ Internal.NoAttribute
1035+ Internal.htmlClass classes.focusedWithin
811 :: List.concat
1037 :: List.concat
812 [ redistributed.parent
1038 [ redistributed.parent
813 , case textOptions.placeholder of
1039 , case textOptions.placeholder of
@@ -815,7 +1041,8 @@ textHelper textInput attrs textOptions =
817 Just place ->
1043 Just place ->
1044+ [ Element.behindContent
818− [ renderPlaceholder place redistributed.cover (textOptions.text == "")
1045+ (renderPlaceholder place redistributed.cover (textOptions.text == ""))
@@ -837,29 +1064,55 @@ textHelper textInput attrs textOptions =
837 wrappedInput
1064 wrappedInput
1069+ Internal.Height h ->
1077+ { top = negate box.top
1078+ , right = negate box.right
1079+ , bottom = negate box.bottom
1080+ , left = negate box.left
1084+renderBox { top, right, bottom, left } =
1087+ ++ String.fromInt right
1089+ ++ String.fromInt bottom
1091+ ++ String.fromInt left
840renderPlaceholder (Placeholder placeholderAttrs placeholderEl) forPlaceholder on =
1095renderPlaceholder (Placeholder placeholderAttrs placeholderEl) forPlaceholder on =
841− Element.inFront
1096+ Element.el
844− :: forPlaceholder
1097+ (forPlaceholder
845− ++ [ Font.color charcoal
1098+ ++ [ Font.color charcoal
846− , Internal.htmlClass (classes.noTextSelection ++ " " ++ classes.passPointerEvents)
1099+ , Internal.htmlClass (classes.noTextSelection ++ " " ++ classes.passPointerEvents)
847− , Border.color (Element.rgba 0 0 0 0)
1101+ , Border.color (Element.rgba 0 0 0 0)
848− , Background.color (Element.rgba 0 0 0 0)
1102+ , Background.color (Element.rgba 0 0 0 0)
849− , Element.height Element.fill
1103+ , Element.height Element.fill
850− , Element.width Element.fill
1104+ , Element.width Element.fill
851− , Element.alpha
1105+ , Element.alpha
852− (if on then
1106+ (if on then
859− ++ placeholderAttrs
1113+ ++ placeholderAttrs
865{-| Because textareas are now shadowed, where they're rendered twice,
1118{-| Because textareas are now shadowed, where they're rendered twice,
@@ -886,7 +1139,7 @@ calcMoveToCompensateForPadding attrs =
886 Internal.NoAttribute
1139 Internal.NoAttribute
888 Just vSpace ->
1141 Just vSpace ->
889− Element.moveUp (toFloat vSpace / 2)
1142+ Element.moveUp (toFloat (floor (toFloat vSpace / 2)))
892{-| Given the list of attributes provided to `Input.multiline` or `Input.text`,
1145{-| Given the list of attributes provided to `Input.multiline` or `Input.text`,
@@ -901,7 +1154,9 @@ redistribute them to the parent, the input, or the cover.
903redistribute :
1156redistribute :
904− List (Attribute msg)
1159+ -> List (Attribute msg)
906 { fullParent : List (Attribute msg)
1161 { fullParent : List (Attribute msg)
907 , parent : List (Attribute msg)
1162 , parent : List (Attribute msg)
@@ -909,8 +1164,8 @@ redistribute :
909 , input : List (Attribute msg)
1164 , input : List (Attribute msg)
910 , cover : List (Attribute msg)
1165 , cover : List (Attribute msg)
912−redistribute attrs =
1167+redistribute isMultiline stacked attrs =
913− List.foldl redistributeOver
1168+ List.foldl (redistributeOver isMultiline stacked)
914 { fullParent = []
1169 { fullParent = []
915 , parent = []
1170 , parent = []
916 , input = []
1171 , input = []
@@ -928,27 +1183,123 @@ redistribute attrs =
1191+ Internal.Content ->
1197+ Internal.Min _ l ->
1200+ Internal.Max _ l ->
1206+ Internal.Content ->
1215+ Internal.Min _ l ->
1218+ Internal.Max _ l ->
1224+ Internal.Content ->
1233+ Internal.Min _ l ->
1236+ Internal.Max _ l ->
1242+ Internal.Content ->
1251+ Internal.Min _ l ->
1254+ Internal.Max _ l ->
1258+{-| isStacked means that the label is above or below
931−redistributeOver attr els =
1260+redistributeOver isMultiline stacked attr els =
932 case attr of
1261 case attr of
933 Internal.Nearby _ _ ->
1262 Internal.Nearby _ _ ->
934 { els | parent = attr :: els.parent }
1263 { els | parent = attr :: els.parent }
936− Internal.Width (Internal.Fill _) ->
1265+ Internal.Width width ->
1266+ if isFill width then
937− { els | fullParent = attr :: els.fullParent }
1268+ | fullParent = attr :: els.fullParent
1269+ , parent = attr :: els.parent
1270+ , input = attr :: els.input
939− Internal.Width _ ->
1273+ else if stacked then
940− { els | parent = attr :: els.parent }
1274+ { els
1275+ | fullParent = attr :: els.fullParent
942− Internal.Height (Internal.Fill _) ->
1278+ else
944− | fullParent = attr :: els.fullParent
945− , parent = attr :: els.parent
1280+ | parent = attr :: els.parent
948− Internal.Height h ->
1283+ Internal.Height height ->
1284+ if not stacked then
1286+ | fullParent = attr :: els.fullParent
1287+ , parent = attr :: els.parent
1290+ else if isFill height then
1292+ | fullParent = attr :: els.fullParent
1293+ , parent = attr :: els.parent
1296+ else if isPixel height then
950− | parent = attr :: els.parent
1297+ { els | parent = attr :: els.parent }
1301+ | parent = attr :: els.parent
953 Internal.AlignX _ ->
1304 Internal.AlignX _ ->
954 { els | fullParent = attr :: els.fullParent }
1305 { els | fullParent = attr :: els.fullParent }
@@ -964,14 +1315,55 @@ redistributeOver attr els =
964 , wrapper = attr :: els.wrapper
1315 , wrapper = attr :: els.wrapper
967− Internal.StyleClass _ (Internal.PaddingStyle _ _ _ _ _) ->
1318+ Internal.StyleClass cls (Internal.PaddingStyle pad t r b l) ->
1319+ if isMultiline then
969− | parent = attr :: els.parent
1321+ | parent = attr :: els.parent
1322+ , cover = attr :: els.cover
971− -- input = attr :: els.input
1325+ else
972− -- , wrapper = attr :: els.wrapper
1326+ let
973− , cover = attr :: els.cover
1327+ newHeight =
1328+ Element.htmlAttribute
1329+ (Html.Attributes.style
1331+ ("calc(1.0em + " ++ String.fromFloat (2 * min t b) ++ "px)")
1335+ Element.htmlAttribute
1336+ (Html.Attributes.style
1338+ ("calc(1.0em + " ++ String.fromFloat (2 * min t b) ++ "px)")
1347+ reducedVerticalPadding =
1348+ Internal.StyleClass Flag.padding
1349+ (Internal.PaddingStyle
1350+ (Internal.paddingNameFloat
1363+ | parent = reducedVerticalPadding :: els.parent
1364+ , input = newHeight :: newLineHeight :: els.input
1365+ , cover = attr :: els.cover
976 Internal.StyleClass _ (Internal.BorderWidth _ _ _ _ _) ->
1368 Internal.StyleClass _ (Internal.BorderWidth _ _ _ _ _) ->
@@ -991,7 +1383,7 @@ redistributeOver attr els =
991 Internal.StyleClass _ (Internal.FontFamily _ _) ->
1383 Internal.StyleClass _ (Internal.FontFamily _ _) ->
992 { els | fullParent = attr :: els.fullParent }
1384 { els | fullParent = attr :: els.fullParent }
994− Internal.StyleClass _ _ ->
1386+ Internal.StyleClass flag cls ->
995 { els | parent = attr :: els.parent }
1387 { els | parent = attr :: els.parent }
997 Internal.NoAttribute ->
1389 Internal.NoAttribute ->
@@ -1010,30 +1402,6 @@ redistributeOver attr els =
1010 { els | input = attr :: els.input }
1402 { els | input = attr :: els.input }
1013−orElse thing maybe =
1022−inheritablePlaceholderAttributes attr =
1024− Internal.StyleClass _ (Internal.PaddingStyle _ _ _ _ _) ->
1027− Internal.StyleClass _ (Internal.BorderWidth _ _ _ _ _) ->
1030− Internal.StyleClass _ (Internal.Transform _) ->
1039 List (Attribute msg)
1407 List (Attribute msg)
@@ -1093,7 +1461,7 @@ search =
1094It's `newPassword` instead of just `password` because it gives the browser a hint on what type of password input it is.
1462It's `newPassword` instead of just `password` because it gives the browser a hint on what type of password input it is.
1096−A password takes all the arguments a normal `Input.text` would, and also `show`, which will remove the password mask (e.g. `****` vs `pass1234`)
1464+A password takes all the arguments a normal `Input.text` would, and also **show**, which will remove the password mask (e.g. `****` vs `pass1234`)
1099newPassword :
1467newPassword :
@@ -1193,7 +1561,13 @@ email =
1564+{-| A multiline text input.
1566+By default it will have a minimum height of one line and resize based on it's contents.
1568+Use `Element.spacing` to change its line-height.
1197multiline :
1571multiline :
1198 List (Attribute msg)
1572 List (Attribute msg)
@@ -1254,33 +1628,32 @@ applyLabel attrs label input =
1254 Internal.element
1628 Internal.element
1255 Internal.asColumn
1629 Internal.asColumn
1256 (Internal.NodeName "label")
1630 (Internal.NodeName "label")
1257− attrs
1631+ (Internal.htmlClass classes.inputLabel :: attrs)
1258 (Internal.Unkeyed [ labelElement, input ])
1632 (Internal.Unkeyed [ labelElement, input ])
1260 Below ->
1634 Below ->
1261 Internal.element
1635 Internal.element
1262 Internal.asColumn
1636 Internal.asColumn
1263 (Internal.NodeName "label")
1637 (Internal.NodeName "label")
1264− attrs
1638+ (Internal.htmlClass classes.inputLabel :: attrs)
1265 (Internal.Unkeyed [ input, labelElement ])
1639 (Internal.Unkeyed [ input, labelElement ])
1267 OnRight ->
1641 OnRight ->
1268 Internal.element
1642 Internal.element
1269 Internal.asRow
1643 Internal.asRow
1270 (Internal.NodeName "label")
1644 (Internal.NodeName "label")
1271− attrs
1645+ (Internal.htmlClass classes.inputLabel :: attrs)
1272 (Internal.Unkeyed [ input, labelElement ])
1646 (Internal.Unkeyed [ input, labelElement ])
1274 OnLeft ->
1648 OnLeft ->
1275 Internal.element
1649 Internal.element
1276 Internal.asRow
1650 Internal.asRow
1277 (Internal.NodeName "label")
1651 (Internal.NodeName "label")
1278− attrs
1652+ (Internal.htmlClass classes.inputLabel :: attrs)
1279 (Internal.Unkeyed [ labelElement, input ])
1653 (Internal.Unkeyed [ labelElement, input ])
1282−{-| Add choices to your radio and select menus.
1284type Option value msg
1657type Option value msg
1285 = Option value (OptionState -> Element msg)
1658 = Option value (OptionState -> Element msg)
@@ -1292,35 +1665,21 @@ type OptionState
1292 | Selected
1665 | Selected
1668+{-| Add a choice to your radio element. This will be rendered with the default radio icon.
1296option : value -> Element msg -> Option value msg
1670option : value -> Element msg -> Option value msg
1297option val txt =
1671option val txt =
1298 Option val (defaultRadioOption txt)
1672 Option val (defaultRadioOption txt)
1675+{-| Customize exactly what your radio option should look like in different states.
1302optionWith : value -> (OptionState -> Element msg) -> Option value msg
1677optionWith : value -> (OptionState -> Element msg) -> Option value msg
1303optionWith val view =
1678optionWith val view =
1304 Option val view
1679 Option val view
1313− { onChange = ChooseLunch
1314− , selected = Just model.lunch
1315− , label = Input.labelAbove [] (text "Lunch")
1317− [ Input.option Burrito (text "Burrito")
1318− , Input.option Taco (text "Taco!")
1319− , Input.option Gyro (text "Gyro")
1325 List (Attribute msg)
1684 List (Attribute msg)
@@ -1746,7 +2105,11 @@ onKeyLookup lookup =
1746 Json.field "key" Json.string
2105 Json.field "key" Json.string
1747 |> Json.andThen decode
2106 |> Json.andThen decode
2108+ -- We generally want these attached to the keydown event becaues it allows us to prevent default on things like spacebar scrolling the page.
2109+ -- https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/button_role
1749− Internal.Attr <| Html.Events.on "keyup" isKey
2110+ Internal.Attr <|
2111+ Html.Events.preventDefaultOn "keydown"
2112+ (Json.map (\fired -> ( fired, True )) isKey)
@@ -1761,17 +2124,6 @@ onFocusIn msg =
1761 Internal.Attr <| Html.Events.on "focusin" (Json.succeed msg)
2124 Internal.Attr <| Html.Events.on "focusin" (Json.succeed msg)
1764−type_ : String -> Attribute msg
1766− Internal.Attr << Html.Attributes.type_
1770−-- checked : Bool -> Attribute msg
1772−-- Internal.Attr << Html.Attributes.checked
1775selected : Bool -> Attribute msg
2127selected : Bool -> Attribute msg
1776selected =
2128selected =
1777 Internal.Attr << Html.Attributes.selected
2129 Internal.Attr << Html.Attributes.selected
@@ -1812,7 +2164,11 @@ autofill =
1812 Internal.Attr << Html.Attributes.attribute "autocomplete"
2164 Internal.Attr << Html.Attributes.attribute "autocomplete"
2167+{-| Attach this attribute to any `Input` that you would like to be automatically focused when the page loads.
2169+You should only have a maximum of one per page.
1816focusedOnLoad : Attribute msg
2172focusedOnLoad : Attribute msg
1817focusedOnLoad =
2173focusedOnLoad =
1818 Internal.Attr <| Html.Attributes.autofocus True
2174 Internal.Attr <| Html.Attributes.autofocus True
@@ -1829,8 +2185,9 @@ defaultTextBoxStyle =
1829 , Border.color darkGrey
2185 , Border.color darkGrey
1830 , Background.color white
2186 , Background.color white
1831 , Border.width 1
2187 , Border.width 1
1832− , Element.spacing 3
2188+ , Element.spacing 5
1833 , Element.width Element.fill
2189 , Element.width Element.fill
2190+ , Element.height Element.shrink
@@ -1839,12 +2196,17 @@ defaultTextPadding =
1839 Element.paddingXY 12 12
2196 Element.paddingXY 12 12
2199+{-| The blue default checked box icon.
2201+You'll likely want to make your own checkbox at some point that fits your design.
1843defaultCheckbox : Bool -> Element msg
2204defaultCheckbox : Bool -> Element msg
1844defaultCheckbox checked =
2205defaultCheckbox checked =
1845 Element.el
2206 Element.el
1846 [ Internal.htmlClass "focusable"
2207 [ Internal.htmlClass "focusable"
1847− , Element.width (Element.px 14)
2208+ , Element.width
1848 , Element.height (Element.px 14)
2210 , Element.height (Element.px 14)
1849 , Font.color white
2211 , Font.color white
1850 , Element.centerY
2212 , Element.centerY
@@ -1857,7 +2219,7 @@ defaultCheckbox checked =
1859 Element.rgb (211 / 255) (211 / 255) (211 / 255)
2221 Element.rgb (211 / 255) (211 / 255) (211 / 255)
1860− , Border.shadow <|
2222+ , Border.shadow
1861 { offset = ( 0, 0 )
2223 { offset = ( 0, 0 )
1862 , blur = 1
2224 , blur = 1
1863 , size = 1
2225 , size = 1
@@ -1880,9 +2242,8 @@ defaultCheckbox checked =
1883− ]
2245+ , Element.inFront
1885− Element.el
2246+ (Element.el
1886 [ Border.color white
2247 [ Border.color white
1887 , Element.height (Element.px 6)
2248 , Element.height (Element.px 6)
1888 , Element.width (Element.px 9)
2249 , Element.width (Element.px 9)
@@ -1890,6 +2251,7 @@ defaultCheckbox checked =
1890 , Element.centerX
2251 , Element.centerX
1891 , Element.centerY
2252 , Element.centerY
1892 , Element.moveUp 1
2253 , Element.moveUp 1
2254+ , Element.transparent (not checked)
1893 , Border.widthEach
2255 , Border.widthEach
1894 { top = 0
2256 { top = 0
1895 , left = 2
2257 , left = 2
@@ -1898,7 +2260,6 @@ defaultCheckbox checked =
1900 Element.none
2262 Element.none
1903− Element.none
2265+ Element.none