git.iko.soy/typeable/elm-ui
on cd088cb help sign in
typeable/elm-ui/src/Element.elm
1644 lines 42 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 laid 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 on the right, and one in the center of the space between the elements on the left and right.
112**Note** For text alignment, check out `Element.Font`!
114@docs centerX, centerY, alignLeft, alignRight, alignTop, alignBottom
117# Transparency
119@docs transparent, alpha, pointer
122# Adjustment
124@docs moveUp, moveDown, moveRight, moveLeft, rotate, scale
127# Clipping and Scrollbars
129Clip the content if it overflows.
131@docs clip, clipX, clipY
133Add a scrollbar if the content is larger than the element.
135@docs scrollbars, scrollbarX, scrollbarY
138# Rendering
140@docs layout, layoutWith, Option, noStaticStyleSheet, forceHover, noHover, focusStyle, FocusStyle
143# Links
145@docs link, newTabLink, download, downloadAs
148# Images
150@docs image
153# Color
155In order to use attributes like `Font.color` and `Background.color`, you'll need to make some colors!
157@docs Color, rgba, rgb, rgb255, rgba255, fromRgb, fromRgb255, toRgb
160# Nearby Elements
162Let'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_.
164 Element.row []
165 [ Element.el
166 [ Element.below (Element.text "I'm below!")
167 ]
168 (Element.text "I'm normal!")
169 ]
171This will result in
173 |- I'm normal! -|
174 I'm below
176Where `"I'm Below"` doesn't change the size of `Element.row`.
178This is very useful for things like dropdown menus or tooltips.
180@docs above, below, onRight, onLeft, inFront, behindContent
183# Temporary Styling
185@docs Attr, Decoration, mouseOver, mouseDown, focused
188# Responsiveness
190The main technique for responsiveness is to store window size information in your model.
192Install the `Browser` package, and set up a subscription for [`Browser.Events.onResize`](https://package.elm-lang.org/packages/elm/browser/latest/Browser-Events#onResize).
194You'll also need to retrieve the initial window size. You can either use [`Browser.Dom.getViewport`](https://package.elm-lang.org/packages/elm/browser/latest/Browser-Dom#getViewport) or 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.
196@docs Device, DeviceClass, Orientation, classifyDevice
199# Scaling
201@docs modular
204## Mapping
206@docs map, mapAttribute
209## Compatibility
211@docs html, htmlAttribute
213-}
215import Html exposing (Html)
216import Html.Attributes
217import Internal.Flag as Flag exposing (Flag)
218import Internal.Model as Internal
219import Internal.Style exposing (classes)
222{-| -}
223type alias Color =
224 Internal.Color
227{-| Provide the red, green, and blue channels for the color.
229Each channel takes a value between 0 and 1.
231-}
232rgb : Float -> Float -> Float -> Color
233rgb r g b =
234 Internal.Rgba r g b 1
237{-| -}
238rgba : Float -> Float -> Float -> Float -> Color
239rgba =
240 Internal.Rgba
243{-| Provide the red, green, and blue channels for the color.
245Each channel takes a value between 0 and 255.
247-}
248rgb255 : Int -> Int -> Int -> Color
249rgb255 red green blue =
250 Internal.Rgba
251 (toFloat red / 255)
252 (toFloat green / 255)
253 (toFloat blue / 255)
254 1
257{-| -}
258rgba255 : Int -> Int -> Int -> Float -> Color
259rgba255 red green blue a =
260 Internal.Rgba
261 (toFloat red / 255)
262 (toFloat green / 255)
263 (toFloat blue / 255)
264 a
267{-| Create a color from an RGB record.
268-}
269fromRgb :
270 { red : Float
271 , green : Float
272 , blue : Float
273 , alpha : Float
274 }
275 -> Color
276fromRgb clr =
277 Internal.Rgba
278 clr.red
279 clr.green
280 clr.blue
281 clr.alpha
284{-| -}
285fromRgb255 :
286 { red : Int
287 , green : Int
288 , blue : Int
289 , alpha : Float
290 }
291 -> Color
292fromRgb255 clr =
293 Internal.Rgba
294 (toFloat clr.red / 255)
295 (toFloat clr.green / 255)
296 (toFloat clr.blue / 255)
297 clr.alpha
300{-| Deconstruct a `Color` into its rgb channels.
301-}
302toRgb :
303 Color
304 ->
305 { red : Float
306 , green : Float
307 , blue : Float
308 , alpha : Float
309 }
310toRgb (Internal.Rgba r g b a) =
311 { red = r
312 , green = g
313 , blue = b
314 , alpha = a
315 }
318{-| The basic building block of your layout.
320 howdy : Element msg
321 howdy =
322 Element.el [] (Element.text "Howdy!")
324-}
325type alias Element msg =
326 Internal.Element msg
329{-| An attribute that can be attached to an `Element`
330-}
331type alias Attribute msg =
332 Internal.Attribute () msg
335{-| This is a special attribute that counts as both a `Attribute msg` and a `Decoration`.
336-}
337type alias Attr decorative msg =
338 Internal.Attribute decorative msg
341{-| Only decorations
342-}
343type alias Decoration =
344 Internal.Attribute Never Never
347{-| -}
348html : Html msg -> Element msg
349html =
350 Internal.unstyled
353{-| -}
354htmlAttribute : Html.Attribute msg -> Attribute msg
355htmlAttribute =
356 Internal.Attr
359{-| -}
360map : (msg -> msg1) -> Element msg -> Element msg1
361map =
362 Internal.map
365{-| -}
366mapAttribute : (msg -> msg1) -> Attribute msg -> Attribute msg1
367mapAttribute =
368 Internal.mapAttr
371{-| -}
372type alias Length =
373 Internal.Length
376{-| -}
377px : Int -> Length
378px =
379 Internal.Px
382{-| Shrink an element to fit its contents.
383-}
384shrink : Length
385shrink =
386 Internal.Content
389{-| Fill the available space. The available space will be split evenly between elements that have `width fill`.
390-}
391fill : Length
392fill =
393 Internal.Fill 1
396{-| Similarly you can set a minimum boundary.
398 el
399 [ height
400 (fill
401 |> maximum 300
402 |> minimum 30
403 )
405 ]
406 (text "I will stop at 300px")
408-}
409minimum : Int -> Length -> Length
410minimum i l =
411 Internal.Min i l
414{-| Add a maximum to a length.
416 el
417 [ height
418 (fill
419 |> maximum 300
420 )
421 ]
422 (text "I will stop at 300px")
424-}
425maximum : Int -> Length -> Length
426maximum i l =
427 Internal.Max i l
430{-| 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.
432So, 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.
434**Also:** `fill == fillPortion 1`
436-}
437fillPortion : Int -> Length
438fillPortion =
439 Internal.Fill
442{-| This is your top level node where you can turn `Element` into `Html`.
443-}
444layout : List (Attribute msg) -> Element msg -> Html msg
445layout =
446 layoutWith { options = [] }
449{-| -}
450layoutWith : { options : List Option } -> List (Attribute msg) -> Element msg -> Html msg
451layoutWith { options } attrs child =
452 Internal.renderRoot options
453 (Internal.htmlClass
454 (String.join " "
455 [ classes.root
456 , classes.any
457 , classes.single
458 ]
459 )
460 :: (Internal.rootStyle ++ attrs)
461 )
462 child
465{-| -}
466type alias Option =
467 Internal.Option
470{-| Elm UI embeds two StyleSheets, one that is constant, and one that changes dynamically based on styles collected from the elments being rendered.
472This option will stop the static/constant stylesheet from rendering.
474If you're embedding multiple elm-ui `layout` elements, you need to guarantee that only one is rendering the static style sheet and that it's above ll the others in the DOM tree.
476-}
477noStaticStyleSheet : Option
478noStaticStyleSheet =
479 Internal.RenderModeOption Internal.NoStaticStyleSheet
482{-| -}
483defaultFocus :
484 { borderColor : Maybe Color
485 , backgroundColor : Maybe Color
486 , shadow :
487 Maybe
488 { color : Color
489 , offset : ( Int, Int )
490 , blur : Int
491 , size : Int
492 }
493 }
494defaultFocus =
495 Internal.focusDefaultStyle
498{-| -}
499type alias FocusStyle =
500 { borderColor : Maybe Color
501 , backgroundColor : Maybe Color
502 , shadow :
503 Maybe
504 { color : Color
505 , offset : ( Int, Int )
506 , blur : Int
507 , size : Int
508 }
509 }
512{-| -}
513focusStyle : FocusStyle -> Option
514focusStyle =
515 Internal.FocusStyleOption
518{-| Disable all `mouseOver` styles.
519-}
520noHover : Option
521noHover =
522 Internal.HoverOption Internal.NoHover
525{-| Any `hover` styles, aka attributes with `mouseOver` in the name, will be always turned on.
527This is useful for when you're targeting a platform that has no mouse, such as mobile.
529-}
530forceHover : Option
531forceHover =
532 Internal.HoverOption Internal.ForceHover
535{-| When you want to render exactly nothing.
536-}
537none : Element msg
538none =
539 Internal.Empty
542{-| Create some plain text.
544 text "Hello, you stylish developer!"
546**Note** text does not wrap by default. In order to get text to wrap, check out `paragraph`!
548-}
549text : String -> Element msg
550text content =
551 Internal.Text content
554{-| The basic building block of your layout.
556You can think of an `el` as a `div`, but it can only have one child.
558If you want multiple children, you'll need to use something like `row` or `column`
560 import Element exposing (Element, rgb)
561 import Element.Background as Background
562 import Element.Border as Border
564 myElement : Element msg
565 myElement =
566 Element.el
567 [ Background.color (rgb 0 0.5 0)
568 , Border.color (rgb 0 0.7 0)
569 ]
570 (Element.text "You've made a stylish element!")
572-}
573el : List (Attribute msg) -> Element msg -> Element msg
574el attrs child =
575 Internal.element
576 Internal.asEl
577 Internal.div
578 (width shrink
579 :: height shrink
580 :: attrs
581 )
582 (Internal.Unkeyed [ child ])
585{-| -}
586row : List (Attribute msg) -> List (Element msg) -> Element msg
587row attrs children =
588 Internal.element
589 Internal.asRow
590 Internal.div
591 (Internal.htmlClass (classes.contentLeft ++ " " ++ classes.contentCenterY)
592 :: width shrink
593 :: height shrink
594 :: attrs
595 )
596 (Internal.Unkeyed children)
599{-| -}
600column : List (Attribute msg) -> List (Element msg) -> Element msg
601column attrs children =
602 Internal.element
603 Internal.asColumn
604 Internal.div
605 (Internal.htmlClass
606 (classes.contentTop
607 ++ " "
608 ++ classes.contentLeft
609 )
610 :: height shrink
611 :: width shrink
612 :: attrs
613 )
614 (Internal.Unkeyed children)
617{-| Same as `row`, but will wrap if it takes up too much horizontal space.
618-}
619wrappedRow : List (Attribute msg) -> List (Element msg) -> Element msg
620wrappedRow attrs children =
621 let
622 ( padded, spaced ) =
623 Internal.extractSpacingAndPadding attrs
624 in
625 case spaced of
626 Nothing ->
627 Internal.element
628 Internal.asRow
629 Internal.div
630 (Internal.htmlClass
631 (classes.contentLeft
632 ++ " "
633 ++ classes.contentCenterY
634 ++ " "
635 ++ classes.wrapped
636 )
637 :: width shrink
638 :: height shrink
639 :: attrs
640 )
641 (Internal.Unkeyed children)
643 Just (Internal.Spaced spaceName x y) ->
644 let
645 newPadding =
646 case padded of
647 Just (Internal.Padding name t r b l) ->
648 if r >= (x // 2) && b >= (y // 2) then
649 Just <|
650 paddingEach
651 { top = t - (y // 2)
652 , right = r - (x // 2)
653 , bottom = b - (y // 2)
654 , left = l - (x // 2)
655 }
657 else
658 Nothing
660 Nothing ->
661 Nothing
662 in
663 case newPadding of
664 Just pad ->
665 Internal.element
666 Internal.asRow
667 Internal.div
668 (Internal.htmlClass
669 (classes.contentLeft
670 ++ " "
671 ++ classes.contentCenterY
672 ++ " "
673 ++ classes.wrapped
674 )
675 :: width shrink
676 :: height shrink
677 :: attrs
678 ++ [ pad ]
679 )
680 (Internal.Unkeyed children)
682 Nothing ->
683 -- Not enough space in padding to compensate for spacing
684 let
685 halfX =
686 negate (toFloat x / 2)
688 halfY =
689 negate (toFloat y / 2)
690 in
691 Internal.element
692 Internal.asEl
693 Internal.div
694 attrs
695 (Internal.Unkeyed
696 [ Internal.element
697 Internal.asRow
698 Internal.div
699 (Internal.htmlClass
700 (classes.contentLeft
701 ++ " "
702 ++ classes.contentCenterY
703 ++ " "
704 ++ classes.wrapped
705 )
706 :: Internal.Attr
707 (Html.Attributes.style "margin"
708 (String.fromFloat halfY
709 ++ "px"
710 ++ " "
711 ++ String.fromFloat halfX
712 ++ "px"
713 )
714 )
715 :: Internal.Attr
716 (Html.Attributes.style "width"
717 ("calc(100% + "
718 ++ String.fromInt x
719 ++ "px)"
720 )
721 )
722 :: Internal.Attr
723 (Html.Attributes.style "height"
724 ("calc(100% + "
725 ++ String.fromInt y
726 ++ "px)"
727 )
728 )
729 :: Internal.StyleClass Flag.spacing (Internal.SpacingStyle spaceName x y)
730 :: []
731 )
732 (Internal.Unkeyed children)
733 ]
734 )
737{-| This is just an alias for `Debug.todo`
738-}
739type alias Todo =
740 String -> Never
743{-| 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!
745**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.
747 el
748 [ Element.explain Debug.todo
749 ]
750 (text "Help, I'm being debugged!")
752-}
753explain : Todo -> Attribute msg
754explain _ =
755 Internal.htmlClass "explain"
758{-| -}
759type alias Column record msg =
760 { header : Element msg
761 , width : Length
762 , view : record -> Element msg
763 }
766{-| Show some tabular data.
768Start with a list of records and specify how each column should be rendered.
770So, if we have a list of `persons`:
772 type alias Person =
773 { firstName : String
774 , lastName : String
775 }
777 persons : List Person
778 persons =
779 [ { firstName = "David"
780 , lastName = "Bowie"
781 }
782 , { firstName = "Florence"
783 , lastName = "Welch"
784 }
785 ]
787We could render it using
789 Element.table []
790 { data = persons
791 , columns =
792 [ { header = Element.text "First Name"
793 , width = fill
794 , view =
795 \person ->
796 Element.text person.firstName
797 }
798 , { header = Element.text "Last Name"
799 , width = fill
800 , view =
801 \person ->
802 Element.text person.lastName
803 }
804 ]
805 }
807**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`.
809-}
810table :
811 List (Attribute msg)
812 ->
813 { data : List records
814 , columns : List (Column records msg)
815 }
816 -> Element msg
817table attrs config =
818 tableHelper attrs
819 { data = config.data
820 , columns =
821 List.map InternalColumn config.columns
822 }
825{-| -}
826type alias IndexedColumn record msg =
827 { header : Element msg
828 , width : Length
829 , view : Int -> record -> Element msg
830 }
833{-| Same as `Element.table` except the `view` for each column will also receive the row index as well as the record.
834-}
835indexedTable :
836 List (Attribute msg)
837 ->
838 { data : List records
839 , columns : List (IndexedColumn records msg)
840 }
841 -> Element msg
842indexedTable attrs config =
843 tableHelper attrs
844 { data = config.data
845 , columns =
846 List.map InternalIndexedColumn config.columns
847 }
850{-| -}
851type alias InternalTable records msg =
852 { data : List records
853 , columns : List (InternalTableColumn records msg)
854 }
857{-| -}
858type InternalTableColumn record msg
859 = InternalIndexedColumn (IndexedColumn record msg)
860 | InternalColumn (Column record msg)
863tableHelper : List (Attribute msg) -> InternalTable data msg -> Element msg
864tableHelper attrs config =
865 let
866 ( sX, sY ) =
867 Internal.getSpacing attrs ( 0, 0 )
869 columnHeader col =
870 case col of
871 InternalIndexedColumn colConfig ->
872 colConfig.header
874 InternalColumn colConfig ->
875 colConfig.header
877 columnWidth col =
878 case col of
879 InternalIndexedColumn colConfig ->
880 colConfig.width
882 InternalColumn colConfig ->
883 colConfig.width
885 maybeHeaders =
886 List.map columnHeader config.columns
887 |> (\headers ->
888 if List.all ((==) Internal.Empty) headers then
889 Nothing
891 else
892 Just (List.indexedMap (\col header -> onGrid 1 (col + 1) header) headers)
893 )
895 template =
896 Internal.StyleClass Flag.gridTemplate <|
897 Internal.GridTemplateStyle
898 { spacing = ( px sX, px sY )
899 , columns = List.map columnWidth config.columns
900 , rows = List.repeat (List.length config.data) Internal.Content
901 }
903 onGrid rowLevel columnLevel elem =
904 Internal.element
905 Internal.asEl
906 Internal.div
907 [ Internal.StyleClass Flag.gridPosition
908 (Internal.GridPosition
909 { row = rowLevel
910 , col = columnLevel
911 , width = 1
912 , height = 1
913 }
914 )
915 ]
916 (Internal.Unkeyed [ elem ])
918 add cell columnConfig cursor =
919 case columnConfig of
920 InternalIndexedColumn col ->
921 { cursor
922 | elements =
923 onGrid cursor.row
924 cursor.column
925 (col.view
926 (if maybeHeaders == Nothing then
927 cursor.row - 1
929 else
930 cursor.row - 2
931 )
932 cell
933 )
934 :: cursor.elements
935 , column = cursor.column + 1
936 }
938 InternalColumn col ->
939 { elements =
940 onGrid cursor.row cursor.column (col.view cell)
941 :: cursor.elements
942 , column = cursor.column + 1
943 , row = cursor.row
944 }
946 build columns rowData cursor =
947 let
948 newCursor =
949 List.foldl (add rowData)
950 cursor
951 columns
952 in
953 { elements = newCursor.elements
954 , row = cursor.row + 1
955 , column = 1
956 }
958 children =
959 List.foldl (build config.columns)
960 { elements = []
961 , row =
962 if maybeHeaders == Nothing then
963 1
965 else
966 2
967 , column = 1
968 }
969 config.data
970 in
971 Internal.element
972 Internal.asGrid
973 Internal.div
974 (width fill
975 :: template
976 :: attrs
977 )
978 (Internal.Unkeyed
979 (case maybeHeaders of
980 Nothing ->
981 children.elements
983 Just renderedHeaders ->
984 renderedHeaders ++ List.reverse children.elements
985 )
986 )
989{-| A paragraph will layout all children as wrapped, inline elements.
991 import Element
992 import Element.Font as Font
994 Element.paragraph []
995 [ text "lots of text ...."
996 , el [ Font.bold ] (text "this is bold")
997 , text "lots of text ...."
998 ]
1000This is really useful when you want to markup text by having some parts be bold, or some be links, or whatever you so desire.
1002Also, 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).
1004This makes it particularly easy to do something like a [dropped capital](https://en.wikipedia.org/wiki/Initial).
1006 import Element
1007 import Element.Font as Font
1009 Element.paragraph []
1010 [ el
1011 [ alignLeft
1012 , padding 5
1014 (text "S")
1015 , text "o much text ...."
1018Which will look something like
1020![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)
1022**Note** `spacing` on a paragraph will set the pixel spacing between lines.
1025paragraph : List (Attribute msg) -> List (Element msg) -> Element msg
1026paragraph attrs children =
1027 Internal.element
1028 Internal.asParagraph
1029 Internal.div
1030 (Internal.Describe Internal.Paragraph
1031 :: width fill
1032 :: spacing 5
1033 :: attrs
1035 (Internal.Unkeyed children)
1038{-| Now that we have a paragraph, we need some way to attach a bunch of paragraph's together.
1040To do that we can use a `textColumn`.
1042The 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.
1044In the following example, we have a `textColumn` where one child has `alignLeft`.
1046 Element.textColumn [ spacing 10, padding 10 ]
1047 [ paragraph [] [ text "lots of text ...." ]
1048 , el [ alignLeft ] none
1049 , paragraph [] [ text "lots of text ...." ]
1052Which will result in something like:
1054![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)
1057textColumn : List (Attribute msg) -> List (Element msg) -> Element msg
1058textColumn attrs children =
1059 Internal.element
1060 Internal.asTextColumn
1061 Internal.div
1062 (width
1063 (fill
1064 |> minimum 500
1065 |> maximum 750
1067 :: attrs
1069 (Internal.Unkeyed children)
1072{-| Both a source and a description are required for images.
1074The description is used for people using screen readers.
1076Leaving 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.
1078So, take a moment to describe your image as you would to someone who has a harder time seeing.
1081image : List (Attribute msg) -> { src : String, description : String } -> Element msg
1082image attrs { src, description } =
1083 let
1084 imageAttributes =
1085 attrs
1086 |> List.filter
1087 (\a ->
1088 case a of
1089 Internal.Width _ ->
1090 True
1092 Internal.Height _ ->
1093 True
1095 _ ->
1096 False
1098 in
1099 Internal.element
1100 Internal.asEl
1101 Internal.div
1102 (Internal.htmlClass classes.imageContainer
1103 :: attrs
1105 (Internal.Unkeyed
1106 [ Internal.element
1107 Internal.asEl
1108 (Internal.NodeName "img")
1109 ([ Internal.Attr <| Html.Attributes.src src
1110 , Internal.Attr <| Html.Attributes.alt description
1112 ++ imageAttributes
1114 (Internal.Unkeyed [])
1119{-|
1121 link []
1122 { url = "http://fruits.com"
1123 , label = text "A link to my favorite fruit provider."
1127link :
1128 List (Attribute msg)
1129 ->
1130 { url : String
1131 , label : Element msg
1133 -> Element msg
1134link attrs { url, label } =
1135 Internal.element
1136 Internal.asEl
1137 (Internal.NodeName "a")
1138 (Internal.Attr (Html.Attributes.href url)
1139 :: Internal.Attr (Html.Attributes.rel "noopener noreferrer")
1140 :: width shrink
1141 :: height shrink
1142 :: Internal.htmlClass
1143 (classes.contentCenterX
1144 ++ " "
1145 ++ classes.contentCenterY
1146 ++ " "
1147 ++ classes.link
1149 :: attrs
1151 (Internal.Unkeyed [ label ])
1154{-| -}
1155newTabLink :
1156 List (Attribute msg)
1157 ->
1158 { url : String
1159 , label : Element msg
1161 -> Element msg
1162newTabLink attrs { url, label } =
1163 Internal.element
1164 Internal.asEl
1165 (Internal.NodeName "a")
1166 (Internal.Attr (Html.Attributes.href url)
1167 :: Internal.Attr (Html.Attributes.rel "noopener noreferrer")
1168 :: Internal.Attr (Html.Attributes.target "_blank")
1169 :: width shrink
1170 :: height shrink
1171 :: Internal.htmlClass
1172 (classes.contentCenterX
1173 ++ " "
1174 ++ classes.contentCenterY
1175 ++ " "
1176 ++ classes.link
1178 :: attrs
1180 (Internal.Unkeyed [ label ])
1183{-| A link to download a file.
1185**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.
1187[Here's a description of what needs to happen](https://github.com/elm/html/issues/175).
1190download :
1191 List (Attribute msg)
1192 ->
1193 { url : String
1194 , label : Element msg
1196 -> Element msg
1197download attrs { url, label } =
1198 Internal.element
1199 Internal.asEl
1200 (Internal.NodeName "a")
1201 (Internal.Attr (Html.Attributes.href url)
1202 :: Internal.Attr (Html.Attributes.download "")
1203 :: width shrink
1204 :: height shrink
1205 :: Internal.htmlClass classes.contentCenterX
1206 :: Internal.htmlClass classes.contentCenterY
1207 :: attrs
1209 (Internal.Unkeyed [ label ])
1212{-| A link to download a file, but you can specify the filename.
1214downloadAs :
1215 List (Attribute msg)
1216 ->
1217 { label : Element msg
1218 , filename : String
1219 , url : String
1221 -> Element msg
1222downloadAs attrs { url, filename, label } =
1223 Internal.element
1224 Internal.asEl
1225 (Internal.NodeName "a")
1226 (Internal.Attr (Html.Attributes.href url)
1227 :: Internal.Attr (Html.Attributes.download filename)
1228 :: width shrink
1229 :: height shrink
1230 :: Internal.htmlClass classes.contentCenterX
1231 :: Internal.htmlClass classes.contentCenterY
1232 :: attrs
1234 (Internal.Unkeyed [ label ])
1238{- NEARBYS -}
1241createNearby : Internal.Location -> Element msg -> Attribute msg
1242createNearby loc element =
1243 case element of
1244 Internal.Empty ->
1245 Internal.NoAttribute
1247 _ ->
1248 Internal.Nearby loc element
1251{-| -}
1252below : Element msg -> Attribute msg
1253below element =
1254 createNearby Internal.Below element
1257{-| -}
1258above : Element msg -> Attribute msg
1259above element =
1260 createNearby Internal.Above element
1263{-| -}
1264onRight : Element msg -> Attribute msg
1265onRight element =
1266 createNearby Internal.OnRight element
1269{-| -}
1270onLeft : Element msg -> Attribute msg
1271onLeft element =
1272 createNearby Internal.OnLeft element
1275{-| This will place an element in front of another.
1277**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.
1280inFront : Element msg -> Attribute msg
1281inFront element =
1282 createNearby Internal.InFront element
1285{-| This will place an element between the background and the content of an element.
1287behindContent : Element msg -> Attribute msg
1288behindContent element =
1289 createNearby Internal.Behind element
1292{-| -}
1293width : Length -> Attribute msg
1294width =
1295 Internal.Width
1298{-| -}
1299height : Length -> Attribute msg
1300height =
1301 Internal.Height
1304{-| -}
1305scale : Float -> Attr decorative msg
1306scale n =
1307 Internal.TransformComponent Flag.scale (Internal.Scale ( n, n, 1 ))
1310{-| Angle is given in radians. [Here are some conversion functions if you want to use another unit.](https://package.elm-lang.org/packages/elm/core/latest/Basics#degrees)
1312rotate : Float -> Attr decorative msg
1313rotate angle =
1314 Internal.TransformComponent Flag.rotate (Internal.Rotate ( 0, 0, 1 ) angle)
1317{-| -}
1318moveUp : Float -> Attr decorative msg
1319moveUp y =
1320 Internal.TransformComponent Flag.moveY (Internal.MoveY (negate y))
1323{-| -}
1324moveDown : Float -> Attr decorative msg
1325moveDown y =
1326 Internal.TransformComponent Flag.moveY (Internal.MoveY y)
1329{-| -}
1330moveRight : Float -> Attr decorative msg
1331moveRight x =
1332 Internal.TransformComponent Flag.moveX (Internal.MoveX x)
1335{-| -}
1336moveLeft : Float -> Attr decorative msg
1337moveLeft x =
1338 Internal.TransformComponent Flag.moveX (Internal.MoveX (negate x))
1341{-| -}
1342padding : Int -> Attribute msg
1343padding x =
1344 Internal.StyleClass Flag.padding (Internal.PaddingStyle ("p-" ++ String.fromInt x) x x x x)
1347{-| Set horizontal and vertical padding.
1349paddingXY : Int -> Int -> Attribute msg
1350paddingXY x y =
1351 if x == y then
1352 Internal.StyleClass Flag.padding (Internal.PaddingStyle ("p-" ++ String.fromInt x) x x x x)
1354 else
1355 Internal.StyleClass Flag.padding
1356 (Internal.PaddingStyle
1357 ("p-" ++ String.fromInt x ++ "-" ++ String.fromInt y)
1365{-| If you find yourself defining unique paddings all the time, you might consider defining
1367 edges =
1368 { top = 0
1369 , right = 0
1370 , bottom = 0
1371 , left = 0
1374And then just do
1376 paddingEach { edges | right = 5 }
1379paddingEach : { top : Int, right : Int, bottom : Int, left : Int } -> Attribute msg
1380paddingEach { top, right, bottom, left } =
1381 if top == right && top == bottom && top == left then
1382 Internal.StyleClass Flag.padding (Internal.PaddingStyle ("p-" ++ String.fromInt top) top top top top)
1384 else
1385 Internal.StyleClass Flag.padding
1386 (Internal.PaddingStyle
1387 (Internal.paddingName top right bottom left)
1388 top
1389 right
1390 bottom
1391 left
1395{-| -}
1396centerX : Attribute msg
1397centerX =
1398 Internal.AlignX Internal.CenterX
1401{-| -}
1402centerY : Attribute msg
1403centerY =
1404 Internal.AlignY Internal.CenterY
1407{-| -}
1408alignTop : Attribute msg
1409alignTop =
1410 Internal.AlignY Internal.Top
1413{-| -}
1414alignBottom : Attribute msg
1415alignBottom =
1416 Internal.AlignY Internal.Bottom
1419{-| -}
1420alignLeft : Attribute msg
1421alignLeft =
1422 Internal.AlignX Internal.Left
1425{-| -}
1426alignRight : Attribute msg
1427alignRight =
1428 Internal.AlignX Internal.Right
1431{-| -}
1432spaceEvenly : Attribute msg
1433spaceEvenly =
1434 Internal.Class Flag.spacing Internal.Style.classes.spaceEvenly
1437{-| -}
1438spacing : Int -> Attribute msg
1439spacing x =
1440 Internal.StyleClass Flag.spacing (Internal.SpacingStyle (Internal.spacingName x x) x x)
1443{-| In the majority of cases you'll just need to use `spacing`, which will work as intended.
1445However for some layouts, like `textColumn`, you may want to set a different spacing for the x axis compared to the y axis.
1448spacingXY : Int -> Int -> Attribute msg
1449spacingXY x y =
1450 Internal.StyleClass Flag.spacing (Internal.SpacingStyle (Internal.spacingName x y) x y)
1453{-| Make an element transparent and have it ignore any mouse or touch events, though it will stil take up space.
1455transparent : Bool -> Attr decorative msg
1456transparent on =
1457 if on then
1458 Internal.StyleClass Flag.transparency (Internal.Transparency "transparent" 1.0)
1460 else
1461 Internal.StyleClass Flag.transparency (Internal.Transparency "visible" 0.0)
1464{-| A capped value between 0.0 and 1.0, where 0.0 is transparent and 1.0 is fully opaque.
1466Semantically equivalent to html opacity.
1469alpha : Float -> Attr decorative msg
1470alpha o =
1471 let
1472 transparency =
1474 |> max 0.0
1475 |> min 1.0
1476 |> (\x -> 1 - x)
1477 in
1478 Internal.StyleClass Flag.transparency <| Internal.Transparency ("transparency-" ++ Internal.floatClass transparency) transparency
1482-- {-| -}
1483-- hidden : Bool -> Attribute msg
1484-- hidden on =
1485-- if on then
1486-- Internal.class "hidden"
1487-- else
1488-- Internal.NoAttribute
1491{-| -}
1492scrollbars : Attribute msg
1493scrollbars =
1494 Internal.Class Flag.overflow classes.scrollbars
1497{-| -}
1498scrollbarY : Attribute msg
1499scrollbarY =
1500 Internal.Class Flag.overflow classes.scrollbarsY
1503{-| -}
1504scrollbarX : Attribute msg
1505scrollbarX =
1506 Internal.Class Flag.overflow classes.scrollbarsX
1509{-| -}
1510clip : Attribute msg
1511clip =
1512 Internal.Class Flag.overflow classes.clip
1515{-| -}
1516clipY : Attribute msg
1517clipY =
1518 Internal.Class Flag.overflow classes.clipY
1521{-| -}
1522clipX : Attribute msg
1523clipX =
1524 Internal.Class Flag.overflow classes.clipX
1527{-| Set the cursor to be a pointing hand when it's hovering over this element.
1529pointer : Attribute msg
1530pointer =
1531 Internal.Class Flag.cursor classes.cursorPointer
1534{-| -}
1535type alias Device =
1536 { class : DeviceClass
1537 , orientation : Orientation
1541{-| -}
1542type DeviceClass
1543 = Phone
1544 | Tablet
1545 | Desktop
1546 | BigDesktop
1549{-| -}
1550type Orientation
1551 = Portrait
1552 | Landscape
1555{-| Takes in a Window.Size and returns a device profile which can be used for responsiveness.
1557If you have more detailed concerns around responsiveness, it probably makes sense to copy this function into your codebase and modify as needed.
1560classifyDevice : { window | height : Int, width : Int } -> Device
1561classifyDevice window =
1562 -- Tested in this ellie:
1563 -- https://ellie-app.com/68QM7wLW8b9a1
1564 { class =
1565 let
1566 longSide =
1567 max window.width window.height
1569 shortSide =
1570 min window.width window.height
1571 in
1572 if shortSide < 600 then
1573 Phone
1575 else if longSide <= 1200 then
1576 Tablet
1578 else if longSide > 1200 && longSide <= 1920 then
1579 Desktop
1581 else
1582 BigDesktop
1583 , orientation =
1584 if window.width < window.height then
1585 Portrait
1587 else
1588 Landscape
1592{-| When designing it's nice to use a modular scale to set spacial rythms.
1594 scaled =
1595 Element.modular 16 1.25
1597A modular scale starts with a number, and multiplies it by a ratio a number of times.
1598Then, when setting font sizes you can use:
1600 Font.size (scaled 1) -- results in 16
1602 Font.size (scaled 2) -- 16 * 1.25 results in 20
1604 Font.size (scaled 4) -- 16 * 1.25 ^ (4 - 1) results in 31.25
1606We can also provide negative numbers to scale below 16px.
1608 Font.size (scaled -1) -- 16 * 1.25 ^ (-1) results in 12.8
1611modular : Float -> Float -> Int -> Float
1612modular normal ratio rescale =
1613 if rescale == 0 then
1614 normal
1616 else if rescale < 0 then
1617 normal * ratio ^ toFloat rescale
1619 else
1620 normal * ratio ^ (toFloat rescale - 1)
1623{-| -}
1624mouseOver : List Decoration -> Attribute msg
1625mouseOver decs =
1626 Internal.StyleClass Flag.hover <|
1627 Internal.PseudoSelector Internal.Hover
1628 (Internal.unwrapDecorations decs)
1631{-| -}
1632mouseDown : List Decoration -> Attribute msg
1633mouseDown decs =
1634 Internal.StyleClass Flag.active <|
1635 Internal.PseudoSelector Internal.Active
1636 (Internal.unwrapDecorations decs)
1639{-| -}
1640focused : List Decoration -> Attribute msg
1641focused decs =
1642 Internal.StyleClass Flag.focus <|
1643 Internal.PseudoSelector Internal.Focus
1644 (Internal.unwrapDecorations decs)