git.iko.soy/typeable/elm-ui
on master help sign in
typeable/elm-ui/src/Element.elm
1686 lines 43 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 elements 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 all 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 >= (toFloat x / 2) && b >= (toFloat y / 2) then
649 let
650 newTop =
651 t - (toFloat y / 2)
653 newRight =
654 r - (toFloat x / 2)
656 newBottom =
657 b - (toFloat y / 2)
659 newLeft =
660 l - (toFloat x / 2)
661 in
662 Just <|
663 Internal.StyleClass Flag.padding
664 (Internal.PaddingStyle
665 (Internal.paddingNameFloat
666 newTop
667 newRight
668 newBottom
669 newLeft
670 )
671 newTop
672 newRight
673 newBottom
674 newLeft
675 )
677 else
678 Nothing
680 Nothing ->
681 Nothing
682 in
683 case newPadding of
684 Just pad ->
685 Internal.element
686 Internal.asRow
687 Internal.div
688 (Internal.htmlClass
689 (classes.contentLeft
690 ++ " "
691 ++ classes.contentCenterY
692 ++ " "
693 ++ classes.wrapped
694 )
695 :: width shrink
696 :: height shrink
697 :: attrs
698 ++ [ pad ]
699 )
700 (Internal.Unkeyed children)
702 Nothing ->
703 -- Not enough space in padding to compensate for spacing
704 let
705 halfX =
706 negate (toFloat x / 2)
708 halfY =
709 negate (toFloat y / 2)
710 in
711 Internal.element
712 Internal.asEl
713 Internal.div
714 attrs
715 (Internal.Unkeyed
716 [ Internal.element
717 Internal.asRow
718 Internal.div
719 (Internal.htmlClass
720 (classes.contentLeft
721 ++ " "
722 ++ classes.contentCenterY
723 ++ " "
724 ++ classes.wrapped
725 )
726 :: Internal.Attr
727 (Html.Attributes.style "margin"
728 (String.fromFloat halfY
729 ++ "px"
730 ++ " "
731 ++ String.fromFloat halfX
732 ++ "px"
733 )
734 )
735 :: Internal.Attr
736 (Html.Attributes.style "width"
737 ("calc(100% + "
738 ++ String.fromInt x
739 ++ "px)"
740 )
741 )
742 :: Internal.Attr
743 (Html.Attributes.style "height"
744 ("calc(100% + "
745 ++ String.fromInt y
746 ++ "px)"
747 )
748 )
749 :: Internal.StyleClass Flag.spacing (Internal.SpacingStyle spaceName x y)
750 :: []
751 )
752 (Internal.Unkeyed children)
753 ]
754 )
757{-| This is just an alias for `Debug.todo`
758-}
759type alias Todo =
760 String -> Never
763{-| 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!
765**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.
767 el
768 [ Element.explain Debug.todo
769 ]
770 (text "Help, I'm being debugged!")
772-}
773explain : Todo -> Attribute msg
774explain _ =
775 Internal.htmlClass "explain"
778{-| -}
779type alias Column record msg =
780 { header : Element msg
781 , width : Length
782 , view : record -> Element msg
783 }
786{-| Show some tabular data.
788Start with a list of records and specify how each column should be rendered.
790So, if we have a list of `persons`:
792 type alias Person =
793 { firstName : String
794 , lastName : String
795 }
797 persons : List Person
798 persons =
799 [ { firstName = "David"
800 , lastName = "Bowie"
801 }
802 , { firstName = "Florence"
803 , lastName = "Welch"
804 }
805 ]
807We could render it using
809 Element.table []
810 { data = persons
811 , columns =
812 [ { header = Element.text "First Name"
813 , width = fill
814 , view =
815 \person ->
816 Element.text person.firstName
817 }
818 , { header = Element.text "Last Name"
819 , width = fill
820 , view =
821 \person ->
822 Element.text person.lastName
823 }
824 ]
825 }
827**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`.
829-}
830table :
831 List (Attribute msg)
832 ->
833 { data : List records
834 , columns : List (Column records msg)
835 }
836 -> Element msg
837table attrs config =
838 tableHelper attrs
839 { data = config.data
840 , columns =
841 List.map InternalColumn config.columns
842 }
845{-| -}
846type alias IndexedColumn record msg =
847 { header : Element msg
848 , width : Length
849 , view : Int -> record -> Element msg
850 }
853{-| Same as `Element.table` except the `view` for each column will also receive the row index as well as the record.
854-}
855indexedTable :
856 List (Attribute msg)
857 ->
858 { data : List records
859 , columns : List (IndexedColumn records msg)
860 }
861 -> Element msg
862indexedTable attrs config =
863 tableHelper attrs
864 { data = config.data
865 , columns =
866 List.map InternalIndexedColumn config.columns
867 }
870{-| -}
871type alias InternalTable records msg =
872 { data : List records
873 , columns : List (InternalTableColumn records msg)
874 }
877{-| -}
878type InternalTableColumn record msg
879 = InternalIndexedColumn (IndexedColumn record msg)
880 | InternalColumn (Column record msg)
883tableHelper : List (Attribute msg) -> InternalTable data msg -> Element msg
884tableHelper attrs config =
885 let
886 ( sX, sY ) =
887 Internal.getSpacing attrs ( 0, 0 )
889 columnHeader col =
890 case col of
891 InternalIndexedColumn colConfig ->
892 colConfig.header
894 InternalColumn colConfig ->
895 colConfig.header
897 columnWidth col =
898 case col of
899 InternalIndexedColumn colConfig ->
900 colConfig.width
902 InternalColumn colConfig ->
903 colConfig.width
905 maybeHeaders =
906 List.map columnHeader config.columns
907 |> (\headers ->
908 if List.all ((==) Internal.Empty) headers then
909 Nothing
911 else
912 Just (List.indexedMap (\col header -> onGrid 1 (col + 1) header) headers)
913 )
915 template =
916 Internal.StyleClass Flag.gridTemplate <|
917 Internal.GridTemplateStyle
918 { spacing = ( px sX, px sY )
919 , columns = List.map columnWidth config.columns
920 , rows = List.repeat (List.length config.data) Internal.Content
921 }
923 onGrid rowLevel columnLevel elem =
924 Internal.element
925 Internal.asEl
926 Internal.div
927 [ Internal.StyleClass Flag.gridPosition
928 (Internal.GridPosition
929 { row = rowLevel
930 , col = columnLevel
931 , width = 1
932 , height = 1
933 }
934 )
935 ]
936 (Internal.Unkeyed [ elem ])
938 add cell columnConfig cursor =
939 case columnConfig of
940 InternalIndexedColumn col ->
941 { cursor
942 | elements =
943 onGrid cursor.row
944 cursor.column
945 (col.view
946 (if maybeHeaders == Nothing then
947 cursor.row - 1
949 else
950 cursor.row - 2
951 )
952 cell
953 )
954 :: cursor.elements
955 , column = cursor.column + 1
956 }
958 InternalColumn col ->
959 { elements =
960 onGrid cursor.row cursor.column (col.view cell)
961 :: cursor.elements
962 , column = cursor.column + 1
963 , row = cursor.row
964 }
966 build columns rowData cursor =
967 let
968 newCursor =
969 List.foldl (add rowData)
970 cursor
971 columns
972 in
973 { elements = newCursor.elements
974 , row = cursor.row + 1
975 , column = 1
976 }
978 children =
979 List.foldl (build config.columns)
980 { elements = []
981 , row =
982 if maybeHeaders == Nothing then
983 1
985 else
986 2
987 , column = 1
988 }
989 config.data
990 in
991 Internal.element
992 Internal.asGrid
993 Internal.div
994 (width fill
995 :: template
996 :: attrs
997 )
998 (Internal.Unkeyed
999 (case maybeHeaders of
1000 Nothing ->
1001 children.elements
1003 Just renderedHeaders ->
1004 renderedHeaders ++ List.reverse children.elements
1009{-| A paragraph will layout all children as wrapped, inline elements.
1011 import Element exposing (el, paragraph, text)
1012 import Element.Font as Font
1014 view =
1015 paragraph []
1016 [ text "lots of text ...."
1017 , el [ Font.bold ] (text "this is bold")
1018 , text "lots of text ...."
1021This is really useful when you want to markup text by having some parts be bold, or some be links, or whatever you so desire.
1023Also, 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).
1025This makes it particularly easy to do something like a [dropped capital](https://en.wikipedia.org/wiki/Initial).
1027 import Element exposing (alignLeft, el, padding, paragraph, text)
1028 import Element.Font as Font
1030 view =
1031 paragraph []
1032 [ el
1033 [ alignLeft
1034 , padding 5
1036 (text "S")
1037 , text "o much text ...."
1040Which will look something like
1042![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)
1044**Note** `spacing` on a paragraph will set the pixel spacing between lines.
1047paragraph : List (Attribute msg) -> List (Element msg) -> Element msg
1048paragraph attrs children =
1049 Internal.element
1050 Internal.asParagraph
1051 Internal.div
1052 (Internal.Describe Internal.Paragraph
1053 :: width fill
1054 :: spacing 5
1055 :: attrs
1057 (Internal.Unkeyed children)
1060{-| Now that we have a paragraph, we need some way to attach a bunch of paragraph's together.
1062To do that we can use a `textColumn`.
1064The 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.
1066In the following example, we have a `textColumn` where one child has `alignLeft`.
1068 Element.textColumn [ spacing 10, padding 10 ]
1069 [ paragraph [] [ text "lots of text ...." ]
1070 , el [ alignLeft ] none
1071 , paragraph [] [ text "lots of text ...." ]
1074Which will result in something like:
1076![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)
1079textColumn : List (Attribute msg) -> List (Element msg) -> Element msg
1080textColumn attrs children =
1081 Internal.element
1082 Internal.asTextColumn
1083 Internal.div
1084 (width
1085 (fill
1086 |> minimum 500
1087 |> maximum 750
1089 :: attrs
1091 (Internal.Unkeyed children)
1094{-| Both a source and a description are required for images.
1096The description is used for people using screen readers.
1098Leaving 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.
1100So, take a moment to describe your image as you would to someone who has a harder time seeing.
1103image : List (Attribute msg) -> { src : String, description : String } -> Element msg
1104image attrs { src, description } =
1105 let
1106 imageAttributes =
1107 attrs
1108 |> List.filter
1109 (\a ->
1110 case a of
1111 Internal.Width _ ->
1112 True
1114 Internal.Height _ ->
1115 True
1117 _ ->
1118 False
1120 in
1121 Internal.element
1122 Internal.asEl
1123 Internal.div
1124 (Internal.htmlClass classes.imageContainer
1125 :: attrs
1127 (Internal.Unkeyed
1128 [ Internal.element
1129 Internal.asEl
1130 (Internal.NodeName "img")
1131 ([ Internal.Attr <| Html.Attributes.src src
1132 , Internal.Attr <| Html.Attributes.alt description
1134 ++ imageAttributes
1136 (Internal.Unkeyed [])
1141{-|
1143 link []
1144 { url = "http://fruits.com"
1145 , label = text "A link to my favorite fruit provider."
1149link :
1150 List (Attribute msg)
1151 ->
1152 { url : String
1153 , label : Element msg
1155 -> Element msg
1156link attrs { url, label } =
1157 Internal.element
1158 Internal.asEl
1159 (Internal.NodeName "a")
1160 (Internal.Attr (Html.Attributes.href url)
1161 :: Internal.Attr (Html.Attributes.rel "noopener noreferrer")
1162 :: width shrink
1163 :: height shrink
1164 :: Internal.htmlClass
1165 (classes.contentCenterX
1166 ++ " "
1167 ++ classes.contentCenterY
1168 ++ " "
1169 ++ classes.link
1171 :: attrs
1173 (Internal.Unkeyed [ label ])
1176{-| -}
1177newTabLink :
1178 List (Attribute msg)
1179 ->
1180 { url : String
1181 , label : Element msg
1183 -> Element msg
1184newTabLink attrs { url, label } =
1185 Internal.element
1186 Internal.asEl
1187 (Internal.NodeName "a")
1188 (Internal.Attr (Html.Attributes.href url)
1189 :: Internal.Attr (Html.Attributes.rel "noopener noreferrer")
1190 :: Internal.Attr (Html.Attributes.target "_blank")
1191 :: width shrink
1192 :: height shrink
1193 :: Internal.htmlClass
1194 (classes.contentCenterX
1195 ++ " "
1196 ++ classes.contentCenterY
1197 ++ " "
1198 ++ classes.link
1200 :: attrs
1202 (Internal.Unkeyed [ label ])
1205{-| A link to download a file.
1207download :
1208 List (Attribute msg)
1209 ->
1210 { url : String
1211 , label : Element msg
1213 -> Element msg
1214download attrs { url, label } =
1215 Internal.element
1216 Internal.asEl
1217 (Internal.NodeName "a")
1218 (Internal.Attr (Html.Attributes.href url)
1219 :: Internal.Attr (Html.Attributes.download "")
1220 :: width shrink
1221 :: height shrink
1222 :: Internal.htmlClass classes.contentCenterX
1223 :: Internal.htmlClass classes.contentCenterY
1224 :: attrs
1226 (Internal.Unkeyed [ label ])
1229{-| A link to download a file, but you can specify the filename.
1231downloadAs :
1232 List (Attribute msg)
1233 ->
1234 { label : Element msg
1235 , filename : String
1236 , url : String
1238 -> Element msg
1239downloadAs attrs { url, filename, label } =
1240 Internal.element
1241 Internal.asEl
1242 (Internal.NodeName "a")
1243 (Internal.Attr (Html.Attributes.href url)
1244 :: Internal.Attr (Html.Attributes.download filename)
1245 :: width shrink
1246 :: height shrink
1247 :: Internal.htmlClass classes.contentCenterX
1248 :: Internal.htmlClass classes.contentCenterY
1249 :: attrs
1251 (Internal.Unkeyed [ label ])
1255{- NEARBYS -}
1258createNearby : Internal.Location -> Element msg -> Attribute msg
1259createNearby loc element =
1260 case element of
1261 Internal.Empty ->
1262 Internal.NoAttribute
1264 _ ->
1265 Internal.Nearby loc element
1268{-| -}
1269below : Element msg -> Attribute msg
1270below element =
1271 createNearby Internal.Below element
1274{-| -}
1275above : Element msg -> Attribute msg
1276above element =
1277 createNearby Internal.Above element
1280{-| -}
1281onRight : Element msg -> Attribute msg
1282onRight element =
1283 createNearby Internal.OnRight element
1286{-| -}
1287onLeft : Element msg -> Attribute msg
1288onLeft element =
1289 createNearby Internal.OnLeft element
1292{-| This will place an element in front of another.
1294**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.
1297inFront : Element msg -> Attribute msg
1298inFront element =
1299 createNearby Internal.InFront element
1302{-| This will place an element between the background and the content of an element.
1304behindContent : Element msg -> Attribute msg
1305behindContent element =
1306 createNearby Internal.Behind element
1309{-| -}
1310width : Length -> Attribute msg
1311width =
1312 Internal.Width
1315{-| -}
1316height : Length -> Attribute msg
1317height =
1318 Internal.Height
1321{-| -}
1322scale : Float -> Attr decorative msg
1323scale n =
1324 Internal.TransformComponent Flag.scale (Internal.Scale ( n, n, 1 ))
1327{-| 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)
1329rotate : Float -> Attr decorative msg
1330rotate angle =
1331 Internal.TransformComponent Flag.rotate (Internal.Rotate ( 0, 0, 1 ) angle)
1334{-| -}
1335moveUp : Float -> Attr decorative msg
1336moveUp y =
1337 Internal.TransformComponent Flag.moveY (Internal.MoveY (negate y))
1340{-| -}
1341moveDown : Float -> Attr decorative msg
1342moveDown y =
1343 Internal.TransformComponent Flag.moveY (Internal.MoveY y)
1346{-| -}
1347moveRight : Float -> Attr decorative msg
1348moveRight x =
1349 Internal.TransformComponent Flag.moveX (Internal.MoveX x)
1352{-| -}
1353moveLeft : Float -> Attr decorative msg
1354moveLeft x =
1355 Internal.TransformComponent Flag.moveX (Internal.MoveX (negate x))
1358{-| -}
1359padding : Int -> Attribute msg
1360padding x =
1361 let
1362 f =
1363 toFloat x
1364 in
1365 Internal.StyleClass Flag.padding (Internal.PaddingStyle ("p-" ++ String.fromInt x) f f f f)
1368{-| Set horizontal and vertical padding.
1370paddingXY : Int -> Int -> Attribute msg
1371paddingXY x y =
1372 if x == y then
1373 let
1374 f =
1375 toFloat x
1376 in
1377 Internal.StyleClass Flag.padding (Internal.PaddingStyle ("p-" ++ String.fromInt x) f f f f)
1379 else
1380 let
1381 xFloat =
1382 toFloat x
1384 yFloat =
1385 toFloat y
1386 in
1387 Internal.StyleClass Flag.padding
1388 (Internal.PaddingStyle
1389 ("p-" ++ String.fromInt x ++ "-" ++ String.fromInt y)
1390 yFloat
1391 xFloat
1392 yFloat
1393 xFloat
1397{-| If you find yourself defining unique paddings all the time, you might consider defining
1399 edges =
1400 { top = 0
1401 , right = 0
1402 , bottom = 0
1403 , left = 0
1406And then just do
1408 paddingEach { edges | right = 5 }
1411paddingEach : { top : Int, right : Int, bottom : Int, left : Int } -> Attribute msg
1412paddingEach { top, right, bottom, left } =
1413 if top == right && top == bottom && top == left then
1414 let
1415 topFloat =
1416 toFloat top
1417 in
1418 Internal.StyleClass Flag.padding
1419 (Internal.PaddingStyle ("p-" ++ String.fromInt top)
1420 topFloat
1421 topFloat
1422 topFloat
1423 topFloat
1426 else
1427 Internal.StyleClass Flag.padding
1428 (Internal.PaddingStyle
1429 (Internal.paddingName top right bottom left)
1430 (toFloat top)
1431 (toFloat right)
1432 (toFloat bottom)
1433 (toFloat left)
1437{-| -}
1438centerX : Attribute msg
1439centerX =
1440 Internal.AlignX Internal.CenterX
1443{-| -}
1444centerY : Attribute msg
1445centerY =
1446 Internal.AlignY Internal.CenterY
1449{-| -}
1450alignTop : Attribute msg
1451alignTop =
1452 Internal.AlignY Internal.Top
1455{-| -}
1456alignBottom : Attribute msg
1457alignBottom =
1458 Internal.AlignY Internal.Bottom
1461{-| -}
1462alignLeft : Attribute msg
1463alignLeft =
1464 Internal.AlignX Internal.Left
1467{-| -}
1468alignRight : Attribute msg
1469alignRight =
1470 Internal.AlignX Internal.Right
1473{-| -}
1474spaceEvenly : Attribute msg
1475spaceEvenly =
1476 Internal.Class Flag.spacing Internal.Style.classes.spaceEvenly
1479{-| -}
1480spacing : Int -> Attribute msg
1481spacing x =
1482 Internal.StyleClass Flag.spacing (Internal.SpacingStyle (Internal.spacingName x x) x x)
1485{-| In the majority of cases you'll just need to use `spacing`, which will work as intended.
1487However for some layouts, like `textColumn`, you may want to set a different spacing for the x axis compared to the y axis.
1490spacingXY : Int -> Int -> Attribute msg
1491spacingXY x y =
1492 Internal.StyleClass Flag.spacing (Internal.SpacingStyle (Internal.spacingName x y) x y)
1495{-| Make an element transparent and have it ignore any mouse or touch events, though it will stil take up space.
1497transparent : Bool -> Attr decorative msg
1498transparent on =
1499 if on then
1500 Internal.StyleClass Flag.transparency (Internal.Transparency "transparent" 1.0)
1502 else
1503 Internal.StyleClass Flag.transparency (Internal.Transparency "visible" 0.0)
1506{-| A capped value between 0.0 and 1.0, where 0.0 is transparent and 1.0 is fully opaque.
1508Semantically equivalent to html opacity.
1511alpha : Float -> Attr decorative msg
1512alpha o =
1513 let
1514 transparency =
1516 |> max 0.0
1517 |> min 1.0
1518 |> (\x -> 1 - x)
1519 in
1520 Internal.StyleClass Flag.transparency <| Internal.Transparency ("transparency-" ++ Internal.floatClass transparency) transparency
1524-- {-| -}
1525-- hidden : Bool -> Attribute msg
1526-- hidden on =
1527-- if on then
1528-- Internal.class "hidden"
1529-- else
1530-- Internal.NoAttribute
1533{-| -}
1534scrollbars : Attribute msg
1535scrollbars =
1536 Internal.Class Flag.overflow classes.scrollbars
1539{-| -}
1540scrollbarY : Attribute msg
1541scrollbarY =
1542 Internal.Class Flag.overflow classes.scrollbarsY
1545{-| -}
1546scrollbarX : Attribute msg
1547scrollbarX =
1548 Internal.Class Flag.overflow classes.scrollbarsX
1551{-| -}
1552clip : Attribute msg
1553clip =
1554 Internal.Class Flag.overflow classes.clip
1557{-| -}
1558clipY : Attribute msg
1559clipY =
1560 Internal.Class Flag.overflow classes.clipY
1563{-| -}
1564clipX : Attribute msg
1565clipX =
1566 Internal.Class Flag.overflow classes.clipX
1569{-| Set the cursor to be a pointing hand when it's hovering over this element.
1571pointer : Attribute msg
1572pointer =
1573 Internal.Class Flag.cursor classes.cursorPointer
1576{-| -}
1577type alias Device =
1578 { class : DeviceClass
1579 , orientation : Orientation
1583{-| -}
1584type DeviceClass
1585 = Phone
1586 | Tablet
1587 | Desktop
1588 | BigDesktop
1591{-| -}
1592type Orientation
1593 = Portrait
1594 | Landscape
1597{-| Takes in a Window.Size and returns a device profile which can be used for responsiveness.
1599If you have more detailed concerns around responsiveness, it probably makes sense to copy this function into your codebase and modify as needed.
1602classifyDevice : { window | height : Int, width : Int } -> Device
1603classifyDevice window =
1604 -- Tested in this ellie:
1605 -- https://ellie-app.com/68QM7wLW8b9a1
1606 { class =
1607 let
1608 longSide =
1609 max window.width window.height
1611 shortSide =
1612 min window.width window.height
1613 in
1614 if shortSide < 600 then
1615 Phone
1617 else if longSide <= 1200 then
1618 Tablet
1620 else if longSide > 1200 && longSide <= 1920 then
1621 Desktop
1623 else
1624 BigDesktop
1625 , orientation =
1626 if window.width < window.height then
1627 Portrait
1629 else
1630 Landscape
1634{-| When designing it's nice to use a modular scale to set spacial rythms.
1636 scaled =
1637 Element.modular 16 1.25
1639A modular scale starts with a number, and multiplies it by a ratio a number of times.
1640Then, when setting font sizes you can use:
1642 Font.size (scaled 1) -- results in 16
1644 Font.size (scaled 2) -- 16 * 1.25 results in 20
1646 Font.size (scaled 4) -- 16 * 1.25 ^ (4 - 1) results in 31.25
1648We can also provide negative numbers to scale below 16px.
1650 Font.size (scaled -1) -- 16 * 1.25 ^ (-1) results in 12.8
1653modular : Float -> Float -> Int -> Float
1654modular normal ratio rescale =
1655 if rescale == 0 then
1656 normal
1658 else if rescale < 0 then
1659 normal * ratio ^ toFloat rescale
1661 else
1662 normal * ratio ^ (toFloat rescale - 1)
1665{-| -}
1666mouseOver : List Decoration -> Attribute msg
1667mouseOver decs =
1668 Internal.StyleClass Flag.hover <|
1669 Internal.PseudoSelector Internal.Hover
1670 (Internal.unwrapDecorations decs)
1673{-| -}
1674mouseDown : List Decoration -> Attribute msg
1675mouseDown decs =
1676 Internal.StyleClass Flag.active <|
1677 Internal.PseudoSelector Internal.Active
1678 (Internal.unwrapDecorations decs)
1681{-| -}
1682focused : List Decoration -> Attribute msg
1683focused decs =
1684 Internal.StyleClass Flag.focus <|
1685 Internal.PseudoSelector Internal.Focus
1686 (Internal.unwrapDecorations decs)