git.iko.soy/typeable/elm-ui
on dee2fda help sign in
typeable/elm-ui/src/Element.elm
1595 lines 40 kB history raw
1module Element exposing
2 ( Element, none, text, el
3 , row, wrappedRow, column
4 , paragraph, textColumn
5 , Column, table, IndexedColumn, indexedTable
6 , Attribute, width, height, Length, px, shrink, fill, fillPortion, maximum, minimum
7 , explain
8 , padding, paddingXY, paddingEach
9 , spacing, spacingXY, spaceEvenly
10 , centerX, centerY, alignLeft, alignRight, alignTop, alignBottom
11 , transparent, alpha, pointer
12 , moveUp, moveDown, moveRight, moveLeft, rotate, scale
13 , clip, clipX, clipY
14 , scrollbars, scrollbarX, scrollbarY
15 , layout, layoutWith, Option, noStaticStyleSheet, forceHover, noHover, focusStyle, FocusStyle
16 , link, newTabLink, download, downloadAs
17 , image
18 , Color, rgba, rgb, rgb255, rgba255, fromRgb, fromRgb255, toRgb
19 , above, below, onRight, onLeft, inFront, behindContent
20 , Attr, Decoration, mouseOver, mouseDown, focused
21 , Device, DeviceClass(..), Orientation(..), classifyDevice
22 , modular
23 , map, mapAttribute
24 , html, htmlAttribute
25 )
27{-|
30# Basic Elements
32@docs Element, none, text, el
35# Rows and Columns
37When we want more than one child on an element, we want to be _specific_ about how they will be layed out.
39So, the common ways to do that would be `row` and `column`.
41@docs row, wrappedRow, column
44# Text Layout
46Text layout needs some specific considerations.
48@docs paragraph, textColumn
51# Data Table
53@docs Column, table, IndexedColumn, indexedTable
56# Size
58@docs Attribute, width, height, Length, px, shrink, fill, fillPortion, maximum, minimum
61# Debugging
63@docs explain
66# Padding and Spacing
68There's no concept of margin in `elm-ui`, instead we have padding and spacing.
70Padding is the distance between the outer edge and the content, and spacing is the space between children.
72So, if we have the following row, with some padding and spacing.
74 Element.row [ padding 10, spacing 7 ]
75 [ Element.el [] none
76 , Element.el [] none
77 , Element.el [] none
78 ]
80Here's what we can expect:
82![Three boxes spaced 7 pixels apart. There's a 10 pixel distance from the edge of the parent to the boxes.](https://mdgriffith.gitbooks.io/style-elements/content/assets/spacing-400.png)
84**Note** `spacing` set on a `paragraph`, will set the pixel spacing between lines.
86@docs padding, paddingXY, paddingEach
88@docs spacing, spacingXY, spaceEvenly
91# Alignment
93Alignment can be used to align an `Element` within another `Element`.
95 Element.el [ centerX, alignTop ] (text "I'm centered and aligned top!")
97If alignment is set on elements in a layout such as `row`, then the element will push the other elements in that direction. Here's an example.
99 Element.row []
100 [ Element.el [] Element.none
101 , Element.el [ alignLeft ] Element.none
102 , Element.el [ centerX ] Element.none
103 , Element.el [ alignRight ] Element.none
104 ]
106will result in a layout like
108 |-|-| |-| |-|
110Where there are two elements on the left, one in the center, and one on the right.
112@docs centerX, centerY, alignLeft, alignRight, alignTop, alignBottom
115# Transparency
117@docs transparent, alpha, pointer
120# Adjustment
122@docs moveUp, moveDown, moveRight, moveLeft, rotate, scale
125# Clipping and Scrollbars
127Clip the content if it overflows.
129@docs clip, clipX, clipY
131Add a scrollbar if the content is larger than the element.
133@docs scrollbars, scrollbarX, scrollbarY
136# Rendering
138@docs layout, layoutWith, Option, noStaticStyleSheet, forceHover, noHover, focusStyle, FocusStyle
141# Links
143@docs link, newTabLink, download, downloadAs
146# Images
148@docs image
151# Color
153In order to use attributes like `Font.color` and `Background.color`, you'll need to make some colors!
155@docs Color, rgba, rgb, rgb255, rgba255, fromRgb, fromRgb255, toRgb
158# Nearby Elements
160Let's say we want a dropdown menu. Essentially we want to say: _put this element below this other element, but don't affect the layout when you do_.
162 Element.row []
163 [ Element.el
164 [ Element.below (Element.text "I'm below!")
165 ]
166 (Element.text "I'm normal!")
167 ]
169This will result in
171 |- I'm normal! -|
172 I'm below
174Where `"I'm Below"` doesn't change the size of `Element.row`.
176This is very useful for things like dropdown menus or tooltips.
178@docs above, below, onRight, onLeft, inFront, behindContent
181# Temporary Styling
183@docs Attr, Decoration, mouseOver, mouseDown, focused
186# Responsiveness
188The main technique for responsiveness is to store window size information in your model.
190Install the `Browser` package, and set up a subscription for [`Browser.Events.onResize`](https://package.elm-lang.org/packages/elm/browser/latest/Browser-Events#onResize).
192You'll also need to retrieve the initial window size. You can either run the following task:
194 Task.perform (\size -> WindowResize { width = size.width, height = size.height }) Window.size
196Or pass in `window.innerWidth` and `window.innerHeight` as flags to your program, which is the preferred way. This requires minor setup on the JS side, but allows you to avoid the state where you don't have window info.
198@docs Device, DeviceClass, Orientation, classifyDevice
201# Scaling
203@docs modular
206## Mapping
208@docs map, mapAttribute
211## Compatibility
213@docs html, htmlAttribute
215-}
217import Html exposing (Html)
218import Html.Attributes
219import Internal.Flag as Flag exposing (Flag)
220import Internal.Model as Internal
221import Internal.Style exposing (classes)
224{-| -}
225type alias Color =
226 Internal.Color
229{-| Provide the red, green, and blue channels for the color.
231Each channel takes a value between 0 and 1.
233-}
234rgb : Float -> Float -> Float -> Color
235rgb r g b =
236 Internal.Rgba r g b 1
239{-| -}
240rgba : Float -> Float -> Float -> Float -> Color
241rgba =
242 Internal.Rgba
245{-| Provide the red, green, and blue channels for the color.
247Each channel takes a value between 0 and 255.
249-}
250rgb255 : Int -> Int -> Int -> Color
251rgb255 red green blue =
252 Internal.Rgba
253 (toFloat red / 255)
254 (toFloat green / 255)
255 (toFloat blue / 255)
256 1
259{-| -}
260rgba255 : Int -> Int -> Int -> Float -> Color
261rgba255 red green blue a =
262 Internal.Rgba
263 (toFloat red / 255)
264 (toFloat green / 255)
265 (toFloat blue / 255)
266 a
269{-| Create a color from an RGB record.
270-}
271fromRgb :
272 { red : Float
273 , green : Float
274 , blue : Float
275 , alpha : Float
276 }
277 -> Color
278fromRgb clr =
279 Internal.Rgba
280 clr.red
281 clr.green
282 clr.blue
283 clr.alpha
286{-| -}
287fromRgb255 :
288 { red : Int
289 , green : Int
290 , blue : Int
291 , alpha : Float
292 }
293 -> Color
294fromRgb255 clr =
295 Internal.Rgba
296 (toFloat clr.red / 255)
297 (toFloat clr.green / 255)
298 (toFloat clr.blue / 255)
299 clr.alpha
302{-| Deconstruct a `Color` into it's rgb channels.
303-}
304toRgb :
305 Color
306 ->
307 { red : Float
308 , green : Float
309 , blue : Float
310 , alpha : Float
311 }
312toRgb (Internal.Rgba r g b a) =
313 { red = r
314 , green = g
315 , blue = b
316 , alpha = a
317 }
320{-| The basic building block of your layout.
322 howdy : Element msg
323 howdy =
324 Element.el [] (Element.text "Howdy!")
326-}
327type alias Element msg =
328 Internal.Element msg
331{-| An attribute that can be attached to an `Element`
332-}
333type alias Attribute msg =
334 Internal.Attribute () msg
337{-| This is a special attribute that counts as both a `Attribute msg` and a `Decoration`.
338-}
339type alias Attr decorative msg =
340 Internal.Attribute decorative msg
343{-| Only decorations
344-}
345type alias Decoration =
346 Internal.Attribute Never Never
349{-| -}
350html : Html msg -> Element msg
351html =
352 Internal.unstyled
355{-| -}
356htmlAttribute : Html.Attribute msg -> Attribute msg
357htmlAttribute =
358 Internal.Attr
361{-| -}
362map : (msg -> msg1) -> Element msg -> Element msg1
363map =
364 Internal.map
367{-| -}
368mapAttribute : (msg -> msg1) -> Attribute msg -> Attribute msg1
369mapAttribute =
370 Internal.mapAttr
373{-| -}
374type alias Length =
375 Internal.Length
378{-| -}
379px : Int -> Length
380px =
381 Internal.Px
384{-| Shrink an element to fit its contents.
385-}
386shrink : Length
387shrink =
388 Internal.Content
391{-| Fill the available space. The available space will be split evenly between elements that have `width fill`.
392-}
393fill : Length
394fill =
395 Internal.Fill 1
398{-| Similarly you can set a minimum boundary.
400 el
401 [ height
402 (fill
403 |> maximum 300
404 |> minimum 30
405 )
407 ]
408 (text "I will stop at 300px")
410-}
411minimum : Int -> Length -> Length
412minimum i l =
413 Internal.Min i l
416{-| Add a maximum to a length.
418 el
419 [ height
420 (fill
421 |> maximum 300
422 )
423 ]
424 (text "I will stop at 300px")
426-}
427maximum : Int -> Length -> Length
428maximum i l =
429 Internal.Max i l
432{-| Sometimes you may not want to split available space evenly. In this case you can use `fillPortion` to define which elements should have what portion of the available space.
434So, two elements, one with `width (fillPortion 2)` and one with `width (fillPortion 3)`. The first would get 2 portions of the available space, while the second would get 3.
436**Also:** `fill == fillPortion 1`
438-}
439fillPortion : Int -> Length
440fillPortion =
441 Internal.Fill
444{-| This is your top level node where you can turn `Element` into `Html`.
445-}
446layout : List (Attribute msg) -> Element msg -> Html msg
447layout =
448 layoutWith { options = [] }
451{-| -}
452layoutWith : { options : List Option } -> List (Attribute msg) -> Element msg -> Html msg
453layoutWith { options } attrs child =
454 Internal.renderRoot options
455 (Internal.htmlClass
456 (String.join " "
457 [ classes.root
458 , classes.any
459 , classes.single
461 -- , classes.contentCenterX
462 -- , classes.contentCenterY
463 ]
464 )
465 :: (Internal.rootStyle ++ attrs)
466 )
467 child
470{-| -}
471type alias Option =
472 Internal.Option
475{-| Elm UI embeds two StyleSheets, one that is constant, and one that changes dynamically based on styles collected from the elments being rendered.
477This option will stop the static/constant stylesheet from rendering.
479Make sure to render the constant/static stylesheet at least once on your page!
481-}
482noStaticStyleSheet : Option
483noStaticStyleSheet =
484 Internal.RenderModeOption Internal.NoStaticStyleSheet
487{-| -}
488defaultFocus :
489 { borderColor : Maybe Color
490 , backgroundColor : Maybe Color
491 , shadow :
492 Maybe
493 { color : Color
494 , offset : ( Int, Int )
495 , blur : Int
496 , size : Int
497 }
498 }
499defaultFocus =
500 Internal.focusDefaultStyle
503{-| -}
504type alias FocusStyle =
505 { borderColor : Maybe Color
506 , backgroundColor : Maybe Color
507 , shadow :
508 Maybe
509 { color : Color
510 , offset : ( Int, Int )
511 , blur : Int
512 , size : Int
513 }
514 }
517{-| -}
518focusStyle : FocusStyle -> Option
519focusStyle =
520 Internal.FocusStyleOption
523{-| Disable all `mouseOver` styles.
524-}
525noHover : Option
526noHover =
527 Internal.HoverOption Internal.NoHover
530{-| Any `hover` styles, aka attributes with `mouseOver` in the name, will be always turned on.
532This is useful for when you're targeting a platform that has no mouse, such as mobile.
534-}
535forceHover : Option
536forceHover =
537 Internal.HoverOption Internal.ForceHover
540{-| When you want to render exactly nothing.
541-}
542none : Element msg
543none =
544 Internal.Empty
547{-| Create some plain text.
549 text "Hello, you stylish developer!"
551**Note** text does not wrap by default. In order to get text to wrap, check out `paragraph`!
553-}
554text : String -> Element msg
555text content =
556 Internal.Text content
559{-| The basic building block of your layout.
561You can think of an `el` as a `div`, but it can only have one child.
563If you want multiple children, you'll need to use something like `row` or `column`
565 import Element exposing (Element, rgb)
566 import Element.Background as Background
567 import Element.Border as Border
569 myElement : Element msg
570 myElement =
571 Element.el
572 [ Background.color (rgb 0 0.5 0)
573 , Border.color (rgb 0 0.7 0)
574 ]
575 (Element.text "You've made a stylish element!")
577-}
578el : List (Attribute msg) -> Element msg -> Element msg
579el attrs child =
580 Internal.element
581 Internal.asEl
582 Internal.div
583 (width shrink
584 :: height shrink
585 :: attrs
586 )
587 (Internal.Unkeyed [ child ])
590{-| -}
591row : List (Attribute msg) -> List (Element msg) -> Element msg
592row attrs children =
593 Internal.element
594 Internal.asRow
595 Internal.div
596 (Internal.htmlClass (classes.contentLeft ++ " " ++ classes.contentCenterY)
597 :: width shrink
598 :: height shrink
599 :: attrs
600 )
601 (Internal.Unkeyed children)
604{-| -}
605column : List (Attribute msg) -> List (Element msg) -> Element msg
606column attrs children =
607 Internal.element
608 Internal.asColumn
609 Internal.div
610 (Internal.htmlClass
611 (classes.contentTop
612 ++ " "
613 ++ classes.contentLeft
614 )
615 :: height shrink
616 :: width shrink
617 :: attrs
618 )
619 (Internal.Unkeyed children)
622{-| Same as `row`, but will wrap if it takes up too much horizontal space.
623-}
624wrappedRow : List (Attribute msg) -> List (Element msg) -> Element msg
625wrappedRow attrs children =
626 let
627 ( padded, spaced ) =
628 Internal.extractSpacingAndPadding attrs
629 in
630 case spaced of
631 Nothing ->
632 Internal.element
633 Internal.asRow
634 Internal.div
635 (Internal.htmlClass
636 (classes.contentLeft
637 ++ " "
638 ++ classes.contentCenterY
639 ++ " "
640 ++ classes.wrapped
641 )
642 :: width shrink
643 :: height shrink
644 :: attrs
645 )
646 (Internal.Unkeyed children)
648 Just (Internal.Spaced spaceName x y) ->
649 let
650 newPadding =
651 case padded of
652 Just (Internal.Padding name t r b l) ->
653 if r >= x && b >= y then
654 Just <|
655 Internal.StyleClass
656 Flag.padding
657 (Internal.PaddingStyle
658 (Internal.paddingName t
659 (r - x)
660 (b - y)
661 l
662 )
663 t
664 (r - x)
665 (b - y)
666 l
667 )
669 else
670 Nothing
672 Nothing ->
673 Nothing
674 in
675 case newPadding of
676 Just pad ->
677 Internal.element
678 Internal.asRow
679 Internal.div
680 (Internal.htmlClass
681 (classes.contentLeft
682 ++ " "
683 ++ classes.contentCenterY
684 ++ " "
685 ++ classes.wrapped
686 )
687 :: width shrink
688 :: height shrink
689 :: attrs
690 ++ [ pad ]
691 )
692 (Internal.Unkeyed children)
694 Nothing ->
695 -- Not enough space in padding to compensate for spacing
696 Internal.element
697 Internal.asEl
698 Internal.div
699 attrs
700 (Internal.Unkeyed
701 [ Internal.element
702 Internal.asRow
703 Internal.div
704 (Internal.htmlClass
705 (classes.contentLeft
706 ++ " "
707 ++ classes.contentCenterY
708 ++ " "
709 ++ classes.wrapped
710 )
711 :: Internal.Attr
712 (Html.Attributes.style "margin-bottom" (String.fromInt (negate y) ++ "px"))
713 :: width fill
714 :: height fill
715 :: Internal.StyleClass Flag.spacing (Internal.SpacingStyle spaceName x y)
716 :: []
717 )
718 (Internal.Unkeyed children)
719 ]
720 )
723{-| This is just an alias for `Debug.todo`
724-}
725type alias Todo =
726 String -> Never
729{-| Highlight the borders of an element and it's children below. This can really help if you're running into some issue with your layout!
731**Note** This attribute needs to be handed `Debug.todo` in order to work, even though it won't do anything with it. This is a safety measure so you don't accidently ship code with `explain` in it, as Elm won't compile with `--optimize` if you still have a `Debug` statement in your code.
733 el
734 [ Element.explain Debug.todo
735 ]
736 (text "Help! I'm being debugged!")
738-}
739explain : Todo -> Attribute msg
740explain _ =
741 Internal.htmlClass "explain"
744{-| -}
745type alias Column record msg =
746 { header : Element msg
747 , width : Length
748 , view : record -> Element msg
749 }
752{-| Show some tabular data.
754Start with a list of records and specify how each column should be rendered.
756So, if we have a list of `persons`:
758 type alias Person =
759 { firstName : String
760 , lastName : String
761 }
763 persons : List Person
764 persons =
765 [ { firstName = "David"
766 , lastName = "Bowie"
767 }
768 , { firstName = "Florence"
769 , lastName = "Welch"
770 }
771 ]
773We could render it using
775 Element.table []
776 { data = persons
777 , columns =
778 [ { header = Element.text "First Name"
779 , width = fill
780 , view =
781 \person ->
782 Element.text person.firstName
783 }
784 , { header = Element.text "Last Name"
785 , width = fill
786 , view =
787 \person ->
788 Element.text person.lastName
789 }
790 ]
791 }
793**Note:** Sometimes you might not have a list of records directly in your model. In this case it can be really nice to write a function that transforms some part of your model into a list of records before feeding it into `Element.table`.
795-}
796table :
797 List (Attribute msg)
798 ->
799 { data : List records
800 , columns : List (Column records msg)
801 }
802 -> Element msg
803table attrs config =
804 tableHelper attrs
805 { data = config.data
806 , columns =
807 List.map InternalColumn config.columns
808 }
811{-| -}
812type alias IndexedColumn record msg =
813 { header : Element msg
814 , width : Length
815 , view : Int -> record -> Element msg
816 }
819{-| Same as `Element.table` except the `view` for each column will also receive the row index as well as the record.
820-}
821indexedTable :
822 List (Attribute msg)
823 ->
824 { data : List records
825 , columns : List (IndexedColumn records msg)
826 }
827 -> Element msg
828indexedTable attrs config =
829 tableHelper attrs
830 { data = config.data
831 , columns =
832 List.map InternalIndexedColumn config.columns
833 }
836{-| -}
837type alias InternalTable records msg =
838 { data : List records
839 , columns : List (InternalTableColumn records msg)
840 }
843{-| -}
844type InternalTableColumn record msg
845 = InternalIndexedColumn (IndexedColumn record msg)
846 | InternalColumn (Column record msg)
849tableHelper : List (Attribute msg) -> InternalTable data msg -> Element msg
850tableHelper attrs config =
851 let
852 ( sX, sY ) =
853 Internal.getSpacing attrs ( 0, 0 )
855 columnHeader col =
856 case col of
857 InternalIndexedColumn colConfig ->
858 colConfig.header
860 InternalColumn colConfig ->
861 colConfig.header
863 columnWidth col =
864 case col of
865 InternalIndexedColumn colConfig ->
866 colConfig.width
868 InternalColumn colConfig ->
869 colConfig.width
871 maybeHeaders =
872 List.map columnHeader config.columns
873 |> (\headers ->
874 if List.all ((==) Internal.Empty) headers then
875 Nothing
877 else
878 Just (List.indexedMap (\col header -> onGrid 1 (col + 1) header) headers)
879 )
881 template =
882 Internal.StyleClass Flag.gridTemplate <|
883 Internal.GridTemplateStyle
884 { spacing = ( px sX, px sY )
885 , columns = List.map columnWidth config.columns
886 , rows = List.repeat (List.length config.data) Internal.Content
887 }
889 onGrid rowLevel columnLevel elem =
890 Internal.element
891 Internal.asEl
892 Internal.div
893 [ Internal.StyleClass Flag.gridPosition
894 (Internal.GridPosition
895 { row = rowLevel
896 , col = columnLevel
897 , width = 1
898 , height = 1
899 }
900 )
901 ]
902 (Internal.Unkeyed [ elem ])
904 add cell columnConfig cursor =
905 case columnConfig of
906 InternalIndexedColumn col ->
907 { cursor
908 | elements =
909 onGrid cursor.row
910 cursor.column
911 (col.view
912 (if maybeHeaders == Nothing then
913 cursor.row - 1
915 else
916 cursor.row - 2
917 )
918 cell
919 )
920 :: cursor.elements
921 , column = cursor.column + 1
922 }
924 InternalColumn col ->
925 { cursor
926 | elements =
927 onGrid cursor.row cursor.column (col.view cell)
928 :: cursor.elements
929 , column = cursor.column + 1
930 }
932 build columns rowData cursor =
933 let
934 newCursor =
935 List.foldl (add rowData)
936 cursor
937 columns
938 in
939 { newCursor
940 | row = cursor.row + 1
941 , column = 1
942 }
944 children =
945 List.foldl (build config.columns)
946 { elements = []
947 , row =
948 if maybeHeaders == Nothing then
949 1
951 else
952 2
953 , column = 1
954 }
955 config.data
956 in
957 Internal.element
958 Internal.asGrid
959 Internal.div
960 (width fill
961 :: template
962 :: attrs
963 )
964 (Internal.Unkeyed
965 (case maybeHeaders of
966 Nothing ->
967 children.elements
969 Just renderedHeaders ->
970 renderedHeaders ++ children.elements
971 )
972 )
975{-| A paragraph will layout all children as wrapped, inline elements.
977 import Element
978 import Element.Font as Font
980 Element.paragraph []
981 [ text "lots of text ...."
982 , el [ Font.bold ] (text "this is bold")
983 , text "lots of text ...."
984 ]
986This is really useful when you want to markup text by having some parts be bold, or some be links, or whatever you so desire.
988Also, if a child element has `alignLeft` or `alignRight`, then it will be moved to that side and the text will flow around it, (ah yes, `float` behavior).
990This makes it particularly easy to do something like a [dropped capital](https://en.wikipedia.org/wiki/Initial).
992 import Element
993 import Element.Font as Font
995 Element.paragraph []
996 [ el
997 [ alignLeft
998 , padding 5
999 ]
1000 (text "S")
1001 , text "o much text ...."
1004Which will look something like
1006![A paragraph where the first letter is twice the height of the others](https://mdgriffith.gitbooks.io/style-elements/content/assets/Screen%20Shot%202017-08-25%20at%209.41.52%20PM.png)
1008**Note** `spacing` on a paragraph will set the pixel spacing between lines.
1011paragraph : List (Attribute msg) -> List (Element msg) -> Element msg
1012paragraph attrs children =
1013 Internal.element
1014 Internal.asParagraph
1015 Internal.div
1016 (Internal.Describe Internal.Paragraph
1017 :: width fill
1018 :: spacing 5
1019 :: attrs
1021 (Internal.Unkeyed children)
1024{-| Now that we have a paragraph, we need some way to attach a bunch of paragraph's together.
1026To do that we can use a `textColumn`.
1028The main difference between a `column` and a `textColumn` is that `textColumn` will flow the text around elements that have `alignRight` or `alignLeft`, just like we just saw with paragraph.
1030In the following example, we have a `textColumn` where one child has `alignLeft`.
1032 Element.textColumn [ spacing 10, padding 10 ]
1033 [ paragraph [] [ text "lots of text ...." ]
1034 , el [ alignLeft ] none
1035 , paragraph [] [ text "lots of text ...." ]
1038Which will result in something like:
1040![A text layout where an image is on the left.](https://mdgriffith.gitbooks.io/style-elements/content/assets/Screen%20Shot%202017-08-25%20at%208.42.39%20PM.png)
1043textColumn : List (Attribute msg) -> List (Element msg) -> Element msg
1044textColumn attrs children =
1045 Internal.element
1046 Internal.asTextColumn
1047 Internal.div
1048 (width
1049 (fill
1050 |> minimum 500
1051 |> maximum 750
1053 :: attrs
1055 (Internal.Unkeyed children)
1058{-| Both a source and a description are required for images.
1060The description is used for people using screen readers.
1062Leaving the description blank will cause the image to be ignored by assistive technology. This can make sense for images that are purely decorative and add no additional information.
1064So, take a moment to describe your image as you would to someone who has a harder time seeing.
1067image : List (Attribute msg) -> { src : String, description : String } -> Element msg
1068image attrs { src, description } =
1069 let
1070 imageAttributes =
1071 attrs
1072 |> List.filter
1073 (\a ->
1074 case a of
1075 Internal.Width _ ->
1076 True
1078 Internal.Height _ ->
1079 True
1081 _ ->
1082 False
1084 in
1085 Internal.element
1086 Internal.asEl
1087 Internal.div
1088 (Internal.htmlClass classes.imageContainer
1089 :: attrs
1091 (Internal.Unkeyed
1092 [ Internal.element
1093 Internal.asEl
1094 (Internal.NodeName "img")
1095 ([ Internal.Attr <| Html.Attributes.src src
1096 , Internal.Attr <| Html.Attributes.alt description
1098 ++ imageAttributes
1100 (Internal.Unkeyed [])
1105{-|
1107 link []
1108 { url = "http://fruits.com"
1109 , label = text "A link to my favorite fruit provider."
1113link :
1114 List (Attribute msg)
1115 ->
1116 { url : String
1117 , label : Element msg
1119 -> Element msg
1120link attrs { url, label } =
1121 Internal.element
1122 Internal.asEl
1123 (Internal.NodeName "a")
1124 (Internal.Attr (Html.Attributes.href url)
1125 :: Internal.Attr (Html.Attributes.rel "noopener noreferrer")
1126 :: width shrink
1127 :: height shrink
1128 :: Internal.htmlClass
1129 (classes.contentCenterX
1130 ++ " "
1131 ++ classes.contentCenterY
1133 :: attrs
1135 (Internal.Unkeyed [ label ])
1138{-| -}
1139newTabLink :
1140 List (Attribute msg)
1141 ->
1142 { url : String
1143 , label : Element msg
1145 -> Element msg
1146newTabLink attrs { url, label } =
1147 Internal.element
1148 Internal.asEl
1149 (Internal.NodeName "a")
1150 (Internal.Attr (Html.Attributes.href url)
1151 :: Internal.Attr (Html.Attributes.rel "noopener noreferrer")
1152 :: Internal.Attr (Html.Attributes.target "_blank")
1153 :: width shrink
1154 :: height shrink
1155 :: Internal.htmlClass (classes.contentCenterX ++ " " ++ classes.contentCenterY)
1156 :: attrs
1158 (Internal.Unkeyed [ label ])
1161{-| A link to download a file.
1163**Note** If you're using `Browser.application`, then this won't be enough to actually trigger a file download due to how `Browser.Navigation` works.
1165[Here's a description of what needs to happen](https://github.com/elm/html/issues/175).
1168download :
1169 List (Attribute msg)
1170 ->
1171 { url : String
1172 , label : Element msg
1174 -> Element msg
1175download attrs { url, label } =
1176 Internal.element
1177 Internal.asEl
1178 (Internal.NodeName "a")
1179 (Internal.Attr (Html.Attributes.href url)
1180 :: Internal.Attr (Html.Attributes.download "")
1181 :: width shrink
1182 :: height shrink
1183 :: Internal.htmlClass classes.contentCenterX
1184 :: Internal.htmlClass classes.contentCenterY
1185 :: attrs
1187 (Internal.Unkeyed [ label ])
1190{-| A link to download a file, but you can specify the filename.
1192downloadAs :
1193 List (Attribute msg)
1194 ->
1195 { label : Element msg
1196 , filename : String
1197 , url : String
1199 -> Element msg
1200downloadAs attrs { url, filename, label } =
1201 Internal.element
1202 Internal.asEl
1203 (Internal.NodeName "a")
1204 (Internal.Attr (Html.Attributes.href url)
1205 :: Internal.Attr (Html.Attributes.download filename)
1206 :: width shrink
1207 :: height shrink
1208 :: Internal.htmlClass classes.contentCenterX
1209 :: Internal.htmlClass classes.contentCenterY
1210 :: attrs
1212 (Internal.Unkeyed [ label ])
1215{-| -}
1216below : Element msg -> Attribute msg
1217below element =
1218 Internal.Nearby Internal.Below element
1221{-| -}
1222above : Element msg -> Attribute msg
1223above element =
1224 Internal.Nearby Internal.Above element
1227{-| -}
1228onRight : Element msg -> Attribute msg
1229onRight element =
1230 Internal.Nearby Internal.OnRight element
1233{-| -}
1234onLeft : Element msg -> Attribute msg
1235onLeft element =
1236 Internal.Nearby Internal.OnLeft element
1239{-| This will place an element in front of another.
1241**Note:** If you use this on a `layout` element, it will place the element as fixed to the viewport which can be useful for modals and overlays.
1244inFront : Element msg -> Attribute msg
1245inFront element =
1246 Internal.Nearby Internal.InFront element
1249{-| This will place an element between the background and the content of an element.
1251behindContent : Element msg -> Attribute msg
1252behindContent element =
1253 Internal.Nearby Internal.Behind element
1256{-| -}
1257width : Length -> Attribute msg
1258width =
1259 Internal.Width
1262{-| -}
1263height : Length -> Attribute msg
1264height =
1265 Internal.Height
1268{-| -}
1269scale : Float -> Attr decorative msg
1270scale n =
1271 Internal.TransformComponent Flag.scale (Internal.Scale ( n, n, 1 ))
1274{-| -}
1275rotate : Float -> Attr decorative msg
1276rotate angle =
1277 Internal.TransformComponent Flag.rotate (Internal.Rotate ( 0, 0, 1 ) angle)
1280{-| -}
1281moveUp : Float -> Attr decorative msg
1282moveUp y =
1283 Internal.TransformComponent Flag.moveY (Internal.MoveY (negate y))
1286{-| -}
1287moveDown : Float -> Attr decorative msg
1288moveDown y =
1289 Internal.TransformComponent Flag.moveY (Internal.MoveY y)
1292{-| -}
1293moveRight : Float -> Attr decorative msg
1294moveRight x =
1295 Internal.TransformComponent Flag.moveX (Internal.MoveX x)
1298{-| -}
1299moveLeft : Float -> Attr decorative msg
1300moveLeft x =
1301 Internal.TransformComponent Flag.moveX (Internal.MoveX (negate x))
1304{-| -}
1305padding : Int -> Attribute msg
1306padding x =
1307 Internal.StyleClass Flag.padding (Internal.PaddingStyle ("p-" ++ String.fromInt x) x x x x)
1310{-| Set horizontal and vertical padding.
1312paddingXY : Int -> Int -> Attribute msg
1313paddingXY x y =
1314 if x == y then
1315 Internal.StyleClass Flag.padding (Internal.PaddingStyle ("p-" ++ String.fromInt x) x x x x)
1317 else
1318 Internal.StyleClass Flag.padding
1319 (Internal.PaddingStyle
1320 ("p-" ++ String.fromInt x ++ "-" ++ String.fromInt y)
1328{-| If you find yourself defining unique paddings all the time, you might consider defining
1330 edges =
1331 { top = 0
1332 , right = 0
1333 , bottom = 0
1334 , left = 0
1337And then just do
1339 paddingEach { edges | right = 5 }
1342paddingEach : { top : Int, right : Int, bottom : Int, left : Int } -> Attribute msg
1343paddingEach { top, right, bottom, left } =
1344 if top == right && top == bottom && top == left then
1345 Internal.StyleClass Flag.padding (Internal.PaddingStyle ("p-" ++ String.fromInt top) top top top top)
1347 else
1348 Internal.StyleClass Flag.padding
1349 (Internal.PaddingStyle
1350 (Internal.paddingName top right bottom left)
1351 top
1352 right
1353 bottom
1354 left
1358{-| -}
1359centerX : Attribute msg
1360centerX =
1361 Internal.AlignX Internal.CenterX
1364{-| -}
1365centerY : Attribute msg
1366centerY =
1367 Internal.AlignY Internal.CenterY
1370{-| -}
1371alignTop : Attribute msg
1372alignTop =
1373 Internal.AlignY Internal.Top
1376{-| -}
1377alignBottom : Attribute msg
1378alignBottom =
1379 Internal.AlignY Internal.Bottom
1382{-| -}
1383alignLeft : Attribute msg
1384alignLeft =
1385 Internal.AlignX Internal.Left
1388{-| -}
1389alignRight : Attribute msg
1390alignRight =
1391 Internal.AlignX Internal.Right
1394{-| -}
1395spaceEvenly : Attribute msg
1396spaceEvenly =
1397 Internal.Class Flag.xAlign (.spaceEvenly Internal.Style.classes)
1400{-| -}
1401spacing : Int -> Attribute msg
1402spacing x =
1403 Internal.StyleClass Flag.spacing (Internal.SpacingStyle (Internal.spacingName x x) x x)
1406{-| In the majority of cases you'll just need to use `spacing`, which will work as intended.
1408However for some layouts, like `textColumn`, you may want to set a different spacing for the x axis compared to the y axis.
1411spacingXY : Int -> Int -> Attribute msg
1412spacingXY x y =
1413 Internal.StyleClass Flag.spacing (Internal.SpacingStyle (Internal.spacingName x y) x y)
1416{-| Make an element transparent and have it ignore any mouse or touch events, though it will stil take up space.
1418transparent : Bool -> Attr decorative msg
1419transparent on =
1420 if on then
1421 Internal.StyleClass Flag.transparency (Internal.Transparency "transparent" 1.0)
1423 else
1424 Internal.StyleClass Flag.transparency (Internal.Transparency "visible" 0.0)
1427{-| A capped value between 0.0 and 1.0, where 0.0 is transparent and 1.0 is fully opaque.
1429Semantically equavalent to html opacity.
1432alpha : Float -> Attr decorative msg
1433alpha o =
1434 let
1435 transparency =
1437 |> max 0.0
1438 |> min 1.0
1439 |> (\x -> 1 - x)
1440 in
1441 Internal.StyleClass Flag.transparency <| Internal.Transparency ("transparency-" ++ Internal.floatClass transparency) transparency
1445-- {-| -}
1446-- hidden : Bool -> Attribute msg
1447-- hidden on =
1448-- if on then
1449-- Internal.class "hidden"
1450-- else
1451-- Internal.NoAttribute
1454{-| -}
1455scrollbars : Attribute msg
1456scrollbars =
1457 Internal.Class Flag.overflow classes.scrollbars
1460{-| -}
1461scrollbarY : Attribute msg
1462scrollbarY =
1463 Internal.Class Flag.overflow classes.scrollbarsY
1466{-| -}
1467scrollbarX : Attribute msg
1468scrollbarX =
1469 Internal.Class Flag.overflow classes.scrollbarsX
1472{-| -}
1473clip : Attribute msg
1474clip =
1475 Internal.Class Flag.overflow classes.clip
1478{-| -}
1479clipY : Attribute msg
1480clipY =
1481 Internal.Class Flag.overflow classes.clipY
1484{-| -}
1485clipX : Attribute msg
1486clipX =
1487 Internal.Class Flag.overflow classes.clipX
1490{-| Set the cursor to be a pointing hand when it's hovering over this element.
1492pointer : Attribute msg
1493pointer =
1494 Internal.Class Flag.cursor classes.cursorPointer
1497{-| -}
1498type alias Device =
1499 { class : DeviceClass
1500 , orientation : Orientation
1504{-| -}
1505type DeviceClass
1506 = Phone
1507 | Tablet
1508 | Desktop
1509 | BigDesktop
1512{-| -}
1513type Orientation
1514 = Portrait
1515 | Landscape
1518{-| Takes in a Window.Size and returns a device profile which can be used for responsiveness.
1520classifyDevice : { window | height : Int, width : Int } -> Device
1521classifyDevice window =
1522 { class =
1523 if window.width <= 600 then
1524 Phone
1526 else if window.width > 600 && window.width <= 1200 then
1527 Tablet
1529 else if window.width > 1200 && window.width <= 1800 then
1530 Desktop
1532 else
1533 BigDesktop
1534 , orientation =
1535 if window.width < window.height then
1536 Portrait
1538 else
1539 Landscape
1543{-| When designing it's nice to use a modular scale to set spacial rythms.
1545 scaled =
1546 Scale.modular 16 1.25
1548A modular scale starts with a number, and multiplies it by a ratio a number of times.
1549Then, when setting font sizes you can use:
1551 Font.size (scaled 1) -- results in 16
1553 Font.size (scaled 2) -- 16 * 1.25 results in 20
1555 Font.size (scaled 4) -- 16 * 1.25 ^ (4 - 1) results in 31.25
1557We can also provide negative numbers to scale below 16px.
1559 Font.size (scaled -1) -- 16 * 1.25 ^ (-1) results in 12.8
1562modular : Float -> Float -> Int -> Float
1563modular normal ratio rescale =
1564 if rescale == 0 then
1565 normal
1567 else if rescale < 0 then
1568 normal * ratio ^ toFloat rescale
1570 else
1571 normal * ratio ^ (toFloat rescale - 1)
1574{-| -}
1575mouseOver : List Decoration -> Attribute msg
1576mouseOver decs =
1577 Internal.StyleClass Flag.hover <|
1578 Internal.PseudoSelector Internal.Hover
1579 (Internal.unwrapDecorations decs)
1582{-| -}
1583mouseDown : List Decoration -> Attribute msg
1584mouseDown decs =
1585 Internal.StyleClass Flag.active <|
1586 Internal.PseudoSelector Internal.Active
1587 (Internal.unwrapDecorations decs)
1590{-| -}
1591focused : List Decoration -> Attribute msg
1592focused decs =
1593 Internal.StyleClass Flag.focus <|
1594 Internal.PseudoSelector Internal.Focus
1595 (Internal.unwrapDecorations decs)