@@ -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
321+ Element.rgb255 238 238 238
163− Input.button []
324+ Input.button
325+ [ Background.color blue
327+ [ Background.color purple ]
164− { onPress = ClickMsg
329+ { onPress = Just ClickMsg
165− , label = text "My Button"
330+ , label = text "My Button"
168−`onPress` takes a `Maybe msg`. If you provide the value `Nothing`, then the button will be disabled.
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
@@ -669,304 +855,198 @@ type alias Text msg =
673− = Padding Int Int Int Int
676−inheritablePlaceholderAttributes attr =
678− Internal.StyleClass _ (Internal.PaddingStyle _ _ _ _ _) ->
681− Internal.StyleClass _ (Internal.BorderWidth _ _ _ _ _) ->
684− Internal.StyleClass _ (Internal.Transform _) ->
696− attribute::width/height fill
697− attribtue::alignment
699− attribute::fontsize/family/lineheight
701− attribute::nearby(placeholder)
702− attribute::width/height fill
710− special height for height-content
712− attribute::lineHeight
717textHelper : TextInput -> List (Attribute msg) -> Text msg -> Element msg
859textHelper : TextInput -> List (Attribute msg) -> Text msg -> Element msg
718textHelper textInput attrs textOptions =
860textHelper textInput attrs textOptions =
720− attributes =
862+ withDefaults =
721 defaultTextBoxStyle ++ attrs
863 defaultTextBoxStyle ++ attrs
723− behavior =
865+ redistributed =
724− [ Internal.Attr (Html.Events.onInput textOptions.onChange) ]
866+ redistribute (textInput.type_ == TextArea)
867+ (isStacked textOptions.label)
726− forPlaceholder =
870+ onlySpacing attr =
727− List.filter inheritablePlaceholderAttributes attributes
730− List.filter (not << forNearby) attributes
733 case attr of
871 case attr of
734− Internal.Nearby _ _ ->
872+ Internal.StyleClass _ (Internal.SpacingStyle _ _ _) ->
740− ( inputNode, inputAttrs, inputChildren ) =
878+ getPadding attr =
880+ Internal.StyleClass cls (Internal.PaddingStyle pad t r b l) ->
881+ -- The - 3 is here to prevent accidental triggering of scrollbars
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)
885+ { top = max 0 (floor (t - 3))
886+ , right = max 0 (floor (r - 3))
887+ , bottom = max 0 (floor (b - 3))
888+ , left = max 0 (floor (l - 3))
741 case textInput.type_ of
895 case textInput.type_ of
742 TextInputNode inputType ->
896 TextInputNode inputType ->
744− , [ Internal.Attr (Html.Attributes.type_ inputType)
745− , value textOptions.text
746− , spellcheck textInput.spellchecked
747− , Internal.htmlClass classes.inputText
748− , case textInput.autofill of
750− Internal.NoAttribute
759 TextArea ->
899 TextArea ->
760− let
900+ withDefaults
761− { maybePadding, heightContent, maybeSpacing, adjustedAttributes, maybeBorder } =
901+ |> List.filterMap getHeight
762− (value textOptions.text :: attributes)
763− |> List.foldr
902+ |> List.reverse
764− (\attr found ->
903+ |> List.head
765− case attr of
904+ |> Maybe.map isConstrained
766− Internal.Describe _ ->
769− Internal.Height len ->
770− case found.heightContent of
773− | heightContent = Just (Internal.isContent len)
774− , adjustedAttributes = attr :: found.adjustedAttributes
780− Internal.StyleClass _ (Internal.BorderWidth _ t r b l) ->
781− case found.maybeBorder of
784− | maybeBorder = Just (Padding t r b l)
785− , adjustedAttributes = attr :: found.adjustedAttributes
791− Internal.StyleClass _ (Internal.PaddingStyle _ t r b l) ->
792− case found.maybePadding of
795− | maybePadding = Just (Padding t r b l)
797− -- Skip storing padding because it's being customized later
798− , adjustedAttributes = found.adjustedAttributes
804− Internal.StyleClass _ (Internal.SpacingStyle _ x y) ->
805− case found.maybeSpacing of
808− | maybeSpacing = Just y
809− , adjustedAttributes = attr :: found.adjustedAttributes
816− { found | adjustedAttributes = attr :: found.adjustedAttributes }
818− { maybePadding = Nothing
819− , heightContent = Nothing
820− , maybeBorder = Nothing
821− , maybeSpacing = Nothing
822− , adjustedAttributes = []
825− -- NOTE: This is where default text spacing is set
827− Maybe.withDefault 5 maybeSpacing
830− , [ spellcheck textInput.spellchecked
831− , Internal.htmlClass classes.inputMultiline
832− , Maybe.map autofill textInput.autofill
833− |> Maybe.withDefault Internal.NoAttribute
905+ |> Maybe.withDefault False
834− , case maybePadding of
906+
835− Nothing ->
907+ parentPadding =
836− Internal.NoAttribute
908+ withDefaults
837−
909+ |> List.filterMap getPadding
838− Just (Padding t r b l) ->
910+ |> List.reverse
839− Element.paddingEach
911+ |> List.head
912+ |> Maybe.withDefault
840− { top = max 0 (t - (spacing // 2))
913+ { top = 0
841− , bottom = max 0 (b - (spacing // 2))
915+ , bottom = 0
842− , left = l
916+ , left = 0
845− , case heightContent of
847− Internal.NoAttribute
850− textHeightContent textOptions.text spacing maybePadding maybeBorder
853− Internal.NoAttribute
855− ++ adjustedAttributes
856− , [ Internal.unstyled (Html.text textOptions.text) ]
859− heightFillFromChild =
860− Internal.get attributes <|
863− Internal.Height (Internal.Fill _) ->
869− attributesFromChild =
870− Internal.get attributes <|
873− Internal.Width (Internal.Fill _) ->
876− Internal.Height (Internal.Fill _) ->
879− Internal.AlignX _ ->
882− Internal.AlignY _ ->
885− Internal.StyleClass _ (Internal.SpacingStyle _ _ _) ->
888− Internal.StyleClass _ (Internal.FontSize _) ->
891− Internal.StyleClass _ (Internal.FontFamily _ _) ->
898− Internal.get attributes <|
901− Internal.Nearby _ _ ->
908− Internal.get attributes <|
911− Internal.StyleClass _ (Internal.PaddingStyle _ _ _ _ _) ->
917 inputElement =
919 inputElement =
918 Internal.element
920 Internal.element
919 Internal.asEl
921 Internal.asEl
920− Internal.div
922+ (case textInput.type_ of
921− (Element.width Element.fill
923+ TextInputNode inputType ->
922− :: List.concat
924+ Internal.NodeName "input"
924− , heightFillFromChild
926+ TextArea ->
925− , case textOptions.placeholder of
927+ Internal.NodeName "textarea"
927− []
929+ ((case textInput.type_ of
928−
930+ TextInputNode inputType ->
929− Just (Placeholder placeholderAttrs placeholderEl) ->
931+ -- Note: Due to a weird edgecase in...Edge...
930− [ Element.inFront
932+ -- `type` needs to come _before_ `value`
931− (Element.el
933+ -- More reading: https://github.com/mdgriffith/elm-ui/pull/94/commits/4f493a27001ccc3cf1f2baa82e092c35d3811876
932− (defaultTextPadding
934+ [ Internal.Attr (Html.Attributes.type_ inputType)
934− ++ [ Font.color charcoal
935− , Internal.htmlClass (classes.noTextSelection ++ " " ++ classes.passPointerEvents)
935+ , Internal.htmlClass classes.inputText
936− , Border.color (Element.rgba 0 0 0 0)
937− , Background.color (Element.rgba 0 0 0 0)
938− , Element.height Element.fill
939− , Element.width Element.fill
941− (if textOptions.text == "" then
940+ , Element.height Element.fill
941+ , Internal.htmlClass classes.inputMultiline
942+ , calcMoveToCompensateForPadding withDefaults
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
959+ ++ redistributed.input
955− (Internal.Unkeyed
961+ (Internal.Unkeyed [])
964+ case textInput.type_ of
966+ -- textarea with height-content means that
967+ -- the input element is rendered `inFront` with a transparent background
968+ -- Then the input text is rendered as the space filling element.
956− [ Internal.element
969+ Internal.element
957 Internal.asEl
970 Internal.asEl
958− (Internal.NodeName inputNode)
971+ Internal.div
959− (List.concat
972+ ((if heightConstrained then
960− [ [ focusDefault attrs
973+ (::) Element.scrollbarY
961− , hiddenLabelAttribute textOptions.label
974+
963− , inputAttrs
976+ identity
978+ [ Element.width Element.fill
979+ , if List.any hasFocusStyle withDefaults then
980+ Internal.NoAttribute
983+ Internal.htmlClass classes.focusedWithin
984+ , Internal.htmlClass classes.inputMultilineWrapper
986+ ++ redistributed.parent
967− (Internal.Unkeyed inputChildren)
988+ (Internal.Unkeyed
990+ Internal.asParagraph
992+ (Element.width Element.fill
993+ :: Element.height Element.fill
994+ :: Element.inFront inputElement
995+ :: Internal.htmlClass classes.inputMultilineParent
996+ :: redistributed.wrapper
999+ (if textOptions.text == "" then
1000+ case textOptions.placeholder of
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}"
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}")
1026+ TextInputNode inputType ->
1030+ (Element.width Element.fill
1031+ :: (if List.any hasFocusStyle withDefaults then
1032+ Internal.NoAttribute
1035+ Internal.htmlClass classes.focusedWithin
1038+ [ redistributed.parent
1039+ , case textOptions.placeholder of
1044+ [ Element.behindContent
1045+ (renderPlaceholder place redistributed.cover (textOptions.text == ""))
1049+ (Internal.Unkeyed [ inputElement ])
971 applyLabel
1051 applyLabel
972 (Internal.Class Flag.cursor classes.cursorText
1052 (Internal.Class Flag.cursor classes.cursorText
@@ -978,45 +1058,348 @@ textHelper textInput attrs textOptions =
980 :: Region.announce
1060 :: Region.announce
981− :: attributesFromChild
1061+ :: redistributed.fullParent
983 textOptions.label
1063 textOptions.label
984− inputElement
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
987−textHeightContent textValue spacing maybePadding maybeBorder =
1084+renderBox { top, right, bottom, left } =
1087+ ++ String.fromInt right
1089+ ++ String.fromInt bottom
1091+ ++ String.fromInt left
1095+renderPlaceholder (Placeholder placeholderAttrs placeholderEl) forPlaceholder on =
1098+ ++ [ Font.color charcoal
1099+ , Internal.htmlClass (classes.noTextSelection ++ " " ++ classes.passPointerEvents)
1101+ , Border.color (Element.rgba 0 0 0 0)
1102+ , Background.color (Element.rgba 0 0 0 0)
1103+ , Element.height Element.fill
1104+ , Element.width Element.fill
1113+ ++ placeholderAttrs
1118+{-| Because textareas are now shadowed, where they're rendered twice,
1119+we to move the literal text area up because spacing is based on line height.
1121+calcMoveToCompensateForPadding : List (Attribute msg) -> Attribute msg
1122+calcMoveToCompensateForPadding attrs =
989− newlineCount =
1124+ gatherSpacing attr found =
990− String.lines textValue
1125+ case attr of
991− |> List.length
1126+ Internal.StyleClass _ (Internal.SpacingStyle _ x y) ->
992− |> (\x ->
1127+ case found of
993− if x < 1 then
1128+ Nothing ->
1000− topBottom (Padding t _ b _) =
1135+ found
1003− additionalSpacing =
1004− ((newlineCount - 1) * spacing)
1005− + Maybe.withDefault 0 (Maybe.map topBottom maybePadding)
1006− + Maybe.withDefault 0 (Maybe.map topBottom maybeBorder)
1008− heightValue count =
1010− ++ String.fromInt count
1012− ++ String.fromInt additionalSpacing
1013− ++ "px) !important"
1137+ case List.foldr gatherSpacing Nothing attrs of
1139+ Internal.NoAttribute
1142+ Element.moveUp (toFloat (floor (toFloat vSpace / 2)))
1145+{-| Given the list of attributes provided to `Input.multiline` or `Input.text`,
1147+redistribute them to the parent, the input, or the cover.
1149+ - fullParent -> Wrapper around label and input
1150+ - parent -> parent of wrapper
1151+ - wrapper -> the element that is here to take up space.
1152+ - cover -> things like placeholders or text areas which are layered on top of input.
1153+ - input -> actual input element
1159+ -> List (Attribute msg)
1161+ { fullParent : List (Attribute msg)
1162+ , parent : List (Attribute msg)
1163+ , wrapper : List (Attribute msg)
1164+ , input : List (Attribute msg)
1165+ , cover : List (Attribute msg)
1167+redistribute isMultiline stacked attrs =
1168+ List.foldl (redistributeOver isMultiline stacked)
1177+ { parent = List.reverse redist.parent
1178+ , fullParent = List.reverse redist.fullParent
1179+ , wrapper = List.reverse redist.wrapper
1180+ , input = List.reverse redist.input
1181+ , cover = List.reverse redist.cover
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
1260+redistributeOver isMultiline stacked attr els =
1262+ Internal.Nearby _ _ ->
1263+ { els | parent = attr :: els.parent }
1265+ Internal.Width width ->
1266+ if isFill width then
1268+ | fullParent = attr :: els.fullParent
1269+ , parent = attr :: els.parent
1270+ , input = attr :: els.input
1273+ else if stacked then
1275+ | fullParent = attr :: els.fullParent
1280+ | parent = attr :: els.parent
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
1297+ { els | parent = attr :: els.parent }
1301+ | parent = attr :: els.parent
1304+ Internal.AlignX _ ->
1305+ { els | fullParent = attr :: els.fullParent }
1307+ Internal.AlignY _ ->
1308+ { els | fullParent = attr :: els.fullParent }
1310+ Internal.StyleClass _ (Internal.SpacingStyle _ _ _) ->
1312+ | fullParent = attr :: els.fullParent
1313+ , parent = attr :: els.parent
1314+ , input = attr :: els.input
1315+ , wrapper = attr :: els.wrapper
1318+ Internal.StyleClass cls (Internal.PaddingStyle pad t r b l) ->
1319+ if isMultiline then
1321+ | parent = attr :: els.parent
1322+ , cover = attr :: els.cover
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 =
1015− Internal.StyleClass Flag.heightTextAreaContent
1348+ Internal.StyleClass Flag.padding
1016− (Internal.Single ("textarea-height-" ++ String.fromInt newlineCount)
1349+ (Internal.PaddingStyle
1017− "height"
1350+ (Internal.paddingNameFloat
1018− (heightValue newlineCount)
1351+ newTop
1363+ | parent = reducedVerticalPadding :: els.parent
1364+ , input = newHeight :: newLineHeight :: els.input
1365+ , cover = attr :: els.cover
1368+ Internal.StyleClass _ (Internal.BorderWidth _ _ _ _ _) ->
1370+ | parent = attr :: els.parent
1371+ , cover = attr :: els.cover
1374+ Internal.StyleClass _ (Internal.Transform _) ->
1376+ | parent = attr :: els.parent
1377+ , cover = attr :: els.cover
1380+ Internal.StyleClass _ (Internal.FontSize _) ->
1381+ { els | fullParent = attr :: els.fullParent }
1383+ Internal.StyleClass _ (Internal.FontFamily _ _) ->
1384+ { els | fullParent = attr :: els.fullParent }
1386+ Internal.StyleClass flag cls ->
1387+ { els | parent = attr :: els.parent }
1389+ Internal.NoAttribute ->
1393+ { els | input = attr :: els.input }
1395+ Internal.Describe _ ->
1396+ { els | input = attr :: els.input }
1398+ Internal.Class _ _ ->
1399+ { els | parent = attr :: els.parent }
1401+ Internal.TransformComponent _ _ ->
1402+ { els | input = attr :: els.input }
@@ -1078,7 +1461,7 @@ search =
1079It'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.
1081−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`)
1084newPassword :
1467newPassword :
@@ -1178,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.
1182multiline :
1571multiline :
1183 List (Attribute msg)
1572 List (Attribute msg)
@@ -1239,33 +1628,32 @@ applyLabel attrs label input =
1239 Internal.element
1628 Internal.element
1240 Internal.asColumn
1629 Internal.asColumn
1241 (Internal.NodeName "label")
1630 (Internal.NodeName "label")
1242− attrs
1631+ (Internal.htmlClass classes.inputLabel :: attrs)
1243 (Internal.Unkeyed [ labelElement, input ])
1632 (Internal.Unkeyed [ labelElement, input ])
1245 Below ->
1634 Below ->
1246 Internal.element
1635 Internal.element
1247 Internal.asColumn
1636 Internal.asColumn
1248 (Internal.NodeName "label")
1637 (Internal.NodeName "label")
1249− attrs
1638+ (Internal.htmlClass classes.inputLabel :: attrs)
1250 (Internal.Unkeyed [ input, labelElement ])
1639 (Internal.Unkeyed [ input, labelElement ])
1252 OnRight ->
1641 OnRight ->
1253 Internal.element
1642 Internal.element
1254 Internal.asRow
1643 Internal.asRow
1255 (Internal.NodeName "label")
1644 (Internal.NodeName "label")
1256− attrs
1645+ (Internal.htmlClass classes.inputLabel :: attrs)
1257 (Internal.Unkeyed [ input, labelElement ])
1646 (Internal.Unkeyed [ input, labelElement ])
1259 OnLeft ->
1648 OnLeft ->
1260 Internal.element
1649 Internal.element
1261 Internal.asRow
1650 Internal.asRow
1262 (Internal.NodeName "label")
1651 (Internal.NodeName "label")
1263− attrs
1652+ (Internal.htmlClass classes.inputLabel :: attrs)
1264 (Internal.Unkeyed [ labelElement, input ])
1653 (Internal.Unkeyed [ labelElement, input ])
1267−{-| Add choices to your radio and select menus.
1269type Option value msg
1657type Option value msg
1270 = Option value (OptionState -> Element msg)
1658 = Option value (OptionState -> Element msg)
@@ -1277,35 +1665,21 @@ type OptionState
1277 | Selected
1665 | Selected
1668+{-| Add a choice to your radio element. This will be rendered with the default radio icon.
1281option : value -> Element msg -> Option value msg
1670option : value -> Element msg -> Option value msg
1282option val txt =
1671option val txt =
1283 Option val (defaultRadioOption txt)
1672 Option val (defaultRadioOption txt)
1675+{-| Customize exactly what your radio option should look like in different states.
1287optionWith : value -> (OptionState -> Element msg) -> Option value msg
1677optionWith : value -> (OptionState -> Element msg) -> Option value msg
1288optionWith val view =
1678optionWith val view =
1289 Option val view
1679 Option val view
1298− { onChange = ChooseLunch
1299− , selected = Just model.lunch
1300− , label = Input.labelAbove [] (text "Lunch")
1302− [ Input.option Burrito (text "Burrito")
1303− , Input.option Taco (text "Taco!")
1304− , Input.option Gyro (text "Gyro")
1310 List (Attribute msg)
1684 List (Attribute msg)
@@ -1731,7 +2105,11 @@ onKeyLookup lookup =
1731 Json.field "key" Json.string
2105 Json.field "key" Json.string
1732 |> 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
1734− Internal.Attr <| Html.Events.on "keyup" isKey
2110+ Internal.Attr <|
2111+ Html.Events.preventDefaultOn "keydown"
2112+ (Json.map (\fired -> ( fired, True )) isKey)
@@ -1746,17 +2124,6 @@ onFocusIn msg =
1746 Internal.Attr <| Html.Events.on "focusin" (Json.succeed msg)
2124 Internal.Attr <| Html.Events.on "focusin" (Json.succeed msg)
1749−type_ : String -> Attribute msg
1751− Internal.Attr << Html.Attributes.type_
1755−-- checked : Bool -> Attribute msg
1757−-- Internal.Attr << Html.Attributes.checked
1760selected : Bool -> Attribute msg
2127selected : Bool -> Attribute msg
1761selected =
2128selected =
1762 Internal.Attr << Html.Attributes.selected
2129 Internal.Attr << Html.Attributes.selected
@@ -1797,7 +2164,11 @@ autofill =
1797 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.
1801focusedOnLoad : Attribute msg
2172focusedOnLoad : Attribute msg
1802focusedOnLoad =
2173focusedOnLoad =
1803 Internal.Attr <| Html.Attributes.autofocus True
2174 Internal.Attr <| Html.Attributes.autofocus True
@@ -1814,8 +2185,9 @@ defaultTextBoxStyle =
1814 , Border.color darkGrey
2185 , Border.color darkGrey
1815 , Background.color white
2186 , Background.color white
1816 , Border.width 1
2187 , Border.width 1
1817− , Element.spacing 3
2188+ , Element.spacing 5
1818 , Element.width Element.fill
2189 , Element.width Element.fill
2190+ , Element.height Element.shrink
@@ -1824,12 +2196,17 @@ defaultTextPadding =
1824 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.
1828defaultCheckbox : Bool -> Element msg
2204defaultCheckbox : Bool -> Element msg
1829defaultCheckbox checked =
2205defaultCheckbox checked =
1830 Element.el
2206 Element.el
1831 [ Internal.htmlClass "focusable"
2207 [ Internal.htmlClass "focusable"
1832− , Element.width (Element.px 14)
2208+ , Element.width
1833 , Element.height (Element.px 14)
2210 , Element.height (Element.px 14)
1834 , Font.color white
2211 , Font.color white
1835 , Element.centerY
2212 , Element.centerY
@@ -1842,7 +2219,7 @@ defaultCheckbox checked =
1844 Element.rgb (211 / 255) (211 / 255) (211 / 255)
2221 Element.rgb (211 / 255) (211 / 255) (211 / 255)
1845− , Border.shadow <|
2222+ , Border.shadow
1846 { offset = ( 0, 0 )
2223 { offset = ( 0, 0 )
1847 , blur = 1
2224 , blur = 1
1848 , size = 1
2225 , size = 1
@@ -1865,9 +2242,8 @@ defaultCheckbox checked =
1868− ]
2245+ , Element.inFront
1870− Element.el
2246+ (Element.el
1871 [ Border.color white
2247 [ Border.color white
1872 , Element.height (Element.px 6)
2248 , Element.height (Element.px 6)
1873 , Element.width (Element.px 9)
2249 , Element.width (Element.px 9)
@@ -1875,6 +2251,7 @@ defaultCheckbox checked =
1875 , Element.centerX
2251 , Element.centerX
1876 , Element.centerY
2252 , Element.centerY
1877 , Element.moveUp 1
2253 , Element.moveUp 1
2254+ , Element.transparent (not checked)
1878 , Border.widthEach
2255 , Border.widthEach
1879 { top = 0
2256 { top = 0
1880 , left = 2
2257 , left = 2
@@ -1883,7 +2260,6 @@ defaultCheckbox checked =
1885 Element.none
2262 Element.none
1888− Element.none
2265+ Element.none