git.iko.soy/typeable/elm-ui
on master help sign in
a stylish new design toolkit
commit25850370dc6bce40c22ae91999fba277ee163245 authorMatthew Griffith <mdg.griffith@gmail.com> dateTue 28 Aug 2018, 19:41 (8 years ago) parent0abf848
65 files changed+18187−4
.gitignore+12−1 CHANGES-FROM-STYLE-ELEMENTS.md+202 CSS-LOOKUP.md+18 LICENSE+1−3 README.md+57 elm.json+26 examples/Basic.elm+29 examples/Form.elm+201 examples/Table.elm+65 examples/elm.json+25 experiments/Accumulator.elm+57 experiments/masks/Test.elm+10 experiments/masks/elm.json+27 experiments/masks/src/MaskedInput.elm+130 experiments/masks/src/Notes.elm+189 experiments/palette/PaletteTest.elm+37 experiments/palette/elm.json+27 experiments/palette/src/Palette.elm+214 src/Element.elm+1587 src/Element/Background.elm+232 src/Element/Border.elm+221 src/Element/Events.elm+272 src/Element/Font.elm+332 src/Element/Input.elm+1834 src/Element/Keyed.elm+67 src/Element/Lazy.elm+117 src/Element/Region.elm+111 src/Internal/Flag.elm+260 src/Internal/Grid.elm+264 src/Internal/Model.elm+2847 src/Internal/Style.elm+1628 tests/Tests/Basic.elm+54 tests/Tests/ColumnAlignment.elm+176 tests/Tests/ColumnSpacing.elm+68 tests/Tests/ElementAlignment.elm+111 tests/Tests/Manual/AlignedForm.elm+286 tests/Tests/Manual/All.elm+1263 tests/Tests/Manual/LazyPerformance.elm+523 tests/Tests/Manual/OtherScrollbars.elm+36 tests/Tests/Manual/Overflow.elm+16 tests/Tests/Manual/Scrollbars.elm+62 tests/Tests/Manual/ScrollbarsPls.elm+58 tests/Tests/Manual/TextLayout.elm+43 tests/Tests/Nearby.elm+393 tests/Tests/Palette.elm+41 tests/Tests/RowAlignment.elm+212 tests/Tests/RowSpacing.elm+69 tests/Tests/Run.elm+28 tests/Tests/Table.elm+123 tests/Tests/Transparency.elm+57 tests/Tests/WidthHeight.elm+61 tests/automation/selenium_test.py+59 tests/elm.json+26 tests/gather-styles.html+83 tests/live.sh+1 tests/src/Generator.elm+53 tests/src/Testable.elm+730 tests/src/Testable/Element.elm+1045 tests/src/Testable/Element/Background.elm+27 tests/src/Testable/Element/Font.elm+48 tests/src/Testable/FuzzMain.elm+629 tests/src/Testable/Runner.elm+444 tests/suite/ClassNames.elm+158 tests/suite/Flags.elm+89 tests/suite/elm-package.json+16
.gitignore+12−1
@@ -1,4 +1,15 @@
11# elm-package generated files
2−elm-stuff
2+elm-stuff/
33# elm-repl generated files
44repl-temp-*
5+.DS_Store
6+*/index.html
7+index.html
8+.elm-static-html
9+
10+tests/html-generation/rendered/
11+
12+env
13+elm.js
14+
15+examples/temporary/
CHANGES-FROM-STYLE-ELEMENTS.mdnew file+202
@@ -0,0 +1,202 @@
1+# Compared to Style Elements
2+
3+
4+This was a MAJOR rewrite of Style Elements.
5+
6+
7+
8+* **Major Performance improvement** - Style Elements v5 is much faster than v4 due to a better rendering strategy and generating very minimal html. The rewritten architecture also allows me to explore a few other optimizations, so things may get even faster than they are now.
9+
10+* **Lazy is here!** - It works with no weird caveats.
11+
12+## No Stylesheet
13+
14+You now define styles on the element itself.
15+
16+```
17+el [ Background.color blue, Font.color white ] (text "I'm so stylish!")
18+```
19+
20+These styles are gathered and rendered into a `stylesheet`. This has a few advantages:
21+
22+1. Much faster than rendering as actual inline styles
23+2. More expressive power by allowing style-elements compile to pseudoclasses, css animations and the like.
24+3. Defining styles like this is a really nice workflow.
25+
26+## Reorganization
27+
28+* No `Style` modules anymore, it's all under `Element`.
29+* `Style.Color` and `Style.Shadow` have been merged into `Element.Font`, `Element.Border`, and `Element.background`. So things like `Font.color` and `Border.glow` now exist.
30+* No more `Element.Attributes`, everything has been either moved to `Element` or to a more appropriate module.
31+
32+
33+## Wait isn't Style Elements about separation of layout and style?!
34+
35+It was! So why is there only `Element` and no `StyleSheet` now?
36+
37+The key insight is that it's not so much the separation of layout and style that is important as it is that _properties affecting layout should all be in the view function_. The main thing is _not_ having layout specified through several layers of indirection, but having everything explict and in one place.
38+
39+The new version moves to a more refined version of this idea: **Everything should be explicit and in your view!**
40+
41+
42+## Style Organization
43+
44+Your next question might be "if we don't have a stylesheet, how to we capture our style logic in a nice way?"
45+
46+The main thing I've found is that stylesheets in general seem to be pretty hard to maintain. Even well-typed stylesheets that only allow single classes! You have to manage names for everything, and in the case of large style refactors it's not obvious that style classes would be organized the same way.
47+
48+So, I'm not sure that stylesheets or something like stylesheets are the way to go.
49+
50+My current thinking is that you have 2 really powerful methods of capturing your styling logic.
51+
52+1. **Just put it in a view function.** If you have a few button variations you want, just create a function for each variation. You probably don't need a huge number of variations. You don't need to think of it like recreating bootstrap in style-elements.
53+
54+2. **Capture your colors, font sizes, etc.** Create a `Style` module that captures the **values** you use for your styling. Keep your colors there, as well as values for spacing, font names, and font sizes all in one place. You should consider using a scaling function for things like spacing and fontsizes.
55+
56+
57+## Element.Area
58+
59+The `Element.Area` module is now how you can do accessibility markup.
60+
61+You can do this by adding an area notation to an element's attributes.
62+
63+```elm
64+import Element.Area as Area
65+
66+row [ Area.navigation ]
67+ [ --..my navigation links
68+ ]
69+
70+```
71+Or you can make something an `<h1>`
72+
73+```
74+el [ Area.heading 1 ] (text "Super important stuff")
75+
76+```
77+
78+This means your accessibility markup is separate from your layout markup, which turns out to be really nice.
79+
80+
81+
82+## Alignment
83+
84+Alignment got a _bunch_ of attention to make it more powerful and intuitive.
85+
86+* _Alignment_ now applies to the element it's attached to, so Alignment on `row` and `column` does not apply to the children but to the `row` or `column` itself.
87+
88+* _It works everywhere!_ Previously, if you set a child as `alignLeft` in a row, nothing would happen. The main weirdness that had to be resolved is what happens to the other elements when an el in the middle of a row is aligned. The answer I came up with is that it will push other elements to the side.
89+
90+```
91+ alignLeft(pushes element on the left of it, to the left)
92+ | center(is the default now)
93+ | | alignRight
94+ v v v
95+ |-el-|-el-|---------|-el-|---------------|-el-|
96+```
97+
98+Also of note, is that if something is `center`, then it will truly be in the center.
99+
100+* _Centered by Default_ - `el`s are centered by default.
101+
102+
103+## Things that have been removed/deprecated
104+
105+
106+**Percent** - `percent` is no longer there for width/height values. Just use `fill`, because you were probably just using `percent 100`, right? If you really need something like percent, you can manage it pretty easy with `fillPortion`. The main reason this goes away is that `percent` allows you to accidently overflow your element when trying to sum up multiple elements.
107+
108+**Grids** - `grid`, and `namedGrid` are gone. The reason for this is that for 95% of cases, just composing something with `row` and `column` results in _much_ nicer code. I'm open to see arguments for `grid`, but I need to see specific realworld usecases that can't be done using `row` and `column`.
109+
110+**WrappedRows/Columns** - `wrappedRow` and `wrappedColumn` are gone. From what I can see these cases don't show up very often. Removing them allows me to be cleaner with the internal style-elements code as well.
111+
112+**When/WhenJust** - `when` and `whenJust` are removed, though you can easily make a convenience function for yourself! I wanted the library to place an emphasis on common elm constructs instead of library-specific ones. As far as shortcuts, they don't actually save you much anyway.
113+
114+**Full, Spacer** - `full` and `spacer` have been removed in order to follow the libraries priority of explicitness. `full` would override the parents padding, while `spacer` would override the parent's `spacing`. Both can be achieved with the more common primities of `row`, `column` and `spacing`, and potentially some nesting of layouts.
115+
116+
117+
118+# New Version of the Alpha
119+
120+- `Font.weight` has been removed in favor of `Font.extraBold`, `Font.regular`, `Font.light`, etc. All weights from 100 - 900 are represented.
121+- `Background.image` and `Background.fittedImage` will place a centered background image, instead of anchoring at the top left.
122+- `fillBetween { min : Maybe Int, max : Maybe Int}` is now present for `min/max height/width` behavior. It works like fill, but with an optional top and lower bound.
123+- `transparent` - Set an element as transparent. It will take up space, but otherwise be transparent and unclickable.
124+- `alpha` can now be set for an element.
125+- `attribute` has been renamed `htmlAttribute` to better convey what it's used for.
126+- `Element.Area` has been renamed `Element.Region` to avoid confusion with `WAI ARIA` stuff.
127+- `center` has been renamed `centerX`
128+
129+
130+
131+# New Default Behavior
132+
133+The default logic has been made more consistent and hopefully more intuitive.
134+
135+Al elements start with `width/height shrink`, which means that they are the size of their contents.
136+
137+
138+# PseudoClass Support
139+
140+`Element.mouseOver`, `Element.focused`, and `Element.mouseDown` are available to style `:hover`, `:focus` and `:active`.
141+
142+Only a small subset of properties are allowed here or else the compiler will give you an error.
143+
144+This also introduced some new type aliases for attributes.
145+
146+`Attribute msg` - What you're used to. This **cannot** be used in a mouseOver/focused/etc.
147+
148+`Attr decorative msg` - A new attribute alias for attributes that can be used as a normal attribute or in `mouseOver`, `focused`, etc. I like to think of this as a *Decorative Attribute*.
149+
150+
151+# Input
152+
153+`Input.select` has been removed. Ultimately this came down to it being recommended against for most UX purposes.
154+
155+If you're looking for a replacement, consider any of these options which will likely create a better experience:
156+
157+- Input.checkbox
158+- Input.radio/Input.radioRow with custom styling
159+- Input.text with some sort of suggestion/autocomplete attached to it.
160+
161+If you still need to have a select menu, you can either:
162+
163+- *Embed one* using `html`
164+- [Craft one by having a hidden `radio` that is shown on focus.](https://gist.github.com/mdgriffith/b99b7ee04eaabaac042572e328a85345) You'll have to store some state that indicates if the menu is open or not, but you'd have to do that anyway if this library was directly supporting `select`.
165+
166+*Input.Notices* have been removed, which includes warnings and errors. Accessibility is important to this library and this change is actually meant to make it easier to have good form validation feedback.
167+
168+You can just use `above`/`below` when you need to show a validation message and it will be announced politely to users using screen readers.
169+
170+Notices were originally annotated as errors or warnings so that `aria-invalid` could be attached. However, it seems to me that having the changes be announced politely is better than having the screen reader just say "Yo, something's invalid". You now have more control over the feedback! Craft your messages well :)
171+
172+
173+Type aliases for the records used for inputs were also removed because it gives a nicer error message which references specific fields instead of the top level type alias.
174+
175+
176+
177+# New Testing Capabilities
178+
179+A test suite of ~1.6k layout tests was written(whew!). All of these tests pass on Chrome, Firefox, Safari, Edge, and IE11.
180+
181+# Overview of other changes
182+
183+- `Font.lineHeight` has been removed. Instead, `spacing` now works on paragraphs.
184+- `Element.empty` has been renamed `Element.none` to be more consistent with other elm libraries.
185+- `Device` no longer includes `window.width` and `window.height`. Previously every view function that depends on `device` was forced to rerender when the window was resized, which meant you couldn't take advantage of lazy. If you do need the window coordinates you can save them separately.
186+- *Fewer nodes rendered* - So, things should be faster!
187+- `fillBetween` has been replaced by `Element.minimum` and `Element.maximum`.
188+
189+So now you can do things like
190+
191+```elm
192+view =
193+ el
194+ [ width
195+ (fill
196+ |> minimum 20
197+ |> maximum 200
198+ )
199+ ]
200+ (text "woohoo, I have a min and max")
201+
202+```
CSS-LOOKUP.mdnew file+18
@@ -0,0 +1,18 @@
1+# CSS Concepts and where to find them
2+
3+This library creates a new language around layout and style, though if you're already used to CSS, you're probably wondering where certain concepts lie.
4+
5+> I know how I can do it in CSS, but how could I approach the problem using Style Elements?
6+
7+
8+CSS | Style Elements | Description
9+-------|------------------|------------
10+`position:absolute` | `above`, `below`, `onRight`, `onLeft`, `inFront`, `behindContent` | In Style Elements we can attach elements relative to another element. They won't affect normal flow, just like `position:absolute`
11+`position:fixed` | `inFront` if it's attached to the `Element.layout` element. | `position:fixed` needs to be at the top of your view or else it can break in seemingly random ways. Did you know `position:fixed` will position something relative to the viewport *OR* any parent that uses `filter`, `transform` or `perspective`? So you add a blur effect and your layout breaks...
12+`z-index` | __N/A__ | One of the goals of the library was to make `z-index` a behind-the-scenes detail. If you ever encounter a situation where you feel like you actually need it, let me know on slack or through the issues.
13+`float:left` `float:right` | `alignLeft` or `alignRight` when inside a `paragraph` or a `textColumn` |
14+`opacity` | `alpha` |
15+`margin` | __N/A__ Instead, check out `padding` and `spacing` | `margin` in CSS was designed to fight with `padding`. This library was designed to minimize override logic and properties that fight with each other in order to create a layout language that is predictable and easy to think about. The result is that in style elements, there's generally only *one place* where an effect can happen.
16+`:hover`, `:focus`, `:active` | `mouseOver`, `focused`, `mouseDown` | Only certain styles are allowed to be in a pseudo state. They have the type `Attr decorative msg`, which means they can be either an `Attribute` or a `Decoration`.
17+`<form>` | __N/a__ | __Elm__ already has a mechanism for submiting data to a server, namely the `Http` package. There has been some mention that the `form` element might be beneficial accessibility-wise, which I'm definitely open to hearing about!
18+`onSubmit` | __N/A__ | Similar to `<form>`, there is no `onSubmit` behavior. Likely if you're attempting to capture some of the keybaord related behavior of `onSubmit`, you're likely better just crafting a keyboard even handler in the first place!
LICENSE+1−3
@@ -1,5 +1,3 @@
1−BSD 3-Clause License
2−
31Copyright (c) 2018, Matthew Griffith
42All rights reserved.
53
@@ -13,7 +11,7 @@ modification, are permitted provided that the following conditions are met:
1311 this list of conditions and the following disclaimer in the documentation
1412 and/or other materials provided with the distribution.
1513
16−* Neither the name of the copyright holder nor the names of its
14+* Neither the name of Elm UI nor the names of its
1715 contributors may be used to endorse or promote products derived from
1816 this software without specific prior written permission.
1917
README.mdnew file+57
@@ -0,0 +1,57 @@
1+# A New Language for Layout and Interface
2+
3+CSS and HTML are actually quite difficult to use when you're trying to do the layout and styling of a web page.
4+
5+This library is a complete alternative to HTML and CSS. Basically you can just write your app using this library and (mostly) never have to think about HTML and CSS again.
6+
7+The high level goal of this library is to be a **design toolkit** that draws inspiration from the domains of design, layout, and typography, as opposed to drawing from the ideas as implemented in CSS and HTML.
8+
9+This means:
10+
11+* Writing and designing your layout and `view` should be as **simple and as fun** as possible.
12+* Many layout errors (like you'd run into using CSS) **are just not possible to write** in the first place!
13+* Everything should just **run fast.**
14+* **Layout and style are explicit and easy to modify.** CSS and HTML as tools for a layout language are hard to modify because there's no central place that represents your layout. You're generally forced to bounce back and forth between multiple definitions in multiple files in order to adjust layout, even though it's probably the most common thing you'll do.
15+
16+
17+```elm
18+import Color exposing (blue, darkBlue)
19+import Element exposing (Element, el, text, row, alignRight)
20+import Element.Background as Background
21+import Element.Border as Border
22+
23+
24+main =
25+ Element.layout []
26+ myElement
27+
28+
29+myRowOfStuff =
30+ row [ width fill ]
31+ [ myElement
32+ , myElement
33+ , el [ alignRight ] myElement
34+ ]
35+
36+
37+myElement : Element msg
38+myElement =
39+ el
40+ [ Background.color blue
41+ , Border.color darkBlue
42+ ]
43+ (text "You've made a stylish element!")
44+```
45+
46+
47+
48+
49+## History
50+
51+The work is based off of a rewrite of the [Style Elements](https://github.com/mdgriffith/style-elements) library. A lot of that work was orignally released under the [Stylish Elephants](https://github.com/mdgriffith/stylish-elephants) project.
52+
53+
54+
55+
56+
57+
elm.jsonnew file+26
@@ -0,0 +1,26 @@
1+{
2+ "type": "package",
3+ "name": "mdgriffith/elm-ui",
4+ "summary": "Layout and style that's easy to refactor, all without thinking about CSS.",
5+ "license": "BSD-3-Clause",
6+ "version": "1.0.0",
7+ "exposed-modules": [
8+ "Element",
9+ "Element.Input",
10+ "Element.Events",
11+ "Element.Background",
12+ "Element.Border",
13+ "Element.Font",
14+ "Element.Lazy",
15+ "Element.Keyed",
16+ "Element.Region"
17+ ],
18+ "elm-version": "0.19.0 <= v < 0.20.0",
19+ "dependencies": {
20+ "elm/core": "1.0.0 <= v < 2.0.0",
21+ "elm/html": "1.0.0 <= v < 2.0.0",
22+ "elm/json": "1.0.0 <= v < 2.0.0",
23+ "elm/virtual-dom": "1.0.0 <= v < 2.0.0"
24+ },
25+ "test-dependencies": {}
26+}
examples/Basic.elmnew file+29
@@ -0,0 +1,29 @@
1+module Main exposing (..)
2+
3+{-| -}
4+
5+import Element exposing (..)
6+import Element.Background as Background
7+import Element.Font as Font
8+import Element.Input
9+import Element.Lazy
10+
11+
12+main =
13+ Element.layout
14+ [ Background.color (rgba 0 0 0 1)
15+ , Font.color (rgba 1 1 1 1)
16+ , Font.italic
17+ , Font.size 32
18+ , Font.family
19+ [ Font.external
20+ { url = "https://fonts.googleapis.com/css?family=EB+Garamond"
21+ , name = "EB Garamond"
22+ }
23+ , Font.sansSerif
24+ ]
25+ ]
26+ <|
27+ el
28+ [ centerX, centerY ]
29+ (text "Hello stylish friend!")
examples/Form.elmnew file+201
@@ -0,0 +1,201 @@
1+module Main exposing (..)
2+
3+{-| -}
4+
5+import Browser
6+import Element exposing (..)
7+import Element.Background as Background
8+import Element.Border as Border
9+import Element.Font as Font
10+import Element.Input as Input
11+import Element.Region as Region
12+
13+
14+white =
15+ Element.rgb 1 1 1
16+
17+
18+grey =
19+ Element.rgb 0.9 0.9 0.9
20+
21+
22+blue =
23+ Element.rgb 0 0 0.8
24+
25+
26+red =
27+ Element.rgb 0.8 0 0
28+
29+
30+darkBlue =
31+ Element.rgb 0 0 0.9
32+
33+
34+main =
35+ Browser.sandbox
36+ { init = init
37+ , view = view
38+ , update = update
39+ }
40+
41+
42+init =
43+ { username = ""
44+ , password = ""
45+ , agreeTOS = False
46+ , comment = "Extra hot sauce?\n\n\nYes pls"
47+ , lunch = Gyro
48+ , spiciness = 2
49+ }
50+
51+
52+type alias Form =
53+ { username : String
54+ , password : String
55+ , agreeTOS : Bool
56+ , comment : String
57+ , lunch : Lunch
58+ , spiciness : Float
59+ }
60+
61+
62+type Msg
63+ = Update Form
64+
65+
66+update msg model =
67+ case Debug.log "msg" msg of
68+ Update new ->
69+ new
70+
71+
72+type Lunch
73+ = Burrito
74+ | Taco
75+ | Gyro
76+
77+
78+view model =
79+ Element.layout
80+ [ Font.size 20
81+ ]
82+ <|
83+ Element.column [ width (px 800), height shrink, centerY, centerX, spacing 36, padding 10, explain Debug.todo ]
84+ [ el
85+ [ Region.heading 1
86+ , alignLeft
87+ , Font.size 36
88+ ]
89+ (text "Welcome to the Stylish Elephants Lunch Emporium")
90+ , Input.radio
91+ [ spacing 12
92+ , Background.color grey
93+ ]
94+ { selected = Just model.lunch
95+ , onChange = \new -> Update { model | lunch = new }
96+ , label = Input.labelAbove [ Font.size 14, paddingXY 0 12 ] (text "What would you like for lunch?")
97+ , options =
98+ [ Input.option Gyro (text "Gyro")
99+ , Input.option Burrito (text "Burrito")
100+ , Input.option Taco (text "Taco")
101+ ]
102+ }
103+ , Input.username
104+ [ spacing 12
105+ , below
106+ (el
107+ [ Font.color red
108+ , Font.size 14
109+ , alignRight
110+ , moveDown 6
111+ ]
112+ (text "This one is wrong")
113+ )
114+ ]
115+ { text = model.username
116+ , placeholder = Just (Input.placeholder [] (text "username"))
117+ , onChange = \new -> Update { model | username = new }
118+ , label = Input.labelAbove [ Font.size 14 ] (text "Username")
119+ }
120+ , Input.currentPassword [ spacing 12, width shrink ]
121+ { text = model.password
122+ , placeholder = Nothing
123+ , onChange = \new -> Update { model | password = new }
124+ , label = Input.labelAbove [ Font.size 14 ] (text "Password")
125+ , show = False
126+ }
127+ , Input.multiline
128+ [ height shrink
129+ , spacing 12
130+
131+ -- , padding 6
132+ ]
133+ { text = model.comment
134+ , placeholder = Just (Input.placeholder [] (text "Extra hot sauce?\n\n\nYes pls"))
135+ , onChange = \new -> Update { model | comment = new }
136+ , label = Input.labelAbove [ Font.size 14 ] (text "Leave a comment!")
137+ , spellcheck = False
138+ }
139+ , Input.checkbox []
140+ { checked = model.agreeTOS
141+ , onChange = \new -> Update { model | agreeTOS = new }
142+ , icon = Input.defaultCheckbox
143+ , label = Input.labelRight [] (text "Agree to Terms of Service")
144+ }
145+ , Input.slider
146+ [ Element.height (Element.px 30)
147+ , Element.behindContent
148+ (Element.el
149+ [ Element.width Element.fill
150+ , Element.height (Element.px 2)
151+ , Element.centerY
152+ , Background.color grey
153+ , Border.rounded 2
154+ ]
155+ Element.none
156+ )
157+ ]
158+ { onChange = \new -> Update { model | spiciness = new }
159+ , label = Input.labelAbove [] (text ("Spiciness: " ++ String.fromFloat model.spiciness))
160+ , min = 0
161+ , max = 3.2
162+ , step = Nothing
163+ , value = model.spiciness
164+ , thumb =
165+ Input.defaultThumb
166+ }
167+ , Input.slider
168+ [ Element.width (Element.px 40)
169+ , Element.height (Element.px 200)
170+ , Element.behindContent
171+ (Element.el
172+ [ Element.height Element.fill
173+ , Element.width (Element.px 2)
174+ , Element.centerX
175+ , Background.color grey
176+ , Border.rounded 2
177+ ]
178+ Element.none
179+ )
180+ ]
181+ { onChange = \new -> Update { model | spiciness = new }
182+ , label = Input.labelAbove [] (text ("Spiciness: " ++ String.fromFloat model.spiciness))
183+ , min = 0
184+ , max = 3.2
185+ , step = Nothing
186+ , value = model.spiciness
187+ , thumb =
188+ Input.defaultThumb
189+ }
190+ , Input.button
191+ [ Background.color blue
192+ , Font.color white
193+ , Border.color darkBlue
194+ , paddingXY 32 16
195+ , Border.rounded 3
196+ , width fill
197+ ]
198+ { onPress = Nothing
199+ , label = Element.text "Place your lunch order!"
200+ }
201+ ]
examples/Table.elmnew file+65
@@ -0,0 +1,65 @@
1+module Main exposing (..)
2+
3+{-| -}
4+
5+import Element exposing (..)
6+import Element.Background as Background
7+import Element.Font as Font
8+import Element.Input
9+import Element.Lazy
10+
11+
12+type alias Person =
13+ { firstName : String
14+ , lastName : String
15+ }
16+
17+
18+persons : List Person
19+persons =
20+ [ { firstName = "David"
21+ , lastName = "Bowie"
22+ }
23+ , { firstName = "Florence"
24+ , lastName = "Welch"
25+ }
26+ ]
27+
28+
29+main =
30+ Element.layout
31+ [ Background.color (rgba 0 0 0 1)
32+ , Font.color (rgba 1 1 1 1)
33+ , Font.italic
34+ , Font.size 32
35+ , Font.family
36+ [ Font.external
37+ { url = "https://fonts.googleapis.com/css?family=EB+Garamond"
38+ , name = "EB Garamond"
39+ }
40+ , Font.sansSerif
41+ ]
42+ ]
43+ <|
44+ Element.table
45+ [ Element.centerX
46+ , Element.centerY
47+ , Element.spacing 5
48+ , Element.padding 10
49+ ]
50+ { data = persons
51+ , columns =
52+ [ { header = Element.text "First Name"
53+ , width = px 200
54+ , view =
55+ \person ->
56+ Element.text person.firstName
57+ }
58+ , { header = Element.text "Last Name"
59+ , width = fill
60+ , view =
61+ \person ->
62+ Element.text person.lastName
63+ }
64+ ]
65+ }
examples/elm.jsonnew file+25
@@ -0,0 +1,25 @@
1+{
2+ "type": "application",
3+ "source-directories": [
4+ ".",
5+ "../src/"
6+ ],
7+ "elm-version": "0.19.0",
8+ "dependencies": {
9+ "direct": {
10+ "elm/browser": "1.0.0",
11+ "elm/core": "1.0.0",
12+ "elm/html": "1.0.0",
13+ "elm/json": "1.0.0",
14+ "elm/virtual-dom": "1.0.0"
15+ },
16+ "indirect": {
17+ "elm/time": "1.0.0",
18+ "elm/url": "1.0.0"
19+ }
20+ },
21+ "test-dependencies": {
22+ "direct": {},
23+ "indirect": {}
24+ }
25+}
experiments/Accumulator.elmnew file+57
@@ -0,0 +1,57 @@
1+module Accumulation exposing (..)
2+
3+{-| -}
4+
5+import Set
6+
7+
8+type Style
9+ = Color String Float Float Float Float
10+ | Spacing String Float
11+ | Font String (List String)
12+
13+
14+key style =
15+ case style of
16+ Color k _ _ _ _ ->
17+ k
18+
19+ Spacing k _ ->
20+ k
21+
22+ Font k _ ->
23+ k
24+
25+
26+type Element
27+ = Element (List Style) (List Element)
28+ | None
29+
30+
31+render element =
32+ case element of
33+ None ->
34+ []
35+
36+ Element styles children ->
37+ let
38+ childrenStyles =
39+ List.foldr render [] children
40+ in
41+ styles ++ childrenStyles
42+
43+
44+finalize styles =
45+ List.foldl deduplicate ( Set.empty, [] ) styles
46+
47+
48+deduplicate style ( cached, styles ) =
49+ if Set.member (key style) then
50+ ( cached, styles )
51+ else
52+ ( Set.insert (key style), style :: styles )
53+
54+
55+toHtml element =
56+ render element
57+ |> finalize
experiments/masks/Test.elmnew file+10
@@ -0,0 +1,10 @@
1+module Main exposing (..)
2+
3+{-| -}
4+
5+import Html
6+import MaskedInput
7+
8+
9+main =
10+ Html.text "Testing"
experiments/masks/elm.jsonnew file+27
@@ -0,0 +1,27 @@
1+{
2+ "type": "application",
3+ "source-directories": [
4+ ".",
5+ "src"
6+ ],
7+ "elm-version": "0.19.0",
8+ "dependencies": {
9+ "direct": {
10+ "elm/browser": "1.0.0",
11+ "elm/core": "1.0.0",
12+ "elm/html": "1.0.0",
13+ "elm/json": "1.0.0",
14+ "elm/parser": "1.0.0",
15+ "elm/virtual-dom": "1.0.0",
16+ "elm-community/list-extra": "8.0.0"
17+ },
18+ "indirect": {
19+ "elm/time": "1.0.0",
20+ "elm/url": "1.0.0"
21+ }
22+ },
23+ "test-dependencies": {
24+ "direct": {},
25+ "indirect": {}
26+ }
27+}
experiments/masks/src/MaskedInput.elmnew file+130
@@ -0,0 +1,130 @@
1+module MaskedInput exposing (..)
2+
3+{-| When we've created a Mask, it should be able to:
4+
5+ - The validator's job is only to say if an entire string is valid.
6+ - If a string is valid, then a message is sent out with the updated value.
7+ - If a string is not valid, then the last(i.e. the current) value is sent out on a message.
8+ - We have to send out a message on every input or else we'll get out of sync.(?)
9+
10+ - The formatter's job is to take an input string and to format it into a view-string
11+
12+ - The capturing parser's job is to transform the input string into the desired value.
13+ - If the parser fails, we keep returning `Partial`
14+ - As soon as it succeeds, we can return `Full`, which can have a value extracted.
15+
16+First pass at full description.
17+
18+Mask is defined in a view.
19+
20+ - An initial string is potentially given.
21+ - String is formatted and displayed
22+ - onInput handler is registered which parses the value, and creates either a Partial or a Full
23+ - The onInput handler will return the formatted string.
24+ ->? we could diff it against a previous formatted string to see what changed. Maybe too complicated
25+ ->? Capture keyboard events directly and make modifications there. What about pasting?
26+ ->! Have the parser operate directly on the formatted string.
27+
28+-}
29+
30+import Parser exposing (Parser)
31+
32+
33+type Masked input
34+ = Mask
35+ { capture : Parser input -- String -> input
36+ , format : List Formatter -- tel: "86" -> "(86 ) - "
37+ , validate : List Validator -- Is a character allowed to be typed
38+ }
39+
40+
41+type Validator
42+ = Validator Int (Char -> Bool)
43+
44+
45+type Formatter
46+ = Exactly String
47+ | FromInput Int (Maybe Hint) -- hint is shown if no string can be retrieved
48+
49+
50+type alias Hint =
51+ String
52+
53+
54+type Captured thing
55+ = Partial String
56+ | Full thing String
57+
58+
59+capture : input -> Masked input
60+capture value =
61+ Mask
62+ { capture = Parser.succeed value
63+ , format = []
64+ , validate = []
65+ }
66+
67+
68+{-| Shows a static string.
69+-}
70+show str (Mask mask) =
71+ Mask
72+ { mask
73+ | format = mask.format ++ [ Exactly str ]
74+ , capture = Parser.token str
75+ }
76+
77+
78+type alias Match =
79+ { length : Int
80+ , valid : Char -> Bool
81+ , hint : Maybe String
82+ }
83+
84+
85+{-| -}
86+match matcher (Mask mask) =
87+ Mask
88+ { mask
89+ | format = mask.format ++ [ FromInput matcher.length matcher.hint ]
90+ , validate = mask.validate ++ [ Validator matcher.length matcher.valid ]
91+ , capture = Parser.exactly matcher.length matcher.valid
92+ }
93+
94+
95+
96+-- map : (a -> b) -> Masked a -> Masked b
97+-- map =
98+-- Debug.crash "TODO"
99+-- map2 :
100+-- (a -> b -> value)
101+-- -> Masked a
102+-- -> Masked b
103+-- -> Masked value
104+-- map2 =
105+-- Debug.crash "TODO"
106+
107+
108+view : String -> Masked input -> Element msg
109+view =
110+ Debug.crash
111+
112+
113+captureValue : String -> Masked input -> Captured input
114+captureValue input (Mask mask) =
115+ case Parser.run mask.capture input of
116+ Ok val ->
117+ Full val input
118+
119+ Err _ ->
120+ Partial input
121+
122+
123+valid : Captured input -> Maybe input
124+valid cap =
125+ case cap of
126+ Partial _ ->
127+ Nothing
128+
129+ Full result _ ->
130+ Just result
experiments/masks/src/Notes.elmnew file+189
@@ -0,0 +1,189 @@
1+module Element.Input.Mask exposing (..)
2+
3+{-| With masked input we can simultaneously descibe:
4+
5+ - a parser to only allow specific arguments
6+ - We have a set of expected characters and how many of them to expect.
7+ - Static formating. Meaning show a `/` between two numbers, but it's not part of the input
8+ - Hints. These are mini placeholder values for a section of your input.
9+ - Autocomplete suggestions. These are suggestions that can be selected and completed with `tab`.
10+
11+-}
12+
13+
14+type Masked input
15+ = Mask (List Pattern)
16+
17+
18+type Pattern
19+ = Capture Limit (Char -> Bool)
20+ | Decimal
21+ | Decoration String
22+
23+
24+type Limit
25+ = NoLimit
26+ | Min Int
27+ | Max Int
28+ | MinMax Int Int
29+
30+
31+type Input thing
32+ = Partial String
33+ | Full thing String
34+
35+
36+type alias CreditCard =
37+ { number : String
38+ , expMonth : String
39+ , expYear : String
40+ , ccv : String
41+ }
42+
43+
44+type alias CreditCardNumber =
45+ String
46+
47+
48+example =
49+ capture (\one two three four -> CreditCardNumber (one ++ two ++ three ++ four))
50+ |> Mask.stringWith
51+ { length = 4
52+ , valid = String.isDigit
53+ , hint = "1234"
54+ }
55+ |> Mask.show " "
56+ |> Mask.stringWith
57+ { length = 4
58+ , valid = String.isDigit
59+ , hint = "1234"
60+ }
61+ |> Mask.show " "
62+ |> Mask.stringWith
63+ { length = 4
64+ , valid = String.isDigit
65+ , hint = "1234"
66+ }
67+ |> Mask.space
68+ |> Mask.stringWith
69+ { length = 4
70+ , valid = String.isDigit
71+ , hint = "1234"
72+ }
73+ |> Mask.andThen
74+ (\creditcard ->
75+ capture (CreditCard creditNumber)
76+ |> Mask.show "1234"
77+ -- last four digits of credit card
78+ |> Mask.space
79+ |> Mask.stringWith
80+ { length = 2
81+ , valid = String.isDigit
82+ , hint = "MM"
83+ }
84+ |> Mask.show "/"
85+ |> Mask.stringWith
86+ { length = 2
87+ , valid = String.isDigit
88+ , hint = "YY"
89+ }
90+ |> Mask.space
91+ |> Mask.stringWith
92+ { length = 4
93+ , valid = String.isDigit
94+ , hint = "CCV"
95+ }
96+ )
97+
98+
99+float =
100+ Mask.int
101+ |> Mask.token "."
102+ |> Mask.andThen
103+ (\one ->
104+ masked (\two -> one + two)
105+ --combine ints in a way
106+ |> Mask.token (toString one)
107+ |> Mask.token "."
108+ |> Mask.stringWith
109+ { length = 3 -- No length restrictions
110+ , valid = String.isDigit
111+ , hint = "CCV"
112+ }
113+ )
114+
115+
116+{-| <https://ellie-app.com/JJxYGFKptVa1>
117+
118+<https://ellie-app.com/JKMJgSTtn2a1>
119+
120+<https://ellie-app.com/KdY8X99fqba1>
121+
122+-}
123+
124+
125+
126+-- type Masked input
127+-- = Mask
128+-- { capture : Parser input -- String -> input
129+-- , format : List Formatter -- tel: "86" -> "(86 ) - "
130+-- , validate : List Validator -- Is a character allowed to be typed
131+-- }
132+-- type Validator
133+-- = Match Int (Char -> Bool)
134+-- type Formatter
135+-- = Exactly String
136+-- | FromInput Int Hint -- hint is shown if no string can be retrieved
137+-- type alias Hint =
138+-- String
139+-- -- type Masked input =
140+-- -- { parser : Parser input
141+-- -- ,
142+-- -- }
143+-- {-| We want this to be in
144+-- ---> formatting
145+-- -x-> result
146+-- -}
147+-- show str mask =
148+-- mask
149+-- {-| ---> formatting
150+-- ---> result
151+-- | if nothing's parsed
152+-- hint ---> formatting
153+-- hint -x-> result
154+-- -}
155+-- stringWith options mask =
156+-- mask
157+-- type Pattern input
158+-- = Capture Limit (Char -> Bool)
159+-- | Decimal
160+-- | Show String
161+-- type Limit
162+-- = NoLimit
163+-- | Min Int
164+-- | Max Int
165+-- | MinMax Int Int
166+-- type Captured input
167+-- = Partial String
168+-- | Full input String
169+-- {-| A placeholder ot represent different pieces having different styles
170+-- -}
171+-- type Styled
172+-- = Styled String String
173+-- render : String -> Masked input -> List Styled
174+-- captureValue : String -> Masked input -> Captured input
175+-- capture : input -> Masked input
176+-- map : (a -> b) -> Masked a -> Masked b
177+-- map2 :
178+-- (a -> b -> value)
179+-- -> Masked a
180+-- -> Masked b
181+-- -> Masked value
182+-- andThen : (a -> Masked b) -> Masked a -> Masked b
183+-- valid : Captured input -> Maybe input
184+-- valid cap =
185+-- case cap of
186+-- Partial _ ->
187+-- Nothing
188+-- Full result _ ->
189+-- Just result
experiments/palette/PaletteTest.elmnew file+37
@@ -0,0 +1,37 @@
1+module Main exposing (..)
2+
3+import Element exposing (..)
4+import Html
5+import Palette
6+
7+
8+{- First, Define our Palette
9+
10+ Usually this would be in a different file.
11+
12+-}
13+
14+
15+type alias MyColorPalette =
16+ { primary : Palette.Protected Color
17+ , secondary : Palette.Protected Color
18+ }
19+
20+myColors : Palette.Colors MyColorPalette
21+myColors =
22+ Palette.colors MyColorPalette
23+ |> Palette.color (rgb 1 0 0)
24+ |> Palette.color (rgb 1 0 1)
25+
26+main =
27+ Palette.layout myColors
28+ []
29+ (Palette.Element
30+ [
31+ -- Palette.bgColor red
32+ --
33+ Palette.bgColor .primary
34+ -- Palette.bgColor (Palette.dynamic (rgb 0 1 0))
35+ ]
36+ []
37+ )
experiments/palette/elm.jsonnew file+27
@@ -0,0 +1,27 @@
1+{
2+ "type": "application",
3+ "source-directories": [
4+ ".",
5+ "src",
6+ "../src/"
7+ ],
8+ "elm-version": "0.19.0",
9+ "dependencies": {
10+ "direct": {
11+ "elm/browser": "1.0.0",
12+ "elm/core": "1.0.0",
13+ "elm/html": "1.0.0",
14+ "elm/json": "1.0.0",
15+ "elm/virtual-dom": "1.0.0",
16+ "elm-community/list-extra": "8.0.0"
17+ },
18+ "indirect": {
19+ "elm/time": "1.0.0",
20+ "elm/url": "1.0.0"
21+ }
22+ },
23+ "test-dependencies": {
24+ "direct": {},
25+ "indirect": {}
26+ }
27+}
experiments/palette/src/Palette.elmnew file+214
@@ -0,0 +1,214 @@
1+module Palette exposing (..)
2+
3+{-| With palettes, we want the dev to be able to
4+
5+ - specify some colors
6+ - those colors then need to be rendered by the layout
7+ - those colors can then be referred to by name in the layout
8+ - A lookup is done to retrieve the classname
9+
10+-}
11+
12+import Element exposing (Color)
13+import Html exposing (Html)
14+import Html.Attributes
15+import Internal.Model as Internal
16+
17+
18+{- Declare a Palette of Colors (or any value, but colors for not) -}
19+{- -}
20+
21+
22+layout : Colors colors -> List (Attribute colors msg) -> Element colors msg -> Html msg
23+layout colorPalette attrs child =
24+ -- Render a "stylesheet"
25+ let
26+ ( html, dynamicStyles ) =
27+ renderPaletteElement colorPalette (Element attrs [ child ])
28+ in
29+ Html.div [ Html.Attributes.class "root" ]
30+ [ Html.text "static stylesheet"
31+ , Html.div
32+ [ Html.Attributes.style "padding" "20px"
33+ , Html.Attributes.style "whitespace" "pre"
34+ , Html.Attributes.style "border-radius" "4px"
35+ , Html.Attributes.style "background-color" "#CCDDCC"
36+ , Html.Attributes.style "font-family" "Open Sans"
37+ ]
38+ [ Html.text (String.join "\n" (renderColorPalette colorPalette)) ]
39+ , Html.node "style"
40+ []
41+ [ Html.text ("html,body,.root {width: 100%; height: 100%;}.ui {min-width:100px; min-height: 100px;}" ++ String.join "\n" (renderColorPalette colorPalette))
42+ ]
43+ , Html.text "dynamic stylesheet"
44+ , Html.div
45+ [ Html.Attributes.style "padding" "20px"
46+ , Html.Attributes.style "whitespace" "pre"
47+ , Html.Attributes.style "border-radius" "4px"
48+ , Html.Attributes.style "background-color" "#FFDDCC"
49+ , Html.Attributes.style "font-family" "Open Sans"
50+ ]
51+ [ Html.text (String.join "\n" dynamicStyles) ]
52+ , Html.node "style" [] [ Html.text (String.join "\n" dynamicStyles) ]
53+ , Html.text "content:"
54+ , html
55+ ]
56+
57+
58+renderColor clr =
59+ Internal.formatColor clr
60+
61+
62+colorRule clr =
63+ ".bg-clr-"
64+ ++ Internal.formatColorClass clr
65+ ++ "{ background-color:"
66+ ++ Internal.formatColor clr
67+ ++ " }"
68+
69+
70+renderColorPalette : Colors colors -> List String
71+renderColorPalette (Colors palette) =
72+ List.map
73+ colorRule
74+ palette.values
75+
76+
77+renderPaletteElement : Colors colors -> InternalElement (colors -> Protected Color) msg -> ( Html msg, List String )
78+renderPaletteElement colorPalette (Element attrs children) =
79+ let
80+ gatherAttr attr ( attributes, myAttrStyles ) =
81+ let
82+ ( cls, style ) =
83+ renderPaletteAttribute colorPalette attr
84+ in
85+ ( cls ++ " " ++ attributes, style :: myAttrStyles )
86+
87+ ( classes, attrStyles ) =
88+ List.foldl gatherAttr ( "", [] ) attrs
89+
90+ ( renderedChildren, styles ) =
91+ List.foldr gather ( [], attrStyles ) children
92+
93+ gather child ( rendered, existingStyles ) =
94+ let
95+ ( childHtml, childStyles ) =
96+ renderPaletteElement colorPalette child
97+ in
98+ ( childHtml :: rendered, childStyles ++ existingStyles )
99+ in
100+ ( Html.div [ Html.Attributes.class ("ui " ++ classes) ]
101+ renderedChildren
102+ , styles
103+ )
104+
105+
106+renderPaletteAttribute : Colors colors -> InternalAttribute (colors -> Protected Color) msg -> ( String, String )
107+renderPaletteAttribute (Colors palette) attribute =
108+ case attribute of
109+ InternalAttribute ->
110+ ( "ui", "" )
111+
112+ ColorStyle colorLookup ->
113+ case colorLookup palette.protected of
114+ Dynamic clr ->
115+ ( "bg-clr-" ++ Internal.formatColorClass clr, colorRule clr )
116+
117+ Protected clr ->
118+ ( "bg-clr-" ++ Internal.formatColorClass clr, "" )
119+
120+
121+renderAttribute : Colors color -> InternalAttribute Color msg -> String
122+renderAttribute (Colors palette) attribute =
123+ case attribute of
124+ InternalAttribute ->
125+ ""
126+
127+ ColorStyle clr ->
128+ "dynamic clr"
129+
130+
131+bgColor : color -> InternalAttribute color msg
132+bgColor clr =
133+ ColorStyle clr
134+
135+
136+{-| Concrete Values
137+-}
138+type alias Attr msg =
139+ InternalAttribute Color msg
140+
141+
142+{-| Palette based attributes
143+-}
144+type alias Attribute colors msg =
145+ InternalAttribute (colors -> Protected Color) msg
146+
147+
148+type alias Element colors msg =
149+ InternalElement (colors -> Protected Color) msg
150+
151+
152+type alias El msg =
153+ InternalElement Color msg
154+
155+
156+type InternalAttribute color msg
157+ = InternalAttribute
158+ | ColorStyle color
159+
160+
161+type InternalElement color msg
162+ = Element (List (InternalAttribute color msg)) (List (InternalElement color msg))
163+
164+
165+{-| This is how we keep track of something that's already rendered, (Protected), or needs to be rendered
166+-}
167+type Protected thing
168+ = Protected thing
169+ | Dynamic thing
170+
171+
172+
173+-- dynamic : Color -> Colors colors -> Protected Color
174+
175+
176+dynamic value palette =
177+ Dynamic value
178+
179+
180+
181+{- COLORS -}
182+
183+
184+type Colors a
185+ = Colors
186+ { protected : a
187+ , values : List Color
188+ }
189+
190+
191+colors : a -> Colors a
192+colors a =
193+ Colors
194+ { protected = a
195+ , values = []
196+ }
197+
198+
199+color : Color -> Colors (Protected Color -> a) -> Colors a
200+color clr pal =
201+ let
202+ addColor p =
203+ { protected = p.protected (Protected clr)
204+ , values = clr :: p.values
205+ }
206+ in
207+ map addColor pal
208+
209+
210+map : ({ protected : a, values : List Color } -> { protected : a1, values : List Color }) -> Colors a -> Colors a1
211+map fn pal =
212+ case pal of
213+ Colors a ->
214+ Colors (fn a)
src/Element.elmnew file+1587
@@ -0,0 +1,1587 @@
1+module 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+ )
26+
27+{-|
28+
29+
30+## Basic Elements
31+
32+@docs Element, none, text, el
33+
34+
35+## Rows and Columns
36+
37+Rows and columns are the most common layouts.
38+
39+@docs row, wrappedRow, column
40+
41+
42+## Text Layout
43+
44+Text needs it's own layout primitives.
45+
46+@docs paragraph, textColumn
47+
48+
49+## Data Table
50+
51+@docs Column, table, IndexedColumn, indexedTable
52+
53+
54+## Size
55+
56+@docs Attribute, width, height, Length, px, shrink, fill, fillPortion, maximum, minimum
57+
58+
59+## Debugging
60+
61+@docs explain
62+
63+
64+## Padding and Spacing
65+
66+There's no concept of margin in `style-elements`, instead we have padding and spacing.
67+
68+Padding is the distance between the outer edge and the content, and spacing is the space between children.
69+
70+So, if we have the following row, with some padding and spacing.
71+
72+ Element.row [ padding 10, spacing 7 ]
73+ [ Element.el [] none
74+ , Element.el [] none
75+ , Element.el [] none
76+ ]
77+
78+Here's what we can expect:
79+
80+![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)
81+
82+@docs padding, paddingXY, paddingEach
83+
84+@docs spacing, spacingXY, spaceEvenly
85+
86+
87+## Alignment
88+
89+Alignment can be used to align an `Element` within another `Element`.
90+
91+ Element.el [ centerX, alignTop ] (text "I'm centered and aligned top!")
92+
93+If 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.
94+
95+ Element.row []
96+ [ Element.el [] Element.none
97+ , Element.el [ alignLeft ] Element.none
98+ , Element.el [ centerX ] Element.none
99+ , Element.el [ alignRight ] Element.none
100+ ]
101+
102+will result in a layout like
103+
104+ |-|-| |-| |-|
105+
106+Where there are two elements on the left, one in the center, and one on the right.
107+
108+@docs centerX, centerY, alignLeft, alignRight, alignTop, alignBottom
109+
110+
111+# Transparency
112+
113+@docs transparent, alpha, pointer
114+
115+
116+# Adjustment
117+
118+@docs moveUp, moveDown, moveRight, moveLeft, rotate, scale
119+
120+
121+# Clipping and Scrollbars
122+
123+Clip the content if it overflows.
124+
125+@docs clip, clipX, clipY
126+
127+Add a scrollbar if the content is larger than the element.
128+
129+@docs scrollbars, scrollbarX, scrollbarY
130+
131+
132+# Rendering
133+
134+@docs layout, layoutWith, Option, noStaticStyleSheet, forceHover, noHover, focusStyle, FocusStyle
135+
136+
137+# Links
138+
139+@docs link, newTabLink, download, downloadAs
140+
141+
142+# Images
143+
144+@docs image
145+
146+
147+# Color
148+
149+In order to use attributes like `Font.color` and `Background.color`, you'll need to make some colors!
150+
151+@docs Color, rgba, rgb, rgb255, rgba255, fromRgb, fromRgb255, toRgb
152+
153+
154+# Nearby Elements
155+
156+Let'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_.
157+
158+ Element.row []
159+ [ Element.el
160+ [ Element.below (Element.text "I'm below!")
161+ ]
162+ (Element.text "I'm normal!")
163+ ]
164+
165+This will result in
166+
167+ |- I'm normal! -|
168+ I'm below
169+
170+Where `"I'm Below"` doesn't change the size of `Element.row`.
171+
172+This is very useful for things like dropdown menus or tooltips.
173+
174+@docs above, below, onRight, onLeft, inFront, behindContent
175+
176+
177+# Temporary Styling
178+
179+@docs Attr, Decoration, mouseOver, mouseDown, focused
180+
181+
182+# Responsiveness
183+
184+The main technique for responsiveness is to store window size information in your model.
185+
186+Install the `Browser` package, and set up a subscription for [`Browser.Events.onResize`](https://package.elm-lang.org/packages/elm/browser/latest/Browser-Events#onResize).
187+
188+You'll also need to retrieve the initial window size. You can either run the following task:
189+
190+ Task.perform (\size -> WindowResize { width = size.width, height = size.height }) Window.size
191+
192+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.
193+
194+@docs Device, DeviceClass, Orientation, classifyDevice
195+
196+
197+# Scaling
198+
199+@docs modular
200+
201+
202+## Mapping
203+
204+@docs map, mapAttribute
205+
206+
207+## Compatibility
208+
209+@docs html, htmlAttribute
210+
211+-}
212+
213+import Html exposing (Html)
214+import Html.Attributes
215+import Internal.Flag as Flag exposing (Flag)
216+import Internal.Model as Internal
217+import Internal.Style exposing (classes)
218+
219+
220+{-| -}
221+type alias Color =
222+ Internal.Color
223+
224+
225+{-| Provide the red, green, and blue channels for the color.
226+
227+Each channel takes a value between 0 and 1.
228+
229+-}
230+rgb : Float -> Float -> Float -> Color
231+rgb r g b =
232+ Internal.Rgba r g b 1
233+
234+
235+{-| -}
236+rgba : Float -> Float -> Float -> Float -> Color
237+rgba =
238+ Internal.Rgba
239+
240+
241+{-| Provide the red, green, and blue channels for the color.
242+
243+Each channel takes a value between 0 and 1.
244+
245+-}
246+rgb255 : Int -> Int -> Int -> Color
247+rgb255 red green blue =
248+ Internal.Rgba
249+ (toFloat red / 255)
250+ (toFloat green / 255)
251+ (toFloat blue / 255)
252+ 1
253+
254+
255+{-| -}
256+rgba255 : Int -> Int -> Int -> Float -> Color
257+rgba255 red green blue a =
258+ Internal.Rgba
259+ (toFloat red / 255)
260+ (toFloat green / 255)
261+ (toFloat blue / 255)
262+ a
263+
264+
265+{-| Create a color from an RGB record.
266+-}
267+fromRgb :
268+ { red : Float
269+ , green : Float
270+ , blue : Float
271+ , alpha : Float
272+ }
273+ -> Color
274+fromRgb clr =
275+ Internal.Rgba
276+ clr.red
277+ clr.green
278+ clr.blue
279+ clr.alpha
280+
281+
282+{-| -}
283+fromRgb255 :
284+ { red : Int
285+ , green : Int
286+ , blue : Int
287+ , alpha : Float
288+ }
289+ -> Color
290+fromRgb255 clr =
291+ Internal.Rgba
292+ (toFloat clr.red / 255)
293+ (toFloat clr.green / 255)
294+ (toFloat clr.blue / 255)
295+ clr.alpha
296+
297+
298+{-| Deconstruct a `Color` into it's rgb channels.
299+-}
300+toRgb :
301+ Color
302+ ->
303+ { red : Float
304+ , green : Float
305+ , blue : Float
306+ , alpha : Float
307+ }
308+toRgb (Internal.Rgba r g b a) =
309+ { red = r
310+ , green = g
311+ , blue = b
312+ , alpha = a
313+ }
314+
315+
316+{-| The basic building block of your layout. Here we create a
317+
318+ import Element
319+
320+ view =
321+ Element.el [] (Element.text "Hello!")
322+
323+-}
324+type alias Element msg =
325+ Internal.Element msg
326+
327+
328+{-| Standard attribute which cannot be a decoration.
329+-}
330+type alias Attribute msg =
331+ Internal.Attribute () msg
332+
333+
334+{-| This is a special attribute that counts as both a `Attribute msg` and a `Decoration`.
335+-}
336+type alias Attr decorative msg =
337+ Internal.Attribute decorative msg
338+
339+
340+{-| Only decorations
341+-}
342+type alias Decoration =
343+ Internal.Attribute Never Never
344+
345+
346+{-| -}
347+html : Html msg -> Element msg
348+html =
349+ Internal.unstyled
350+
351+
352+{-| -}
353+htmlAttribute : Html.Attribute msg -> Attribute msg
354+htmlAttribute =
355+ Internal.Attr
356+
357+
358+{-| -}
359+map : (msg -> msg1) -> Element msg -> Element msg1
360+map =
361+ Internal.map
362+
363+
364+{-| -}
365+mapAttribute : (msg -> msg1) -> Attribute msg -> Attribute msg1
366+mapAttribute =
367+ Internal.mapAttr
368+
369+
370+{-| -}
371+type alias Length =
372+ Internal.Length
373+
374+
375+{-| -}
376+px : Int -> Length
377+px =
378+ Internal.Px
379+
380+
381+{-| Shrink an element to fit it's contents.
382+-}
383+shrink : Length
384+shrink =
385+ Internal.Content
386+
387+
388+{-| Fill the available space. The available space will be split evenly between elements that have `width fill`.
389+-}
390+fill : Length
391+fill =
392+ Internal.Fill 1
393+
394+
395+{-| Similarly you can set a minimum boundary.
396+
397+ el
398+ [ height
399+ (fill
400+ |> maximum 300
401+ |> minimum 30
402+ )
403+
404+ ]
405+ (text "I will stop at 300px")
406+
407+-}
408+minimum : Int -> Length -> Length
409+minimum i l =
410+ Internal.Min i l
411+
412+
413+{-| Add a maximum to a length.
414+
415+ el
416+ [ height
417+ (fill
418+ |> maximum 300
419+ )
420+ ]
421+ (text "I will stop at 300px")
422+
423+-}
424+maximum : Int -> Length -> Length
425+maximum i l =
426+ Internal.Max i l
427+
428+
429+{-| 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.
430+
431+So, 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.
432+
433+**Also:** `fill == fillPortion 1`
434+
435+-}
436+fillPortion : Int -> Length
437+fillPortion =
438+ Internal.Fill
439+
440+
441+{-| This is your top level node where you can turn `Element` into `Html`.
442+-}
443+layout : List (Attribute msg) -> Element msg -> Html msg
444+layout =
445+ layoutWith { options = [] }
446+
447+
448+{-| -}
449+layoutWith : { options : List Option } -> List (Attribute msg) -> Element msg -> Html msg
450+layoutWith { options } attrs child =
451+ Internal.renderRoot options
452+ (Internal.htmlClass
453+ (String.join " "
454+ [ classes.root
455+ , classes.any
456+ , classes.single
457+
458+ -- , classes.contentCenterX
459+ -- , classes.contentCenterY
460+ ]
461+ )
462+ :: (Internal.rootStyle ++ attrs)
463+ )
464+ child
465+
466+
467+{-| -}
468+type alias Option =
469+ Internal.Option
470+
471+
472+{-| Style elements embeds two StyleSheets, one that is constant, and one that changes dynamically based on styles collected from the elments being rendered.
473+
474+This option will stop the static/constant stylesheet from rendering.
475+
476+Make sure to render the constant/static stylesheet at least once on your page!
477+
478+-}
479+noStaticStyleSheet : Option
480+noStaticStyleSheet =
481+ Internal.RenderModeOption Internal.NoStaticStyleSheet
482+
483+
484+{-| -}
485+defaultFocus :
486+ { borderColor : Maybe Color
487+ , backgroundColor : Maybe Color
488+ , shadow :
489+ Maybe
490+ { color : Color
491+ , offset : ( Int, Int )
492+ , blur : Int
493+ , size : Int
494+ }
495+ }
496+defaultFocus =
497+ Internal.focusDefaultStyle
498+
499+
500+{-| -}
501+type alias FocusStyle =
502+ { borderColor : Maybe Color
503+ , backgroundColor : Maybe Color
504+ , shadow :
505+ Maybe
506+ { color : Color
507+ , offset : ( Int, Int )
508+ , blur : Int
509+ , size : Int
510+ }
511+ }
512+
513+
514+{-| -}
515+focusStyle : FocusStyle -> Option
516+focusStyle =
517+ Internal.FocusStyleOption
518+
519+
520+{-| Disable all `mouseOver` styles.
521+-}
522+noHover : Option
523+noHover =
524+ Internal.HoverOption Internal.NoHover
525+
526+
527+{-| Any `hover` styles, aka attributes with `mouseOver` in the name, will be always turned on.
528+
529+This is useful for when you're targeting a platform that has no mouse, such as mobile.
530+
531+-}
532+forceHover : Option
533+forceHover =
534+ Internal.HoverOption Internal.ForceHover
535+
536+
537+{-| Nothing to see here!
538+-}
539+none : Element msg
540+none =
541+ Internal.Empty
542+
543+
544+{-| Create some plain text.
545+
546+ text "Hello, you stylish developer!"
547+
548+**Note** text does not wrap by default. In order to get text to wrap, check out `paragraph`!
549+
550+-}
551+text : String -> Element msg
552+text content =
553+ Internal.Text content
554+
555+
556+{-| The basic building block of your layout.
557+
558+You can think of an `el` as a `div`, but it can only hae one child.
559+
560+If you want multiple children, you'll need to use something like `row` or `column`
561+
562+ import Color exposing (blue, darkBlue)
563+ import Element exposing (Element)
564+ import Element.Background as Background
565+ import Element.Border as Border
566+
567+ myElement : Element msg
568+ myElement =
569+ Element.el
570+ [ Background.color blue
571+ , Border.color darkBlue
572+ ]
573+ (Element.text "You've made a stylish element!")
574+
575+-}
576+el : List (Attribute msg) -> Element msg -> Element msg
577+el attrs child =
578+ Internal.element
579+ Internal.asEl
580+ Internal.div
581+ (width shrink
582+ :: height shrink
583+ :: attrs
584+ )
585+ (Internal.Unkeyed [ child ])
586+
587+
588+{-| If you want a row of elements, use `row`!
589+-}
590+row : List (Attribute msg) -> List (Element msg) -> Element msg
591+row attrs children =
592+ Internal.element
593+ Internal.asRow
594+ Internal.div
595+ (Internal.htmlClass (classes.contentLeft ++ " " ++ classes.contentCenterY)
596+ :: width shrink
597+ :: height shrink
598+ :: attrs
599+ )
600+ (Internal.Unkeyed children)
601+
602+
603+{-| -}
604+column : List (Attribute msg) -> List (Element msg) -> Element msg
605+column attrs children =
606+ Internal.element
607+ Internal.asColumn
608+ Internal.div
609+ (Internal.htmlClass
610+ (classes.contentTop
611+ ++ " "
612+ ++ classes.contentLeft
613+ )
614+ :: height shrink
615+ :: width shrink
616+ :: attrs
617+ )
618+ (Internal.Unkeyed children)
619+
620+
621+{-| -}
622+wrappedRow : List (Attribute msg) -> List (Element msg) -> Element msg
623+wrappedRow attrs children =
624+ let
625+ ( padded, spaced ) =
626+ Internal.extractSpacingAndPadding attrs
627+ in
628+ case spaced of
629+ Nothing ->
630+ Internal.element
631+ Internal.asRow
632+ Internal.div
633+ (Internal.htmlClass
634+ (classes.contentLeft
635+ ++ " "
636+ ++ classes.contentCenterY
637+ ++ " "
638+ ++ classes.wrapped
639+ )
640+ :: width shrink
641+ :: height shrink
642+ :: attrs
643+ )
644+ (Internal.Unkeyed children)
645+
646+ Just (Internal.Spaced spaceName x y) ->
647+ let
648+ newPadding =
649+ case padded of
650+ Just (Internal.Padding name t r b l) ->
651+ if r >= x && b >= y then
652+ Just <|
653+ Internal.StyleClass
654+ Flag.padding
655+ (Internal.PaddingStyle
656+ (Internal.paddingName t
657+ (r - x)
658+ (b - y)
659+ l
660+ )
661+ t
662+ (r - x)
663+ (b - y)
664+ l
665+ )
666+
667+ else
668+ Nothing
669+
670+ Nothing ->
671+ Nothing
672+ in
673+ case newPadding of
674+ Just pad ->
675+ Internal.element
676+ Internal.asRow
677+ Internal.div
678+ (Internal.htmlClass
679+ (classes.contentLeft
680+ ++ " "
681+ ++ classes.contentCenterY
682+ ++ " "
683+ ++ classes.wrapped
684+ )
685+ :: width shrink
686+ :: height shrink
687+ :: attrs
688+ ++ [ pad ]
689+ )
690+ (Internal.Unkeyed children)
691+
692+ Nothing ->
693+ -- Not enough space in padding to compensate for spacing
694+ Internal.element
695+ Internal.asEl
696+ Internal.div
697+ attrs
698+ (Internal.Unkeyed
699+ [ Internal.element
700+ Internal.asRow
701+ Internal.div
702+ (Internal.htmlClass
703+ (classes.contentLeft
704+ ++ " "
705+ ++ classes.contentCenterY
706+ ++ " "
707+ ++ classes.wrapped
708+ )
709+ :: Internal.Attr
710+ (Html.Attributes.style "margin-bottom" (String.fromInt (negate y) ++ "px"))
711+ :: width fill
712+ :: height fill
713+ :: Internal.StyleClass Flag.spacing (Internal.SpacingStyle spaceName x y)
714+ :: []
715+ )
716+ (Internal.Unkeyed children)
717+ ]
718+ )
719+
720+
721+{-| This is just an alias for `Debug.todo`
722+-}
723+type alias Todo =
724+ String -> Never
725+
726+
727+{-| 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!
728+
729+**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.
730+
731+ el
732+ [ Element.explain Debug.todo
733+ ]
734+ (text "Help! I'm being debugged!")
735+
736+-}
737+explain : Todo -> Attribute msg
738+explain _ =
739+ Internal.htmlClass "explain"
740+
741+
742+{-| -}
743+type alias Column record msg =
744+ { header : Element msg
745+ , width : Length
746+ , view : record -> Element msg
747+ }
748+
749+
750+{-| Show some tabular data.
751+
752+Start with a list of records and specify how each column should be rendered.
753+
754+So, if we have a list of `persons`:
755+
756+ type alias Person =
757+ { firstName : String
758+ , lastName : String
759+ }
760+
761+ persons : List Person
762+ persons =
763+ [ { firstName = "David"
764+ , lastName = "Bowie"
765+ }
766+ , { firstName = "Florence"
767+ , lastName = "Welch"
768+ }
769+ ]
770+
771+We could render it using
772+
773+ Element.table []
774+ { data = persons
775+ , columns =
776+ [ { header = Element.text "First Name"
777+ , width = fill
778+ , view =
779+ \person ->
780+ Element.text person.firstName
781+ }
782+ , { header = Element.text "Last Name"
783+ , width = fill
784+ , view =
785+ \person ->
786+ Element.text person.lastName
787+ }
788+ ]
789+ }
790+
791+**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`.
792+
793+-}
794+table :
795+ List (Attribute msg)
796+ ->
797+ { data : List records
798+ , columns : List (Column records msg)
799+ }
800+ -> Element msg
801+table attrs config =
802+ tableHelper attrs
803+ { data = config.data
804+ , columns =
805+ List.map InternalColumn config.columns
806+ }
807+
808+
809+{-| -}
810+type alias IndexedColumn record msg =
811+ { header : Element msg
812+ , width : Length
813+ , view : Int -> record -> Element msg
814+ }
815+
816+
817+{-| Same as `Element.table` except the `view` for each column will also receive the row index as well as the record.
818+-}
819+indexedTable :
820+ List (Attribute msg)
821+ ->
822+ { data : List records
823+ , columns : List (IndexedColumn records msg)
824+ }
825+ -> Element msg
826+indexedTable attrs config =
827+ tableHelper attrs
828+ { data = config.data
829+ , columns =
830+ List.map InternalIndexedColumn config.columns
831+ }
832+
833+
834+{-| -}
835+type alias InternalTable records msg =
836+ { data : List records
837+ , columns : List (InternalTableColumn records msg)
838+ }
839+
840+
841+{-| -}
842+type InternalTableColumn record msg
843+ = InternalIndexedColumn (IndexedColumn record msg)
844+ | InternalColumn (Column record msg)
845+
846+
847+tableHelper : List (Attribute msg) -> InternalTable data msg -> Element msg
848+tableHelper attrs config =
849+ let
850+ ( sX, sY ) =
851+ Internal.getSpacing attrs ( 0, 0 )
852+
853+ columnHeader col =
854+ case col of
855+ InternalIndexedColumn colConfig ->
856+ colConfig.header
857+
858+ InternalColumn colConfig ->
859+ colConfig.header
860+
861+ columnWidth col =
862+ case col of
863+ InternalIndexedColumn colConfig ->
864+ colConfig.width
865+
866+ InternalColumn colConfig ->
867+ colConfig.width
868+
869+ maybeHeaders =
870+ List.map columnHeader config.columns
871+ |> (\headers ->
872+ if List.all ((==) Internal.Empty) headers then
873+ Nothing
874+
875+ else
876+ Just (List.indexedMap (\col header -> onGrid 1 (col + 1) header) headers)
877+ )
878+
879+ template =
880+ Internal.StyleClass Flag.gridTemplate <|
881+ Internal.GridTemplateStyle
882+ { spacing = ( px sX, px sY )
883+ , columns = List.map columnWidth config.columns
884+ , rows = List.repeat (List.length config.data) Internal.Content
885+ }
886+
887+ onGrid rowLevel columnLevel elem =
888+ Internal.element
889+ Internal.asEl
890+ Internal.div
891+ [ Internal.StyleClass Flag.gridPosition
892+ (Internal.GridPosition
893+ { row = rowLevel
894+ , col = columnLevel
895+ , width = 1
896+ , height = 1
897+ }
898+ )
899+ ]
900+ (Internal.Unkeyed [ elem ])
901+
902+ add cell columnConfig cursor =
903+ case columnConfig of
904+ InternalIndexedColumn col ->
905+ { cursor
906+ | elements =
907+ onGrid cursor.row
908+ cursor.column
909+ (col.view
910+ (if maybeHeaders == Nothing then
911+ cursor.row - 1
912+
913+ else
914+ cursor.row - 2
915+ )
916+ cell
917+ )
918+ :: cursor.elements
919+ , column = cursor.column + 1
920+ }
921+
922+ InternalColumn col ->
923+ { cursor
924+ | elements =
925+ onGrid cursor.row cursor.column (col.view cell)
926+ :: cursor.elements
927+ , column = cursor.column + 1
928+ }
929+
930+ build columns rowData cursor =
931+ let
932+ newCursor =
933+ List.foldl (add rowData)
934+ cursor
935+ columns
936+ in
937+ { newCursor
938+ | row = cursor.row + 1
939+ , column = 1
940+ }
941+
942+ children =
943+ List.foldl (build config.columns)
944+ { elements = []
945+ , row =
946+ if maybeHeaders == Nothing then
947+ 1
948+
949+ else
950+ 2
951+ , column = 1
952+ }
953+ config.data
954+ in
955+ Internal.element
956+ Internal.asGrid
957+ Internal.div
958+ (width fill
959+ :: template
960+ :: attrs
961+ )
962+ (Internal.Unkeyed
963+ (case maybeHeaders of
964+ Nothing ->
965+ children.elements
966+
967+ Just renderedHeaders ->
968+ renderedHeaders ++ children.elements
969+ )
970+ )
971+
972+
973+{-| A paragraph will layout all children as wrapped, inline elements.
974+
975+ import Element
976+ import Element.Font as Font
977+
978+ Element.paragraph []
979+ [ text "lots of text ...."
980+ , el [ Font.bold ] (text "this is bold")
981+ , text "lots of text ...."
982+ ]
983+
984+This is really useful when you want to markup text by having some parts be bold, or some be links, or whatever you so desire.
985+
986+Also, 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).
987+
988+This makes it particularly easy to do something like a [dropped capital](https://en.wikipedia.org/wiki/Initial).
989+
990+ import Element
991+ import Element.Font as Font
992+
993+ Element.paragraph []
994+ [ el
995+ [ alignLeft
996+ , padding 5
997+ , Font.lineHeight 1
998+ ]
999+ (text "S")
1000+ , text "o much text ...."
1001+ ]
1002+
1003+Which will look something like
1004+
1005+![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)
1006+
1007+-}
1008+paragraph : List (Attribute msg) -> List (Element msg) -> Element msg
1009+paragraph attrs children =
1010+ Internal.element
1011+ Internal.asParagraph
1012+ Internal.div
1013+ (Internal.Describe Internal.Paragraph
1014+ :: width fill
1015+ :: spacing 5
1016+ :: attrs
1017+ )
1018+ (Internal.Unkeyed children)
1019+
1020+
1021+{-| Now that we have a paragraph, we need some way to attach a bunch of paragraph's together.
1022+
1023+To do that we can use a `textColumn`.
1024+
1025+The 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.
1026+
1027+In the following example, we have a `textColumn` where one child has `alignLeft`.
1028+
1029+ Element.textColumn [ spacing 10, padding 10 ]
1030+ [ paragraph [] [ text "lots of text ...." ]
1031+ , el [ alignLeft ] none
1032+ , paragraph [] [ text "lots of text ...." ]
1033+ ]
1034+
1035+Which will result in something like:
1036+
1037+![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)
1038+
1039+-}
1040+textColumn : List (Attribute msg) -> List (Element msg) -> Element msg
1041+textColumn attrs children =
1042+ Internal.element
1043+ Internal.asTextColumn
1044+ Internal.div
1045+ (width
1046+ (fill
1047+ |> minimum 500
1048+ |> maximum 750
1049+ )
1050+ :: attrs
1051+ )
1052+ (Internal.Unkeyed children)
1053+
1054+
1055+{-| Both a source and a description are required for images.
1056+
1057+The description is used for people using screen readers.
1058+
1059+Leaving 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.
1060+
1061+So, take a moment to describe your image as you would to someone who has a harder time seeing.
1062+
1063+-}
1064+image : List (Attribute msg) -> { src : String, description : String } -> Element msg
1065+image attrs { src, description } =
1066+ let
1067+ imageAttributes =
1068+ attrs
1069+ |> List.filter
1070+ (\a ->
1071+ case a of
1072+ Internal.Width _ ->
1073+ True
1074+
1075+ Internal.Height _ ->
1076+ True
1077+
1078+ _ ->
1079+ False
1080+ )
1081+ in
1082+ Internal.element
1083+ Internal.asEl
1084+ Internal.div
1085+ (Internal.htmlClass classes.imageContainer
1086+ :: attrs
1087+ )
1088+ (Internal.Unkeyed
1089+ [ Internal.element
1090+ Internal.asEl
1091+ (Internal.NodeName "img")
1092+ ([ Internal.Attr <| Html.Attributes.src src
1093+ , Internal.Attr <| Html.Attributes.alt description
1094+ ]
1095+ ++ imageAttributes
1096+ )
1097+ (Internal.Unkeyed [])
1098+ ]
1099+ )
1100+
1101+
1102+{-|
1103+
1104+ link []
1105+ { url = "http://fruits.com"
1106+ , label = text "A link to my favorite fruit provider."
1107+ }
1108+
1109+-}
1110+link :
1111+ List (Attribute msg)
1112+ ->
1113+ { url : String
1114+ , label : Element msg
1115+ }
1116+ -> Element msg
1117+link attrs { url, label } =
1118+ Internal.element
1119+ Internal.asEl
1120+ (Internal.NodeName "a")
1121+ (Internal.Attr (Html.Attributes.href url)
1122+ :: Internal.Attr (Html.Attributes.rel "noopener noreferrer")
1123+ :: width shrink
1124+ :: height shrink
1125+ :: Internal.htmlClass
1126+ (classes.contentCenterX
1127+ ++ " "
1128+ ++ classes.contentCenterY
1129+ )
1130+ :: attrs
1131+ )
1132+ (Internal.Unkeyed [ label ])
1133+
1134+
1135+{-| -}
1136+newTabLink :
1137+ List (Attribute msg)
1138+ ->
1139+ { url : String
1140+ , label : Element msg
1141+ }
1142+ -> Element msg
1143+newTabLink attrs { url, label } =
1144+ Internal.element
1145+ Internal.asEl
1146+ (Internal.NodeName "a")
1147+ (Internal.Attr (Html.Attributes.href url)
1148+ :: Internal.Attr (Html.Attributes.rel "noopener noreferrer")
1149+ :: Internal.Attr (Html.Attributes.target "_blank")
1150+ :: width shrink
1151+ :: height shrink
1152+ :: Internal.htmlClass (classes.contentCenterX ++ " " ++ classes.contentCenterY)
1153+ :: attrs
1154+ )
1155+ (Internal.Unkeyed [ label ])
1156+
1157+
1158+{-| A link to download a file.
1159+-}
1160+download :
1161+ List (Attribute msg)
1162+ ->
1163+ { url : String
1164+ , label : Element msg
1165+ }
1166+ -> Element msg
1167+download attrs { url, label } =
1168+ Internal.element
1169+ Internal.asEl
1170+ (Internal.NodeName "a")
1171+ (Internal.Attr (Html.Attributes.href url)
1172+ :: Internal.Attr (Html.Attributes.download "")
1173+ :: width shrink
1174+ :: height shrink
1175+ :: Internal.htmlClass classes.contentCenterX
1176+ :: Internal.htmlClass classes.contentCenterY
1177+ :: attrs
1178+ )
1179+ (Internal.Unkeyed [ label ])
1180+
1181+
1182+{-| A link to download a file, but you can specify the filename.
1183+-}
1184+downloadAs :
1185+ List (Attribute msg)
1186+ ->
1187+ { label : Element msg
1188+ , filename : String
1189+ , url : String
1190+ }
1191+ -> Element msg
1192+downloadAs attrs { url, filename, label } =
1193+ Internal.element
1194+ Internal.asEl
1195+ (Internal.NodeName "a")
1196+ (Internal.Attr (Html.Attributes.href url)
1197+ :: Internal.Attr (Html.Attributes.download filename)
1198+ :: width shrink
1199+ :: height shrink
1200+ :: Internal.htmlClass classes.contentCenterX
1201+ :: Internal.htmlClass classes.contentCenterY
1202+ :: attrs
1203+ )
1204+ (Internal.Unkeyed [ label ])
1205+
1206+
1207+{-| -}
1208+below : Element msg -> Attribute msg
1209+below element =
1210+ Internal.Nearby Internal.Below element
1211+
1212+
1213+{-| -}
1214+above : Element msg -> Attribute msg
1215+above element =
1216+ Internal.Nearby Internal.Above element
1217+
1218+
1219+{-| -}
1220+onRight : Element msg -> Attribute msg
1221+onRight element =
1222+ Internal.Nearby Internal.OnRight element
1223+
1224+
1225+{-| -}
1226+onLeft : Element msg -> Attribute msg
1227+onLeft element =
1228+ Internal.Nearby Internal.OnLeft element
1229+
1230+
1231+{-| This will place an element in front of another.
1232+
1233+**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.
1234+
1235+-}
1236+inFront : Element msg -> Attribute msg
1237+inFront element =
1238+ Internal.Nearby Internal.InFront element
1239+
1240+
1241+{-| This will place an element between the background and the content of an element.
1242+-}
1243+behindContent : Element msg -> Attribute msg
1244+behindContent element =
1245+ Internal.Nearby Internal.Behind element
1246+
1247+
1248+{-| -}
1249+width : Length -> Attribute msg
1250+width =
1251+ Internal.Width
1252+
1253+
1254+{-| -}
1255+height : Length -> Attribute msg
1256+height =
1257+ Internal.Height
1258+
1259+
1260+{-| -}
1261+scale : Float -> Attr decorative msg
1262+scale n =
1263+ Internal.TransformComponent Flag.scale (Internal.Scale ( n, n, 1 ))
1264+
1265+
1266+{-| -}
1267+rotate : Float -> Attr decorative msg
1268+rotate angle =
1269+ Internal.TransformComponent Flag.rotate (Internal.Rotate ( 0, 0, 1 ) angle)
1270+
1271+
1272+{-| -}
1273+moveUp : Float -> Attr decorative msg
1274+moveUp y =
1275+ Internal.TransformComponent Flag.moveY (Internal.MoveY (negate y))
1276+
1277+
1278+{-| -}
1279+moveDown : Float -> Attr decorative msg
1280+moveDown y =
1281+ Internal.TransformComponent Flag.moveY (Internal.MoveY y)
1282+
1283+
1284+{-| -}
1285+moveRight : Float -> Attr decorative msg
1286+moveRight x =
1287+ Internal.TransformComponent Flag.moveX (Internal.MoveX x)
1288+
1289+
1290+{-| -}
1291+moveLeft : Float -> Attr decorative msg
1292+moveLeft x =
1293+ Internal.TransformComponent Flag.moveX (Internal.MoveX (negate x))
1294+
1295+
1296+{-| -}
1297+padding : Int -> Attribute msg
1298+padding x =
1299+ Internal.StyleClass Flag.padding (Internal.PaddingStyle ("p-" ++ String.fromInt x) x x x x)
1300+
1301+
1302+{-| Set horizontal and vertical padding.
1303+-}
1304+paddingXY : Int -> Int -> Attribute msg
1305+paddingXY x y =
1306+ if x == y then
1307+ Internal.StyleClass Flag.padding (Internal.PaddingStyle ("p-" ++ String.fromInt x) x x x x)
1308+
1309+ else
1310+ Internal.StyleClass Flag.padding
1311+ (Internal.PaddingStyle
1312+ ("p-" ++ String.fromInt x ++ "-" ++ String.fromInt y)
1313+ y
1314+ x
1315+ y
1316+ x
1317+ )
1318+
1319+
1320+{-| If you find yourself defining unique paddings all the time, you might consider defining
1321+
1322+ edges =
1323+ { top = 0
1324+ , right = 0
1325+ , bottom = 0
1326+ , left = 0
1327+ }
1328+
1329+And then just do
1330+
1331+ paddingEach { edges | right 5 }
1332+
1333+-}
1334+paddingEach : { top : Int, right : Int, bottom : Int, left : Int } -> Attribute msg
1335+paddingEach { top, right, bottom, left } =
1336+ if top == right && top == bottom && top == left then
1337+ Internal.StyleClass Flag.padding (Internal.PaddingStyle ("p-" ++ String.fromInt top) top top top top)
1338+
1339+ else
1340+ Internal.StyleClass Flag.padding
1341+ (Internal.PaddingStyle
1342+ (Internal.paddingName top right bottom left)
1343+ top
1344+ right
1345+ bottom
1346+ left
1347+ )
1348+
1349+
1350+{-| -}
1351+centerX : Attribute msg
1352+centerX =
1353+ Internal.AlignX Internal.CenterX
1354+
1355+
1356+{-| -}
1357+centerY : Attribute msg
1358+centerY =
1359+ Internal.AlignY Internal.CenterY
1360+
1361+
1362+{-| -}
1363+alignTop : Attribute msg
1364+alignTop =
1365+ Internal.AlignY Internal.Top
1366+
1367+
1368+{-| -}
1369+alignBottom : Attribute msg
1370+alignBottom =
1371+ Internal.AlignY Internal.Bottom
1372+
1373+
1374+{-| -}
1375+alignLeft : Attribute msg
1376+alignLeft =
1377+ Internal.AlignX Internal.Left
1378+
1379+
1380+{-| -}
1381+alignRight : Attribute msg
1382+alignRight =
1383+ Internal.AlignX Internal.Right
1384+
1385+
1386+{-| -}
1387+spaceEvenly : Attribute msg
1388+spaceEvenly =
1389+ Internal.Class Flag.xAlign (.spaceEvenly Internal.Style.classes)
1390+
1391+
1392+{-| -}
1393+spacing : Int -> Attribute msg
1394+spacing x =
1395+ Internal.StyleClass Flag.spacing (Internal.SpacingStyle (Internal.spacingName x x) x x)
1396+
1397+
1398+{-| In the majority of cases you'll just need to use `spacing`, which will work as intended.
1399+
1400+However for some layouts, like `textColumn`, you may want to set a different spacing for the x axis compared to the y axis.
1401+
1402+-}
1403+spacingXY : Int -> Int -> Attribute msg
1404+spacingXY x y =
1405+ Internal.StyleClass Flag.spacing (Internal.SpacingStyle (Internal.spacingName x y) x y)
1406+
1407+
1408+{-| Make an element transparent and have it ignore any mouse or touch events, though it will stil take up space.
1409+-}
1410+transparent : Bool -> Attr decorative msg
1411+transparent on =
1412+ if on then
1413+ Internal.StyleClass Flag.transparency (Internal.Transparency "transparent" 1.0)
1414+
1415+ else
1416+ Internal.StyleClass Flag.transparency (Internal.Transparency "visible" 0.0)
1417+
1418+
1419+{-| A capped value between 0.0 and 1.0, where 0.0 is transparent and 1.0 is fully opaque.
1420+
1421+Semantically equavalent to html opacity.
1422+
1423+-}
1424+alpha : Float -> Attr decorative msg
1425+alpha o =
1426+ let
1427+ transparency =
1428+ o
1429+ |> max 0.0
1430+ |> min 1.0
1431+ |> (\x -> 1 - x)
1432+ in
1433+ Internal.StyleClass Flag.transparency <| Internal.Transparency ("transparency-" ++ Internal.floatClass transparency) transparency
1434+
1435+
1436+
1437+-- {-| -}
1438+-- hidden : Bool -> Attribute msg
1439+-- hidden on =
1440+-- if on then
1441+-- Internal.class "hidden"
1442+-- else
1443+-- Internal.NoAttribute
1444+
1445+
1446+{-| -}
1447+scrollbars : Attribute msg
1448+scrollbars =
1449+ Internal.Class Flag.overflow classes.scrollbars
1450+
1451+
1452+{-| -}
1453+scrollbarY : Attribute msg
1454+scrollbarY =
1455+ Internal.Class Flag.overflow classes.scrollbarsY
1456+
1457+
1458+{-| -}
1459+scrollbarX : Attribute msg
1460+scrollbarX =
1461+ Internal.Class Flag.overflow classes.scrollbarsX
1462+
1463+
1464+{-| -}
1465+clip : Attribute msg
1466+clip =
1467+ Internal.Class Flag.overflow classes.clip
1468+
1469+
1470+{-| -}
1471+clipY : Attribute msg
1472+clipY =
1473+ Internal.Class Flag.overflow classes.clipY
1474+
1475+
1476+{-| -}
1477+clipX : Attribute msg
1478+clipX =
1479+ Internal.Class Flag.overflow classes.clipX
1480+
1481+
1482+{-| Set the cursor to be a pointing hand when it's hovering over this element.
1483+-}
1484+pointer : Attribute msg
1485+pointer =
1486+ Internal.Class Flag.cursor classes.cursorPointer
1487+
1488+
1489+{-| -}
1490+type alias Device =
1491+ { class : DeviceClass
1492+ , orientation : Orientation
1493+ }
1494+
1495+
1496+{-| -}
1497+type DeviceClass
1498+ = Phone
1499+ | Tablet
1500+ | Desktop
1501+ | BigDesktop
1502+
1503+
1504+{-| -}
1505+type Orientation
1506+ = Portrait
1507+ | Landscape
1508+
1509+
1510+{-| Takes in a Window.Size and returns a device profile which can be used for responsiveness.
1511+-}
1512+classifyDevice : { window | height : Int, width : Int } -> Device
1513+classifyDevice window =
1514+ { class =
1515+ if window.width <= 600 then
1516+ Phone
1517+
1518+ else if window.width > 600 && window.width <= 1200 then
1519+ Tablet
1520+
1521+ else if window.width > 1200 && window.width <= 1800 then
1522+ Desktop
1523+
1524+ else
1525+ BigDesktop
1526+ , orientation =
1527+ if window.width < window.height then
1528+ Portrait
1529+
1530+ else
1531+ Landscape
1532+ }
1533+
1534+
1535+{-| When designing it's nice to use a modular scale to set spacial rythms.
1536+
1537+ scaled =
1538+ Scale.modular 16 1.25
1539+
1540+A modular scale starts with a number, and multiplies it by a ratio a number of times.
1541+Then, when setting font sizes you can use:
1542+
1543+ Font.size (scaled 1) -- results in 16
1544+
1545+ Font.size (scaled 2) -- 16 * 1.25 results in 20
1546+
1547+ Font.size (scaled 4) -- 16 * 1.25 ^ (4 - 1) results in 31.25
1548+
1549+We can also provide negative numbers to scale below 16px.
1550+
1551+ Font.size (scaled -1) -- 16 * 1.25 ^ (-1) results in 12.8
1552+
1553+-}
1554+modular : Float -> Float -> Int -> Float
1555+modular normal ratio rescale =
1556+ if rescale == 0 then
1557+ normal
1558+
1559+ else if rescale < 0 then
1560+ normal * ratio ^ toFloat rescale
1561+
1562+ else
1563+ normal * ratio ^ (toFloat rescale - 1)
1564+
1565+
1566+{-| -}
1567+mouseOver : List Decoration -> Attribute msg
1568+mouseOver decs =
1569+ Internal.StyleClass Flag.hover <|
1570+ Internal.PseudoSelector Internal.Hover
1571+ (Internal.unwrapDecorations decs)
1572+
1573+
1574+{-| -}
1575+mouseDown : List Decoration -> Attribute msg
1576+mouseDown decs =
1577+ Internal.StyleClass Flag.active <|
1578+ Internal.PseudoSelector Internal.Active
1579+ (Internal.unwrapDecorations decs)
1580+
1581+
1582+{-| -}
1583+focused : List Decoration -> Attribute msg
1584+focused decs =
1585+ Internal.StyleClass Flag.focus <|
1586+ Internal.PseudoSelector Internal.Focus
1587+ (Internal.unwrapDecorations decs)
src/Element/Background.elmnew file+232
@@ -0,0 +1,232 @@
1+module Element.Background
2+ exposing
3+ ( color
4+ , gradient
5+ , image
6+ , tiled
7+ , tiledX
8+ , tiledY
9+ , uncropped
10+ )
11+
12+{-|
13+
14+@docs color, gradient
15+
16+
17+# Images
18+
19+@docs image, uncropped, tiled, tiledX, tiledY
20+
21+**Note** if you want more control over a background image than is provided here, you should try just using a normal `Element.image` with something like `Element.behind`.
22+
23+-}
24+
25+import Element exposing (Attr, Attribute, Color)
26+import Internal.Flag as Flag
27+import Internal.Model as Internal
28+import VirtualDom
29+
30+
31+{-| -}
32+color : Color -> Attr decorative msg
33+color clr =
34+ Internal.StyleClass Flag.bgColor (Internal.Colored ("bg-" ++ Internal.formatColorClass clr) "background-color" clr)
35+
36+
37+{-| Resize the image to fit the containing element while maintaining proportions and cropping the overflow.
38+-}
39+image : String -> Attribute msg
40+image src =
41+ Internal.Attr (VirtualDom.style "background" ("url(\"" ++ src ++ "\") center / cover no-repeat"))
42+
43+
44+{-| A centered background image that keeps it's natural propostions, but scales to fit the space.
45+-}
46+uncropped : String -> Attribute msg
47+uncropped src =
48+ Internal.Attr (VirtualDom.style "background" ("url(\"" ++ src ++ "\") center / contain no-repeat"))
49+
50+
51+{-| Tile an image in the x and y axes.
52+-}
53+tiled : String -> Attribute msg
54+tiled src =
55+ Internal.Attr (VirtualDom.style "background" ("url(\"" ++ src ++ "\") repeat"))
56+
57+
58+{-| Tile an image in the x axis.
59+-}
60+tiledX : String -> Attribute msg
61+tiledX src =
62+ Internal.Attr (VirtualDom.style "background" ("url(\"" ++ src ++ "\") repeat-x"))
63+
64+
65+{-| Tile an image in the y axis.
66+-}
67+tiledY : String -> Attribute msg
68+tiledY src =
69+ Internal.Attr (VirtualDom.style "background" ("url(\"" ++ src ++ "\") repeat-y"))
70+
71+
72+type Direction
73+ = ToUp
74+ | ToDown
75+ | ToRight
76+ | ToTopRight
77+ | ToBottomRight
78+ | ToLeft
79+ | ToTopLeft
80+ | ToBottomLeft
81+ | ToAngle Float
82+
83+
84+type Step
85+ = ColorStep Color
86+ | PercentStep Float Color
87+ | PxStep Int Color
88+
89+
90+{-| -}
91+step : Color -> Step
92+step =
93+ ColorStep
94+
95+
96+{-| -}
97+percent : Float -> Color -> Step
98+percent =
99+ PercentStep
100+
101+
102+{-| -}
103+px : Int -> Color -> Step
104+px =
105+ PxStep
106+
107+
108+{-| A linear gradient.
109+
110+First you need to specify what direction the gradient is going by providing an angle in radians. `0` is up and `pi` is down.
111+
112+The colors will be evenly spaced.
113+
114+-}
115+gradient :
116+ { angle : Float
117+ , steps : List Color
118+ }
119+ -> Attr decorative msg
120+gradient { angle, steps } =
121+ case steps of
122+ [] ->
123+ Internal.NoAttribute
124+
125+ clr :: [] ->
126+ Internal.StyleClass Flag.bgColor
127+ (Internal.Colored ("bg-" ++ Internal.formatColorClass clr) "background-color" clr)
128+
129+ _ ->
130+ Internal.StyleClass Flag.bgGradient <|
131+ Internal.Single ("bg-grad-" ++ (String.join "-" <| Internal.floatClass angle :: List.map Internal.formatColorClass steps))
132+ "background-image"
133+ ("linear-gradient(" ++ (String.join ", " <| (String.fromFloat angle ++ "rad") :: List.map Internal.formatColor steps) ++ ")")
134+
135+
136+
137+-- {-| -}
138+-- gradientWith : { direction : Direction, steps : List Step } -> Attribute msg
139+-- gradientWith { direction, steps } =
140+-- StyleClass <|
141+-- Single ("bg-gradient-" ++ (String.join "-" <| renderDirectionClass direction :: List.map renderStepClass steps))
142+-- "background"
143+-- ("linear-gradient(" ++ (String.join ", " <| renderDirection direction :: List.map renderStep steps) ++ ")")
144+-- {-| -}
145+-- renderStep : Step -> String
146+-- renderStep step =
147+-- case step of
148+-- ColorStep color ->
149+-- formatColor color
150+-- PercentStep percent color ->
151+-- formatColor color ++ " " ++ toString percent ++ "%"
152+-- PxStep px color ->
153+-- formatColor color ++ " " ++ toString px ++ "px"
154+-- {-| -}
155+-- renderStepClass : Step -> String
156+-- renderStepClass step =
157+-- case step of
158+-- ColorStep color ->
159+-- formatColorClass color
160+-- PercentStep percent color ->
161+-- formatColorClass color ++ "-" ++ floatClass percent ++ "p"
162+-- PxStep px color ->
163+-- formatColorClass color ++ "-" ++ toString px ++ "px"
164+-- toUp : Direction
165+-- toUp =
166+-- ToUp
167+-- toDown : Direction
168+-- toDown =
169+-- ToDown
170+-- toRight : Direction
171+-- toRight =
172+-- ToRight
173+-- toTopRight : Direction
174+-- toTopRight =
175+-- ToTopRight
176+-- toBottomRight : Direction
177+-- toBottomRight =
178+-- ToBottomRight
179+-- toLeft : Direction
180+-- toLeft =
181+-- ToLeft
182+-- toTopLeft : Direction
183+-- toTopLeft =
184+-- ToTopLeft
185+-- toBottomLeft : Direction
186+-- toBottomLeft =
187+-- ToBottomLeft
188+-- angle : Float -> Direction
189+-- angle rad =
190+-- ToAngle rad
191+-- renderDirection : Direction -> String
192+-- renderDirection dir =
193+-- case dir of
194+-- ToUp ->
195+-- "to top"
196+-- ToDown ->
197+-- "to bottom"
198+-- ToRight ->
199+-- "to right"
200+-- ToTopRight ->
201+-- "to top right"
202+-- ToBottomRight ->
203+-- "to bottom right"
204+-- ToLeft ->
205+-- "to left"
206+-- ToTopLeft ->
207+-- "to top left"
208+-- ToBottomLeft ->
209+-- "to bottom left"
210+-- ToAngle angle ->
211+-- toString angle ++ "rad"
212+-- renderDirectionClass : Direction -> String
213+-- renderDirectionClass dir =
214+-- case dir of
215+-- ToUp ->
216+-- "to-top"
217+-- ToDown ->
218+-- "to-bottom"
219+-- ToRight ->
220+-- "to-right"
221+-- ToTopRight ->
222+-- "to-top-right"
223+-- ToBottomRight ->
224+-- "to-bottom-right"
225+-- ToLeft ->
226+-- "to-left"
227+-- ToTopLeft ->
228+-- "to-top-left"
229+-- ToBottomLeft ->
230+-- "to-bottom-left"
231+-- ToAngle angle ->
232+-- floatClass angle ++ "rad"
src/Element/Border.elmnew file+221
@@ -0,0 +1,221 @@
1+module Element.Border
2+ exposing
3+ ( color
4+ , dashed
5+ , dotted
6+ , glow
7+ , innerGlow
8+ , innerShadow
9+ , roundEach
10+ , rounded
11+ , shadow
12+ , solid
13+ , width
14+ , widthEach
15+ , widthXY
16+ )
17+
18+{-|
19+
20+@docs color
21+
22+
23+## Border Widths
24+
25+@docs width, widthXY, widthEach
26+
27+
28+## Border Styles
29+
30+@docs solid, dashed, dotted
31+
32+
33+## Rounded Corners
34+
35+@docs rounded, roundEach
36+
37+
38+## Shadows
39+
40+@docs glow, innerGlow, shadow, innerShadow
41+
42+-}
43+
44+import Element exposing (Attr, Attribute, Color)
45+import Internal.Flag as Flag
46+import Internal.Model as Internal
47+import Internal.Style as Style exposing (classes)
48+
49+
50+{-| -}
51+color : Color -> Attr decorative msg
52+color clr =
53+ Internal.StyleClass Flag.borderColor (Internal.Colored ("border-color-" ++ Internal.formatColorClass clr) "border-color" clr)
54+
55+
56+{-| -}
57+width : Int -> Attribute msg
58+width v =
59+ Internal.StyleClass Flag.borderWidth (Internal.Single ("border-" ++ String.fromInt v) "border-width" (String.fromInt v ++ "px"))
60+
61+
62+{-| Set horizontal and vertical borders.
63+-}
64+widthXY : Int -> Int -> Attribute msg
65+widthXY x y =
66+ Internal.StyleClass Flag.borderWidth (Internal.Single ("border-" ++ String.fromInt x ++ "-" ++ String.fromInt y) "border-width" (String.fromInt y ++ "px " ++ String.fromInt x ++ "px"))
67+
68+
69+{-| -}
70+widthEach : { bottom : Int, left : Int, right : Int, top : Int } -> Attribute msg
71+widthEach { bottom, top, left, right } =
72+ Internal.StyleClass Flag.borderWidth
73+ (Internal.Single ("border-" ++ String.fromInt top ++ "-" ++ String.fromInt right ++ String.fromInt bottom ++ "-" ++ String.fromInt left)
74+ "border-width"
75+ (String.fromInt top
76+ ++ "px "
77+ ++ String.fromInt right
78+ ++ "px "
79+ ++ String.fromInt bottom
80+ ++ "px "
81+ ++ String.fromInt left
82+ ++ "px"
83+ )
84+ )
85+
86+
87+
88+-- {-| No Borders
89+-- -}
90+-- none : Attribute msg
91+-- none =
92+-- Class "border" "border-none"
93+
94+
95+{-| -}
96+solid : Attribute msg
97+solid =
98+ Internal.Class Flag.borderStyle classes.borderSolid
99+
100+
101+{-| -}
102+dashed : Attribute msg
103+dashed =
104+ Internal.Class Flag.borderStyle classes.borderDashed
105+
106+
107+{-| -}
108+dotted : Attribute msg
109+dotted =
110+ Internal.Class Flag.borderStyle classes.borderDotted
111+
112+
113+{-| Round all corners.
114+-}
115+rounded : Int -> Attribute msg
116+rounded radius =
117+ Internal.StyleClass Flag.borderRound (Internal.Single ("border-radius-" ++ String.fromInt radius) "border-radius" (String.fromInt radius ++ "px"))
118+
119+
120+{-| -}
121+roundEach : { topLeft : Int, topRight : Int, bottomLeft : Int, bottomRight : Int } -> Attribute msg
122+roundEach { topLeft, topRight, bottomLeft, bottomRight } =
123+ Internal.StyleClass Flag.borderRound
124+ (Internal.Single ("border-radius-" ++ String.fromInt topLeft ++ "-" ++ String.fromInt topRight ++ String.fromInt bottomLeft ++ "-" ++ String.fromInt bottomRight)
125+ "border-radius"
126+ (String.fromInt topLeft
127+ ++ "px "
128+ ++ String.fromInt topRight
129+ ++ "px "
130+ ++ String.fromInt bottomRight
131+ ++ "px "
132+ ++ String.fromInt bottomLeft
133+ ++ "px"
134+ )
135+ )
136+
137+
138+{-| A simple glow by specifying the color and size.
139+-}
140+glow : Color -> Float -> Attr decorative msg
141+glow clr size =
142+ shadow
143+ { offset = ( 0, 0 )
144+ , size = size
145+ , blur = size * 2
146+ , color = clr
147+ }
148+
149+
150+{-| -}
151+innerGlow : Color -> Float -> Attr decorative msg
152+innerGlow clr size =
153+ innerShadow
154+ { offset = ( 0, 0 )
155+ , size = size
156+ , blur = size * 2
157+ , color = clr
158+ }
159+
160+
161+{-| -}
162+shadow :
163+ { offset : ( Float, Float )
164+ , size : Float
165+ , blur : Float
166+ , color : Color
167+ }
168+ -> Attr decorative msg
169+shadow almostShade =
170+ let
171+ shade =
172+ { inset = False
173+ , offset = almostShade.offset
174+ , size = almostShade.size
175+ , blur = almostShade.blur
176+ , color = almostShade.color
177+ }
178+ in
179+ Internal.StyleClass Flag.shadows <|
180+ Internal.Single (Internal.boxShadowName shade) "box-shadow" (Internal.formatBoxShadow shade)
181+
182+
183+{-| -}
184+innerShadow :
185+ { offset : ( Float, Float )
186+ , size : Float
187+ , blur : Float
188+ , color : Color
189+ }
190+ -> Attr decorative msg
191+innerShadow almostShade =
192+ let
193+ shade =
194+ { inset = True
195+ , offset = almostShade.offset
196+ , size = almostShade.size
197+ , blur = almostShade.blur
198+ , color = almostShade.color
199+ }
200+ in
201+ Internal.StyleClass Flag.shadows <|
202+ Internal.Single (Internal.boxShadowName shade) "box-shadow" (Internal.formatBoxShadow shade)
203+
204+
205+
206+-- {-| -}
207+-- shadow :
208+-- { offset : ( Float, Float )
209+-- , blur : Float
210+-- , size : Float
211+-- , color : Color
212+-- }
213+-- -> Attr decorative msg
214+-- shadow shade =
215+-- Internal.BoxShadow
216+-- { inset = False
217+-- , offset = shade.offset
218+-- , size = shade.size
219+-- , blur = shade.blur
220+-- , color = shade.color
221+-- }
src/Element/Events.elmnew file+272
@@ -0,0 +1,272 @@
1+module Element.Events
2+ exposing
3+ ( onClick
4+ -- , onClickCoords
5+ -- , onClickPageCoords
6+ -- , onClickScreenCoords
7+ , onDoubleClick
8+ , onFocus
9+ , onLoseFocus
10+ -- , onMouseCoords
11+ , onMouseDown
12+ , onMouseEnter
13+ , onMouseLeave
14+ , onMouseMove
15+ -- , onMousePageCoords
16+ -- , onMouseScreenCoords
17+ , onMouseUp
18+ )
19+
20+{-|
21+
22+
23+## Mouse Events
24+
25+@docs onClick, onDoubleClick, onMouseDown, onMouseUp, onMouseEnter, onMouseLeave, onMouseMove
26+
27+
28+## Focus Events
29+
30+@docs onFocus, onLoseFocus
31+
32+-}
33+
34+import Element exposing (Attribute)
35+import Html.Events
36+import Internal.Model as Internal
37+import Json.Decode as Json
38+import VirtualDom
39+
40+
41+-- MOUSE EVENTS
42+
43+
44+{-| -}
45+onMouseDown : msg -> Attribute msg
46+onMouseDown =
47+ Internal.Attr << Html.Events.onMouseDown
48+
49+
50+{-| -}
51+onMouseUp : msg -> Attribute msg
52+onMouseUp =
53+ Internal.Attr << Html.Events.onMouseUp
54+
55+
56+{-| -}
57+onClick : msg -> Attribute msg
58+onClick =
59+ Internal.Attr << Html.Events.onClick
60+
61+
62+{-| -}
63+onDoubleClick : msg -> Attribute msg
64+onDoubleClick =
65+ Internal.Attr << Html.Events.onDoubleClick
66+
67+
68+{-| -}
69+onMouseEnter : msg -> Attribute msg
70+onMouseEnter =
71+ Internal.Attr << Html.Events.onMouseEnter
72+
73+
74+{-| -}
75+onMouseLeave : msg -> Attribute msg
76+onMouseLeave =
77+ Internal.Attr << Html.Events.onMouseLeave
78+
79+
80+{-| -}
81+onMouseMove : msg -> Attribute msg
82+onMouseMove msg =
83+ on "mousemove" (Json.succeed msg)
84+
85+
86+
87+-- onClickWith
88+-- { button = primary
89+-- , send = localCoords Button
90+-- }
91+-- type alias Click =
92+-- { button : Button
93+-- , send : Track
94+-- }
95+-- type Button = Primary | Secondary
96+-- type Track
97+-- = ElementCoords
98+-- | PageCoords
99+-- | ScreenCoords
100+-- |
101+
102+
103+{-| -}
104+onClickCoords : (Coords -> msg) -> Attribute msg
105+onClickCoords msg =
106+ on "click" (Json.map msg localCoords)
107+
108+
109+{-| -}
110+onClickScreenCoords : (Coords -> msg) -> Attribute msg
111+onClickScreenCoords msg =
112+ on "click" (Json.map msg screenCoords)
113+
114+
115+{-| -}
116+onClickPageCoords : (Coords -> msg) -> Attribute msg
117+onClickPageCoords msg =
118+ on "click" (Json.map msg pageCoords)
119+
120+
121+{-| -}
122+onMouseCoords : (Coords -> msg) -> Attribute msg
123+onMouseCoords msg =
124+ on "mousemove" (Json.map msg localCoords)
125+
126+
127+{-| -}
128+onMouseScreenCoords : (Coords -> msg) -> Attribute msg
129+onMouseScreenCoords msg =
130+ on "mousemove" (Json.map msg screenCoords)
131+
132+
133+{-| -}
134+onMousePageCoords : (Coords -> msg) -> Attribute msg
135+onMousePageCoords msg =
136+ on "mousemove" (Json.map msg pageCoords)
137+
138+
139+type alias Coords =
140+ { x : Int
141+ , y : Int
142+ }
143+
144+
145+screenCoords : Json.Decoder Coords
146+screenCoords =
147+ Json.map2 Coords
148+ (Json.field "screenX" Json.int)
149+ (Json.field "screenY" Json.int)
150+
151+
152+{-| -}
153+localCoords : Json.Decoder Coords
154+localCoords =
155+ Json.map2 Coords
156+ (Json.field "offsetX" Json.int)
157+ (Json.field "offsetY" Json.int)
158+
159+
160+pageCoords : Json.Decoder Coords
161+pageCoords =
162+ Json.map2 Coords
163+ (Json.field "pageX" Json.int)
164+ (Json.field "pageY" Json.int)
165+
166+
167+
168+-- FOCUS EVENTS
169+
170+
171+{-| -}
172+onLoseFocus : msg -> Attribute msg
173+onLoseFocus =
174+ Internal.Attr << Html.Events.onBlur
175+
176+
177+{-| -}
178+onFocus : msg -> Attribute msg
179+onFocus =
180+ Internal.Attr << Html.Events.onFocus
181+
182+
183+
184+-- CUSTOM EVENTS
185+
186+
187+{-| Create a custom event listener. Normally this will not be necessary, but
188+you have the power! Here is how `onClick` is defined for example:
189+
190+ import Json.Decode as Json
191+
192+ onClick : msg -> Attribute msg
193+ onClick message =
194+ on "click" (Json.succeed message)
195+
196+The first argument is the event name in the same format as with JavaScript's
197+[`addEventListener`][aEL] function.
198+The second argument is a JSON decoder. Read more about these [here][decoder].
199+When an event occurs, the decoder tries to turn the event object into an Elm
200+value. If successful, the value is routed to your `update` function. In the
201+case of `onClick` we always just succeed with the given `message`.
202+If this is confusing, work through the [Elm Architecture Tutorial][tutorial].
203+It really does help!
204+[aEL]: <https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener>
205+[decoder]: <http://package.elm-lang.org/packages/elm-lang/core/latest/Json-Decode>
206+[tutorial]: <https://github.com/evancz/elm-architecture-tutorial/>
207+
208+-}
209+on : String -> Json.Decoder msg -> Attribute msg
210+on event decode =
211+ Internal.Attr <| Html.Events.on event decode
212+
213+
214+
215+-- {-| Same as `on` but you can set a few options.
216+-- -}
217+-- onWithOptions : String -> Html.Events.Options -> Json.Decoder msg -> Attribute msg
218+-- onWithOptions event options decode =
219+-- Internal.Attr <| Html.Events.onWithOptions event options decode
220+-- COMMON DECODERS
221+
222+
223+{-| A `Json.Decoder` for grabbing `event.target.value`. We use this to define
224+`onInput` as follows:
225+
226+ import Json.Decode as Json
227+
228+ onInput : (String -> msg) -> Attribute msg
229+ onInput tagger =
230+ on "input" (Json.map tagger targetValue)
231+
232+You probably will never need this, but hopefully it gives some insights into
233+how to make custom event handlers.
234+
235+-}
236+targetValue : Json.Decoder String
237+targetValue =
238+ Json.at [ "target", "value" ] Json.string
239+
240+
241+{-| A `Json.Decoder` for grabbing `event.target.checked`. We use this to define
242+`onCheck` as follows:
243+
244+ import Json.Decode as Json
245+
246+ onCheck : (Bool -> msg) -> Attribute msg
247+ onCheck tagger =
248+ on "input" (Json.map tagger targetChecked)
249+
250+-}
251+targetChecked : Json.Decoder Bool
252+targetChecked =
253+ Json.at [ "target", "checked" ] Json.bool
254+
255+
256+{-| A `Json.Decoder` for grabbing `event.keyCode`. This helps you define
257+keyboard listeners like this:
258+
259+ import Json.Decode as Json
260+
261+ onKeyUp : (Int -> msg) -> Attribute msg
262+ onKeyUp tagger =
263+ on "keyup" (Json.map tagger keyCode)
264+
265+**Note:** It looks like the spec is moving away from `event.keyCode` and
266+towards `event.key`. Once this is supported in more browsers, we may add
267+helpers here for `onKeyUp`, `onKeyDown`, `onKeyPress`, etc.
268+
269+-}
270+keyCode : Json.Decoder Int
271+keyCode =
272+ Json.field "keyCode" Json.int
src/Element/Font.elmnew file+332
@@ -0,0 +1,332 @@
1+module Element.Font
2+ exposing
3+ ( Font
4+ , alignLeft
5+ , alignRight
6+ , bold
7+ , center
8+ , color
9+ , external
10+ , extraBold
11+ , extraLight
12+ , family
13+ , glow
14+ , hairline
15+ , heavy
16+ , italic
17+ , justify
18+ , letterSpacing
19+ , light
20+ , medium
21+ , monospace
22+ , regular
23+ , sansSerif
24+ , semiBold
25+ , serif
26+ , shadow
27+ , size
28+ , strike
29+ , typeface
30+ , underline
31+ , unitalicized
32+ , wordSpacing
33+ )
34+
35+{-|
36+
37+ import Color exposing (blue)
38+ import Element
39+ import Element.Font as Font
40+
41+ view =
42+ Element.el
43+ [ Font.color blue
44+ , Font.size 18
45+ , Font.family
46+ [ Font.typeface "Open Sans"
47+ , Font.sansSerif
48+ ]
49+ ]
50+ (Element.text "Woohoo, I'm stylish text")
51+
52+**Note**: `Font.color`, `Font.size`, and `Font.family` are inherited, meaning you can set them at the top of your view and all subsequent nodes will have that value.
53+
54+@docs color, size
55+
56+
57+## Typefaces
58+
59+@docs family, Font, typeface, serif, sansSerif, monospace
60+
61+@docs external
62+
63+`Font.external` can be used to import font files. Let's say you found a neat font on <http://fonts.google.com>:
64+
65+ import Element
66+ import Element.Font as Font
67+
68+ view =
69+ Element.el
70+ [ Font.family
71+ [ Font.external
72+ { name = "Roboto"
73+ , url = "https://fonts.googleapis.com/css?family=Roboto"
74+ }
75+ , Font.sansSerif
76+ ]
77+ ]
78+ (Element.text "Woohoo, I'm stylish text")
79+
80+
81+## Alignment and Spacing
82+
83+@docs alignLeft, alignRight, center, justify, letterSpacing, wordSpacing
84+
85+
86+## Font Styles
87+
88+@docs underline, strike, italic, unitalicized
89+
90+
91+## Font Weight
92+
93+@docs heavy, extraBold, bold, semiBold, medium, regular, light, extraLight, hairline
94+
95+
96+## Shadows
97+
98+@docs glow, shadow
99+
100+-}
101+
102+import Element exposing (Attr, Attribute, Color)
103+import Internal.Flag as Flag
104+import Internal.Model as Internal
105+import Internal.Style exposing (classes)
106+
107+
108+{-| -}
109+type alias Font =
110+ Internal.Font
111+
112+
113+{-| -}
114+color : Color -> Attr decorative msg
115+color fontColor =
116+ Internal.StyleClass Flag.fontColor (Internal.Colored ("fc-" ++ Internal.formatColorClass fontColor) "color" fontColor)
117+
118+
119+{-|
120+
121+ import Element
122+ import Element.Font as Font
123+
124+ myElement =
125+ Element.el
126+ [ Font.family
127+ [ Font.typeface "Helvetica"
128+ , Font.sansSerif
129+ ]
130+ ]
131+ (text "")
132+
133+-}
134+family : List Font -> Attribute msg
135+family families =
136+ Internal.StyleClass Flag.fontFamily <| Internal.FontFamily (List.foldl Internal.renderFontClassName "ff-" families) families
137+
138+
139+{-| -}
140+serif : Font
141+serif =
142+ Internal.Serif
143+
144+
145+{-| -}
146+sansSerif : Font
147+sansSerif =
148+ Internal.SansSerif
149+
150+
151+{-| -}
152+monospace : Font
153+monospace =
154+ Internal.Monospace
155+
156+
157+{-| -}
158+typeface : String -> Font
159+typeface =
160+ Internal.Typeface
161+
162+
163+{-| -}
164+external : { url : String, name : String } -> Font
165+external { url, name } =
166+ Internal.ImportFont name url
167+
168+
169+{-| Font sizes are always given as `px`.
170+-}
171+size : Int -> Attr decorative msg
172+size i =
173+ Internal.StyleClass Flag.fontSize (Internal.FontSize i)
174+
175+
176+{-| In `px`.
177+-}
178+letterSpacing : Float -> Attribute msg
179+letterSpacing offset =
180+ Internal.StyleClass Flag.letterSpacing <|
181+ Internal.Single
182+ ("ls-" ++ Internal.floatClass offset)
183+ "letter-spacing"
184+ (String.fromFloat offset ++ "px")
185+
186+
187+{-| In `px`.
188+-}
189+wordSpacing : Float -> Attribute msg
190+wordSpacing offset =
191+ Internal.StyleClass Flag.wordSpacing <|
192+ Internal.Single ("ws-" ++ Internal.floatClass offset) "word-spacing" (String.fromFloat offset ++ "px")
193+
194+
195+{-| Align the font to the left.
196+-}
197+alignLeft : Attribute msg
198+alignLeft =
199+ Internal.Class Flag.fontAlignment classes.textLeft
200+
201+
202+{-| Align the font to the right.
203+-}
204+alignRight : Attribute msg
205+alignRight =
206+ Internal.Class Flag.fontAlignment classes.textRight
207+
208+
209+{-| Center align the font.
210+-}
211+center : Attribute msg
212+center =
213+ Internal.Class Flag.fontAlignment classes.textCenter
214+
215+
216+{-| -}
217+justify : Attribute msg
218+justify =
219+ Internal.Class Flag.fontAlignment classes.textJustify
220+
221+
222+
223+-- {-| -}
224+-- justifyAll : Attribute msg
225+-- justifyAll =
226+-- Internal.class classesTextJustifyAll
227+
228+
229+{-| -}
230+underline : Attribute msg
231+underline =
232+ Internal.htmlClass classes.underline
233+
234+
235+{-| -}
236+strike : Attribute msg
237+strike =
238+ Internal.htmlClass classes.strike
239+
240+
241+{-| -}
242+italic : Attribute msg
243+italic =
244+ Internal.htmlClass classes.italic
245+
246+
247+{-| -}
248+bold : Attribute msg
249+bold =
250+ Internal.Class Flag.fontWeight classes.bold
251+
252+
253+{-| -}
254+light : Attribute msg
255+light =
256+ Internal.Class Flag.fontWeight classes.textLight
257+
258+
259+{-| -}
260+hairline : Attribute msg
261+hairline =
262+ Internal.Class Flag.fontWeight classes.textThin
263+
264+
265+{-| -}
266+extraLight : Attribute msg
267+extraLight =
268+ Internal.Class Flag.fontWeight classes.textExtraLight
269+
270+
271+{-| -}
272+regular : Attribute msg
273+regular =
274+ Internal.Class Flag.fontWeight classes.textNormalWeight
275+
276+
277+{-| -}
278+semiBold : Attribute msg
279+semiBold =
280+ Internal.Class Flag.fontWeight classes.textSemiBold
281+
282+
283+{-| -}
284+medium : Attribute msg
285+medium =
286+ Internal.Class Flag.fontWeight classes.textMedium
287+
288+
289+{-| -}
290+extraBold : Attribute msg
291+extraBold =
292+ Internal.Class Flag.fontWeight classes.textExtraBold
293+
294+
295+{-| -}
296+heavy : Attribute msg
297+heavy =
298+ Internal.Class Flag.fontWeight classes.textHeavy
299+
300+
301+{-| This will reset bold and italic.
302+-}
303+unitalicized : Attribute msg
304+unitalicized =
305+ Internal.htmlClass classes.textUnitalicized
306+
307+
308+{-| -}
309+shadow :
310+ { offset : ( Float, Float )
311+ , blur : Float
312+ , color : Color
313+ }
314+ -> Attr decorative msg
315+shadow shade =
316+ Internal.StyleClass Flag.txtShadows <|
317+ Internal.Single (Internal.textShadowName shade) "text-shadow" (Internal.formatTextShadow shade)
318+
319+
320+{-| A glow is just a simplified shadow
321+-}
322+glow : Color -> Float -> Attr decorative msg
323+glow clr i =
324+ let
325+ shade =
326+ { offset = ( 0, 0 )
327+ , blur = i * 2
328+ , color = clr
329+ }
330+ in
331+ Internal.StyleClass Flag.txtShadows <|
332+ Internal.Single (Internal.textShadowName shade) "text-shadow" (Internal.formatTextShadow shade)
src/Element/Input.elmnew file+1834
@@ -0,0 +1,1834 @@
1+module Element.Input
2+ exposing
3+ ( Label
4+ , Option
5+ , OptionState(..)
6+ , Placeholder
7+ , Thumb
8+ , button
9+ , checkbox
10+ , currentPassword
11+ , defaultCheckbox
12+ , defaultThumb
13+ , email
14+ , focusedOnLoad
15+ , labelAbove
16+ , labelBelow
17+ , labelLeft
18+ , labelRight
19+ , multiline
20+ , newPassword
21+ , option
22+ , optionWith
23+ , placeholder
24+ , radio
25+ , radioRow
26+ , search
27+ , slider
28+ , spellChecked
29+ , text
30+ , thumb
31+ , username
32+ )
33+
34+{-|
35+
36+@docs button
37+
38+@docs checkbox, defaultCheckbox
39+
40+
41+## Text Input
42+
43+@docs text, Placeholder, placeholder, username, newPassword, currentPassword, email, search, spellChecked
44+
45+
46+## Multiline Text
47+
48+@docs multiline
49+
50+
51+## Slider
52+
53+@docs slider, Thumb, thumb, defaultThumb
54+
55+
56+## Radio Buttons
57+
58+@docs radio, radioRow, Option, option, optionWith, OptionState
59+
60+
61+## Labels
62+
63+@docs Label, labelAbove, labelBelow, labelLeft, labelRight
64+
65+@docs focusedOnLoad
66+
67+-}
68+
69+import Element exposing (Attribute, Color, Element)
70+import Element.Background as Background
71+import Element.Border as Border
72+import Element.Events as Events
73+import Element.Font as Font
74+import Element.Region as Region
75+import Html
76+import Html.Attributes
77+import Html.Events
78+import Internal.Flag as Flag
79+import Internal.Model as Internal
80+import Internal.Style exposing (classes)
81+import Json.Decode as Json
82+
83+
84+{-| -}
85+type Placeholder msg
86+ = Placeholder (List (Attribute msg)) (Element msg)
87+
88+
89+white =
90+ Element.rgb 1 1 1
91+
92+
93+darkGrey =
94+ Element.rgb (186 / 255) (189 / 255) (182 / 255)
95+
96+
97+charcoal =
98+ Element.rgb
99+ (136 / 255)
100+ (138 / 255)
101+ (133 / 255)
102+
103+
104+{-| -}
105+placeholder : List (Attribute msg) -> Element msg -> Placeholder msg
106+placeholder =
107+ Placeholder
108+
109+
110+{-| Every input has a required `label`.
111+-}
112+type Label msg
113+ = Label Internal.Location (List (Attribute msg)) (Element msg)
114+
115+
116+{-| -}
117+labelRight : List (Attribute msg) -> Element msg -> Label msg
118+labelRight =
119+ Label Internal.OnRight
120+
121+
122+{-| -}
123+labelLeft : List (Attribute msg) -> Element msg -> Label msg
124+labelLeft =
125+ Label Internal.OnLeft
126+
127+
128+{-| -}
129+labelAbove : List (Attribute msg) -> Element msg -> Label msg
130+labelAbove =
131+ Label Internal.Above
132+
133+
134+{-| -}
135+labelBelow : List (Attribute msg) -> Element msg -> Label msg
136+labelBelow =
137+ Label Internal.Below
138+
139+
140+{-| A standard button.
141+
142+The `onPress` handler will be fired either `onClick` or when the element is focused and the enter key has been pressed.
143+
144+ import Element.Input as Input
145+
146+ Input.button []
147+ { onPress = Just ClickMsg
148+ , label = text "My Button"
149+ }
150+
151+`onPress` takes a `Maybe msg`. If you provide the value `Nothing`, then the button will be disabled.
152+
153+-}
154+button :
155+ List (Attribute msg)
156+ ->
157+ { onPress : Maybe msg
158+ , label : Element msg
159+ }
160+ -> Element msg
161+button attrs { onPress, label } =
162+ Internal.element
163+ Internal.asEl
164+ -- We don't explicitly label this node as a button,
165+ -- because buttons fire a bunch of times when you hold down the enter key.
166+ -- We'd like to fire just once on the enter key, which means using keyup instead of keydown.
167+ -- Because we have no way to disable keydown, though our messages get doubled.
168+ Internal.div
169+ (Element.width Element.shrink
170+ :: Element.height Element.shrink
171+ :: Internal.htmlClass classes.contentCenterX
172+ :: Internal.htmlClass classes.contentCenterY
173+ :: Internal.htmlClass classes.seButton
174+ :: Element.pointer
175+ :: focusDefault attrs
176+ :: Internal.Describe Internal.Button
177+ :: Internal.Attr (Html.Attributes.tabindex 0)
178+ :: (case onPress of
179+ Nothing ->
180+ Internal.Attr (Html.Attributes.disabled True) :: attrs
181+
182+ Just msg ->
183+ Events.onClick msg
184+ :: onEnter msg
185+ :: attrs
186+ )
187+ )
188+ (Internal.Unkeyed [ label ])
189+
190+
191+focusDefault attrs =
192+ if List.any hasFocusStyle attrs then
193+ Internal.NoAttribute
194+ else
195+ Internal.htmlClass "focusable"
196+
197+
198+hasFocusStyle attr =
199+ case attr of
200+ Internal.StyleClass _ (Internal.PseudoSelector Internal.Focus _) ->
201+ True
202+
203+ _ ->
204+ False
205+
206+
207+{-| -}
208+type alias Checkbox msg =
209+ { onChange : Maybe (Bool -> msg)
210+ , icon : Maybe (Element msg)
211+ , checked : Bool
212+ , label : Label msg
213+ }
214+
215+
216+{-| -}
217+checkbox :
218+ List (Attribute msg)
219+ ->
220+ { onChange : Bool -> msg
221+ , icon : Bool -> Element msg
222+ , checked : Bool
223+ , label : Label msg
224+ }
225+ -> Element msg
226+checkbox attrs { label, icon, checked, onChange } =
227+ let
228+ attributes =
229+ Element.spacing 6
230+ :: [ Internal.Attr (Html.Events.onClick (onChange (not checked)))
231+ , Region.announce
232+ , onKeyLookup <|
233+ \code ->
234+ if code == enter then
235+ Just <| onChange (not checked)
236+ else if code == space then
237+ Just <| onChange (not checked)
238+ else
239+ Nothing
240+ ]
241+ ++ (tabindex 0 :: Element.pointer :: Element.alignLeft :: Element.width Element.fill :: attrs)
242+ in
243+ applyLabel attributes
244+ label
245+ (Internal.element
246+ Internal.asEl
247+ Internal.div
248+ [ Internal.Attr <|
249+ Html.Attributes.attribute "role" "checkbox"
250+ , Internal.Attr <|
251+ Html.Attributes.attribute "aria-checked" <|
252+ if checked then
253+ "true"
254+ else
255+ "false"
256+ , Element.centerY
257+ , Element.height Element.fill
258+ , Element.width Element.shrink
259+ ]
260+ (Internal.Unkeyed
261+ [ icon checked
262+ ]
263+ )
264+ )
265+
266+
267+{-| -}
268+type Thumb
269+ = Thumb (List (Attribute Never))
270+
271+
272+{-| -}
273+thumb : List (Attribute Never) -> Thumb
274+thumb =
275+ Thumb
276+
277+
278+{-| -}
279+defaultThumb : Thumb
280+defaultThumb =
281+ Thumb
282+ [ Element.width (Element.px 16)
283+ , Element.height (Element.px 16)
284+ , Border.rounded 8
285+ , Border.width 1
286+ , Border.color (Element.rgb 0.5 0.5 0.5)
287+ , Background.color (Element.rgb 1 1 1)
288+ ]
289+
290+
291+{-| A slider input, good for capturing float values.
292+
293+ Input.slider
294+ [ Element.height (Element.px 30)
295+ -- Here is where we're creating/styling the "track"
296+ , Element.behindContent
297+ (Element.el
298+ [ Element.width Element.fill
299+ , Element.height (Element.px 2)
300+ , Element.centerY
301+ , Background.color grey
302+ , Border.rounded 2
303+ ]
304+ Element.none
305+ )
306+ ]
307+ { onChange = AdjustValue
308+ , label = Input.labelAbove [] (text "My Slider Value")
309+ , min = 0
310+ , max = 75
311+ , step = Nothing
312+ , value = model.sliderValue
313+ , thumb =
314+ Input.defaultThumb
315+ }
316+
317+The `thumb` is the icon that you can move around.
318+
319+The slider can be vertical or horizontal depending on the width/height of the slider.
320+
321+ - `height fill` and `width (px someWidth)` will cause the slider to be vertical.
322+ - `height (px someHeight)` and `width (px someWidth)` where `someHeight` > `someWidth` will also do it.
323+ - otherwise, the slider will be horizontal.
324+
325+**Note:** If you want a slider for an `Int` value:
326+
327+ - set `step` to be `Just 1`, or some other whole value
328+ - `value = toFloat model.myInt`
329+ - And finally, round the value before making a message `onChange = round >> AdjustValue`
330+
331+-}
332+slider :
333+ List (Attribute msg)
334+ ->
335+ { onChange : Float -> msg
336+ , label : Label msg
337+ , min : Float
338+ , max : Float
339+ , value : Float
340+ , thumb : Thumb
341+ , step : Maybe Float
342+ }
343+ -> Element msg
344+slider attributes input =
345+ let
346+ (Thumb thumbAttributes) =
347+ input.thumb
348+
349+ width =
350+ Internal.getWidth thumbAttributes
351+
352+ height =
353+ Internal.getHeight thumbAttributes
354+
355+ vertical =
356+ case ( trackWidth, trackHeight ) of
357+ ( Nothing, Nothing ) ->
358+ False
359+
360+ ( Just (Internal.Px w), Just (Internal.Px h) ) ->
361+ h > w
362+
363+ ( Just (Internal.Px _), Just (Internal.Fill _) ) ->
364+ True
365+
366+ _ ->
367+ False
368+
369+ trackHeight =
370+ Internal.getHeight attributes
371+
372+ trackWidth =
373+ Internal.getWidth attributes
374+
375+ ( spacingX, spacingY ) =
376+ Internal.getSpacing attributes ( 5, 5 )
377+
378+ factor =
379+ (input.value - input.min)
380+ / (input.max - input.min)
381+
382+ {- Needed attributes
383+
384+ Thumb Attributes
385+ - Width/Height of thumb so that the input can shadow it.
386+
387+
388+ Attributes
389+
390+ OnParent ->
391+ Spacing
392+
393+
394+ On track ->
395+ Everything else
396+
397+
398+
399+
400+ The `<input>`
401+
402+
403+ -}
404+ className =
405+ "thmb-" ++ thumbWidthString ++ "-" ++ thumbHeightString
406+
407+ thumbWidthString =
408+ case width of
409+ Nothing ->
410+ "20px"
411+
412+ Just (Internal.Px px) ->
413+ String.fromInt px ++ "px"
414+
415+ _ ->
416+ "100%"
417+
418+ thumbHeightString =
419+ case height of
420+ Nothing ->
421+ "20px"
422+
423+ Just (Internal.Px px) ->
424+ String.fromInt px ++ "px"
425+
426+ _ ->
427+ "100%"
428+
429+ thumbShadowStyle =
430+ [ Internal.Property "width"
431+ thumbWidthString
432+ , Internal.Property "height"
433+ thumbHeightString
434+ ]
435+ in
436+ applyLabel
437+ [ Element.spacingXY spacingX spacingY
438+ , Region.announce
439+ , Element.width Element.fill
440+ ]
441+ input.label
442+ (Element.row
443+ [ Element.width
444+ (Maybe.withDefault Element.fill trackWidth)
445+ , Element.height
446+ (Maybe.withDefault (Element.px 20) trackHeight)
447+ ]
448+ [ Internal.element
449+ Internal.asEl
450+ (Internal.NodeName "input")
451+ [ Internal.StyleClass Flag.active
452+ (Internal.Style
453+ ("input[type=\"range\"]." ++ className ++ "::-moz-range-thumb")
454+ thumbShadowStyle
455+ )
456+ , Internal.StyleClass Flag.hover
457+ (Internal.Style
458+ ("input[type=\"range\"]." ++ className ++ "::-webkit-slider-thumb")
459+ thumbShadowStyle
460+ )
461+ , Internal.StyleClass Flag.focus
462+ (Internal.Style
463+ ("input[type=\"range\"]." ++ className ++ "::-ms-thumb")
464+ thumbShadowStyle
465+ )
466+ , Internal.Attr (Html.Attributes.class (className ++ " focusable-parent"))
467+ , Internal.Attr
468+ (Html.Events.onInput
469+ (\str ->
470+ case String.toFloat str of
471+ Nothing ->
472+ -- This should never happen because the browser
473+ -- should always provide a Float.
474+ input.onChange 0
475+
476+ Just val ->
477+ input.onChange val
478+ )
479+ )
480+ , Internal.Attr <|
481+ Html.Attributes.type_ "range"
482+ , Internal.Attr <|
483+ Html.Attributes.step
484+ (case input.step of
485+ Nothing ->
486+ -- Note: If we set `any` here,
487+ -- Firefox makes a single press of the arrows keys equal to 1
488+ -- We could set the step manually to the effective range / 100
489+ -- String.fromFloat ((input.max - input.min) / 100)
490+ -- Which matches Chrome's default behavior
491+ -- HOWEVER, that means manually moving a slider with the mouse will snap to that interval.
492+ "any"
493+
494+ Just step ->
495+ String.fromFloat step
496+ )
497+ , Internal.Attr <|
498+ Html.Attributes.min (String.fromFloat input.min)
499+ , Internal.Attr <|
500+ Html.Attributes.max (String.fromFloat input.max)
501+ , Internal.Attr <|
502+ Html.Attributes.value (String.fromFloat input.value)
503+ , if vertical then
504+ Internal.Attr <|
505+ Html.Attributes.attribute "orient" "vertical"
506+ else
507+ Internal.NoAttribute
508+ , Element.width <|
509+ if vertical then
510+ Maybe.withDefault (Element.px 20) trackHeight
511+ else
512+ Maybe.withDefault Element.fill trackWidth
513+ , Element.height <|
514+ if vertical then
515+ Maybe.withDefault Element.fill trackWidth
516+ else
517+ Maybe.withDefault (Element.px 20) trackHeight
518+ ]
519+ (Internal.Unkeyed [])
520+ , Element.el
521+ (Element.width
522+ (Maybe.withDefault Element.fill trackWidth)
523+ :: Element.height
524+ (Maybe.withDefault (Element.px 20) trackHeight)
525+ :: attributes
526+ -- This is after `attributes` because the thumb should be in front of everything.
527+ ++ [ Element.behindContent <|
528+ if vertical then
529+ viewVerticalThumb factor thumbAttributes trackWidth
530+ else
531+ viewHorizontalThumb factor thumbAttributes trackHeight
532+ ]
533+ )
534+ Element.none
535+ ]
536+ )
537+
538+
539+viewHorizontalThumb factor thumbAttributes trackHeight =
540+ Element.row
541+ [ Element.width Element.fill
542+ , Element.height (Maybe.withDefault Element.fill trackHeight)
543+ , Element.centerY
544+ ]
545+ [ Element.el
546+ [ Element.width (Element.fillPortion (round <| factor * 10000))
547+ ]
548+ Element.none
549+ , Element.el
550+ (Element.centerY
551+ :: List.map (Internal.mapAttr Basics.never) thumbAttributes
552+ )
553+ Element.none
554+ , Element.el
555+ [ Element.width (Element.fillPortion (round <| (abs <| 1 - factor) * 10000))
556+ ]
557+ Element.none
558+ ]
559+
560+
561+viewVerticalThumb factor thumbAttributes trackWidth =
562+ Element.column
563+ [ Element.height Element.fill
564+ , Element.width (Maybe.withDefault Element.fill trackWidth)
565+ , Element.centerX
566+ ]
567+ [ Element.el
568+ [ Element.height (Element.fillPortion (round <| (abs <| 1 - factor) * 10000))
569+ ]
570+ Element.none
571+ , Element.el
572+ (Element.centerX
573+ :: List.map (Internal.mapAttr Basics.never) thumbAttributes
574+ )
575+ Element.none
576+ , Element.el
577+ [ Element.height (Element.fillPortion (round <| factor * 10000))
578+ ]
579+ Element.none
580+ ]
581+
582+
583+type alias TextInput =
584+ { type_ : TextKind
585+ , spellchecked : Bool
586+ , autofill : Maybe String
587+ }
588+
589+
590+type TextKind
591+ = TextInputNode String
592+ | TextArea
593+
594+
595+{-| -}
596+type alias Text msg =
597+ { onChange : String -> msg
598+ , text : String
599+ , placeholder : Maybe (Placeholder msg)
600+ , label : Label msg
601+ }
602+
603+
604+type Padding
605+ = Padding Int Int Int Int
606+
607+
608+{-|
609+
610+ attributes
611+
612+ <parent>
613+ attribute::width/height fill
614+ attribtue::alignment
615+ attribute::spacing
616+ attribute::fontsize/family/lineheight
617+ <el-wrapper>
618+ attribute::nearby(placeholder)
619+ attribute::width/height fill
620+ inFront ->
621+ placeholder
622+ attribute::padding
623+
624+
625+ <input>
626+ textarea ->
627+ special height for height-content
628+ attribtue::padding
629+ attribute::lineHeight
630+ attributes
631+ <label>
632+
633+-}
634+textHelper : TextInput -> List (Attribute msg) -> Text msg -> Element msg
635+textHelper textInput attrs textOptions =
636+ let
637+ attributes =
638+ Element.width Element.fill :: (defaultTextBoxStyle ++ attrs)
639+
640+ behavior =
641+ [ Internal.Attr (Html.Events.onInput textOptions.onChange) ]
642+
643+ noNearbys =
644+ List.filter (not << forNearby) attributes
645+
646+ forNearby attr =
647+ case attr of
648+ Internal.Nearby _ _ ->
649+ True
650+
651+ _ ->
652+ False
653+
654+ ( inputNode, inputAttrs, inputChildren ) =
655+ case textInput.type_ of
656+ TextInputNode inputType ->
657+ ( "input"
658+ , [ value textOptions.text
659+ , Internal.Attr (Html.Attributes.type_ inputType)
660+ , spellcheck textInput.spellchecked
661+ , Internal.htmlClass classes.inputText
662+ , case textInput.autofill of
663+ Nothing ->
664+ Internal.NoAttribute
665+
666+ Just fill ->
667+ autofill fill
668+ ]
669+ ++ noNearbys
670+ , []
671+ )
672+
673+ TextArea ->
674+ let
675+ { maybePadding, heightContent, maybeSpacing, adjustedAttributes } =
676+ attributes
677+ |> List.foldr
678+ (\attr found ->
679+ case attr of
680+ Internal.Describe _ ->
681+ found
682+
683+ Internal.Height val ->
684+ case found.heightContent of
685+ Nothing ->
686+ case val of
687+ Internal.Content ->
688+ { found
689+ | heightContent = Just val
690+ , adjustedAttributes = attr :: found.adjustedAttributes
691+ }
692+
693+ _ ->
694+ { found | heightContent = Just val }
695+
696+ Just i ->
697+ found
698+
699+ Internal.StyleClass _ (Internal.PaddingStyle _ t r b l) ->
700+ case found.maybePadding of
701+ Nothing ->
702+ { found
703+ | maybePadding = Just (Padding t r b l)
704+ , adjustedAttributes = found.adjustedAttributes
705+ }
706+
707+ _ ->
708+ found
709+
710+ Internal.StyleClass _ (Internal.SpacingStyle _ x y) ->
711+ case found.maybeSpacing of
712+ Nothing ->
713+ { found
714+ | maybeSpacing = Just y
715+ , adjustedAttributes = attr :: found.adjustedAttributes
716+ }
717+
718+ _ ->
719+ found
720+
721+ _ ->
722+ { found | adjustedAttributes = attr :: found.adjustedAttributes }
723+ )
724+ { maybePadding = Nothing
725+ , heightContent = Nothing
726+ , maybeSpacing = Nothing
727+ , adjustedAttributes = []
728+ }
729+
730+ -- NOTE: This is where default text spacing is set
731+ spacing =
732+ Maybe.withDefault 5 maybeSpacing
733+ in
734+ ( "textarea"
735+ , [ spellcheck textInput.spellchecked
736+ , Internal.htmlClass classes.inputMultiline
737+ , Maybe.map autofill textInput.autofill
738+ |> Maybe.withDefault Internal.NoAttribute
739+ , case maybePadding of
740+ Nothing ->
741+ Internal.NoAttribute
742+
743+ Just (Padding t r b l) ->
744+ Element.paddingEach
745+ { top = max 0 (t - (spacing // 2))
746+ , bottom = max 0 (b - (spacing // 2))
747+ , left = l
748+ , right = r
749+ }
750+ , case heightContent of
751+ Nothing ->
752+ Internal.NoAttribute
753+
754+ Just Internal.Content ->
755+ let
756+ newlineCount =
757+ String.lines textOptions.text
758+ |> List.length
759+ |> (\x ->
760+ if x < 1 then
761+ 1
762+ else
763+ x
764+ )
765+
766+ heightValue count =
767+ case maybePadding of
768+ Nothing ->
769+ "calc(" ++ String.fromInt count ++ "em + " ++ String.fromInt ((count - 1) * spacing) ++ "px) !important"
770+
771+ Just (Padding t r b l) ->
772+ "calc(" ++ String.fromInt count ++ "em + " ++ String.fromInt ((t + b) + ((count - 1) * spacing)) ++ "px) !important"
773+ in
774+ Internal.StyleClass Flag.height
775+ (Internal.Single ("textarea-height-" ++ String.fromInt newlineCount)
776+ "height"
777+ (heightValue newlineCount)
778+ )
779+
780+ Just x ->
781+ Internal.Height x
782+ ]
783+ ++ adjustedAttributes
784+ , [ Internal.unstyled (Html.text textOptions.text) ]
785+ )
786+
787+ attributesFromChild =
788+ Internal.get attributes <|
789+ \attr ->
790+ case attr of
791+ Internal.Width (Internal.Fill _) ->
792+ True
793+
794+ Internal.Height (Internal.Fill _) ->
795+ True
796+
797+ Internal.AlignX _ ->
798+ True
799+
800+ Internal.AlignY _ ->
801+ True
802+
803+ Internal.StyleClass _ (Internal.SpacingStyle _ _ _) ->
804+ True
805+
806+ Internal.StyleClass _ (Internal.FontSize _) ->
807+ True
808+
809+ Internal.StyleClass _ (Internal.FontFamily _ _) ->
810+ True
811+
812+ _ ->
813+ False
814+
815+ nearbys =
816+ Internal.get attributes <|
817+ \attr ->
818+ case attr of
819+ Internal.Nearby _ _ ->
820+ True
821+
822+ _ ->
823+ False
824+
825+ inputPadding =
826+ Internal.get attributes <|
827+ \attr ->
828+ case attr of
829+ Internal.StyleClass _ (Internal.PaddingStyle _ _ _ _ _) ->
830+ True
831+
832+ _ ->
833+ False
834+
835+ inputElement =
836+ Internal.element
837+ Internal.asEl
838+ Internal.div
839+ (Element.width Element.fill
840+ :: List.concat
841+ [ nearbys
842+ , case textOptions.placeholder of
843+ Nothing ->
844+ []
845+
846+ Just (Placeholder placeholderAttrs placeholderEl) ->
847+ [ Element.inFront
848+ (Element.el
849+ (defaultTextPadding
850+ :: noNearbys
851+ ++ [ Font.color charcoal
852+ , Internal.htmlClass (classes.noTextSelection ++ " " ++ classes.passPointerEvents)
853+ , Border.color (Element.rgba 0 0 0 0)
854+ , Background.color (Element.rgba 0 0 0 0)
855+ , Element.height Element.fill
856+ , Element.width Element.fill
857+ , Element.alpha
858+ (if textOptions.text == "" then
859+ 1
860+ else
861+ 0
862+ )
863+ ]
864+ ++ placeholderAttrs
865+ )
866+ placeholderEl
867+ )
868+ ]
869+ ]
870+ )
871+ (Internal.Unkeyed
872+ [ Internal.element
873+ Internal.asEl
874+ (Internal.NodeName inputNode)
875+ (List.concat
876+ [ [ focusDefault attrs
877+ ]
878+ , inputAttrs
879+ , behavior
880+ ]
881+ )
882+ (Internal.Unkeyed inputChildren)
883+ ]
884+ )
885+ in
886+ applyLabel
887+ (Internal.Class Flag.cursor classes.cursorText
888+ :: Element.spacing 5
889+ :: Region.announce
890+ :: attributesFromChild
891+ )
892+ textOptions.label
893+ inputElement
894+
895+
896+{-| -}
897+text :
898+ List (Attribute msg)
899+ ->
900+ { onChange : String -> msg
901+ , text : String
902+ , placeholder : Maybe (Placeholder msg)
903+ , label : Label msg
904+ }
905+ -> Element msg
906+text =
907+ textHelper
908+ { type_ = TextInputNode "text"
909+ , spellchecked = False
910+ , autofill = Nothing
911+ }
912+
913+
914+{-| -}
915+spellChecked :
916+ List (Attribute msg)
917+ ->
918+ { onChange : String -> msg
919+ , text : String
920+ , placeholder : Maybe (Placeholder msg)
921+ , label : Label msg
922+ }
923+ -> Element msg
924+spellChecked =
925+ textHelper
926+ { type_ = TextInputNode "text"
927+ , spellchecked = True
928+ , autofill = Nothing
929+ }
930+
931+
932+{-| -}
933+search :
934+ List (Attribute msg)
935+ ->
936+ { onChange : String -> msg
937+ , text : String
938+ , placeholder : Maybe (Placeholder msg)
939+ , label : Label msg
940+ }
941+ -> Element msg
942+search =
943+ textHelper
944+ { type_ = TextInputNode "search"
945+ , spellchecked = False
946+ , autofill = Nothing
947+ }
948+
949+
950+{-| A password input that allows the browser to autofill.
951+
952+It's `newPassword` instead of just `password` because it gives the browser a hint on what type of password input it is.
953+
954+A password takes all the arguments a normal `Input.text` would, and also `show`, which will remove the password mask (e.g. `****` vs `pass1234`)
955+
956+-}
957+newPassword :
958+ List (Attribute msg)
959+ ->
960+ { onChange : String -> msg
961+ , text : String
962+ , placeholder : Maybe (Placeholder msg)
963+ , label : Label msg
964+ , show : Bool
965+ }
966+ -> Element msg
967+newPassword attrs pass =
968+ textHelper
969+ { type_ =
970+ TextInputNode <|
971+ if pass.show then
972+ "text"
973+ else
974+ "password"
975+ , spellchecked = False
976+ , autofill = Just "new-password"
977+ }
978+ attrs
979+ { onChange = pass.onChange
980+ , text = pass.text
981+ , placeholder = pass.placeholder
982+ , label = pass.label
983+ }
984+
985+
986+{-| -}
987+currentPassword :
988+ List (Attribute msg)
989+ ->
990+ { onChange : String -> msg
991+ , text : String
992+ , placeholder : Maybe (Placeholder msg)
993+ , label : Label msg
994+ , show : Bool
995+ }
996+ -> Element msg
997+currentPassword attrs pass =
998+ textHelper
999+ { type_ =
1000+ TextInputNode <|
1001+ if pass.show then
1002+ "text"
1003+ else
1004+ "password"
1005+ , spellchecked = False
1006+ , autofill = Just "current-password"
1007+ }
1008+ attrs
1009+ { onChange = pass.onChange
1010+ , text = pass.text
1011+ , placeholder = pass.placeholder
1012+ , label = pass.label
1013+ }
1014+
1015+
1016+{-| -}
1017+username :
1018+ List (Attribute msg)
1019+ ->
1020+ { onChange : String -> msg
1021+ , text : String
1022+ , placeholder : Maybe (Placeholder msg)
1023+ , label : Label msg
1024+ }
1025+ -> Element msg
1026+username =
1027+ textHelper
1028+ { type_ = TextInputNode "text"
1029+ , spellchecked = False
1030+ , autofill = Just "username"
1031+ }
1032+
1033+
1034+{-| -}
1035+email :
1036+ List (Attribute msg)
1037+ ->
1038+ { onChange : String -> msg
1039+ , text : String
1040+ , placeholder : Maybe (Placeholder msg)
1041+ , label : Label msg
1042+ }
1043+ -> Element msg
1044+email =
1045+ textHelper
1046+ { type_ = TextInputNode "email"
1047+ , spellchecked = False
1048+ , autofill = Just "email"
1049+ }
1050+
1051+
1052+{-| -}
1053+multiline :
1054+ List (Attribute msg)
1055+ ->
1056+ { onChange : String -> msg
1057+ , text : String
1058+ , placeholder : Maybe (Placeholder msg)
1059+ , label : Label msg
1060+ , spellcheck : Bool
1061+ }
1062+ -> Element msg
1063+multiline attrs multi =
1064+ textHelper
1065+ { type_ =
1066+ TextArea
1067+ , spellchecked = multi.spellcheck
1068+ , autofill = Nothing
1069+ }
1070+ attrs
1071+ { onChange = multi.onChange
1072+ , text = multi.text
1073+ , placeholder = multi.placeholder
1074+ , label = multi.label
1075+ }
1076+
1077+
1078+applyLabel : List (Attribute msg) -> Label msg -> Element msg -> Element msg
1079+applyLabel attrs label input =
1080+ case label of
1081+ Label position labelAttrs labelChild ->
1082+ let
1083+ labelElement =
1084+ Internal.element
1085+ Internal.asEl
1086+ Internal.div
1087+ labelAttrs
1088+ (Internal.Unkeyed [ labelChild ])
1089+ in
1090+ case position of
1091+ Internal.Above ->
1092+ Internal.element
1093+ Internal.asColumn
1094+ (Internal.NodeName "label")
1095+ attrs
1096+ (Internal.Unkeyed [ labelElement, input ])
1097+
1098+ Internal.Below ->
1099+ Internal.element
1100+ Internal.asColumn
1101+ (Internal.NodeName "label")
1102+ attrs
1103+ (Internal.Unkeyed [ input, labelElement ])
1104+
1105+ Internal.OnRight ->
1106+ Internal.element
1107+ Internal.asRow
1108+ (Internal.NodeName "label")
1109+ attrs
1110+ (Internal.Unkeyed [ input, labelElement ])
1111+
1112+ Internal.OnLeft ->
1113+ Internal.element
1114+ Internal.asRow
1115+ (Internal.NodeName "label")
1116+ attrs
1117+ (Internal.Unkeyed [ labelElement, input ])
1118+
1119+ Internal.InFront ->
1120+ Internal.element
1121+ Internal.asRow
1122+ (Internal.NodeName "label")
1123+ attrs
1124+ (Internal.Unkeyed [ labelElement, input ])
1125+
1126+ Internal.Behind ->
1127+ Internal.element
1128+ Internal.asRow
1129+ (Internal.NodeName "label")
1130+ attrs
1131+ (Internal.Unkeyed [ labelElement, input ])
1132+
1133+
1134+{-| Add choices to your radio and select menus.
1135+-}
1136+type Option value msg
1137+ = Option value (OptionState -> Element msg)
1138+
1139+
1140+{-| -}
1141+type OptionState
1142+ = Idle
1143+ | Focused
1144+ | Selected
1145+
1146+
1147+{-| -}
1148+option : value -> Element msg -> Option value msg
1149+option val txt =
1150+ Option val (defaultRadioOption txt)
1151+
1152+
1153+{-| -}
1154+optionWith : value -> (OptionState -> Element msg) -> Option value msg
1155+optionWith val view =
1156+ Option val view
1157+
1158+
1159+{-|
1160+
1161+ Input.radio
1162+ [ padding 10
1163+ , spacing 20
1164+ ]
1165+ { onChange = ChooseLunch
1166+ , selected = Just model.lunch
1167+ , label = Input.labelAbove (text "Lunch")
1168+ , options =
1169+ [ Input.styledChoice Burrito <|
1170+ \selected ->
1171+ Element.row
1172+ [ spacing 5 ]
1173+ [ el None [] <|
1174+ if selected then
1175+ text ":D"
1176+ else
1177+ text ":("
1178+ , text "burrito"
1179+ ]
1180+ , Input.option Taco (text "Taco!")
1181+ , Input.option Gyro (text "Gyro")
1182+ ]
1183+ }
1184+
1185+-}
1186+radio :
1187+ List (Attribute msg)
1188+ ->
1189+ { onChange : option -> msg
1190+ , options : List (Option option msg)
1191+ , selected : Maybe option
1192+ , label : Label msg
1193+ }
1194+ -> Element msg
1195+radio =
1196+ radioHelper Column
1197+
1198+
1199+{-| Same as radio, but displayed as a row
1200+-}
1201+radioRow :
1202+ List (Attribute msg)
1203+ ->
1204+ { onChange : option -> msg
1205+ , options : List (Option option msg)
1206+ , selected : Maybe option
1207+ , label : Label msg
1208+ }
1209+ -> Element msg
1210+radioRow =
1211+ radioHelper Row
1212+
1213+
1214+defaultRadioOption : Element msg -> OptionState -> Element msg
1215+defaultRadioOption optionLabel status =
1216+ Element.row
1217+ [ Element.spacing 10
1218+ , Element.alignLeft
1219+ , Element.width Element.shrink
1220+ ]
1221+ [ Element.el
1222+ [ Element.width (Element.px 14)
1223+ , Element.height (Element.px 14)
1224+ , Background.color white
1225+ , Border.rounded 7
1226+ , case status of
1227+ Selected ->
1228+ Internal.htmlClass "focusable"
1229+
1230+ _ ->
1231+ Internal.NoAttribute
1232+
1233+ -- , Border.shadow <|
1234+ -- -- case status of
1235+ -- -- Idle ->
1236+ -- -- { offset = ( 0, 0 )
1237+ -- -- , blur =
1238+ -- -- 1
1239+ -- -- , color = Color.rgb 235 235 235
1240+ -- -- }
1241+ -- -- Focused ->
1242+ -- -- { offset = ( 0, 0 )
1243+ -- -- , blur =
1244+ -- -- 0
1245+ -- -- , color = Color.rgba 235 235 235 0
1246+ -- -- }
1247+ -- -- Selected ->
1248+ -- { offset = ( 0, 0 )
1249+ -- , blur =
1250+ -- 1
1251+ -- , color = Color.rgba 235 235 235 0
1252+ -- }
1253+ , Border.width <|
1254+ case status of
1255+ Idle ->
1256+ 1
1257+
1258+ Focused ->
1259+ 1
1260+
1261+ Selected ->
1262+ 5
1263+ , Border.color <|
1264+ case status of
1265+ Idle ->
1266+ Element.rgb (208 / 255) (208 / 255) (208 / 255)
1267+
1268+ Focused ->
1269+ Element.rgb (208 / 255) (208 / 255) (208 / 255)
1270+
1271+ Selected ->
1272+ Element.rgb (59 / 255) (153 / 255) (252 / 255)
1273+ ]
1274+ Element.none
1275+ , Element.el [ Element.width Element.fill, Internal.htmlClass "unfocusable" ] optionLabel
1276+ ]
1277+
1278+
1279+radioHelper :
1280+ Orientation
1281+ -> List (Attribute msg)
1282+ ->
1283+ { onChange : option -> msg
1284+ , options : List (Option option msg)
1285+ , selected : Maybe option
1286+ , label : Label msg
1287+ }
1288+ -> Element msg
1289+radioHelper orientation attrs input =
1290+ let
1291+ renderOption (Option val view) =
1292+ let
1293+ status =
1294+ if Just val == input.selected then
1295+ Selected
1296+ else
1297+ Idle
1298+ in
1299+ Element.el
1300+ [ Element.pointer
1301+ , case orientation of
1302+ Row ->
1303+ Element.width Element.shrink
1304+
1305+ Column ->
1306+ Element.width Element.fill
1307+ , Events.onClick (input.onChange val)
1308+ , case status of
1309+ Selected ->
1310+ Internal.Attr <|
1311+ Html.Attributes.attribute "aria-checked"
1312+ "true"
1313+
1314+ _ ->
1315+ Internal.Attr <|
1316+ Html.Attributes.attribute "aria-checked"
1317+ "false"
1318+ , Internal.Attr <|
1319+ Html.Attributes.attribute "role" "radio"
1320+ ]
1321+ (view status)
1322+
1323+ optionArea =
1324+ case orientation of
1325+ Row ->
1326+ row attrs
1327+ (List.map renderOption input.options)
1328+
1329+ Column ->
1330+ column attrs
1331+ (List.map renderOption input.options)
1332+
1333+ prevNext =
1334+ case input.options of
1335+ [] ->
1336+ Nothing
1337+
1338+ (Option val _) :: _ ->
1339+ List.foldl track ( NotFound, val, val ) input.options
1340+ |> (\( found, b, a ) ->
1341+ case found of
1342+ NotFound ->
1343+ Just ( b, val )
1344+
1345+ BeforeFound ->
1346+ Just ( b, val )
1347+
1348+ _ ->
1349+ Just ( b, a )
1350+ )
1351+
1352+ track opt ( found, prev, nxt ) =
1353+ case opt of
1354+ Option val _ ->
1355+ case found of
1356+ NotFound ->
1357+ if Just val == input.selected then
1358+ ( BeforeFound, prev, nxt )
1359+ else
1360+ ( found, val, nxt )
1361+
1362+ BeforeFound ->
1363+ ( AfterFound, prev, val )
1364+
1365+ AfterFound ->
1366+ ( found, prev, nxt )
1367+
1368+ inputVisible =
1369+ List.isEmpty <|
1370+ Internal.get attrs <|
1371+ \attr ->
1372+ case attr of
1373+ Internal.StyleClass _ (Internal.Transparency _ _) ->
1374+ True
1375+
1376+ Internal.Class _ "hidden" ->
1377+ True
1378+
1379+ _ ->
1380+ False
1381+
1382+ labelVisible =
1383+ case input.label of
1384+ Label _ labelAttrs _ ->
1385+ List.isEmpty <|
1386+ Internal.get labelAttrs <|
1387+ \attr ->
1388+ case attr of
1389+ Internal.StyleClass _ (Internal.Transparency _ _) ->
1390+ True
1391+
1392+ Internal.Class _ "hidden" ->
1393+ True
1394+
1395+ _ ->
1396+ False
1397+
1398+ hideIfEverythingisInvisible =
1399+ if not labelVisible && not inputVisible then
1400+ let
1401+ pseudos =
1402+ List.filterMap
1403+ (\attr ->
1404+ case attr of
1405+ Internal.StyleClass _ style ->
1406+ case style of
1407+ Internal.PseudoSelector pseudo styles ->
1408+ let
1409+ transparent =
1410+ List.filter forTransparency styles
1411+
1412+ forTransparency psuedoStyle =
1413+ case psuedoStyle of
1414+ Internal.Transparency _ _ ->
1415+ True
1416+
1417+ _ ->
1418+ False
1419+
1420+ flag =
1421+ case pseudo of
1422+ Internal.Hover ->
1423+ Flag.hover
1424+
1425+ Internal.Focus ->
1426+ Flag.focus
1427+
1428+ Internal.Active ->
1429+ Flag.active
1430+ in
1431+ case transparent of
1432+ [] ->
1433+ Nothing
1434+
1435+ _ ->
1436+ Just <| Internal.StyleClass flag <| Internal.PseudoSelector pseudo transparent
1437+
1438+ _ ->
1439+ Nothing
1440+
1441+ _ ->
1442+ Nothing
1443+ )
1444+ attrs
1445+ in
1446+ Internal.StyleClass Flag.transparency (Internal.Transparency "transparent" 1.0) :: pseudos
1447+ else
1448+ []
1449+
1450+ events =
1451+ Internal.get
1452+ attrs
1453+ <|
1454+ \attr ->
1455+ case attr of
1456+ Internal.Width (Internal.Fill _) ->
1457+ True
1458+
1459+ Internal.Height (Internal.Fill _) ->
1460+ True
1461+
1462+ Internal.Attr _ ->
1463+ True
1464+
1465+ _ ->
1466+ False
1467+ in
1468+ applyLabel
1469+ (List.filterMap identity
1470+ [ Just Element.alignLeft
1471+ , Just (tabindex 0)
1472+ , Just (Internal.htmlClass "focus")
1473+ , Just Region.announce
1474+ , Just <|
1475+ Internal.Attr <|
1476+ Html.Attributes.attribute "role" "radiogroup"
1477+ , case prevNext of
1478+ Nothing ->
1479+ Nothing
1480+
1481+ Just ( prev, next ) ->
1482+ Just
1483+ (onKeyLookup <|
1484+ \code ->
1485+ if code == leftArrow then
1486+ Just (input.onChange prev)
1487+ else if code == upArrow then
1488+ Just (input.onChange prev)
1489+ else if code == rightArrow then
1490+ Just (input.onChange next)
1491+ else if code == downArrow then
1492+ Just (input.onChange next)
1493+ else if code == space then
1494+ case input.selected of
1495+ Nothing ->
1496+ Just (input.onChange prev)
1497+
1498+ _ ->
1499+ Nothing
1500+ else
1501+ Nothing
1502+ )
1503+ ]
1504+ ++ events
1505+ ++ hideIfEverythingisInvisible
1506+ )
1507+ input.label
1508+ optionArea
1509+
1510+
1511+type Found
1512+ = NotFound
1513+ | BeforeFound
1514+ | AfterFound
1515+
1516+
1517+type Orientation
1518+ = Row
1519+ | Column
1520+
1521+
1522+column : List (Attribute msg) -> List (Internal.Element msg) -> Internal.Element msg
1523+column attributes children =
1524+ Internal.element
1525+ Internal.asColumn
1526+ Internal.div
1527+ (Element.height Element.shrink
1528+ :: Element.width Element.fill
1529+ :: attributes
1530+ )
1531+ (Internal.Unkeyed children)
1532+
1533+
1534+row : List (Attribute msg) -> List (Internal.Element msg) -> Internal.Element msg
1535+row attributes children =
1536+ Internal.element
1537+ Internal.asRow
1538+ Internal.div
1539+ (Element.width Element.fill
1540+ :: attributes
1541+ )
1542+ (Internal.Unkeyed children)
1543+
1544+
1545+
1546+{- Event Handlers -}
1547+
1548+
1549+{-| -}
1550+onEnter : msg -> Attribute msg
1551+onEnter msg =
1552+ onKey enter msg
1553+
1554+
1555+{-| -}
1556+onSpace : msg -> Attribute msg
1557+onSpace msg =
1558+ onKey space msg
1559+
1560+
1561+{-| -}
1562+onUpArrow : msg -> Attribute msg
1563+onUpArrow msg =
1564+ onKey upArrow msg
1565+
1566+
1567+{-| -}
1568+onRightArrow : msg -> Attribute msg
1569+onRightArrow msg =
1570+ onKey rightArrow msg
1571+
1572+
1573+{-| -}
1574+onLeftArrow : msg -> Attribute msg
1575+onLeftArrow msg =
1576+ onKey leftArrow msg
1577+
1578+
1579+{-| -}
1580+onDownArrow : msg -> Attribute msg
1581+onDownArrow msg =
1582+ onKey downArrow msg
1583+
1584+
1585+enter : String
1586+enter =
1587+ "Enter"
1588+
1589+
1590+tab : String
1591+tab =
1592+ "Tab"
1593+
1594+
1595+delete : String
1596+delete =
1597+ "Delete"
1598+
1599+
1600+backspace : String
1601+backspace =
1602+ "Backspace"
1603+
1604+
1605+upArrow : String
1606+upArrow =
1607+ "ArrowUp"
1608+
1609+
1610+leftArrow : String
1611+leftArrow =
1612+ "ArrowLeft"
1613+
1614+
1615+rightArrow : String
1616+rightArrow =
1617+ "ArrowRight"
1618+
1619+
1620+downArrow : String
1621+downArrow =
1622+ "ArrowDown"
1623+
1624+
1625+space : String
1626+space =
1627+ " "
1628+
1629+
1630+{-| -}
1631+onKey : String -> msg -> Attribute msg
1632+onKey desiredCode msg =
1633+ let
1634+ decode code =
1635+ if code == desiredCode then
1636+ Json.succeed msg
1637+ else
1638+ Json.fail "Not the enter key"
1639+
1640+ isKey =
1641+ Json.field "key" Json.string
1642+ |> Json.andThen decode
1643+ in
1644+ Internal.Attr <|
1645+ Html.Events.preventDefaultOn "keyup"
1646+ (Json.map (\fired -> ( fired, True )) isKey)
1647+
1648+
1649+
1650+-- preventKeydown : String -> a -> Attribute a
1651+-- preventKeydown desiredCode msg =
1652+-- let
1653+-- decode code =
1654+-- if code == desiredCode then
1655+-- Json.succeed msg
1656+-- else
1657+-- Json.fail "Not the enter key"
1658+-- isKey =
1659+-- Json.field "key" Json.string
1660+-- |> Json.andThen decode
1661+-- in
1662+-- Events.onWithOptions "keydown"
1663+-- { stopPropagation = False
1664+-- , preventDefault = True
1665+-- }
1666+-- isKey
1667+
1668+
1669+{-| -}
1670+onKeyLookup : (String -> Maybe msg) -> Attribute msg
1671+onKeyLookup lookup =
1672+ let
1673+ decode code =
1674+ case lookup code of
1675+ Nothing ->
1676+ Json.fail "No key matched"
1677+
1678+ Just msg ->
1679+ Json.succeed msg
1680+
1681+ isKey =
1682+ Json.field "key" Json.string
1683+ |> Json.andThen decode
1684+ in
1685+ Internal.Attr <| Html.Events.on "keyup" isKey
1686+
1687+
1688+{-| -}
1689+onFocusOut : msg -> Attribute msg
1690+onFocusOut msg =
1691+ Internal.Attr <| Html.Events.on "focusout" (Json.succeed msg)
1692+
1693+
1694+{-| -}
1695+onFocusIn : msg -> Attribute msg
1696+onFocusIn msg =
1697+ Internal.Attr <| Html.Events.on "focusin" (Json.succeed msg)
1698+
1699+
1700+type_ : String -> Attribute msg
1701+type_ =
1702+ Internal.Attr << Html.Attributes.type_
1703+
1704+
1705+
1706+-- checked : Bool -> Attribute msg
1707+-- checked =
1708+-- Internal.Attr << Html.Attributes.checked
1709+
1710+
1711+selected : Bool -> Attribute msg
1712+selected =
1713+ Internal.Attr << Html.Attributes.selected
1714+
1715+
1716+name : String -> Attribute msg
1717+name =
1718+ Internal.Attr << Html.Attributes.name
1719+
1720+
1721+value : String -> Attribute msg
1722+value =
1723+ Internal.Attr << Html.Attributes.value
1724+
1725+
1726+tabindex : Int -> Attribute msg
1727+tabindex =
1728+ Internal.Attr << Html.Attributes.tabindex
1729+
1730+
1731+disabled : Bool -> Attribute msg
1732+disabled =
1733+ Internal.Attr << Html.Attributes.disabled
1734+
1735+
1736+spellcheck : Bool -> Attribute msg
1737+spellcheck =
1738+ Internal.Attr << Html.Attributes.spellcheck
1739+
1740+
1741+readonly : Bool -> Attribute msg
1742+readonly =
1743+ Internal.Attr << Html.Attributes.readonly
1744+
1745+
1746+autofill : String -> Attribute msg
1747+autofill =
1748+ Internal.Attr << Html.Attributes.attribute "autocomplete"
1749+
1750+
1751+{-| -}
1752+focusedOnLoad : Attribute msg
1753+focusedOnLoad =
1754+ Internal.Attr <| Html.Attributes.autofocus True
1755+
1756+
1757+
1758+{- Style Defaults -}
1759+
1760+
1761+defaultTextBoxStyle : List (Attribute msg)
1762+defaultTextBoxStyle =
1763+ [ defaultTextPadding
1764+ , Border.rounded 3
1765+ , Border.color darkGrey
1766+ , Background.color white
1767+ , Border.width 1
1768+ , Element.spacing 3
1769+ ]
1770+
1771+
1772+defaultTextPadding : Attribute msg
1773+defaultTextPadding =
1774+ Element.paddingXY 12 12
1775+
1776+
1777+{-| -}
1778+defaultCheckbox : Bool -> Element msg
1779+defaultCheckbox checked =
1780+ Element.el
1781+ [ Internal.htmlClass "focusable"
1782+ , Element.width (Element.px 14)
1783+ , Element.height (Element.px 14)
1784+ , Font.color white
1785+ , Element.centerY
1786+ , Font.size 9
1787+ , Font.center
1788+ , Border.rounded 3
1789+ , Border.color <|
1790+ if checked then
1791+ Element.rgb (59 / 255) (153 / 255) (252 / 255)
1792+ else
1793+ Element.rgb (211 / 255) (211 / 255) (211 / 255)
1794+ , Border.shadow <|
1795+ { offset = ( 0, 0 )
1796+ , blur = 1
1797+ , size = 1
1798+ , color =
1799+ if checked then
1800+ Element.rgba (238 / 255) (238 / 255) (238 / 255) 0
1801+ else
1802+ Element.rgb (238 / 255) (238 / 255) (238 / 255)
1803+ }
1804+ , Background.color <|
1805+ if checked then
1806+ Element.rgb (59 / 255) (153 / 255) (252 / 255)
1807+ else
1808+ white
1809+ , Border.width <|
1810+ if checked then
1811+ 0
1812+ else
1813+ 1
1814+ ]
1815+ (if checked then
1816+ Element.el
1817+ [ Border.color white
1818+ , Element.height (Element.px 6)
1819+ , Element.width (Element.px 9)
1820+ , Element.rotate (degrees -45)
1821+ , Element.centerX
1822+ , Element.centerY
1823+ , Element.moveUp 1
1824+ , Border.widthEach
1825+ { top = 0
1826+ , left = 2
1827+ , bottom = 2
1828+ , right = 0
1829+ }
1830+ ]
1831+ Element.none
1832+ else
1833+ Element.none
1834+ )
src/Element/Keyed.elmnew file+67
@@ -0,0 +1,67 @@
1+module Element.Keyed exposing (column, el, row)
2+
3+{-| Notes from the `Html.Keyed` on how keyed works:
4+
5+---
6+
7+A keyed node helps optimize cases where children are getting added, moved, removed, etc. Common examples include:
8+
9+ - The user can delete items from a list.
10+ - The user can create new items in a list.
11+ - You can sort a list based on name or date or whatever.
12+
13+When you use a keyed node, every child is paired with a string identifier. This makes it possible for the underlying diffing algorithm to reuse nodes more efficiently.
14+
15+This means if a key is changed between renders, then the diffing step will be skipped and the node will be forced to rerender.
16+
17+---
18+
19+@docs el, column, row
20+
21+-}
22+
23+import Element exposing (Attribute, Element, fill, height, width)
24+import Internal.Model as Internal
25+import Internal.Style exposing (classes)
26+
27+
28+{-| -}
29+el : List (Attribute msg) -> ( String, Element msg ) -> Element msg
30+el attrs child =
31+ Internal.element
32+ Internal.asEl
33+ Internal.div
34+ (width Element.shrink
35+ :: height Element.shrink
36+ :: attrs
37+ )
38+ (Internal.Keyed [ child ])
39+
40+
41+{-| -}
42+row : List (Attribute msg) -> List ( String, Element msg ) -> Element msg
43+row attrs children =
44+ Internal.element
45+ Internal.asRow
46+ Internal.div
47+ (Internal.htmlClass classes.contentLeft
48+ :: Internal.htmlClass classes.contentCenterY
49+ :: width fill
50+ :: attrs
51+ )
52+ (Internal.Keyed children)
53+
54+
55+{-| -}
56+column : List (Attribute msg) -> List ( String, Element msg ) -> Element msg
57+column attrs children =
58+ Internal.element
59+ Internal.asColumn
60+ Internal.div
61+ (Internal.htmlClass classes.contentTop
62+ :: Internal.htmlClass classes.contentLeft
63+ :: height fill
64+ :: width fill
65+ :: attrs
66+ )
67+ (Internal.Keyed children)
src/Element/Lazy.elmnew file+117
@@ -0,0 +1,117 @@
1+module Element.Lazy exposing (lazy, lazy2, lazy3, lazy4, lazy5)
2+
3+{-| Same as `Html.lazy`. In case you're unfamiliar, here's a note from the `Html` library!
4+
5+---
6+
7+Since all Elm functions are pure we have a guarantee that the same input
8+will always result in the same output. This module gives us tools to be lazy
9+about building `Html` that utilize this fact.
10+
11+Rather than immediately applying functions to their arguments, the `lazy`
12+functions just bundle the function and arguments up for later. When diffing
13+the old and new virtual DOM, it checks to see if all the arguments are equal
14+by reference. If so, it skips calling the function!
15+
16+This is a really cheap test and often makes things a lot faster, but definitely
17+benchmark to be sure!
18+
19+---
20+
21+@docs lazy, lazy2, lazy3, lazy4, lazy5
22+
23+-}
24+
25+import Internal.Model exposing (..)
26+import VirtualDom
27+
28+
29+{-| -}
30+lazy : (a -> Element msg) -> a -> Element msg
31+lazy fn a =
32+ Unstyled <| VirtualDom.lazy3 apply1 fn a
33+
34+
35+{-| -}
36+lazy2 : (a -> b -> Element msg) -> a -> b -> Element msg
37+lazy2 fn a b =
38+ Unstyled <| VirtualDom.lazy4 apply2 fn a b
39+
40+
41+{-| -}
42+lazy3 : (a -> b -> c -> Element msg) -> a -> b -> c -> Element msg
43+lazy3 fn a b c =
44+ Unstyled <| VirtualDom.lazy5 apply3 fn a b c
45+
46+
47+{-| -}
48+lazy4 : (a -> b -> c -> d -> Element msg) -> a -> b -> c -> d -> Element msg
49+lazy4 fn a b c d =
50+ Unstyled <| VirtualDom.lazy6 apply4 fn a b c d
51+
52+
53+{-| -}
54+lazy5 : (a -> b -> c -> d -> e -> Element msg) -> a -> b -> c -> d -> e -> Element msg
55+lazy5 fn a b c d e =
56+ Unstyled <| VirtualDom.lazy7 apply5 fn a b c d e
57+
58+
59+apply1 fn a =
60+ embed (fn a)
61+
62+
63+apply2 fn a b =
64+ embed (fn a b)
65+
66+
67+apply3 fn a b c =
68+ embed (fn a b c)
69+
70+
71+apply4 fn a b c d =
72+ embed (fn a b c d)
73+
74+
75+apply5 fn a b c d e =
76+ embed (fn a b c d e)
77+
78+
79+{-| -}
80+embed : Element msg -> LayoutContext -> VirtualDom.Node msg
81+embed x =
82+ case x of
83+ Unstyled html ->
84+ html
85+
86+ Styled styled ->
87+ styled.html
88+ (Internal.Model.OnlyDynamic
89+ { hover = AllowHover
90+ , focus =
91+ { borderColor = Nothing
92+ , shadow = Nothing
93+ , backgroundColor = Nothing
94+ }
95+ , mode = Layout
96+ }
97+ styled.styles
98+ )
99+
100+ -- -- (Just
101+ -- -- (toStyleSheetString
102+ -- { hover = AllowHover
103+ -- , focus =
104+ -- { borderColor = Nothing
105+ -- , shadow = Nothing
106+ -- , backgroundColor = Nothing
107+ -- }
108+ -- , mode = Layout
109+ -- }
110+ -- -- styled.styles
111+ -- -- )
112+ -- -- )
113+ Text text ->
114+ always (VirtualDom.text text)
115+
116+ Empty ->
117+ always (VirtualDom.text "")
src/Element/Region.elmnew file+111
@@ -0,0 +1,111 @@
1+module Element.Region
2+ exposing
3+ ( announce
4+ , announceUrgently
5+ , aside
6+ , description
7+ , footer
8+ , heading
9+ , mainContent
10+ , navigation
11+ )
12+
13+{-| This module is meant to make accessibility easy! They're sign posts that accessibility software like screen readers can use to navigate your app.
14+
15+All you have to do is add them to elements in your app where you see fit.
16+
17+Here's an example of annotating your navigation region:
18+
19+ import Element.Region as Region
20+
21+ myNavigation =
22+ Element.row [ Region.navigation ]
23+ [-- ..your navigation links
24+ ]
25+
26+@docs mainContent, navigation, heading, aside, footer
27+
28+@docs description
29+
30+@docs announce, announceUrgently
31+
32+-}
33+
34+import Element exposing (Attribute)
35+import Internal.Model as Internal exposing (Description(..))
36+
37+
38+{-| -}
39+mainContent : Attribute msg
40+mainContent =
41+ Internal.Describe Main
42+
43+
44+{-| -}
45+aside : Attribute msg
46+aside =
47+ Internal.Describe Complementary
48+
49+
50+{-| -}
51+navigation : Attribute msg
52+navigation =
53+ Internal.Describe Navigation
54+
55+
56+
57+-- form : Attribute msg
58+-- form =
59+-- Internal.Describe Form
60+-- search : Attribute msg
61+-- search =
62+-- Internal.Describe Search
63+
64+
65+{-| -}
66+footer : Attribute msg
67+footer =
68+ Internal.Describe ContentInfo
69+
70+
71+{-| This will mark an element as `h1`, `h2`, etc where possible.
72+
73+Though it's also smart enough to not conflict with existing nodes.
74+
75+So, this code
76+
77+ link [ Region.heading 1 ]
78+ { url = "http://fruits.com"
79+ , label = text "Best site ever"
80+ }
81+
82+will generate
83+
84+ <a href="http://fruits.com">
85+ <h1>Best site ever</h1>
86+ </a>
87+
88+-}
89+heading : Int -> Attribute msg
90+heading =
91+ Internal.Describe << Heading
92+
93+
94+{-| Screen readers will announce changes to this element and potentially interrupt any other announcement.
95+-}
96+announceUrgently : Attribute msg
97+announceUrgently =
98+ Internal.Describe LiveAssertive
99+
100+
101+{-| Screen readers will announce when changes to this element are made.
102+-}
103+announce : Attribute msg
104+announce =
105+ Internal.Describe LivePolite
106+
107+
108+{-| -}
109+description : String -> Attribute msg
110+description =
111+ Internal.Describe << Internal.Label
src/Internal/Flag.elmnew file+260
@@ -0,0 +1,260 @@
1+module Internal.Flag exposing (..)
2+
3+{-| -}
4+
5+import Bitwise
6+
7+
8+type Field
9+ = Field Int Int
10+
11+
12+type Flag
13+ = Flag Int
14+ | Second Int
15+
16+
17+none : Field
18+none =
19+ Field 0 0
20+
21+
22+value myFlag =
23+ case myFlag of
24+ Flag first ->
25+ round (logBase 2 (toFloat first))
26+
27+ Second second ->
28+ round (logBase 2 (toFloat second)) + 32
29+
30+
31+{-| If the query is in the truth, return True
32+-}
33+present : Flag -> Field -> Bool
34+present myFlag (Field fieldOne fieldTwo) =
35+ case myFlag of
36+ Flag first ->
37+ Bitwise.and first fieldOne == first
38+
39+ Second second ->
40+ Bitwise.and second fieldTwo == second
41+
42+
43+{-| Add a flag to a field.
44+-}
45+add : Flag -> Field -> Field
46+add myFlag (Field one two) =
47+ case myFlag of
48+ Flag first ->
49+ Field (Bitwise.or first one) two
50+
51+ Second second ->
52+ Field one (Bitwise.or second two)
53+
54+
55+{-| Generally you want to use `add`, which keeps a distinction between Fields and Flags.
56+
57+Merging will combine two fields
58+
59+-}
60+merge : Field -> Field -> Field
61+merge (Field one two) (Field three four) =
62+ Field (Bitwise.or one three) (Bitwise.or two four)
63+
64+
65+flag : Int -> Flag
66+flag i =
67+ if i > 31 then
68+ Second
69+ (Bitwise.shiftLeftBy (i - 32) 1)
70+ else
71+ Flag
72+ (Bitwise.shiftLeftBy i 1)
73+
74+
75+
76+{- Used for Style invalidation -}
77+
78+
79+transparency =
80+ flag 0
81+
82+
83+padding =
84+ flag 2
85+
86+
87+spacing =
88+ flag 3
89+
90+
91+fontSize =
92+ flag 4
93+
94+
95+fontFamily =
96+ flag 5
97+
98+
99+width =
100+ flag 6
101+
102+
103+height =
104+ flag 7
105+
106+
107+bgColor =
108+ flag 8
109+
110+
111+bgImage =
112+ flag 9
113+
114+
115+bgGradient =
116+ flag 10
117+
118+
119+borderStyle =
120+ flag 11
121+
122+
123+fontAlignment =
124+ flag 12
125+
126+
127+fontWeight =
128+ flag 13
129+
130+
131+fontColor =
132+ flag 14
133+
134+
135+wordSpacing =
136+ flag 15
137+
138+
139+letterSpacing =
140+ flag 16
141+
142+
143+borderRound =
144+ flag 17
145+
146+
147+txtShadows =
148+ flag 18
149+
150+
151+shadows =
152+ flag 19
153+
154+
155+overflow =
156+ flag 20
157+
158+
159+cursor =
160+ flag 21
161+
162+
163+scale =
164+ flag 23
165+
166+
167+rotate =
168+ flag 24
169+
170+
171+moveX =
172+ flag 25
173+
174+
175+moveY =
176+ flag 26
177+
178+
179+borderWidth =
180+ flag 27
181+
182+
183+borderColor =
184+ flag 28
185+
186+
187+yAlign =
188+ flag 29
189+
190+
191+xAlign =
192+ flag 30
193+
194+
195+focus =
196+ flag 31
197+
198+
199+active =
200+ flag 32
201+
202+
203+hover =
204+ flag 33
205+
206+
207+gridTemplate =
208+ flag 34
209+
210+
211+gridPosition =
212+ flag 35
213+
214+
215+
216+{- Notes -}
217+
218+
219+heightContent =
220+ flag 36
221+
222+
223+heightFill =
224+ flag 37
225+
226+
227+widthContent =
228+ flag 38
229+
230+
231+widthFill =
232+ flag 39
233+
234+
235+alignRight =
236+ flag 40
237+
238+
239+alignBottom =
240+ flag 41
241+
242+
243+centerX =
244+ flag 42
245+
246+
247+centerY =
248+ flag 43
249+
250+
251+widthBetween =
252+ flag 44
253+
254+
255+heightBetween =
256+ flag 45
257+
258+
259+behind =
260+ flag 46
src/Internal/Grid.elmnew file+264
@@ -0,0 +1,264 @@
1+module Internal.Grid exposing (..)
2+
3+{-| Relative positioning within a grid.
4+
5+A relatively positioned grid, means a 3x3 grid with the primary element in the center.
6+
7+-}
8+
9+import Element
10+import Internal.Flag as Flag
11+import Internal.Model as Internal
12+
13+
14+type RelativePosition
15+ = OnRight
16+ | OnLeft
17+ | Above
18+ | Below
19+ | InFront
20+
21+
22+type Layout
23+ = GridElement
24+ | Row
25+ | Column
26+
27+
28+type alias Around alignment msg =
29+ { right : Maybe (PositionedElement alignment msg)
30+ , left : Maybe (PositionedElement alignment msg)
31+ , primary : ( Maybe String, List (Internal.Attribute alignment msg), List (Internal.Element msg) )
32+
33+ -- , primaryWidth : Internal.Length
34+ , defaultWidth : Internal.Length
35+ , below : Maybe (PositionedElement alignment msg)
36+ , above : Maybe (PositionedElement alignment msg)
37+ , inFront : Maybe (PositionedElement alignment msg)
38+ }
39+
40+
41+type alias PositionedElement alignment msg =
42+ { layout : Layout
43+ , child : List (Internal.Element msg)
44+ , attrs : List (Internal.Attribute alignment msg)
45+ , width : Int
46+ , height : Int
47+ }
48+
49+
50+relative : Maybe String -> List (Internal.Attribute alignment msg) -> Around alignment msg -> Internal.Element msg
51+relative node attributes around =
52+ let
53+ ( sX, sY ) =
54+ Internal.getSpacing attributes ( 7, 7 )
55+
56+ make positioned =
57+ Internal.element Internal.noStyleSheet
58+ Internal.asEl
59+ Nothing
60+ positioned.attrs
61+ (Internal.Unkeyed positioned.child)
62+
63+ ( template, children ) =
64+ createGrid ( sX, sY ) around
65+ in
66+ Internal.element Internal.noStyleSheet
67+ Internal.asGrid
68+ node
69+ (template ++ attributes)
70+ (Internal.Unkeyed
71+ children
72+ )
73+
74+
75+createGrid : ( Int, Int ) -> Around alignment msg -> ( List (Internal.Attribute alignment msg1), List (Element.Element msg) )
76+createGrid ( spacingX, spacingY ) nearby =
77+ let
78+ rowCount =
79+ List.sum
80+ [ 1
81+ , if Nothing == nearby.above then
82+ 0
83+ else
84+ 1
85+ , if Nothing == nearby.below then
86+ 0
87+ else
88+ 1
89+ ]
90+
91+ colCount =
92+ List.sum
93+ [ 1
94+ , if Nothing == nearby.left then
95+ 0
96+ else
97+ 1
98+ , if Nothing == nearby.right then
99+ 0
100+ else
101+ 1
102+ ]
103+
104+ rows =
105+ if nearby.above == Nothing then
106+ { above = 0
107+ , primary = 1
108+ , below = 2
109+ }
110+ else
111+ { above = 1
112+ , primary = 2
113+ , below = 3
114+ }
115+
116+ columns =
117+ if Nothing == nearby.left then
118+ { left = 0
119+ , primary = 1
120+ , right = 2
121+ }
122+ else
123+ { left = 1
124+ , primary = 2
125+ , right = 3
126+ }
127+
128+ rowCoord pos =
129+ case pos of
130+ Above ->
131+ rows.above
132+
133+ Below ->
134+ rows.below
135+
136+ OnRight ->
137+ rows.primary
138+
139+ OnLeft ->
140+ rows.primary
141+
142+ InFront ->
143+ rows.primary
144+
145+ colCoord pos =
146+ case pos of
147+ Above ->
148+ columns.primary
149+
150+ Below ->
151+ columns.primary
152+
153+ OnRight ->
154+ columns.right
155+
156+ OnLeft ->
157+ columns.left
158+
159+ InFront ->
160+ columns.primary
161+
162+ place pos el =
163+ build (rowCoord pos) (colCoord pos) spacingX spacingY el
164+ in
165+ ( [ Internal.StyleClass Flag.gridTemplate
166+ (Internal.GridTemplateStyle
167+ { spacing = ( Internal.Px spacingX, Internal.Px spacingY )
168+ , columns =
169+ List.filterMap identity
170+ [ nearby.left
171+ |> Maybe.map (\el -> Maybe.withDefault nearby.defaultWidth (getWidth el.attrs))
172+ , nearby.primary
173+ |> (\( node, attrs, el ) -> getWidth attrs)
174+ |> Maybe.withDefault nearby.defaultWidth
175+ |> Just
176+ , nearby.right
177+ |> Maybe.map (\el -> Maybe.withDefault nearby.defaultWidth (getWidth el.attrs))
178+ ]
179+ , rows = List.map (always Internal.Content) (List.range 1 rowCount)
180+ }
181+ )
182+ ]
183+ , List.filterMap identity
184+ [ Just <|
185+ case nearby.primary of
186+ ( primaryNode, primaryAttrs, primaryChildren ) ->
187+ Internal.element Internal.noStyleSheet
188+ Internal.asEl
189+ primaryNode
190+ (Internal.StyleClass Flag.gridPosition
191+ (Internal.GridPosition
192+ { row = rows.primary
193+ , col = columns.primary
194+ , width = 1
195+ , height = 1
196+ }
197+ )
198+ :: primaryAttrs
199+ )
200+ (Internal.Unkeyed primaryChildren)
201+ , Maybe.map (place OnLeft) nearby.left
202+ , Maybe.map (place OnRight) nearby.right
203+ , Maybe.map (place Above) nearby.above
204+ , Maybe.map (place Below) nearby.below
205+ , Maybe.map (place InFront) nearby.inFront
206+ ]
207+ )
208+
209+
210+build : Int -> Int -> Int -> Int -> { a | attrs : List (Internal.Attribute alignment msg), height : Int, layout : Layout, width : Int, child : List (Internal.Element msg) } -> Internal.Element msg
211+build rowCoord colCoord spacingX spacingY positioned =
212+ let
213+ attributes =
214+ Internal.StyleClass Flag.gridPosition
215+ (Internal.GridPosition
216+ { row = rowCoord
217+ , col = colCoord
218+ , width = positioned.width
219+ , height = positioned.height
220+ }
221+ )
222+ :: Internal.StyleClass Flag.spacing (Internal.SpacingStyle spacingX spacingY)
223+ :: positioned.attrs
224+ in
225+ case positioned.layout of
226+ GridElement ->
227+ Internal.element Internal.noStyleSheet
228+ Internal.asEl
229+ Nothing
230+ attributes
231+ (Internal.Unkeyed <| positioned.child)
232+
233+ Row ->
234+ Internal.element Internal.noStyleSheet
235+ Internal.asRow
236+ Nothing
237+ attributes
238+ (Internal.Unkeyed positioned.child)
239+
240+ Column ->
241+ Internal.element Internal.noStyleSheet
242+ Internal.asColumn
243+ Nothing
244+ attributes
245+ (Internal.Unkeyed positioned.child)
246+
247+
248+getWidth : List (Internal.Attribute align msg) -> Maybe Internal.Length
249+getWidth attrs =
250+ let
251+ widthPlease attr found =
252+ case found of
253+ Just x ->
254+ Just x
255+
256+ Nothing ->
257+ case attr of
258+ Internal.Width w ->
259+ Just w
260+
261+ _ ->
262+ Nothing
263+ in
264+ List.foldr widthPlease Nothing attrs
src/Internal/Model.elmnew file+2847
@@ -0,0 +1,2847 @@
1+module Internal.Model exposing (..)
2+
3+{-| -}
4+
5+import Html
6+import Html.Attributes
7+import Html.Keyed
8+import Internal.Flag as Flag exposing (Flag)
9+import Internal.Style exposing (classes, dot)
10+import Json.Encode as Json
11+import Set exposing (Set)
12+import VirtualDom
13+
14+
15+type Element msg
16+ = Unstyled (LayoutContext -> VirtualDom.Node msg)
17+ | Styled
18+ { styles : List Style
19+ , html : EmbedStyle -> LayoutContext -> VirtualDom.Node msg
20+ }
21+ | Text String
22+ | Empty
23+
24+
25+type EmbedStyle
26+ = NoStyleSheet
27+ | StaticRootAndDynamic OptionRecord (List Style)
28+ | OnlyDynamic OptionRecord (List Style)
29+
30+
31+noStyleSheet : EmbedStyle
32+noStyleSheet =
33+ NoStyleSheet
34+
35+
36+type LayoutContext
37+ = AsRow
38+ | AsColumn
39+ | AsEl
40+ | AsGrid
41+ | AsParagraph
42+ | AsTextColumn
43+
44+
45+type Aligned
46+ = Unaligned
47+ | Aligned (Maybe HAlign) (Maybe VAlign)
48+
49+
50+type HAlign
51+ = Left
52+ | CenterX
53+ | Right
54+
55+
56+type VAlign
57+ = Top
58+ | CenterY
59+ | Bottom
60+
61+
62+type Style
63+ = Style String (List Property)
64+ -- class prop val
65+ | FontFamily String (List Font)
66+ | FontSize Int
67+ -- classname, prop, value
68+ | Single String String String
69+ | Colored String String Color
70+ | SpacingStyle String Int Int
71+ | PaddingStyle String Int Int Int Int
72+ | GridTemplateStyle
73+ { spacing : ( Length, Length )
74+ , columns : List Length
75+ , rows : List Length
76+ }
77+ | GridPosition
78+ { row : Int
79+ , col : Int
80+ , width : Int
81+ , height : Int
82+ }
83+ | Transform Transformation
84+ | PseudoSelector PseudoClass (List Style)
85+ | Transparency String Float
86+ | Shadows String String
87+
88+
89+type Transformation
90+ = Untransformed
91+ | Moved XYZ
92+ -- translate, scale, rotate
93+ | FullTransform XYZ XYZ XYZ Angle
94+
95+
96+type PseudoClass
97+ = Focus
98+ | Hover
99+ | Active
100+
101+
102+type Font
103+ = Serif
104+ | SansSerif
105+ | Monospace
106+ | Typeface String
107+ | ImportFont String String
108+
109+
110+type Property
111+ = Property String String
112+
113+
114+type alias XYZ =
115+ ( Float, Float, Float )
116+
117+
118+type alias Angle =
119+ Float
120+
121+
122+type Attribute aligned msg
123+ = NoAttribute
124+ | Attr (VirtualDom.Attribute msg)
125+ | Describe Description
126+ -- invalidation key and literal class
127+ | Class Flag String
128+ -- invalidation key "border-color" as opposed to "border-color-10-10-10" that will be the key for the class
129+ | StyleClass Flag Style
130+ | AlignY VAlign
131+ | AlignX HAlign
132+ | Width Length
133+ | Height Length
134+ | Nearby Location (Element msg)
135+ | TransformComponent Flag TransformComponent
136+
137+
138+type TransformComponent
139+ = MoveX Float
140+ | MoveY Float
141+ | MoveZ Float
142+ | MoveXYZ XYZ
143+ | Rotate XYZ Float
144+ | Scale XYZ
145+
146+
147+type Description
148+ = Main
149+ | Navigation
150+ -- | Search
151+ | ContentInfo
152+ | Complementary
153+ | Heading Int
154+ | Label String
155+ | LivePolite
156+ | LiveAssertive
157+ | Button
158+ | Paragraph
159+
160+
161+type Length
162+ = Px Int
163+ | Content
164+ | Fill Int
165+ | Min Int Length
166+ | Max Int Length
167+
168+
169+type Axis
170+ = XAxis
171+ | YAxis
172+ | AllAxis
173+
174+
175+type Location
176+ = Above
177+ | Below
178+ | OnRight
179+ | OnLeft
180+ | InFront
181+ | Behind
182+
183+
184+{-| -}
185+type Color
186+ = Rgba Float Float Float Float
187+
188+
189+type NodeName
190+ = Generic
191+ | NodeName String
192+ | Embedded String String
193+
194+
195+div =
196+ Generic
197+
198+
199+type alias Gathered msg =
200+ { node : NodeName
201+ , attributes : List (VirtualDom.Attribute msg)
202+ , styles : List Style
203+ , children : List (VirtualDom.Node msg)
204+ , has : Flag.Field
205+ }
206+
207+
208+htmlClass : String -> Attribute aligned msg
209+htmlClass cls =
210+ Attr <| Html.Attributes.class cls
211+
212+
213+{-| -}
214+unstyled : VirtualDom.Node msg -> Element msg
215+unstyled =
216+ Unstyled << always
217+
218+
219+finalizeNode has node attributes children embedMode parentContext =
220+ let
221+ createNode nodeName attrs =
222+ case children of
223+ Keyed keyed ->
224+ VirtualDom.keyedNode nodeName
225+ attrs
226+ (case embedMode of
227+ NoStyleSheet ->
228+ keyed
229+
230+ OnlyDynamic opts styles ->
231+ embedKeyed False opts styles keyed
232+
233+ StaticRootAndDynamic opts styles ->
234+ embedKeyed True opts styles keyed
235+ )
236+
237+ Unkeyed unkeyed ->
238+ (case nodeName of
239+ "div" ->
240+ Html.div
241+
242+ "p" ->
243+ Html.p
244+
245+ _ ->
246+ VirtualDom.node nodeName
247+ )
248+ attrs
249+ (case embedMode of
250+ NoStyleSheet ->
251+ unkeyed
252+
253+ OnlyDynamic opts styles ->
254+ embedWith False opts styles unkeyed
255+
256+ StaticRootAndDynamic opts styles ->
257+ embedWith True opts styles unkeyed
258+ )
259+
260+ html =
261+ case node of
262+ Generic ->
263+ createNode "div" attributes
264+
265+ NodeName nodeName ->
266+ createNode nodeName attributes
267+
268+ Embedded nodeName internal ->
269+ VirtualDom.node nodeName
270+ attributes
271+ [ createNode internal [ Html.Attributes.class (classes.any ++ " " ++ classes.single) ]
272+ ]
273+ in
274+ case parentContext of
275+ AsRow ->
276+ if Flag.present Flag.widthFill has && not (Flag.present Flag.widthBetween has) then
277+ html
278+ else if Flag.present Flag.alignRight has then
279+ Html.u
280+ [ Html.Attributes.class
281+ (String.join " "
282+ [ classes.any
283+ , classes.single
284+ , classes.container
285+ , classes.contentCenterY
286+ , classes.alignContainerRight
287+ ]
288+ )
289+ ]
290+ [ html ]
291+ else if Flag.present Flag.centerX has then
292+ Html.s
293+ [ Html.Attributes.class
294+ (String.join " "
295+ [ classes.any
296+ , classes.single
297+ , classes.container
298+ , classes.contentCenterY
299+ , classes.alignContainerCenterX
300+ ]
301+ )
302+ ]
303+ [ html ]
304+ else
305+ html
306+
307+ AsColumn ->
308+ if Flag.present Flag.heightFill has && not (Flag.present Flag.heightBetween has) then
309+ html
310+ else if Flag.present Flag.centerY has then
311+ Html.s
312+ [ Html.Attributes.class
313+ (String.join " "
314+ [ classes.any
315+ , classes.single
316+ , classes.container
317+ , classes.alignContainerCenterY
318+ ]
319+ )
320+ ]
321+ [ html ]
322+ else if Flag.present Flag.alignBottom has then
323+ Html.u
324+ [ Html.Attributes.class
325+ (String.join " "
326+ [ classes.any
327+ , classes.single
328+ , classes.container
329+ , classes.alignContainerBottom
330+ ]
331+ )
332+ ]
333+ [ html ]
334+ else
335+ html
336+
337+ _ ->
338+ html
339+
340+
341+embedWith static opts styles children =
342+ if static then
343+ staticRoot
344+ :: (styles
345+ |> List.foldl reduceStyles ( Set.empty, [ renderFocusStyle opts.focus ] )
346+ |> Tuple.second
347+ -- |> reduceStylesRecursive Set.empty [ ]) --renderFocusStyle opts.focus ]
348+ -- |> sortedReduce
349+ |> toStyleSheet opts
350+ )
351+ :: children
352+ else
353+ (styles
354+ |> List.foldl reduceStyles ( Set.empty, [ renderFocusStyle opts.focus ] )
355+ |> Tuple.second
356+ -- |> reduceStylesRecursive Set.empty [ ]) --renderFocusStyle opts.focus ]
357+ -- |> sortedReduce
358+ |> toStyleSheet opts
359+ )
360+ :: children
361+
362+
363+embedKeyed static opts styles children =
364+ if static then
365+ ( "static-stylesheet", staticRoot )
366+ :: ( "dynamic-stylesheet"
367+ , styles
368+ |> List.foldl reduceStyles ( Set.empty, [ renderFocusStyle opts.focus ] )
369+ |> Tuple.second
370+ -- |> reduceStylesRecursive Set.empty [ ]) --renderFocusStyle opts.focus ]
371+ -- |> sortedReduce
372+ |> toStyleSheet opts
373+ )
374+ :: children
375+ else
376+ ( "dynamic-stylesheet"
377+ , styles
378+ |> List.foldl reduceStyles ( Set.empty, [ renderFocusStyle opts.focus ] )
379+ |> Tuple.second
380+ -- |> reduceStylesRecursive Set.empty [ ]) --renderFocusStyle opts.focus ]
381+ -- |> sortedReduce
382+ |> toStyleSheet opts
383+ )
384+ :: children
385+
386+
387+reduceStylesRecursive : Set String -> List Style -> List Style -> List Style
388+reduceStylesRecursive cache found styles =
389+ case styles of
390+ [] ->
391+ found
392+
393+ head :: remaining ->
394+ let
395+ styleName =
396+ getStyleName head
397+ in
398+ if Set.member styleName cache then
399+ reduceStylesRecursive cache found remaining
400+ else
401+ reduceStylesRecursive (Set.insert styleName cache) (head :: found) remaining
402+
403+
404+reduceStyles : Style -> ( Set String, List Style ) -> ( Set String, List Style )
405+reduceStyles style (( cache, existing ) as nevermind) =
406+ let
407+ styleName =
408+ getStyleName style
409+ in
410+ if Set.member styleName cache then
411+ nevermind
412+ else
413+ ( Set.insert styleName cache
414+ , style :: existing
415+ )
416+
417+
418+sortedReduce styles =
419+ styles
420+ -- |> List.map (\x -> ( getStyleName x, x ))
421+ |> List.sortBy getStyleName
422+ |> reduceRecursiveCalcName []
423+
424+
425+reduceRecursiveCalcName : List Style -> List Style -> List Style
426+reduceRecursiveCalcName found styles =
427+ case styles of
428+ [] ->
429+ found
430+
431+ headOfList :: [] ->
432+ headOfList :: found
433+
434+ headOfList :: other :: remaining ->
435+ if headOfList /= other then
436+ reduceRecursiveCalcName (headOfList :: found) (other :: remaining)
437+ else
438+ reduceRecursiveCalcName found (other :: remaining)
439+
440+
441+reduceRecursive : List Style -> List ( String, Style ) -> List Style
442+reduceRecursive found styles =
443+ case styles of
444+ [] ->
445+ found
446+
447+ ( _, headOfList ) :: [] ->
448+ headOfList :: found
449+
450+ ( headOfListName, headOfList ) :: ( otherName, other ) :: remaining ->
451+ if headOfListName /= otherName then
452+ reduceRecursive (headOfList :: found) (( otherName, other ) :: remaining)
453+ else
454+ reduceRecursive found (( otherName, other ) :: remaining)
455+
456+
457+addNodeName : String -> NodeName -> NodeName
458+addNodeName newNode old =
459+ case old of
460+ Generic ->
461+ NodeName newNode
462+
463+ NodeName name ->
464+ Embedded name newNode
465+
466+ Embedded x y ->
467+ Embedded x y
468+
469+
470+alignXName : HAlign -> String
471+alignXName align =
472+ case align of
473+ Left ->
474+ classes.alignedHorizontally ++ " " ++ classes.alignLeft
475+
476+ Right ->
477+ classes.alignedHorizontally ++ " " ++ classes.alignRight
478+
479+ CenterX ->
480+ classes.alignedHorizontally ++ " " ++ classes.alignCenterX
481+
482+
483+alignYName : VAlign -> String
484+alignYName align =
485+ case align of
486+ Top ->
487+ classes.alignedVertically ++ " " ++ classes.alignTop
488+
489+ Bottom ->
490+ classes.alignedVertically ++ " " ++ classes.alignBottom
491+
492+ CenterY ->
493+ classes.alignedVertically ++ " " ++ classes.alignCenterY
494+
495+
496+transformClass transform =
497+ case transform of
498+ Untransformed ->
499+ Nothing
500+
501+ Moved ( x, y, z ) ->
502+ Just <|
503+ "mv-"
504+ ++ floatClass x
505+ ++ "-"
506+ ++ floatClass y
507+ ++ "-"
508+ ++ floatClass z
509+
510+ FullTransform ( tx, ty, tz ) ( sx, sy, sz ) ( ox, oy, oz ) angle ->
511+ Just <|
512+ "tfrm-"
513+ ++ floatClass tx
514+ ++ "-"
515+ ++ floatClass ty
516+ ++ "-"
517+ ++ floatClass tz
518+ ++ "-"
519+ ++ floatClass sx
520+ ++ "-"
521+ ++ floatClass sy
522+ ++ "-"
523+ ++ floatClass sz
524+ ++ "-"
525+ ++ floatClass ox
526+ ++ "-"
527+ ++ floatClass oy
528+ ++ "-"
529+ ++ floatClass oz
530+ ++ "-"
531+ ++ floatClass angle
532+
533+
534+transformValue transform =
535+ case transform of
536+ Untransformed ->
537+ Nothing
538+
539+ Moved ( x, y, z ) ->
540+ Just <|
541+ "translate3d("
542+ ++ String.fromFloat x
543+ ++ "px, "
544+ ++ String.fromFloat y
545+ ++ "px, "
546+ ++ String.fromFloat z
547+ ++ "px)"
548+
549+ FullTransform ( tx, ty, tz ) ( sx, sy, sz ) ( ox, oy, oz ) angle ->
550+ let
551+ translate =
552+ "translate3d("
553+ ++ String.fromFloat tx
554+ ++ "px, "
555+ ++ String.fromFloat ty
556+ ++ "px, "
557+ ++ String.fromFloat tz
558+ ++ "px)"
559+
560+ scale =
561+ "scale3d("
562+ ++ String.fromFloat sx
563+ ++ ", "
564+ ++ String.fromFloat sy
565+ ++ ", "
566+ ++ String.fromFloat sz
567+ ++ ")"
568+
569+ rotate =
570+ "rotate3d("
571+ ++ String.fromFloat ox
572+ ++ ", "
573+ ++ String.fromFloat oy
574+ ++ ", "
575+ ++ String.fromFloat oz
576+ ++ ", "
577+ ++ String.fromFloat angle
578+ ++ "rad)"
579+ in
580+ Just <| translate ++ " " ++ scale ++ " " ++ rotate
581+
582+
583+composeTransformation transform component =
584+ case transform of
585+ Untransformed ->
586+ case component of
587+ MoveX x ->
588+ Moved ( x, 0, 0 )
589+
590+ MoveY y ->
591+ Moved ( 0, y, 0 )
592+
593+ MoveZ z ->
594+ Moved ( 0, 0, z )
595+
596+ MoveXYZ xyz ->
597+ Moved xyz
598+
599+ Rotate xyz angle ->
600+ FullTransform ( 0, 0, 0 ) ( 1, 1, 1 ) xyz angle
601+
602+ Scale xyz ->
603+ FullTransform ( 0, 0, 0 ) xyz ( 0, 0, 1 ) 0
604+
605+ Moved (( x, y, z ) as moved) ->
606+ case component of
607+ MoveX newX ->
608+ Moved ( newX, y, z )
609+
610+ MoveY newY ->
611+ Moved ( x, newY, z )
612+
613+ MoveZ newZ ->
614+ Moved ( x, y, newZ )
615+
616+ MoveXYZ xyz ->
617+ Moved xyz
618+
619+ Rotate xyz angle ->
620+ FullTransform moved ( 1, 1, 1 ) xyz angle
621+
622+ Scale scale ->
623+ FullTransform moved scale ( 0, 0, 1 ) 0
624+
625+ FullTransform (( x, y, z ) as moved) scaled origin angle ->
626+ case component of
627+ MoveX newX ->
628+ FullTransform ( newX, y, z ) scaled origin angle
629+
630+ MoveY newY ->
631+ FullTransform ( x, newY, z ) scaled origin angle
632+
633+ MoveZ newZ ->
634+ FullTransform ( x, y, newZ ) scaled origin angle
635+
636+ MoveXYZ newMove ->
637+ FullTransform newMove scaled origin angle
638+
639+ Rotate newOrigin newAngle ->
640+ FullTransform moved scaled newOrigin newAngle
641+
642+ Scale newScale ->
643+ FullTransform moved newScale origin angle
644+
645+
646+skippable flag style =
647+ if flag == Flag.borderWidth then
648+ case style of
649+ Single _ _ val ->
650+ case val of
651+ "0px" ->
652+ True
653+
654+ "1px" ->
655+ True
656+
657+ "2px" ->
658+ True
659+
660+ "3px" ->
661+ True
662+
663+ "4px" ->
664+ True
665+
666+ "5px" ->
667+ True
668+
669+ "6px" ->
670+ True
671+
672+ _ ->
673+ False
674+
675+ _ ->
676+ False
677+ else
678+ case style of
679+ FontSize i ->
680+ i >= 8 && i <= 32
681+
682+ PaddingStyle name t r b l ->
683+ t == b && t == r && t == l && t >= 0 && t <= 24
684+
685+ -- SpacingStyle _ _ _ ->
686+ -- True
687+ -- FontFamily _ _ ->
688+ -- True
689+ _ ->
690+ False
691+
692+
693+gatherAttrRecursive : String -> NodeName -> Flag.Field -> Transformation -> List Style -> List (VirtualDom.Attribute msg) -> List (VirtualDom.Node msg) -> List (Attribute aligned msg) -> Gathered msg
694+gatherAttrRecursive classes node has transform styles attrs children elementAttrs =
695+ case elementAttrs of
696+ [] ->
697+ case transformClass transform of
698+ Nothing ->
699+ { attributes = Html.Attributes.class classes :: attrs
700+ , styles = styles
701+ , node = node
702+ , children = children
703+ , has = has
704+ }
705+
706+ Just class ->
707+ { attributes = Html.Attributes.class (classes ++ " " ++ class) :: attrs
708+ , styles = Transform transform :: styles
709+ , node = node
710+ , children = children
711+ , has = has
712+ }
713+
714+ attribute :: remaining ->
715+ case attribute of
716+ NoAttribute ->
717+ gatherAttrRecursive classes node has transform styles attrs children remaining
718+
719+ Class flag exactClassName ->
720+ if Flag.present flag has then
721+ gatherAttrRecursive classes node has transform styles attrs children remaining
722+ else
723+ gatherAttrRecursive (exactClassName ++ " " ++ classes) node (Flag.add flag has) transform styles attrs children remaining
724+
725+ Attr actualAttribute ->
726+ gatherAttrRecursive classes node has transform styles (actualAttribute :: attrs) children remaining
727+
728+ StyleClass flag style ->
729+ if Flag.present flag has then
730+ gatherAttrRecursive classes node has transform styles attrs children remaining
731+ else if skippable flag style then
732+ gatherAttrRecursive (getStyleName style ++ " " ++ classes)
733+ node
734+ (Flag.add flag has)
735+ transform
736+ styles
737+ attrs
738+ children
739+ remaining
740+ else
741+ gatherAttrRecursive (getStyleName style ++ " " ++ classes)
742+ node
743+ (Flag.add flag has)
744+ transform
745+ (style :: styles)
746+ attrs
747+ children
748+ remaining
749+
750+ TransformComponent flag component ->
751+ gatherAttrRecursive classes
752+ node
753+ (Flag.add flag has)
754+ (composeTransformation transform component)
755+ styles
756+ attrs
757+ children
758+ remaining
759+
760+ Width width ->
761+ if Flag.present Flag.width has then
762+ gatherAttrRecursive classes node has transform styles attrs children remaining
763+ else
764+ case width of
765+ Px px ->
766+ gatherAttrRecursive ((Internal.Style.classes.widthExact ++ " width-px-" ++ String.fromInt px) ++ " " ++ classes)
767+ node
768+ (Flag.add Flag.width has)
769+ transform
770+ (Single ("width-px-" ++ String.fromInt px) "width" (String.fromInt px ++ "px") :: styles)
771+ attrs
772+ children
773+ remaining
774+
775+ Content ->
776+ gatherAttrRecursive (classes ++ " " ++ Internal.Style.classes.widthContent)
777+ node
778+ (Flag.add Flag.widthContent (Flag.add Flag.width has))
779+ transform
780+ styles
781+ attrs
782+ children
783+ remaining
784+
785+ Fill portion ->
786+ if portion == 1 then
787+ gatherAttrRecursive (classes ++ " " ++ Internal.Style.classes.widthFill)
788+ node
789+ (Flag.add Flag.widthFill (Flag.add Flag.width has))
790+ transform
791+ styles
792+ attrs
793+ children
794+ remaining
795+ else
796+ gatherAttrRecursive (classes ++ " " ++ Internal.Style.classes.widthFillPortion ++ " width-fill-" ++ String.fromInt portion)
797+ node
798+ (Flag.add Flag.widthFill (Flag.add Flag.width has))
799+ transform
800+ (Single
801+ (Internal.Style.classes.any
802+ ++ "."
803+ ++ Internal.Style.classes.row
804+ ++ " > "
805+ ++ (Internal.Style.dot <| "width-fill-" ++ String.fromInt portion)
806+ )
807+ "flex-grow"
808+ (String.fromInt (portion * 100000))
809+ :: styles
810+ )
811+ attrs
812+ children
813+ remaining
814+
815+ _ ->
816+ let
817+ ( addToFlags, newClass, newStyles ) =
818+ renderWidth width
819+ in
820+ gatherAttrRecursive (classes ++ " " ++ newClass)
821+ node
822+ (Flag.merge addToFlags has)
823+ transform
824+ (newStyles ++ styles)
825+ attrs
826+ children
827+ remaining
828+
829+ Height height ->
830+ if Flag.present Flag.height has then
831+ gatherAttrRecursive classes node has transform styles attrs children remaining
832+ else
833+ case height of
834+ Px px ->
835+ let
836+ val =
837+ String.fromInt px ++ "px"
838+
839+ name =
840+ "height-px-" ++ val
841+ in
842+ gatherAttrRecursive (name ++ " " ++ classes)
843+ node
844+ (Flag.add Flag.height has)
845+ transform
846+ (Single name "height " val :: styles)
847+ attrs
848+ children
849+ remaining
850+
851+ Content ->
852+ gatherAttrRecursive (Internal.Style.classes.heightContent ++ " " ++ classes)
853+ node
854+ (Flag.add Flag.heightContent (Flag.add Flag.height has))
855+ transform
856+ styles
857+ attrs
858+ children
859+ remaining
860+
861+ Fill portion ->
862+ if portion == 1 then
863+ gatherAttrRecursive (Internal.Style.classes.heightFill ++ " " ++ classes)
864+ node
865+ (Flag.add Flag.heightFill (Flag.add Flag.height has))
866+ transform
867+ styles
868+ attrs
869+ children
870+ remaining
871+ else
872+ gatherAttrRecursive (classes ++ " " ++ (Internal.Style.classes.heightFillPortion ++ " height-fill-" ++ String.fromInt portion))
873+ node
874+ (Flag.add Flag.heightFill (Flag.add Flag.height has))
875+ transform
876+ (Single
877+ (Internal.Style.classes.any
878+ ++ "."
879+ ++ Internal.Style.classes.column
880+ ++ " > "
881+ ++ (Internal.Style.dot <| "height-fill-" ++ String.fromInt portion)
882+ )
883+ "flex-grow"
884+ (String.fromInt (portion * 100000))
885+ :: styles
886+ )
887+ attrs
888+ children
889+ remaining
890+
891+ _ ->
892+ let
893+ ( addToFlags, newClass, newStyles ) =
894+ renderHeight height
895+ in
896+ gatherAttrRecursive (classes ++ " " ++ newClass)
897+ node
898+ (Flag.merge addToFlags has)
899+ transform
900+ (newStyles ++ styles)
901+ attrs
902+ children
903+ remaining
904+
905+ Describe description ->
906+ case description of
907+ Main ->
908+ gatherAttrRecursive classes (addNodeName "main" node) has transform styles attrs children remaining
909+
910+ Navigation ->
911+ gatherAttrRecursive classes (addNodeName "nav" node) has transform styles attrs children remaining
912+
913+ ContentInfo ->
914+ gatherAttrRecursive classes (addNodeName "footer" node) has transform styles attrs children remaining
915+
916+ Complementary ->
917+ gatherAttrRecursive classes (addNodeName "aside" node) has transform styles attrs children remaining
918+
919+ Heading i ->
920+ if i <= 1 then
921+ gatherAttrRecursive classes (addNodeName "h1" node) has transform styles attrs children remaining
922+ else if i < 7 then
923+ gatherAttrRecursive classes (addNodeName ("h" ++ String.fromInt i) node) has transform styles attrs children remaining
924+ else
925+ gatherAttrRecursive classes (addNodeName "h6" node) has transform styles attrs children remaining
926+
927+ Paragraph ->
928+ let
929+ newNode =
930+ case node of
931+ Generic ->
932+ NodeName "p"
933+
934+ NodeName name ->
935+ NodeName name
936+
937+ Embedded x y ->
938+ Embedded x y
939+ in
940+ gatherAttrRecursive classes
941+ newNode
942+ has
943+ transform
944+ styles
945+ attrs
946+ children
947+ remaining
948+
949+ Button ->
950+ gatherAttrRecursive classes node has transform styles (VirtualDom.attribute "role" "button" :: attrs) children remaining
951+
952+ Label label ->
953+ gatherAttrRecursive classes node has transform styles (VirtualDom.attribute "aria-label" label :: attrs) children remaining
954+
955+ LivePolite ->
956+ gatherAttrRecursive classes node has transform styles (VirtualDom.attribute "aria-live" "polite" :: attrs) children remaining
957+
958+ LiveAssertive ->
959+ gatherAttrRecursive classes node has transform styles (VirtualDom.attribute "aria-live" "assertive" :: attrs) children remaining
960+
961+ Nearby location elem ->
962+ let
963+ newStyles =
964+ case elem of
965+ Empty ->
966+ styles
967+
968+ Text str ->
969+ styles
970+
971+ Unstyled html ->
972+ styles
973+
974+ Styled styled ->
975+ styles ++ styled.styles
976+
977+ nearbyElement =
978+ Html.div
979+ [ Html.Attributes.class <|
980+ case location of
981+ Above ->
982+ String.join " "
983+ [ Internal.Style.classes.any
984+ , Internal.Style.classes.single
985+ , Internal.Style.classes.above
986+ ]
987+
988+ Below ->
989+ String.join " "
990+ [ Internal.Style.classes.any
991+ , Internal.Style.classes.single
992+ , Internal.Style.classes.below
993+ ]
994+
995+ OnRight ->
996+ String.join " "
997+ [ Internal.Style.classes.any
998+ , Internal.Style.classes.single
999+ , Internal.Style.classes.onRight
1000+ ]
1001+
1002+ OnLeft ->
1003+ String.join " "
1004+ [ Internal.Style.classes.any
1005+ , Internal.Style.classes.single
1006+ , Internal.Style.classes.onLeft
1007+ ]
1008+
1009+ InFront ->
1010+ String.join " "
1011+ [ Internal.Style.classes.any
1012+ , Internal.Style.classes.single
1013+ , Internal.Style.classes.inFront
1014+ ]
1015+
1016+ Behind ->
1017+ String.join " "
1018+ [ Internal.Style.classes.any
1019+ , Internal.Style.classes.single
1020+ , Internal.Style.classes.behind
1021+ ]
1022+ ]
1023+ [ case elem of
1024+ Empty ->
1025+ VirtualDom.text ""
1026+
1027+ Text str ->
1028+ textElement str
1029+
1030+ Unstyled html ->
1031+ html asEl
1032+
1033+ Styled styled ->
1034+ styled.html NoStyleSheet asEl
1035+ ]
1036+
1037+ newClasses =
1038+ if location == Behind then
1039+ Internal.Style.classes.hasBehind ++ " " ++ classes
1040+ else
1041+ classes
1042+ in
1043+ gatherAttrRecursive newClasses node has transform newStyles attrs (nearbyElement :: children) remaining
1044+
1045+ AlignX x ->
1046+ if Flag.present Flag.xAlign has then
1047+ gatherAttrRecursive classes node has transform styles attrs children remaining
1048+ else
1049+ gatherAttrRecursive (alignXName x ++ " " ++ classes)
1050+ node
1051+ (has
1052+ |> Flag.add Flag.xAlign
1053+ |> (\flags ->
1054+ case x of
1055+ CenterX ->
1056+ Flag.add Flag.centerX flags
1057+
1058+ Right ->
1059+ Flag.add Flag.alignRight flags
1060+
1061+ _ ->
1062+ flags
1063+ )
1064+ )
1065+ transform
1066+ styles
1067+ attrs
1068+ children
1069+ remaining
1070+
1071+ AlignY y ->
1072+ if Flag.present Flag.yAlign has then
1073+ gatherAttrRecursive classes node has transform styles attrs children remaining
1074+ else
1075+ gatherAttrRecursive (alignYName y ++ " " ++ classes)
1076+ node
1077+ (Flag.add Flag.yAlign has
1078+ |> (\flags ->
1079+ case y of
1080+ CenterY ->
1081+ Flag.add Flag.centerY flags
1082+
1083+ Bottom ->
1084+ Flag.add Flag.alignBottom flags
1085+
1086+ _ ->
1087+ flags
1088+ )
1089+ )
1090+ transform
1091+ styles
1092+ attrs
1093+ children
1094+ remaining
1095+
1096+
1097+renderWidth w =
1098+ case w of
1099+ Px px ->
1100+ ( Flag.none
1101+ , Internal.Style.classes.widthExact ++ " width-px-" ++ String.fromInt px
1102+ , [ Single ("width-px-" ++ String.fromInt px) "width" (String.fromInt px ++ "px") ]
1103+ )
1104+
1105+ Content ->
1106+ ( Flag.add Flag.widthContent Flag.none
1107+ , Internal.Style.classes.widthContent
1108+ , []
1109+ )
1110+
1111+ Fill portion ->
1112+ if portion == 1 then
1113+ ( Flag.add Flag.widthFill Flag.none
1114+ , Internal.Style.classes.widthFill
1115+ , []
1116+ )
1117+ else
1118+ ( Flag.add Flag.widthFill Flag.none
1119+ , Internal.Style.classes.widthFillPortion ++ " width-fill-" ++ String.fromInt portion
1120+ , [ Single
1121+ (Internal.Style.classes.any
1122+ ++ "."
1123+ ++ Internal.Style.classes.row
1124+ ++ " > "
1125+ ++ (Internal.Style.dot <| "width-fill-" ++ String.fromInt portion)
1126+ )
1127+ "flex-grow"
1128+ (String.fromInt (portion * 100000))
1129+ ]
1130+ )
1131+
1132+ Min minSize len ->
1133+ let
1134+ cls =
1135+ "min-width-"
1136+ ++ String.fromInt minSize
1137+
1138+ style =
1139+ Single
1140+ cls
1141+ "min-width"
1142+ (String.fromInt minSize ++ "px")
1143+
1144+ ( newFlag, newAttrs, newStyle ) =
1145+ renderWidth len
1146+ in
1147+ ( Flag.add Flag.widthBetween newFlag
1148+ , cls ++ " " ++ newAttrs
1149+ , style :: newStyle
1150+ )
1151+
1152+ Max maxSize len ->
1153+ let
1154+ cls =
1155+ "max-width-" ++ String.fromInt maxSize
1156+
1157+ style =
1158+ Single cls
1159+ "max-width"
1160+ (String.fromInt maxSize ++ "px")
1161+
1162+ ( newFlag, newAttrs, newStyle ) =
1163+ renderWidth len
1164+ in
1165+ ( Flag.add Flag.widthBetween newFlag
1166+ , cls ++ " " ++ newAttrs
1167+ , style :: newStyle
1168+ )
1169+
1170+
1171+renderHeight h =
1172+ case h of
1173+ Px px ->
1174+ let
1175+ val =
1176+ String.fromInt px
1177+
1178+ name =
1179+ "height-px-" ++ val
1180+ in
1181+ ( Flag.none
1182+ , name
1183+ , [ Single name "height" (val ++ "px") ]
1184+ )
1185+
1186+ Content ->
1187+ ( Flag.add Flag.heightContent Flag.none
1188+ , Internal.Style.classes.heightContent
1189+ , []
1190+ )
1191+
1192+ Fill portion ->
1193+ if portion == 1 then
1194+ ( Flag.add Flag.heightFill Flag.none
1195+ , Internal.Style.classes.heightFill
1196+ , []
1197+ )
1198+ else
1199+ ( Flag.add Flag.heightFill Flag.none
1200+ , Internal.Style.classes.heightFillPortion ++ " height-fill-" ++ String.fromInt portion
1201+ , [ Single
1202+ (Internal.Style.classes.any
1203+ ++ "."
1204+ ++ Internal.Style.classes.row
1205+ ++ " > "
1206+ ++ (Internal.Style.dot <| "height-fill-" ++ String.fromInt portion)
1207+ )
1208+ "flex-grow"
1209+ (String.fromInt (portion * 100000))
1210+ ]
1211+ )
1212+
1213+ Min minSize len ->
1214+ let
1215+ cls =
1216+ "min-height-"
1217+ ++ String.fromInt minSize
1218+
1219+ style =
1220+ Single
1221+ cls
1222+ "min-height"
1223+ (String.fromInt minSize ++ "px")
1224+
1225+ ( newFlag, newAttrs, newStyle ) =
1226+ renderHeight len
1227+ in
1228+ ( Flag.add Flag.heightBetween newFlag
1229+ , cls ++ " " ++ newAttrs
1230+ , style :: newStyle
1231+ )
1232+
1233+ Max maxSize len ->
1234+ let
1235+ cls =
1236+ "max-height-" ++ String.fromInt maxSize
1237+
1238+ style =
1239+ Single cls
1240+ "max-height"
1241+ (String.fromInt maxSize ++ "px")
1242+
1243+ ( newFlag, newAttrs, newStyle ) =
1244+ renderHeight len
1245+ in
1246+ ( Flag.add Flag.heightBetween newFlag
1247+ , cls ++ " " ++ newAttrs
1248+ , style :: newStyle
1249+ )
1250+
1251+
1252+rowClass =
1253+ classes.any ++ " " ++ classes.row
1254+
1255+
1256+columnClass =
1257+ classes.any ++ " " ++ classes.column
1258+
1259+
1260+singleClass =
1261+ classes.any ++ " " ++ classes.single
1262+
1263+
1264+gridClass =
1265+ classes.any ++ " " ++ classes.grid
1266+
1267+
1268+paragraphClass =
1269+ classes.any ++ " " ++ classes.paragraph
1270+
1271+
1272+pageClass =
1273+ classes.any ++ " " ++ classes.page
1274+
1275+
1276+contextClasses context =
1277+ case context of
1278+ AsRow ->
1279+ rowClass
1280+
1281+ AsColumn ->
1282+ columnClass
1283+
1284+ AsEl ->
1285+ singleClass
1286+
1287+ AsGrid ->
1288+ gridClass
1289+
1290+ AsParagraph ->
1291+ paragraphClass
1292+
1293+ AsTextColumn ->
1294+ pageClass
1295+
1296+
1297+element : LayoutContext -> NodeName -> List (Attribute aligned msg) -> Children (Element msg) -> Element msg
1298+element context node attributes children =
1299+ attributes
1300+ |> List.reverse
1301+ |> gatherAttrRecursive (contextClasses context) node Flag.none untransformed [] [] []
1302+ |> createElement context children
1303+
1304+
1305+untransformed =
1306+ Untransformed
1307+
1308+
1309+
1310+-- space =
1311+-- VirtualDom.text " "
1312+-- keyedSpace =
1313+-- ( " ", space )
1314+
1315+
1316+createElement : LayoutContext -> Children (Element msg) -> Gathered msg -> Element msg
1317+createElement context children rendered =
1318+ let
1319+ gather child ( htmls, existingStyles ) =
1320+ case child of
1321+ Unstyled html ->
1322+ if context == asParagraph then
1323+ ( html context :: htmls
1324+ , existingStyles
1325+ )
1326+ else
1327+ ( html context :: htmls
1328+ , existingStyles
1329+ )
1330+
1331+ Styled styled ->
1332+ if context == asParagraph then
1333+ ( styled.html NoStyleSheet context :: htmls
1334+ , if List.isEmpty existingStyles then
1335+ styled.styles
1336+ else
1337+ styled.styles ++ existingStyles
1338+ )
1339+ else
1340+ ( styled.html NoStyleSheet context :: htmls
1341+ , if List.isEmpty existingStyles then
1342+ styled.styles
1343+ else
1344+ styled.styles ++ existingStyles
1345+ )
1346+
1347+ Text str ->
1348+ -- TEXT OPTIMIZATION
1349+ -- You can have raw text if the element is an el, and has `width-content` and `height-content`
1350+ -- Same if it's a column or row with one child and width-content, height-content
1351+ -- interferes with css grid
1352+ -- Maybe we could unpack text elements in a paragraph as well,
1353+ -- however embedded style elements that are larger than the line height will overlap with exisitng text.
1354+ -- I don't think that's what we want.
1355+ if
1356+ context
1357+ == asEl
1358+ || context
1359+ == asParagraph
1360+ then
1361+ ( VirtualDom.text
1362+ (if context == asParagraph then
1363+ str ++ " "
1364+ else
1365+ str
1366+ )
1367+ :: htmls
1368+ , existingStyles
1369+ )
1370+ else
1371+ ( textElement
1372+ (if context == asParagraph then
1373+ str ++ " "
1374+ else
1375+ str
1376+ )
1377+ :: htmls
1378+ , existingStyles
1379+ )
1380+
1381+ Empty ->
1382+ ( htmls, existingStyles )
1383+
1384+ gatherKeyed ( key, child ) ( htmls, existingStyles ) =
1385+ case child of
1386+ Unstyled html ->
1387+ if context == asParagraph then
1388+ ( ( key, html context ) :: htmls
1389+ , existingStyles
1390+ )
1391+ else
1392+ ( ( key, html context ) :: htmls
1393+ , existingStyles
1394+ )
1395+
1396+ Styled styled ->
1397+ if context == asParagraph then
1398+ ( ( key, styled.html NoStyleSheet context )
1399+ :: htmls
1400+ , if List.isEmpty existingStyles then
1401+ styled.styles
1402+ else
1403+ styled.styles ++ existingStyles
1404+ )
1405+ else
1406+ ( ( key, styled.html NoStyleSheet context ) :: htmls
1407+ , if List.isEmpty existingStyles then
1408+ styled.styles
1409+ else
1410+ styled.styles ++ existingStyles
1411+ )
1412+
1413+ Text str ->
1414+ -- TEXT OPTIMIZATION
1415+ -- You can have raw text if the element is an el, and has `width-content` and `height-content`
1416+ -- Same if it's a column or row with one child and width-content, height-content
1417+ if
1418+ context
1419+ == asEl
1420+ || context
1421+ == asParagraph
1422+ then
1423+ ( ( key
1424+ , VirtualDom.text
1425+ (if context == asParagraph then
1426+ str ++ " "
1427+ else
1428+ str
1429+ )
1430+ )
1431+ :: htmls
1432+ , existingStyles
1433+ )
1434+ else
1435+ ( ( key
1436+ , textElement
1437+ (if context == asParagraph then
1438+ str ++ " "
1439+ else
1440+ str
1441+ )
1442+ )
1443+ :: htmls
1444+ , existingStyles
1445+ )
1446+
1447+ Empty ->
1448+ ( htmls, existingStyles )
1449+ in
1450+ case children of
1451+ Keyed keyedChildren ->
1452+ case List.foldr gatherKeyed ( [], [] ) keyedChildren of
1453+ ( keyed, styles ) ->
1454+ let
1455+ newStyles =
1456+ if List.isEmpty styles then
1457+ rendered.styles
1458+ else
1459+ rendered.styles ++ styles
1460+ in
1461+ case newStyles of
1462+ [] ->
1463+ Unstyled
1464+ (finalizeNode rendered.has
1465+ rendered.node
1466+ rendered.attributes
1467+ (Keyed <| List.map (\x -> ( "nearby-elements-pls", x )) rendered.children ++ keyed)
1468+ NoStyleSheet
1469+ )
1470+
1471+ allStyles ->
1472+ Styled
1473+ { styles = allStyles
1474+ , html =
1475+ finalizeNode rendered.has rendered.node rendered.attributes (Keyed (List.map (\x -> ( "nearby-elements-pls", x )) rendered.children ++ keyed))
1476+ }
1477+
1478+ Unkeyed unkeyedChildren ->
1479+ case List.foldr gather ( [], [] ) unkeyedChildren of
1480+ ( unkeyed, styles ) ->
1481+ let
1482+ newStyles =
1483+ if List.isEmpty styles then
1484+ rendered.styles
1485+ else
1486+ rendered.styles ++ styles
1487+ in
1488+ case newStyles of
1489+ [] ->
1490+ Unstyled
1491+ (finalizeNode rendered.has
1492+ rendered.node
1493+ rendered.attributes
1494+ (Unkeyed <| rendered.children ++ unkeyed)
1495+ NoStyleSheet
1496+ )
1497+
1498+ allStyles ->
1499+ Styled
1500+ { styles = allStyles
1501+ , html = finalizeNode rendered.has rendered.node rendered.attributes (Unkeyed (rendered.children ++ unkeyed))
1502+ }
1503+
1504+
1505+unit =
1506+ 0
1507+
1508+
1509+defaultOptions =
1510+ { hover = AllowHover
1511+ , focus = focusDefaultStyle
1512+ , mode = Layout
1513+ }
1514+
1515+
1516+staticRoot =
1517+ VirtualDom.node "style" [] [ VirtualDom.text Internal.Style.rules ]
1518+
1519+
1520+addWhen ifThis x to =
1521+ if ifThis then
1522+ x :: to
1523+ else
1524+ to
1525+
1526+
1527+{-| TODO:
1528+
1529+This doesn't reduce equivalent attributes completely.
1530+
1531+-}
1532+filter : List (Attribute aligned msg) -> List (Attribute aligned msg)
1533+filter attrs =
1534+ Tuple.first <|
1535+ List.foldr
1536+ (\x ( found, has ) ->
1537+ case x of
1538+ NoAttribute ->
1539+ ( found, has )
1540+
1541+ Class key _ ->
1542+ ( x :: found, has )
1543+
1544+ Attr attr ->
1545+ ( x :: found, has )
1546+
1547+ StyleClass _ style ->
1548+ ( x :: found, has )
1549+
1550+ Width width ->
1551+ if Set.member "width" has then
1552+ ( found, has )
1553+ else
1554+ ( x :: found, Set.insert "width" has )
1555+
1556+ Height height ->
1557+ if Set.member "height" has then
1558+ ( found, has )
1559+ else
1560+ ( x :: found, Set.insert "height" has )
1561+
1562+ Describe description ->
1563+ if Set.member "described" has then
1564+ ( found, has )
1565+ else
1566+ ( x :: found, Set.insert "described" has )
1567+
1568+ Nearby location elem ->
1569+ ( x :: found, has )
1570+
1571+ AlignX _ ->
1572+ if Set.member "align-x" has then
1573+ ( found, has )
1574+ else
1575+ ( x :: found, Set.insert "align-x" has )
1576+
1577+ AlignY _ ->
1578+ if Set.member "align-y" has then
1579+ ( found, has )
1580+ else
1581+ ( x :: found, Set.insert "align-y" has )
1582+
1583+ TransformComponent _ _ ->
1584+ if Set.member "transform" has then
1585+ ( found, has )
1586+ else
1587+ ( x :: found, Set.insert "transform" has )
1588+ )
1589+ ( [], Set.empty )
1590+ attrs
1591+
1592+
1593+get : List (Attribute aligned msg) -> (Attribute aligned msg -> Bool) -> List (Attribute aligned msg)
1594+get attrs isAttr =
1595+ attrs
1596+ |> filter
1597+ |> List.foldr
1598+ (\x found ->
1599+ if isAttr x then
1600+ x :: found
1601+ else
1602+ found
1603+ )
1604+ []
1605+
1606+
1607+type Spacing
1608+ = Spaced String Int Int
1609+
1610+
1611+type Padding
1612+ = Padding String Int Int Int Int
1613+
1614+
1615+extractSpacingAndPadding : List (Attribute aligned msg) -> ( Maybe Padding, Maybe Spacing )
1616+extractSpacingAndPadding attrs =
1617+ List.foldr
1618+ (\attr ( pad, spacing ) ->
1619+ ( case pad of
1620+ Just x ->
1621+ pad
1622+
1623+ Nothing ->
1624+ case attr of
1625+ StyleClass _ (PaddingStyle name t r b l) ->
1626+ Just (Padding name t r b l)
1627+
1628+ _ ->
1629+ Nothing
1630+ , case spacing of
1631+ Just x ->
1632+ spacing
1633+
1634+ Nothing ->
1635+ case attr of
1636+ StyleClass _ (SpacingStyle name x y) ->
1637+ Just (Spaced name x y)
1638+
1639+ _ ->
1640+ Nothing
1641+ )
1642+ )
1643+ ( Nothing, Nothing )
1644+ attrs
1645+
1646+
1647+getSpacing : List (Attribute aligned msg) -> ( Int, Int ) -> ( Int, Int )
1648+getSpacing attrs default =
1649+ attrs
1650+ |> List.foldr
1651+ (\attr acc ->
1652+ case acc of
1653+ Just x ->
1654+ Just x
1655+
1656+ Nothing ->
1657+ case attr of
1658+ StyleClass _ (SpacingStyle _ x y) ->
1659+ Just ( x, y )
1660+
1661+ _ ->
1662+ Nothing
1663+ )
1664+ Nothing
1665+ |> Maybe.withDefault default
1666+
1667+
1668+getWidth : List (Attribute aligned msg) -> Maybe Length
1669+getWidth attrs =
1670+ attrs
1671+ |> List.foldr
1672+ (\attr acc ->
1673+ case acc of
1674+ Just x ->
1675+ Just x
1676+
1677+ Nothing ->
1678+ case attr of
1679+ Width len ->
1680+ Just len
1681+
1682+ _ ->
1683+ Nothing
1684+ )
1685+ Nothing
1686+
1687+
1688+getHeight : List (Attribute aligned msg) -> Maybe Length
1689+getHeight attrs =
1690+ attrs
1691+ |> List.foldr
1692+ (\attr acc ->
1693+ case acc of
1694+ Just x ->
1695+ Just x
1696+
1697+ Nothing ->
1698+ case attr of
1699+ Height len ->
1700+ Just len
1701+
1702+ _ ->
1703+ Nothing
1704+ )
1705+ Nothing
1706+
1707+
1708+textElement : String -> VirtualDom.Node msg
1709+textElement str =
1710+ Html.div
1711+ [ Html.Attributes.class
1712+ (String.join " "
1713+ [ classes.any
1714+ , classes.text
1715+ , classes.widthContent
1716+ , classes.heightContent
1717+ ]
1718+ )
1719+ ]
1720+ [ Html.text str ]
1721+
1722+
1723+textElementFill : String -> VirtualDom.Node msg
1724+textElementFill str =
1725+ VirtualDom.node "div"
1726+ [ VirtualDom.property "className"
1727+ (Json.string <|
1728+ String.join " "
1729+ [ classes.any
1730+ , classes.text
1731+ , classes.widthFill
1732+ , classes.heightFill
1733+ ]
1734+ )
1735+ ]
1736+ [ VirtualDom.text str ]
1737+
1738+
1739+type Children x
1740+ = Unkeyed (List x)
1741+ | Keyed (List ( String, x ))
1742+
1743+
1744+
1745+-- toHtml : OptionRecord -> Element msg -> VirtualDom.Node msg
1746+
1747+
1748+toHtml mode el =
1749+ case el of
1750+ Unstyled html ->
1751+ html asEl
1752+
1753+ Styled { styles, html } ->
1754+ html (mode styles) asEl
1755+
1756+ Text text ->
1757+ textElement text
1758+
1759+ Empty ->
1760+ textElement ""
1761+
1762+
1763+{-| -}
1764+renderRoot : List Option -> List (Attribute aligned msg) -> Element msg -> VirtualDom.Node msg
1765+renderRoot optionList attributes child =
1766+ let
1767+ options =
1768+ optionsToRecord optionList
1769+
1770+ embedStyle =
1771+ case options.mode of
1772+ NoStaticStyleSheet ->
1773+ OnlyDynamic options
1774+
1775+ _ ->
1776+ StaticRootAndDynamic options
1777+ in
1778+ element asEl div attributes (Unkeyed [ child ])
1779+ |> toHtml embedStyle
1780+
1781+
1782+type RenderMode
1783+ = Viewport
1784+ | Layout
1785+ | NoStaticStyleSheet
1786+ | WithVirtualCss
1787+
1788+
1789+type alias OptionRecord =
1790+ { hover : HoverSetting
1791+ , focus : FocusStyle
1792+ , mode : RenderMode
1793+ }
1794+
1795+
1796+type HoverSetting
1797+ = NoHover
1798+ | AllowHover
1799+ | ForceHover
1800+
1801+
1802+type Option
1803+ = HoverOption HoverSetting
1804+ | FocusStyleOption FocusStyle
1805+ | RenderModeOption RenderMode
1806+
1807+
1808+type alias FocusStyle =
1809+ { borderColor : Maybe Color
1810+ , shadow : Maybe Shadow
1811+ , backgroundColor : Maybe Color
1812+ }
1813+
1814+
1815+type alias Shadow =
1816+ { color : Color
1817+ , offset : ( Int, Int )
1818+ , blur : Int
1819+ , size : Int
1820+ }
1821+
1822+
1823+rootStyle : List (Attribute aligned msg)
1824+rootStyle =
1825+ let
1826+ families =
1827+ [ Typeface "Open Sans"
1828+ , Typeface "Helvetica"
1829+ , Typeface "Verdana"
1830+ , SansSerif
1831+ ]
1832+ in
1833+ [ StyleClass Flag.bgColor (Colored ("bg-color-" ++ formatColorClass (Rgba 1 1 1 1)) "background-color" (Rgba 1 1 1 1))
1834+ , StyleClass Flag.fontColor (Colored ("font-color-" ++ formatColorClass (Rgba 0 0 0 1)) "color" (Rgba 0 0 0 1))
1835+
1836+ -- , StyleClass Flag.fontSize (Single "font-size-20" "font-size" "20px")
1837+ , StyleClass Flag.fontSize (FontSize 20)
1838+ , StyleClass Flag.fontFamily <|
1839+ FontFamily (List.foldl renderFontClassName "font-" families)
1840+ families
1841+ ]
1842+
1843+
1844+renderFontClassName : Font -> String -> String
1845+renderFontClassName font current =
1846+ current
1847+ ++ (case font of
1848+ Serif ->
1849+ "serif"
1850+
1851+ SansSerif ->
1852+ "sans-serif"
1853+
1854+ Monospace ->
1855+ "monospace"
1856+
1857+ Typeface name ->
1858+ name
1859+ |> String.toLower
1860+ |> String.words
1861+ |> String.join "-"
1862+
1863+ ImportFont name url ->
1864+ name
1865+ |> String.toLower
1866+ |> String.words
1867+ |> String.join "-"
1868+ )
1869+
1870+
1871+renderFocusStyle :
1872+ FocusStyle
1873+ -> Style
1874+renderFocusStyle focus =
1875+ Style (Internal.Style.dot classes.any ++ ":focus .focusable, " ++ Internal.Style.dot classes.any ++ ".focusable:focus")
1876+ (List.filterMap identity
1877+ [ Maybe.map (\color -> Property "border-color" (formatColor color)) focus.borderColor
1878+ , Maybe.map (\color -> Property "background-color" (formatColor color)) focus.backgroundColor
1879+ , Maybe.map
1880+ (\shadow ->
1881+ Property "box-shadow"
1882+ (formatBoxShadow
1883+ { color = shadow.color
1884+ , offset =
1885+ shadow.offset
1886+ |> Tuple.mapFirst toFloat
1887+ |> Tuple.mapSecond toFloat
1888+ , inset = False
1889+ , blur = toFloat shadow.blur
1890+ , size = toFloat shadow.size
1891+ }
1892+ )
1893+ )
1894+ focus.shadow
1895+ , Just <| Property "outline" "none"
1896+ ]
1897+ )
1898+
1899+
1900+focusDefaultStyle : { backgroundColor : Maybe Color, borderColor : Maybe Color, shadow : Maybe Shadow }
1901+focusDefaultStyle =
1902+ { backgroundColor = Nothing
1903+ , borderColor = Nothing
1904+ , shadow =
1905+ Just
1906+ { color =
1907+ Rgba (155 / 255) (203 / 255) 1 1
1908+ , offset = ( 0, 0 )
1909+ , blur = 3
1910+ , size = 3
1911+ }
1912+ }
1913+
1914+
1915+optionsToRecord : List Option -> OptionRecord
1916+optionsToRecord options =
1917+ let
1918+ combine opt record =
1919+ case opt of
1920+ HoverOption hoverable ->
1921+ case record.hover of
1922+ Nothing ->
1923+ { record | hover = Just hoverable }
1924+
1925+ _ ->
1926+ record
1927+
1928+ FocusStyleOption focusStyle ->
1929+ case record.focus of
1930+ Nothing ->
1931+ { record | focus = Just focusStyle }
1932+
1933+ _ ->
1934+ record
1935+
1936+ RenderModeOption renderMode ->
1937+ case record.mode of
1938+ Nothing ->
1939+ { record | mode = Just renderMode }
1940+
1941+ _ ->
1942+ record
1943+
1944+ andFinally record =
1945+ { hover =
1946+ case record.hover of
1947+ Nothing ->
1948+ AllowHover
1949+
1950+ Just hoverable ->
1951+ hoverable
1952+ , focus =
1953+ case record.focus of
1954+ Nothing ->
1955+ focusDefaultStyle
1956+
1957+ Just focusable ->
1958+ focusable
1959+ , mode =
1960+ case record.mode of
1961+ Nothing ->
1962+ Layout
1963+
1964+ Just actualMode ->
1965+ actualMode
1966+ }
1967+ in
1968+ andFinally <|
1969+ List.foldr combine
1970+ { hover = Nothing
1971+ , focus = Nothing
1972+ , mode = Nothing
1973+ }
1974+ options
1975+
1976+
1977+renderFont : List Font -> String
1978+renderFont families =
1979+ let
1980+ fontName font =
1981+ case font of
1982+ Serif ->
1983+ "serif"
1984+
1985+ SansSerif ->
1986+ "sans-serif"
1987+
1988+ Monospace ->
1989+ "monospace"
1990+
1991+ Typeface name ->
1992+ "\"" ++ name ++ "\""
1993+
1994+ ImportFont name url ->
1995+ "\"" ++ name ++ "\""
1996+ in
1997+ families
1998+ |> List.map fontName
1999+ |> String.join ", "
2000+
2001+
2002+toStyleSheet : OptionRecord -> List Style -> VirtualDom.Node msg
2003+toStyleSheet options styleSheet =
2004+ VirtualDom.node "style" [] [ VirtualDom.text (toStyleSheetString options styleSheet) ]
2005+
2006+
2007+toStyleSheetString : OptionRecord -> List Style -> String
2008+toStyleSheetString options stylesheet =
2009+ let
2010+ renderTopLevels rule =
2011+ case rule of
2012+ FontFamily name typefaces ->
2013+ let
2014+ getImports font =
2015+ case font of
2016+ ImportFont _ url ->
2017+ Just ("@import url('" ++ url ++ "');")
2018+
2019+ _ ->
2020+ Nothing
2021+ in
2022+ typefaces
2023+ |> List.filterMap getImports
2024+ |> String.join "\n"
2025+ |> Just
2026+
2027+ _ ->
2028+ Nothing
2029+
2030+ renderProps force (Property key val) existing =
2031+ if force then
2032+ existing ++ "\n " ++ key ++ ": " ++ val ++ " !important;"
2033+ else
2034+ existing ++ "\n " ++ key ++ ": " ++ val ++ ";"
2035+
2036+ renderStyle maybePseudo selector props =
2037+ case maybePseudo of
2038+ Nothing ->
2039+ selector ++ "{" ++ List.foldl (renderProps False) "" props ++ "\n}"
2040+
2041+ Just pseudo ->
2042+ case pseudo of
2043+ Hover ->
2044+ case options.hover of
2045+ NoHover ->
2046+ ""
2047+
2048+ ForceHover ->
2049+ selector ++ "-hv {" ++ List.foldl (renderProps True) "" props ++ "\n}"
2050+
2051+ AllowHover ->
2052+ selector ++ "-hv:hover {" ++ List.foldl (renderProps False) "" props ++ "\n}"
2053+
2054+ Focus ->
2055+ let
2056+ renderedProps =
2057+ List.foldl (renderProps False) "" props
2058+ in
2059+ String.join "\n"
2060+ [ selector
2061+ ++ "-fs:focus {"
2062+ ++ renderedProps
2063+ ++ "\n}"
2064+ , "."
2065+ ++ classes.any
2066+ ++ ":focus ~ "
2067+ ++ selector
2068+ ++ "-fs:not(.focus) {"
2069+ ++ renderedProps
2070+ ++ "\n}"
2071+ , "."
2072+ ++ classes.any
2073+ ++ ":focus "
2074+ ++ selector
2075+ ++ "-fs {"
2076+ ++ renderedProps
2077+ ++ "\n}"
2078+ , ".focusable-parent:focus ~ "
2079+ ++ "."
2080+ ++ classes.any
2081+ ++ " "
2082+ ++ selector
2083+ ++ "-fs {"
2084+ ++ renderedProps
2085+ ++ "\n}"
2086+ ]
2087+
2088+ Active ->
2089+ selector ++ "-act:active {" ++ List.foldl (renderProps False) "" props ++ "\n}"
2090+
2091+ renderStyleRule rule maybePseudo =
2092+ case rule of
2093+ Style selector props ->
2094+ renderStyle maybePseudo selector props
2095+
2096+ Shadows name prop ->
2097+ renderStyle
2098+ maybePseudo
2099+ ("." ++ name)
2100+ [ Property "box-shadow" prop
2101+ ]
2102+
2103+ Transparency name transparency ->
2104+ let
2105+ opacity =
2106+ (1 - transparency)
2107+ |> min 1
2108+ |> max 0
2109+ in
2110+ renderStyle
2111+ maybePseudo
2112+ ("." ++ name)
2113+ [ Property "opacity" (String.fromFloat opacity)
2114+ ]
2115+
2116+ FontSize i ->
2117+ renderStyle
2118+ maybePseudo
2119+ (".font-size-" ++ String.fromInt i)
2120+ [ Property "font-size" (String.fromInt i ++ "px")
2121+ ]
2122+
2123+ FontFamily name typefaces ->
2124+ renderStyle
2125+ maybePseudo
2126+ ("." ++ name)
2127+ [ Property "font-family" (renderFont typefaces)
2128+ ]
2129+
2130+ Single class prop val ->
2131+ renderStyle
2132+ maybePseudo
2133+ ("." ++ class)
2134+ [ Property prop val
2135+ ]
2136+
2137+ Colored class prop color ->
2138+ renderStyle
2139+ maybePseudo
2140+ ("." ++ class)
2141+ [ Property prop (formatColor color)
2142+ ]
2143+
2144+ SpacingStyle cls x y ->
2145+ let
2146+ class =
2147+ "." ++ cls
2148+
2149+ xPx =
2150+ String.fromInt x ++ "px"
2151+
2152+ yPx =
2153+ String.fromInt y ++ "px"
2154+
2155+ row =
2156+ "." ++ Internal.Style.classes.row
2157+
2158+ wrappedRow =
2159+ "." ++ Internal.Style.classes.wrapped ++ row
2160+
2161+ column =
2162+ "." ++ Internal.Style.classes.column
2163+
2164+ page =
2165+ "." ++ Internal.Style.classes.page
2166+
2167+ paragraph =
2168+ "." ++ Internal.Style.classes.paragraph
2169+
2170+ left =
2171+ "." ++ Internal.Style.classes.alignLeft
2172+
2173+ right =
2174+ "." ++ Internal.Style.classes.alignRight
2175+
2176+ any =
2177+ "." ++ Internal.Style.classes.any
2178+
2179+ single =
2180+ "." ++ Internal.Style.classes.single
2181+ in
2182+ String.concat
2183+ [ renderStyle maybePseudo (class ++ row ++ " > " ++ any) [ Property "margin-right" xPx ]
2184+
2185+ -- margins don't apply to last element of normal, unwrapped rows
2186+ , renderStyle maybePseudo (class ++ row ++ " > " ++ any ++ ":last-child") [ Property "margin" "0" ]
2187+
2188+ -- For wrapped rows, margins always apply because we handle "canceling out" the other margins manually in the element.
2189+ , renderStyle maybePseudo
2190+ (class ++ wrappedRow ++ " > " ++ any)
2191+ [ Property "margin" ("0 " ++ xPx ++ " " ++ yPx ++ " 0")
2192+ ]
2193+ , renderStyle maybePseudo
2194+ (class ++ wrappedRow ++ " > " ++ any ++ ":last-child")
2195+ [ Property "margin-right" "0"
2196+ ]
2197+
2198+ -- columns
2199+ , renderStyle maybePseudo (class ++ column ++ " > " ++ any ++ " + " ++ any) [ Property "margin-top" yPx ]
2200+ , renderStyle maybePseudo (class ++ page ++ " > " ++ any ++ " + " ++ any) [ Property "margin-top" yPx ]
2201+ , renderStyle maybePseudo (class ++ page ++ " > " ++ left) [ Property "margin-right" xPx ]
2202+ , renderStyle maybePseudo (class ++ page ++ " > " ++ right) [ Property "margin-left" xPx ]
2203+ , renderStyle
2204+ maybePseudo
2205+ (class ++ paragraph)
2206+ [ Property "line-height" ("calc(1em + " ++ String.fromInt y ++ "px)")
2207+ ]
2208+ , renderStyle
2209+ maybePseudo
2210+ ("textarea" ++ class)
2211+ [ Property "line-height" ("calc(1em + " ++ String.fromInt y ++ "px)")
2212+ ]
2213+
2214+ -- , renderStyle
2215+ -- maybePseudo
2216+ -- (class ++ paragraph ++ " > " ++ any)
2217+ -- [ Property "margin-right" xPx
2218+ -- , Property "margin-bottom" yPx
2219+ -- ]
2220+ , renderStyle
2221+ maybePseudo
2222+ (class ++ paragraph ++ " > " ++ left)
2223+ [ Property "margin-right" xPx
2224+ ]
2225+ , renderStyle
2226+ maybePseudo
2227+ (class ++ paragraph ++ " > " ++ right)
2228+ [ Property "margin-left" xPx
2229+ ]
2230+ , renderStyle
2231+ maybePseudo
2232+ (class ++ paragraph ++ "::after")
2233+ [ Property "content" "''"
2234+ , Property "display" "block"
2235+ , Property "height" "0"
2236+ , Property "width" "0"
2237+ , Property "margin-top" (String.fromInt (-1 * (y // 2)) ++ "px")
2238+ ]
2239+ , renderStyle
2240+ maybePseudo
2241+ (class ++ paragraph ++ "::before")
2242+ [ Property "content" "''"
2243+ , Property "display" "block"
2244+ , Property "height" "0"
2245+ , Property "width" "0"
2246+ , Property "margin-bottom" (String.fromInt (-1 * (y // 2)) ++ "px")
2247+ ]
2248+ ]
2249+
2250+ PaddingStyle cls top right bottom left ->
2251+ let
2252+ class =
2253+ "."
2254+ ++ cls
2255+ in
2256+ renderStyle
2257+ maybePseudo
2258+ class
2259+ [ Property "padding"
2260+ (String.fromInt top
2261+ ++ "px "
2262+ ++ String.fromInt right
2263+ ++ "px "
2264+ ++ String.fromInt bottom
2265+ ++ "px "
2266+ ++ String.fromInt left
2267+ ++ "px"
2268+ )
2269+ ]
2270+
2271+ GridTemplateStyle template ->
2272+ let
2273+ class =
2274+ ".grid-rows-"
2275+ ++ String.join "-" (List.map lengthClassName template.rows)
2276+ ++ "-cols-"
2277+ ++ String.join "-" (List.map lengthClassName template.columns)
2278+ ++ "-space-x-"
2279+ ++ lengthClassName (Tuple.first template.spacing)
2280+ ++ "-space-y-"
2281+ ++ lengthClassName (Tuple.second template.spacing)
2282+
2283+ ySpacing =
2284+ toGridLength (Tuple.second template.spacing)
2285+
2286+ xSpacing =
2287+ toGridLength (Tuple.first template.spacing)
2288+
2289+ toGridLength x =
2290+ toGridLengthHelper Nothing Nothing x
2291+
2292+ toGridLengthHelper minimum maximum x =
2293+ case x of
2294+ Px px ->
2295+ String.fromInt px ++ "px"
2296+
2297+ Content ->
2298+ case ( minimum, maximum ) of
2299+ ( Nothing, Nothing ) ->
2300+ "max-content"
2301+
2302+ ( Just minSize, Nothing ) ->
2303+ "minmax(" ++ String.fromInt minSize ++ "px, " ++ "max-content)"
2304+
2305+ ( Nothing, Just maxSize ) ->
2306+ "minmax(max-content, " ++ String.fromInt maxSize ++ "px)"
2307+
2308+ ( Just minSize, Just maxSize ) ->
2309+ "minmax(" ++ String.fromInt minSize ++ "px, " ++ String.fromInt maxSize ++ "px)"
2310+
2311+ Fill i ->
2312+ case ( minimum, maximum ) of
2313+ ( Nothing, Nothing ) ->
2314+ String.fromInt i ++ "fr"
2315+
2316+ ( Just minSize, Nothing ) ->
2317+ "minmax(" ++ String.fromInt minSize ++ "px, " ++ String.fromInt i ++ "fr" ++ "fr)"
2318+
2319+ ( Nothing, Just maxSize ) ->
2320+ "minmax(max-content, " ++ String.fromInt maxSize ++ "px)"
2321+
2322+ ( Just minSize, Just maxSize ) ->
2323+ "minmax(" ++ String.fromInt minSize ++ "px, " ++ String.fromInt maxSize ++ "px)"
2324+
2325+ Min m len ->
2326+ toGridLengthHelper (Just m) maximum len
2327+
2328+ Max m len ->
2329+ toGridLengthHelper minimum (Just m) len
2330+
2331+ msColumns =
2332+ template.columns
2333+ |> List.map toGridLength
2334+ |> String.join ySpacing
2335+ |> (\x -> "-ms-grid-columns: " ++ x ++ ";")
2336+
2337+ msRows =
2338+ template.columns
2339+ |> List.map toGridLength
2340+ |> String.join ySpacing
2341+ |> (\x -> "-ms-grid-rows: " ++ x ++ ";")
2342+
2343+ base =
2344+ class ++ "{" ++ msColumns ++ msRows ++ "}"
2345+
2346+ columns =
2347+ template.columns
2348+ |> List.map toGridLength
2349+ |> String.join " "
2350+ |> (\x -> "grid-template-columns: " ++ x ++ ";")
2351+
2352+ rows =
2353+ template.rows
2354+ |> List.map toGridLength
2355+ |> String.join " "
2356+ |> (\x -> "grid-template-rows: " ++ x ++ ";")
2357+
2358+ gapX =
2359+ "grid-column-gap:" ++ toGridLength (Tuple.first template.spacing) ++ ";"
2360+
2361+ gapY =
2362+ "grid-row-gap:" ++ toGridLength (Tuple.second template.spacing) ++ ";"
2363+
2364+ modernGrid =
2365+ class ++ "{" ++ columns ++ rows ++ gapX ++ gapY ++ "}"
2366+
2367+ supports =
2368+ "@supports (display:grid) {" ++ modernGrid ++ "}"
2369+ in
2370+ base ++ supports
2371+
2372+ GridPosition position ->
2373+ let
2374+ class =
2375+ ".grid-pos-"
2376+ ++ String.fromInt position.row
2377+ ++ "-"
2378+ ++ String.fromInt position.col
2379+ ++ "-"
2380+ ++ String.fromInt position.width
2381+ ++ "-"
2382+ ++ String.fromInt position.height
2383+
2384+ msPosition =
2385+ String.join " "
2386+ [ "-ms-grid-row: "
2387+ ++ String.fromInt position.row
2388+ ++ ";"
2389+ , "-ms-grid-row-span: "
2390+ ++ String.fromInt position.height
2391+ ++ ";"
2392+ , "-ms-grid-column: "
2393+ ++ String.fromInt position.col
2394+ ++ ";"
2395+ , "-ms-grid-column-span: "
2396+ ++ String.fromInt position.width
2397+ ++ ";"
2398+ ]
2399+
2400+ base =
2401+ class ++ "{" ++ msPosition ++ "}"
2402+
2403+ modernPosition =
2404+ String.join " "
2405+ [ "grid-row: "
2406+ ++ String.fromInt position.row
2407+ ++ " / "
2408+ ++ String.fromInt (position.row + position.height)
2409+ ++ ";"
2410+ , "grid-column: "
2411+ ++ String.fromInt position.col
2412+ ++ " / "
2413+ ++ String.fromInt (position.col + position.width)
2414+ ++ ";"
2415+ ]
2416+
2417+ modernGrid =
2418+ class ++ "{" ++ modernPosition ++ "}"
2419+
2420+ supports =
2421+ "@supports (display:grid) {" ++ modernGrid ++ "}"
2422+ in
2423+ base ++ supports
2424+
2425+ PseudoSelector class styles ->
2426+ let
2427+ renderPseudoRule style =
2428+ renderStyleRule style (Just class)
2429+ in
2430+ List.map renderPseudoRule styles
2431+ |> String.join " "
2432+
2433+ Transform transform ->
2434+ let
2435+ val =
2436+ transformValue transform
2437+
2438+ class =
2439+ transformClass transform
2440+ in
2441+ case ( class, val ) of
2442+ ( Just cls, Just v ) ->
2443+ renderStyle
2444+ maybePseudo
2445+ ("." ++ cls)
2446+ [ Property "transform"
2447+ v
2448+ ]
2449+
2450+ _ ->
2451+ ""
2452+
2453+ combine style rendered =
2454+ { rendered
2455+ | rules = rendered.rules ++ renderStyleRule style Nothing
2456+ , topLevel =
2457+ case renderTopLevels style of
2458+ Nothing ->
2459+ rendered.topLevel
2460+
2461+ Just topLevel ->
2462+ rendered.topLevel ++ topLevel
2463+ }
2464+ in
2465+ case List.foldl combine { rules = "", topLevel = "" } stylesheet of
2466+ { topLevel, rules } ->
2467+ topLevel ++ rules
2468+
2469+
2470+lengthClassName : Length -> String
2471+lengthClassName x =
2472+ case x of
2473+ Px px ->
2474+ String.fromInt px ++ "px"
2475+
2476+ Content ->
2477+ "auto"
2478+
2479+ Fill i ->
2480+ String.fromInt i ++ "fr"
2481+
2482+ Min min len ->
2483+ "min" ++ String.fromInt min ++ lengthClassName len
2484+
2485+ Max max len ->
2486+ "max" ++ String.fromInt max ++ lengthClassName len
2487+
2488+
2489+formatDropShadow shadow =
2490+ String.join " "
2491+ [ String.fromFloat (Tuple.first shadow.offset) ++ "px"
2492+ , String.fromFloat (Tuple.second shadow.offset) ++ "px"
2493+ , String.fromFloat shadow.blur ++ "px"
2494+ , formatColor shadow.color
2495+ ]
2496+
2497+
2498+formatTextShadow shadow =
2499+ String.join " "
2500+ [ String.fromFloat (Tuple.first shadow.offset) ++ "px"
2501+ , String.fromFloat (Tuple.second shadow.offset) ++ "px"
2502+ , String.fromFloat shadow.blur ++ "px"
2503+ , formatColor shadow.color
2504+ ]
2505+
2506+
2507+textShadowName shadow =
2508+ String.concat
2509+ [ "txt"
2510+ , String.fromFloat (Tuple.first shadow.offset) ++ "px"
2511+ , String.fromFloat (Tuple.second shadow.offset) ++ "px"
2512+ , String.fromFloat shadow.blur ++ "px"
2513+ , formatColorClass shadow.color
2514+ ]
2515+
2516+
2517+formatBoxShadow shadow =
2518+ String.join " " <|
2519+ List.filterMap identity
2520+ [ if shadow.inset then
2521+ Just "inset"
2522+ else
2523+ Nothing
2524+ , Just <| String.fromFloat (Tuple.first shadow.offset) ++ "px"
2525+ , Just <| String.fromFloat (Tuple.second shadow.offset) ++ "px"
2526+ , Just <| String.fromFloat shadow.blur ++ "px"
2527+ , Just <| String.fromFloat shadow.size ++ "px"
2528+ , Just <| formatColor shadow.color
2529+ ]
2530+
2531+
2532+boxShadowName shadow =
2533+ String.concat <|
2534+ [ if shadow.inset then
2535+ "box-inset"
2536+ else
2537+ "box-"
2538+ , String.fromFloat (Tuple.first shadow.offset) ++ "px"
2539+ , String.fromFloat (Tuple.second shadow.offset) ++ "px"
2540+ , String.fromFloat shadow.blur ++ "px"
2541+ , String.fromFloat shadow.size ++ "px"
2542+ , formatColorClass shadow.color
2543+ ]
2544+
2545+
2546+floatClass : Float -> String
2547+floatClass x =
2548+ String.fromInt (round (x * 255))
2549+
2550+
2551+formatColor : Color -> String
2552+formatColor (Rgba red green blue alpha) =
2553+ "rgba("
2554+ ++ String.fromInt (round (red * 255))
2555+ ++ ("," ++ String.fromInt (round (green * 255)))
2556+ ++ ("," ++ String.fromInt (round (blue * 255)))
2557+ ++ ("," ++ String.fromFloat alpha ++ ")")
2558+
2559+
2560+formatColorClass : Color -> String
2561+formatColorClass (Rgba red green blue alpha) =
2562+ floatClass red
2563+ ++ "-"
2564+ ++ floatClass green
2565+ ++ "-"
2566+ ++ floatClass blue
2567+ ++ "-"
2568+ ++ floatClass alpha
2569+
2570+
2571+spacingName x y =
2572+ "spacing-" ++ String.fromInt x ++ "-" ++ String.fromInt y
2573+
2574+
2575+paddingName top right bottom left =
2576+ "pad-"
2577+ ++ String.fromInt top
2578+ ++ "-"
2579+ ++ String.fromInt right
2580+ ++ "-"
2581+ ++ String.fromInt bottom
2582+ ++ "-"
2583+ ++ String.fromInt left
2584+
2585+
2586+getStyleName : Style -> String
2587+getStyleName style =
2588+ case style of
2589+ Shadows name _ ->
2590+ name
2591+
2592+ Transparency name o ->
2593+ name
2594+
2595+ Style class _ ->
2596+ class
2597+
2598+ FontFamily name _ ->
2599+ name
2600+
2601+ FontSize i ->
2602+ "font-size-" ++ String.fromInt i
2603+
2604+ Single class _ _ ->
2605+ class
2606+
2607+ Colored class _ _ ->
2608+ class
2609+
2610+ SpacingStyle cls x y ->
2611+ cls
2612+
2613+ PaddingStyle cls top right bottom left ->
2614+ cls
2615+
2616+ GridTemplateStyle template ->
2617+ "grid-rows-"
2618+ ++ String.join "-" (List.map lengthClassName template.rows)
2619+ ++ "-cols-"
2620+ ++ String.join "-" (List.map lengthClassName template.columns)
2621+ ++ "-space-x-"
2622+ ++ lengthClassName (Tuple.first template.spacing)
2623+ ++ "-space-y-"
2624+ ++ lengthClassName (Tuple.second template.spacing)
2625+
2626+ GridPosition pos ->
2627+ "gp grid-pos-"
2628+ ++ String.fromInt pos.row
2629+ ++ "-"
2630+ ++ String.fromInt pos.col
2631+ ++ "-"
2632+ ++ String.fromInt pos.width
2633+ ++ "-"
2634+ ++ String.fromInt pos.height
2635+
2636+ PseudoSelector selector subStyle ->
2637+ let
2638+ name =
2639+ case selector of
2640+ Focus ->
2641+ "fs"
2642+
2643+ Hover ->
2644+ "hv"
2645+
2646+ Active ->
2647+ "act"
2648+ in
2649+ List.map
2650+ (\sty ->
2651+ case getStyleName sty of
2652+ "" ->
2653+ ""
2654+
2655+ styleName ->
2656+ styleName ++ "-" ++ name
2657+ )
2658+ subStyle
2659+ |> String.join " "
2660+
2661+ Transform x ->
2662+ Maybe.withDefault "" (transformClass x)
2663+
2664+
2665+
2666+{- Constants -}
2667+
2668+
2669+asGrid : LayoutContext
2670+asGrid =
2671+ AsGrid
2672+
2673+
2674+asRow : LayoutContext
2675+asRow =
2676+ AsRow
2677+
2678+
2679+asColumn : LayoutContext
2680+asColumn =
2681+ AsColumn
2682+
2683+
2684+asEl : LayoutContext
2685+asEl =
2686+ AsEl
2687+
2688+
2689+asParagraph : LayoutContext
2690+asParagraph =
2691+ AsParagraph
2692+
2693+
2694+asTextColumn : LayoutContext
2695+asTextColumn =
2696+ AsTextColumn
2697+
2698+
2699+
2700+{- Mapping -}
2701+
2702+
2703+map : (msg -> msg1) -> Element msg -> Element msg1
2704+map fn el =
2705+ case el of
2706+ Styled styled ->
2707+ Styled
2708+ { styles = styled.styles
2709+ , html = \add context -> VirtualDom.map fn <| styled.html add context
2710+ }
2711+
2712+ Unstyled html ->
2713+ Unstyled (VirtualDom.map fn << html)
2714+
2715+ Text str ->
2716+ Text str
2717+
2718+ Empty ->
2719+ Empty
2720+
2721+
2722+mapAttr : (msg -> msg1) -> Attribute aligned msg -> Attribute aligned msg1
2723+mapAttr fn attr =
2724+ case attr of
2725+ NoAttribute ->
2726+ NoAttribute
2727+
2728+ Describe description ->
2729+ Describe description
2730+
2731+ AlignX x ->
2732+ AlignX x
2733+
2734+ AlignY y ->
2735+ AlignY y
2736+
2737+ Width x ->
2738+ Width x
2739+
2740+ Height x ->
2741+ Height x
2742+
2743+ Class x y ->
2744+ Class x y
2745+
2746+ StyleClass flag style ->
2747+ StyleClass flag style
2748+
2749+ Nearby location elem ->
2750+ Nearby location (map fn elem)
2751+
2752+ Attr htmlAttr ->
2753+ Attr (VirtualDom.mapAttribute fn htmlAttr)
2754+
2755+ TransformComponent fl trans ->
2756+ TransformComponent fl trans
2757+
2758+
2759+mapAttrFromStyle : (msg -> msg1) -> Attribute Never msg -> Attribute () msg1
2760+mapAttrFromStyle fn attr =
2761+ case attr of
2762+ NoAttribute ->
2763+ NoAttribute
2764+
2765+ Describe description ->
2766+ Describe description
2767+
2768+ AlignX x ->
2769+ AlignX x
2770+
2771+ AlignY y ->
2772+ AlignY y
2773+
2774+ Width x ->
2775+ Width x
2776+
2777+ Height x ->
2778+ Height x
2779+
2780+ -- invalidation key "border-color" as opposed to "border-color-10-10-10" that will be the key for the class
2781+ Class x y ->
2782+ Class x y
2783+
2784+ StyleClass flag style ->
2785+ StyleClass flag style
2786+
2787+ Nearby location elem ->
2788+ Nearby location (map fn elem)
2789+
2790+ Attr htmlAttr ->
2791+ Attr (VirtualDom.mapAttribute fn htmlAttr)
2792+
2793+ TransformComponent fl trans ->
2794+ TransformComponent fl trans
2795+
2796+
2797+unwrapDecorations : List (Attribute Never Never) -> List Style
2798+unwrapDecorations attrs =
2799+ case List.foldl unwrapDecsHelper ( [], Untransformed ) attrs of
2800+ ( styles, transform ) ->
2801+ Transform transform :: styles
2802+
2803+
2804+unwrapDecsHelper attr ( styles, trans ) =
2805+ case removeNever attr of
2806+ StyleClass _ style ->
2807+ ( style :: styles, trans )
2808+
2809+ TransformComponent flag component ->
2810+ ( styles, composeTransformation trans component )
2811+
2812+ _ ->
2813+ ( styles, trans )
2814+
2815+
2816+removeNever : Attribute Never Never -> Attribute () msg
2817+removeNever style =
2818+ mapAttrFromStyle Basics.never style
2819+
2820+
2821+tag : String -> Style -> Style
2822+tag label style =
2823+ case style of
2824+ Single class prop val ->
2825+ Single (label ++ "-" ++ class) prop val
2826+
2827+ Colored class prop val ->
2828+ Colored (label ++ "-" ++ class) prop val
2829+
2830+ Style class props ->
2831+ Style (label ++ "-" ++ class) props
2832+
2833+ Transparency class o ->
2834+ Transparency (label ++ "-" ++ class) o
2835+
2836+ x ->
2837+ x
2838+
2839+
2840+onlyStyles : Attribute aligned msg -> Maybe Style
2841+onlyStyles attr =
2842+ case attr of
2843+ StyleClass _ style ->
2844+ Just style
2845+
2846+ _ ->
2847+ Nothing
src/Internal/Style.elmnew file+1628
@@ -0,0 +1,1628 @@
1+module Internal.Style exposing (classes, dot, rules)
2+
3+{-| -}
4+
5+
6+type Class
7+ = Class String (List Rule)
8+
9+
10+type Rule
11+ = Prop String String
12+ | Child String (List Rule)
13+ | Supports ( String, String ) (List ( String, String ))
14+ | Descriptor String (List Rule)
15+ | Adjacent String (List Rule)
16+ | Batch (List Rule)
17+
18+
19+type StyleClasses
20+ = Root
21+ | Any
22+ | Single
23+ | Row
24+ | Column
25+ | Paragraph
26+ | Page
27+ | Text
28+ | Grid
29+ | Spacer
30+
31+
32+
33+-- lengths
34+
35+
36+alignments =
37+ [ Top
38+ , Bottom
39+ , Right
40+ , Left
41+ , CenterX
42+ , CenterY
43+ ]
44+
45+
46+type Location
47+ = Above
48+ | Below
49+ | OnRight
50+ | OnLeft
51+ | Within
52+ | Behind
53+
54+
55+locations =
56+ let
57+ loc =
58+ Above
59+
60+ _ =
61+ case loc of
62+ Above ->
63+ ()
64+
65+ Below ->
66+ ()
67+
68+ OnRight ->
69+ ()
70+
71+ OnLeft ->
72+ ()
73+
74+ Within ->
75+ ()
76+
77+ Behind ->
78+ ()
79+ in
80+ [ Above
81+ , Below
82+ , OnRight
83+ , OnLeft
84+ , Within
85+ , Behind
86+ ]
87+
88+
89+selfName desc =
90+ case desc of
91+ Self Top ->
92+ dot classes.alignTop
93+
94+ Self Bottom ->
95+ dot classes.alignBottom
96+
97+ Self Right ->
98+ dot classes.alignRight
99+
100+ Self Left ->
101+ dot classes.alignLeft
102+
103+ Self CenterX ->
104+ dot classes.alignCenterX
105+
106+ Self CenterY ->
107+ dot classes.alignCenterY
108+
109+
110+contentName desc =
111+ case desc of
112+ Content Top ->
113+ dot classes.contentTop
114+
115+ Content Bottom ->
116+ dot classes.contentBottom
117+
118+ Content Right ->
119+ dot classes.contentRight
120+
121+ Content Left ->
122+ dot classes.contentLeft
123+
124+ Content CenterX ->
125+ dot classes.contentCenterX
126+
127+ Content CenterY ->
128+ dot classes.contentCenterY
129+
130+
131+classes =
132+ { root = "ui"
133+ , any = "s"
134+ , single = "e"
135+ , row = "r"
136+ , column = "c"
137+ , page = "pg"
138+ , paragraph = "p"
139+ , text = "t"
140+ , grid = "g"
141+ , imageContainer = "ic"
142+ , wrapped = "wrp"
143+
144+ -- widhts/heights
145+ , widthFill = "wf"
146+ , widthContent = "wc"
147+ , widthExact = "we"
148+ , widthFillPortion = "wfp"
149+ , heightFill = "hf"
150+ , heightContent = "hc"
151+ , heightFillPortion = "hfp"
152+ , seButton = "sbt"
153+
154+ -- nearby elements
155+ , above = "a"
156+ , below = "b"
157+ , onRight = "or"
158+ , onLeft = "ol"
159+ , inFront = "fr"
160+ , behind = "bh"
161+ , hasBehind = "hbh"
162+
163+ -- alignments
164+ , alignTop = "at"
165+ , alignBottom = "ab"
166+ , alignRight = "ar"
167+ , alignLeft = "al"
168+ , alignCenterX = "cx"
169+ , alignCenterY = "cy"
170+ , alignedHorizontally = "ah"
171+ , alignedVertically = "av"
172+
173+ -- space evenly
174+ , spaceEvenly = "sev"
175+ , container = "ctr"
176+ , alignContainerRight = "acr"
177+ , alignContainerBottom = "acb"
178+ , alignContainerCenterX = "accx"
179+ , alignContainerCenterY = "accy"
180+
181+ -- content alignments
182+ , contentTop = "ct"
183+ , contentBottom = "cb"
184+ , contentRight = "cr"
185+ , contentLeft = "cl"
186+ , contentCenterX = "ccx"
187+ , contentCenterY = "ccy"
188+
189+ -- selection
190+ , noTextSelection = "notxt"
191+ , cursorPointer = "cptr"
192+ , cursorText = "ctxt"
193+
194+ -- pointer events
195+ , passPointerEvents = "ppe"
196+ , capturePointerEvents = "cpe"
197+ , transparent = "clr"
198+ , opaque = "oq"
199+ , overflowHidden = "oh"
200+
201+ -- special state classes
202+ , hover = "hv"
203+
204+ -- , hoverOpaque = "hover-opaque"
205+ , focus = "fcs"
206+
207+ -- , focusOpaque = "focus-opaque"
208+ , active = "atv"
209+
210+ -- , activeOpaque = "active-opaque"
211+ --scrollbars
212+ , scrollbars = "sb"
213+ , scrollbarsX = "sbx"
214+ , scrollbarsY = "sby"
215+ , clip = "cp"
216+ , clipX = "cpx"
217+ , clipY = "cpy"
218+
219+ -- borders
220+ , borderNone = "bn"
221+ , borderDashed = "bd"
222+ , borderDotted = "bdt"
223+ , borderSolid = "bs"
224+
225+ -- text weight
226+ , textThin = "w1"
227+ , textExtraLight = "w2"
228+ , textLight = "w3"
229+ , textNormalWeight = "w4"
230+ , textMedium = "w5"
231+ , textSemiBold = "w6"
232+ , bold = "w7"
233+ , textExtraBold = "w8"
234+ , textHeavy = "w9"
235+ , italic = "i"
236+ , strike = "sk"
237+ , underline = "u"
238+ , textUnitalicized = "tun"
239+
240+ -- text alignment
241+ , textJustify = "tj"
242+ , textJustifyAll = "tja"
243+ , textCenter = "tc"
244+ , textRight = "tr"
245+ , textLeft = "tl"
246+ , transition = "ts"
247+
248+ -- inputText
249+ , inputText = "it"
250+ , inputMultiline = "iml"
251+ }
252+
253+
254+{-| The indulgent unicode character version.
255+-}
256+unicode =
257+ { root = "style-elements"
258+ , any = "s"
259+ , single = "e"
260+ , row = "⋯"
261+ , column = "⋮"
262+ , page = "🗏"
263+ , paragraph = "p"
264+ , text = "text"
265+ , grid = "▦"
266+
267+ -- widhts/heights
268+ , widthFill = "↔"
269+ , widthContent = "width-content"
270+ , widthExact = "width-exact"
271+ , heightFill = "↕"
272+ , heightContent = "height-content"
273+ , heightExact = "height-exact"
274+
275+ -- nearby elements
276+ , above = "above"
277+ , below = "below"
278+ , onRight = "on-right"
279+ , onLeft = "on-left"
280+ , inFront = "infront"
281+ , behind = "behind"
282+
283+ -- alignments
284+ , alignTop = "⤒"
285+ , alignBottom = "⤓"
286+ , alignRight = "⇥"
287+ , alignLeft = "⇤"
288+ , alignCenterX = "self-center-x"
289+ , alignCenterY = "self-center-y"
290+
291+ -- space evenly
292+ , spaceEvenly = "space-evenly"
293+ , container = "container"
294+
295+ -- content alignments
296+ , contentTop = "content-top"
297+ , contentBottom = "content-bottom"
298+ , contentRight = "content-right"
299+ , contentLeft = "content-left"
300+ , contentCenterX = "content-center-x"
301+ , contentCenterY = "content-center-y"
302+
303+ -- selection
304+ , noTextSelection = "no-text-selection"
305+ , cursorPointer = "cursor-pointer"
306+ , cursorText = "cursor-text"
307+
308+ -- pointer events
309+ , passPointerEvents = "pass-pointer-events"
310+ , capturePointerEvents = "capture-pointer-events"
311+ , transparent = "transparent"
312+ , opaque = "opaque"
313+
314+ --scrollbars
315+ , scrollbars = "scrollbars"
316+ , scrollbarsX = "scrollbars-x"
317+ , scrollbarsY = "scrollbars-y"
318+ , clip = "✂"
319+ , clipX = "✂x"
320+ , clipY = "✂y"
321+
322+ -- borders
323+ , borderNone = "border-none"
324+ , borderDashed = "border-dashed"
325+ , borderDotted = "border-dotted"
326+ , borderSolid = "border-solid"
327+
328+ -- text weight
329+ , textThin = "text-thin"
330+ , textExtraLight = "text-extra-light"
331+ , textLight = "text-light"
332+ , textNormalWeight = "text-normal-weight"
333+ , textMedium = "text-medium"
334+ , textSemiBold = "text-semi-bold"
335+ , bold = "bold"
336+ , textExtraBold = "text-extra-bold"
337+ , textHeavy = "text-heavy"
338+ , italic = "italic"
339+ , strike = "strike"
340+ , underline = "underline"
341+ , textUnitalicized = "text-unitalicized"
342+
343+ -- text alignment
344+ , textJustify = "text-justify"
345+ , textJustifyAll = "text-justify-all"
346+ , textCenter = "text-center"
347+ , textRight = "text-right"
348+ , textLeft = "text-left"
349+ }
350+
351+
352+single =
353+ { root = "z"
354+ , any = "s"
355+ , single = "e"
356+ , row = "r"
357+ , column = "c"
358+ , page = "l"
359+ , paragraph = "p"
360+ , text = "t"
361+ , grid = "g"
362+
363+ -- widhts/heights
364+ , widthFill = "↔"
365+ , widthContent = "wc"
366+ , widthExact = "w"
367+ , heightFill = "↕"
368+ , heightContent = "hc"
369+ , heightExact = "h"
370+
371+ -- nearby elements
372+ , above = "o"
373+ , below = "u"
374+ , onRight = "r"
375+ , onLeft = "l"
376+ , inFront = "f"
377+ , behind = "b"
378+
379+ -- alignments
380+ , alignTop = "⤒"
381+ , alignBottom = "⤓"
382+ , alignRight = "⇥"
383+ , alignLeft = "⇤"
384+ , alignCenterX = "self-center-x"
385+ , alignCenterY = "self-center-y"
386+
387+ -- space evenly
388+ , spaceEvenly = "space-evenly"
389+ , container = "container"
390+
391+ -- content alignments
392+ , contentTop = "c⤒"
393+ , contentBottom = "c⤓"
394+ , contentRight = "c⇥"
395+ , contentLeft = "c⇤"
396+ , contentCenterX = "content-center-x"
397+ , contentCenterY = "content-center-y"
398+
399+ -- selection
400+ , noTextSelection = "no-text-selection"
401+ , cursorPointer = "cursor-pointer"
402+ , cursorText = "cursor-text"
403+
404+ -- pointer events
405+ , passPointerEvents = "pass-pointer-events"
406+ , capturePointerEvents = "capture-pointer-events"
407+ , transparent = "transparent"
408+ , opaque = "opaque"
409+
410+ --scrollbars
411+ , scrollbars = "scrollbars"
412+ , scrollbarsX = "scrollbars-x"
413+ , scrollbarsY = "scrollbars-y"
414+ , clip = "✂"
415+ , clipX = "✂x"
416+ , clipY = "✂y"
417+
418+ -- borders
419+ , borderNone = "border-none"
420+ , borderDashed = "border-dashed"
421+ , borderDotted = "border-dotted"
422+ , borderSolid = "border-solid"
423+
424+ -- text weight
425+ , textThin = "text-thin"
426+ , textExtraLight = "text-extra-light"
427+ , textLight = "text-light"
428+ , textNormalWeight = "text-normal-weight"
429+ , textMedium = "text-medium"
430+ , textSemiBold = "text-semi-bold"
431+ , bold = "b"
432+ , textExtraBold = "text-extra-bold"
433+ , textHeavy = "text-heavy"
434+ , italic = "i"
435+ , strike = "-"
436+ , underline = "u"
437+ , textUnitalicized = "text-unitalicized"
438+
439+ -- text alignment
440+ , textJustify = "text-justify"
441+ , textJustifyAll = "text-justify-all"
442+ , textCenter = "text-center"
443+ , textRight = "text-right"
444+ , textLeft = "text-left"
445+ }
446+
447+
448+describeAlignment values =
449+ let
450+ createDescription alignment =
451+ let
452+ ( content, indiv ) =
453+ values alignment
454+ in
455+ [ Descriptor (contentName (Content alignment)) <|
456+ content
457+ , Child (dot classes.any)
458+ [ Descriptor (selfName <| Self alignment) indiv
459+ ]
460+ ]
461+ in
462+ Batch <|
463+ List.concatMap createDescription alignments
464+
465+
466+gridAlignments values =
467+ let
468+ createDescription alignment =
469+ [ Child (dot classes.any)
470+ [ Descriptor (selfName <| Self alignment) (values alignment)
471+ ]
472+ ]
473+ in
474+ Batch <|
475+ List.concatMap createDescription alignments
476+
477+
478+type SelfDescriptor
479+ = Self Alignment
480+
481+
482+type ContentDescriptor
483+ = Content Alignment
484+
485+
486+type Alignment
487+ = Top
488+ | Bottom
489+ | Right
490+ | Left
491+ | CenterX
492+ | CenterY
493+
494+
495+type Intermediate
496+ = Intermediate
497+ { selector : String
498+ , props : List ( String, String )
499+ , closing : String
500+ , others : List Intermediate
501+ }
502+
503+
504+emptyIntermediate : String -> String -> Intermediate
505+emptyIntermediate selector closing =
506+ Intermediate
507+ { selector = selector
508+ , props = []
509+ , closing = closing
510+ , others = []
511+ }
512+
513+
514+renderRules : Intermediate -> List Rule -> Intermediate
515+renderRules (Intermediate parent) rulesToRender =
516+ let
517+ generateIntermediates rule rendered =
518+ case rule of
519+ Prop name val ->
520+ { rendered | props = ( name, val ) :: rendered.props }
521+
522+ Supports ( prop, value ) props ->
523+ { rendered
524+ | others =
525+ Intermediate
526+ { selector = "@supports (" ++ prop ++ ":" ++ value ++ ") {" ++ parent.selector
527+ , props = props
528+ , closing = "\n}"
529+ , others = []
530+ }
531+ :: rendered.others
532+ }
533+
534+ Adjacent selector adjRules ->
535+ { rendered
536+ | others =
537+ renderRules
538+ (emptyIntermediate (parent.selector ++ " + " ++ selector) "")
539+ adjRules
540+ :: rendered.others
541+ }
542+
543+ Child child childRules ->
544+ { rendered
545+ | others =
546+ renderRules
547+ (emptyIntermediate (parent.selector ++ " > " ++ child) "")
548+ childRules
549+ :: rendered.others
550+ }
551+
552+ Descriptor descriptor descriptorRules ->
553+ { rendered
554+ | others =
555+ renderRules
556+ (emptyIntermediate (parent.selector ++ descriptor) "")
557+ descriptorRules
558+ :: rendered.others
559+ }
560+
561+ Batch batched ->
562+ { rendered
563+ | others =
564+ renderRules (emptyIntermediate parent.selector "") batched
565+ :: rendered.others
566+ }
567+ in
568+ Intermediate <| List.foldr generateIntermediates parent rulesToRender
569+
570+
571+render : List Class -> String
572+render classNames =
573+ let
574+ renderValues values =
575+ values
576+ |> List.map (\( x, y ) -> " " ++ x ++ ": " ++ y ++ ";")
577+ |> String.join "\n"
578+
579+ renderClass rule =
580+ case rule.props of
581+ [] ->
582+ ""
583+
584+ _ ->
585+ rule.selector ++ " {\n" ++ renderValues rule.props ++ rule.closing ++ "\n}"
586+
587+ renderIntermediate (Intermediate rule) =
588+ renderClass rule
589+ ++ String.join "\n" (List.map renderIntermediate rule.others)
590+ in
591+ classNames
592+ |> List.foldr
593+ (\(Class name styleRules) existing ->
594+ renderRules (emptyIntermediate name "") styleRules :: existing
595+ )
596+ []
597+ |> List.map renderIntermediate
598+ |> String.join "\n"
599+
600+
601+renderCompact : List Class -> String
602+renderCompact styleClasses =
603+ let
604+ renderValues values =
605+ values
606+ |> List.map (\( x, y ) -> x ++ ":" ++ y ++ ";")
607+ |> String.concat
608+
609+ renderClass rule =
610+ case rule.props of
611+ [] ->
612+ ""
613+
614+ _ ->
615+ rule.selector ++ "{" ++ renderValues rule.props ++ rule.closing ++ "}"
616+
617+ renderIntermediate (Intermediate rule) =
618+ renderClass rule
619+ ++ String.concat (List.map renderIntermediate rule.others)
620+ in
621+ styleClasses
622+ |> List.foldr
623+ (\(Class name styleRules) existing ->
624+ renderRules (emptyIntermediate name "") styleRules :: existing
625+ )
626+ []
627+ |> List.map renderIntermediate
628+ |> String.concat
629+
630+
631+viewportRules : String
632+viewportRules =
633+ """html, body {
634+ height: 100%;
635+ width: 100%;
636+} """ ++ rules
637+
638+
639+describeText : String -> List Rule -> Rule
640+describeText cls props =
641+ Descriptor cls
642+ (List.map makeImportant props
643+ ++ [ Child ".text"
644+ props
645+ , Child ".el"
646+ props
647+ , Child ".el > .text"
648+ props
649+ ]
650+ )
651+
652+
653+makeImportant : Rule -> Rule
654+makeImportant rule =
655+ case rule of
656+ Prop name prop ->
657+ Prop name (prop ++ " !important")
658+
659+ _ ->
660+ rule
661+
662+
663+dot c =
664+ "." ++ c
665+
666+
667+overrides =
668+ """@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {"""
669+ ++ dot classes.any
670+ ++ dot classes.row
671+ ++ " > "
672+ ++ dot classes.any
673+ ++ " { flex-basis: auto !important; } "
674+ ++ dot classes.any
675+ ++ dot classes.row
676+ ++ " > "
677+ ++ dot classes.any
678+ ++ dot classes.container
679+ ++ " { flex-basis: auto !important; }}"
680+ ++ sliderOverrides
681+ ++ explainer
682+
683+
684+sliderOverrides =
685+ """
686+
687+/* General Input Reset */
688+input[type=range] {
689+ -webkit-appearance: none; /* Hides the slider so that custom slider can be made */
690+ /* width: 100%; Specific width is required for Firefox. */
691+ background: transparent; /* Otherwise white in Chrome */
692+ position:absolute;
693+ left:0;
694+ top:0;
695+ z-index:10;
696+ width: 100%;
697+ outline: dashed 1px;
698+ height: 100%;
699+ opacity: 0;
700+}
701+
702+/* Hide all syling for track */
703+input[type=range]::-moz-range-track {
704+ background: transparent;
705+ cursor: pointer;
706+}
707+input[type=range]::-ms-track {
708+ background: transparent;
709+ cursor: pointer;
710+}
711+input[type=range]::-webkit-slider-runnable-track {
712+ background: transparent;
713+ cursor: pointer;
714+}
715+
716+/* Thumbs */
717+input[type=range]::-webkit-slider-thumb {
718+ -webkit-appearance: none;
719+ opacity: 0.5;
720+ width: 80px;
721+ height: 80px;
722+ background-color: black;
723+ border:none;
724+ border-radius: 5px;
725+}
726+input[type=range]::-moz-range-thumb {
727+ opacity: 0.5;
728+ width: 80px;
729+ height: 80px;
730+ background-color: black;
731+ border:none;
732+ border-radius: 5px;
733+}
734+input[type=range]::-ms-thumb {
735+ opacity: 0.5;
736+ width: 80px;
737+ height: 80px;
738+ background-color: black;
739+ border:none;
740+ border-radius: 5px;
741+}
742+input[type=range][orient=vertical]{
743+ writing-mode: bt-lr; /* IE */
744+ -webkit-appearance: slider-vertical; /* WebKit */
745+}
746+"""
747+
748+
749+explainer =
750+ """
751+.explain {
752+ border: 6px solid rgb(174, 121, 15) !important;
753+}
754+.explain > .""" ++ classes.any ++ """ {
755+ border: 4px dashed rgb(0, 151, 167) !important;
756+}
757+
758+.ctr {
759+ border: none !important;
760+}
761+.explain > .ctr > .""" ++ classes.any ++ """ {
762+ border: 4px dashed rgb(0, 151, 167) !important;
763+}
764+
765+"""
766+
767+
768+commonValues =
769+ List.concat
770+ [ List.map
771+ (\x ->
772+ Class (".border-" ++ String.fromInt x) [ Prop "border-width" (String.fromInt x ++ "px") ]
773+ )
774+ (List.range 0 6)
775+ , List.map
776+ (\i ->
777+ Class (".font-size-" ++ String.fromInt i) [ Prop "font-size" (String.fromInt i ++ "px") ]
778+ )
779+ (List.range 8 32)
780+ , List.map
781+ (\i ->
782+ Class (".p-" ++ String.fromInt i) [ Prop "padding" (String.fromInt i ++ "px") ]
783+ )
784+ (List.range 0 24)
785+ ]
786+
787+
788+rules : String
789+rules =
790+ overrides
791+ ++ renderCompact (baseSheet ++ commonValues)
792+
793+
794+baseSheet =
795+ [ Class "html,body"
796+ [ Prop "height" "100%"
797+ , Prop "padding" "0"
798+ , Prop "margin" "0"
799+ ]
800+ , Class (dot classes.any ++ dot classes.single ++ dot classes.imageContainer)
801+ [ Prop "display" "block"
802+ ]
803+ , Class (dot classes.any ++ ":focus")
804+ [ Prop "outline" "none"
805+ ]
806+ , Class (dot classes.root)
807+ [ Prop "width" "100%"
808+ , Prop "height" "auto"
809+ , Prop "min-height" "100%"
810+ , Prop "z-index" "0"
811+ , Descriptor
812+ (dot classes.any
813+ -- ++ dot classes.single
814+ ++ dot classes.heightFill
815+ )
816+ [ Prop "height" "100%"
817+ , Child (dot classes.heightFill)
818+ [ Prop "height" "100%"
819+ ]
820+ ]
821+ , Child (dot classes.inFront)
822+ [ Descriptor (dot classes.any)
823+ [ Prop "position" "fixed"
824+ ]
825+ ]
826+ ]
827+ , Class (dot classes.any)
828+ [ Prop "position" "relative"
829+ , Prop "border" "none"
830+ , Prop "flex-shrink" "0"
831+ , Prop "display" "flex"
832+ , Prop "flex-direction" "row"
833+ , Prop "flex-basis" "auto"
834+ , Prop "resize" "none"
835+
836+ -- , Prop "flex-basis" "0%"
837+ , Prop "box-sizing" "border-box"
838+ , Prop "margin" "0"
839+ , Prop "padding" "0"
840+ , Prop "border-width" "0"
841+ , Prop "border-style" "solid"
842+
843+ -- inheritable font properties
844+ , Prop "font-size" "inherit"
845+ , Prop "color" "inherit"
846+ , Prop "font-family" "inherit"
847+
848+ -- , Prop "line-height" "inherit"
849+ , Prop "line-height" "1"
850+ , Prop "font-weight" "inherit"
851+
852+ -- Text decoration is *mandatorily inherited* in the css spec.
853+ -- There's no way to change this. How crazy is that?
854+ , Prop "text-decoration" "none"
855+ , Prop "font-style" "inherit"
856+ , Descriptor (dot classes.wrapped)
857+ [ Prop "flex-wrap" "wrap"
858+ ]
859+ , Descriptor (dot classes.noTextSelection)
860+ [ Prop "-moz-user-select" "none"
861+ , Prop "-webkit-user-select" "none"
862+ , Prop "-ms-user-select" "none"
863+ , Prop "user-select" "none"
864+ ]
865+ , Descriptor (dot classes.cursorPointer)
866+ [ Prop "cursor" "pointer"
867+ ]
868+ , Descriptor (dot classes.cursorText)
869+ [ Prop "cursor" "text"
870+ ]
871+ , Descriptor (dot classes.passPointerEvents)
872+ [ Prop "pointer-events" "none !important"
873+ ]
874+ , Descriptor (dot classes.capturePointerEvents)
875+ [ Prop "pointer-events" "auto !important"
876+ ]
877+ , Descriptor (dot classes.transparent)
878+ [ Prop "opacity" "0"
879+ ]
880+ , Descriptor (dot classes.opaque)
881+ [ Prop "opacity" "1"
882+ ]
883+ , Descriptor (dot (classes.hover ++ classes.transparent) ++ ":hover")
884+ [ Prop "opacity" "0"
885+ ]
886+ , Descriptor (dot (classes.hover ++ classes.opaque) ++ ":hover")
887+ [ Prop "opacity" "1"
888+ ]
889+ , Descriptor (dot (classes.focus ++ classes.transparent) ++ ":focus")
890+ [ Prop "opacity" "0"
891+ ]
892+ , Descriptor (dot (classes.focus ++ classes.opaque) ++ ":focus")
893+ [ Prop "opacity" "1"
894+ ]
895+ , Descriptor (dot (classes.active ++ classes.transparent) ++ ":active")
896+ [ Prop "opacity" "0"
897+ ]
898+ , Descriptor (dot (classes.active ++ classes.opaque) ++ ":active")
899+ [ Prop "opacity" "1"
900+ ]
901+ , Descriptor (dot classes.transition)
902+ [ Prop "transition"
903+ (String.join ", " <|
904+ List.map (\x -> x ++ " 160ms")
905+ [ "transform"
906+ , "opacity"
907+ , "filter"
908+ , "background-color"
909+ , "color"
910+ , "font-size"
911+ ]
912+ )
913+ ]
914+ , Descriptor (dot classes.scrollbars)
915+ [ Prop "overflow" "auto"
916+ , Prop "flex-shrink" "1"
917+ ]
918+ , Descriptor (dot classes.scrollbarsX)
919+ [ Prop "overflow-x" "auto"
920+ , Descriptor (dot classes.row)
921+ [ Prop "flex-shrink" "1"
922+ ]
923+ ]
924+ , Descriptor (dot classes.scrollbarsY)
925+ [ Prop "overflow-y" "auto"
926+ , Descriptor (dot classes.column)
927+ [ Prop "flex-shrink" "1"
928+ ]
929+ , Descriptor (dot classes.single)
930+ [ Prop "flex-shrink" "1"
931+ ]
932+ ]
933+ , Descriptor (dot classes.clip)
934+ [ Prop "overflow" "hidden"
935+ ]
936+ , Descriptor (dot classes.clipX)
937+ [ Prop "overflow-x" "hidden"
938+ ]
939+ , Descriptor (dot classes.clipY)
940+ [ Prop "overflow-y" "hidden"
941+ ]
942+ , Descriptor (dot classes.widthContent)
943+ [ Prop "width" "auto"
944+ ]
945+ , Descriptor (dot classes.borderNone)
946+ [ Prop "border-width" "0"
947+ ]
948+ , Descriptor (dot classes.borderDashed)
949+ [ Prop "border-style" "dashed"
950+ ]
951+ , Descriptor (dot classes.borderDotted)
952+ [ Prop "border-style" "dotted"
953+ ]
954+ , Descriptor (dot classes.borderSolid)
955+ [ Prop "border-style" "solid"
956+ ]
957+ , Descriptor (dot classes.text)
958+ [ Prop "white-space" "pre"
959+ , Prop "display" "inline-block"
960+ ]
961+ , Descriptor (dot classes.inputText)
962+ -- chrome and safari have a minimum recognized line height for text input of 1.05
963+ -- If it's 1, it bumps up to something like 1.2
964+ [ Prop "line-height" "1.05"
965+ ]
966+ , Descriptor (dot classes.single)
967+ [ Prop "display" "flex"
968+ , Prop "flex-direction" "column"
969+ , Prop "white-space" "pre"
970+ , Descriptor (dot classes.hasBehind)
971+ [ Prop "z-index" "0"
972+ , Child (dot classes.behind)
973+ [ Prop "z-index" "-1"
974+ ]
975+ ]
976+ , Descriptor (dot classes.seButton)
977+ -- Special default for text in a button.
978+ -- This is overridden is they put the text inside an `el`
979+ [ Child (dot classes.text)
980+ [ Descriptor (dot classes.heightFill)
981+ [ Prop "flex-grow" "0"
982+ ]
983+ , Descriptor (dot classes.widthFill)
984+ [ Prop "align-self" "auto !important"
985+ ]
986+ ]
987+ ]
988+ , Child (dot classes.heightContent)
989+ [ Prop "height" "auto"
990+ ]
991+ , Child (dot classes.heightFill)
992+ [ Prop "flex-grow" "100000"
993+ ]
994+ , Child (dot classes.widthFill)
995+ [ -- alignLeft, alignRight, centerX are overridden by width.
996+ -- Prop "align-self" "stretch !important"
997+ Prop "width" "100%"
998+ ]
999+ , Child (dot classes.widthContent)
1000+ [ Prop "align-self" "flex-start"
1001+ ]
1002+
1003+ -- , Child (dot classes.widthFill)
1004+ -- [ Prop "align-self" "stretch"
1005+ -- , Descriptor (dot classes.alignedHorizontally)
1006+ -- [ Prop "width" "100%"
1007+ -- ]
1008+ -- ]
1009+ , describeAlignment <|
1010+ \alignment ->
1011+ case alignment of
1012+ Top ->
1013+ ( [ Prop "justify-content" "flex-start" ]
1014+ , [ Prop "margin-bottom" "auto !important"
1015+ , Prop "margin-top" "0 !important"
1016+ ]
1017+ )
1018+
1019+ Bottom ->
1020+ ( [ Prop "justify-content" "flex-end" ]
1021+ , [ Prop "margin-top" "auto !important"
1022+ , Prop "margin-bottom" "0 !important"
1023+ ]
1024+ )
1025+
1026+ Right ->
1027+ ( [ Prop "align-items" "flex-end" ]
1028+ , [ Prop "align-self" "flex-end" ]
1029+ )
1030+
1031+ Left ->
1032+ ( [ Prop "align-items" "flex-start" ]
1033+ , [ Prop "align-self" "flex-start" ]
1034+ )
1035+
1036+ CenterX ->
1037+ ( [ Prop "align-items" "center" ]
1038+ , [ Prop "align-self" "center"
1039+ ]
1040+ )
1041+
1042+ CenterY ->
1043+ ( [ -- Prop "justify-content" "center"
1044+ Child (dot classes.any)
1045+ [ Prop "margin-top" "auto"
1046+ , Prop "margin-bottom" "auto"
1047+ ]
1048+ ]
1049+ , [ Prop "margin-top" "auto !important"
1050+ , Prop "margin-bottom" "auto !important"
1051+ ]
1052+ )
1053+ ]
1054+ , Descriptor (dot classes.row)
1055+ [ Prop "display" "flex"
1056+ , Prop "flex-direction" "row"
1057+ , Child (dot classes.any)
1058+ [ Prop "flex-basis" "0%"
1059+ , Descriptor (dot classes.widthExact)
1060+ [ Prop "flex-basis" "auto"
1061+ ]
1062+ ]
1063+ , Child (dot classes.heightFill)
1064+ [ --Prop "height" "100%"
1065+ -- alignTop, centerY, and alignBottom need to be disabled
1066+ Prop "align-self" "stretch !important"
1067+ ]
1068+ , Child (dot classes.heightFillPortion)
1069+ [ --Prop "height" "100%"
1070+ -- alignTop, centerY, and alignBottom need to be disabled
1071+ Prop "align-self" "stretch !important"
1072+ ]
1073+
1074+ -- TODO:: This may be necessary..should it move to classes.heightFIll?
1075+ -- , Child (dot classes.heightFillBetween)
1076+ -- [ Prop "align-self" "stretch"
1077+ -- , Descriptor ".aligned-vertically"
1078+ -- [ Prop "height" "100%"
1079+ -- ]
1080+ -- ]
1081+ , Child (dot classes.widthFill)
1082+ [ Prop "flex-grow" "100000"
1083+ ]
1084+ , Child (dot classes.container)
1085+ [ Prop "flex-grow" "0"
1086+ , Prop "flex-basis" "auto"
1087+ , Prop "align-self" "stretch"
1088+ ]
1089+
1090+ -- , Child "alignLeft:last-of-type.align-container-left"
1091+ -- [ Prop "flex-grow" "1"
1092+ -- ]
1093+ -- alignRight -> <u>
1094+ --centerX -> <s>
1095+ , Child ("u:first-of-type." ++ classes.alignContainerRight)
1096+ [ Prop "flex-grow" "1"
1097+ ]
1098+
1099+ -- first center y
1100+ , Child ("s:first-of-type." ++ classes.alignContainerCenterX)
1101+ [ Prop "flex-grow" "1"
1102+ , Child (dot classes.alignCenterX)
1103+ [ Prop "margin-left" "auto !important"
1104+ ]
1105+ ]
1106+ , Child ("s:last-of-type." ++ classes.alignContainerCenterX)
1107+ [ Prop "flex-grow" "1"
1108+ , Child (dot classes.alignCenterX)
1109+ [ Prop "margin-right" "auto !important"
1110+ ]
1111+ ]
1112+
1113+ -- lonley centerX
1114+ , Child ("s:only-of-type." ++ classes.alignContainerCenterX)
1115+ [ Prop "flex-grow" "1"
1116+ , Child (dot classes.alignCenterY)
1117+ [ Prop "margin-top" "auto !important"
1118+ , Prop "margin-bottom" "auto !important"
1119+ ]
1120+ ]
1121+
1122+ -- alignBottom's after a centerX should not grow
1123+ , Child
1124+ ("s:last-of-type." ++ classes.alignContainerCenterX ++ " ~ u")
1125+ [ Prop "flex-grow" "0"
1126+ ]
1127+
1128+ -- centerX's after an alignBottom should be ignored
1129+ , Child ("u:first-of-type." ++ classes.alignContainerRight ++ " ~ s." ++ classes.alignContainerCenterX)
1130+ -- Bottom alignment always overrides center alignment
1131+ [ Prop "flex-grow" "0"
1132+ ]
1133+ , describeAlignment <|
1134+ \alignment ->
1135+ case alignment of
1136+ Top ->
1137+ ( [ Prop "align-items" "flex-start" ]
1138+ , [ Prop "align-self" "flex-start"
1139+ ]
1140+ )
1141+
1142+ Bottom ->
1143+ ( [ Prop "align-items" "flex-end" ]
1144+ , [ Prop "align-self" "flex-end"
1145+ ]
1146+ )
1147+
1148+ Right ->
1149+ ( [ Prop "justify-content" "flex-end"
1150+ ]
1151+ , []
1152+ )
1153+
1154+ Left ->
1155+ ( [ Prop "justify-content" "flex-start"
1156+ ]
1157+ , []
1158+ )
1159+
1160+ CenterX ->
1161+ ( [ Prop "justify-content" "center"
1162+ ]
1163+ , []
1164+ )
1165+
1166+ CenterY ->
1167+ ( [ Prop "align-items" "center" ]
1168+ , [ Prop "align-self" "center"
1169+ ]
1170+ )
1171+
1172+ -- Must be below the alignment rules or else it interferes
1173+ , Descriptor (dot classes.spaceEvenly)
1174+ [ Prop "justify-content" "space-between"
1175+ ]
1176+ ]
1177+ , Descriptor (dot classes.column)
1178+ [ Prop "display" "flex"
1179+ , Prop "flex-direction" "column"
1180+ , Child (dot classes.heightFill)
1181+ [ Prop "flex-grow" "100000"
1182+ ]
1183+ , Child (dot classes.widthFill)
1184+ [ -- alignLeft, alignRight, centerX need to be disabled
1185+ -- Prop "align-self" "stretch !important"
1186+ Prop "width" "100%"
1187+ ]
1188+ , Child (dot classes.widthFillPortion)
1189+ [ -- alignLeft, alignRight, centerX need to be disabled
1190+ -- Prop "align-self" "stretch !important"
1191+ Prop "width" "100%"
1192+ ]
1193+
1194+ -- TODO:: THIs might be necessary, maybe it should move to widthFill?
1195+ -- , Child (dot classes.widthFill)
1196+ -- [ Prop "align-self" "stretch"
1197+ -- , Descriptor (dot classes.alignedHorizontally)
1198+ -- [ Prop "width" "100%"
1199+ -- ]
1200+ -- ]
1201+ , Child (dot classes.widthContent)
1202+ [ Prop "align-self" "flex-start"
1203+ ]
1204+
1205+ -- , Child "alignTop:last-of-type.align-container-top"
1206+ -- [ Prop "flex-grow" "1"
1207+ -- ]
1208+ , Child ("u:first-of-type." ++ classes.alignContainerBottom)
1209+ [ Prop "flex-grow" "1"
1210+ ]
1211+
1212+ -- centerY -> <s>
1213+ -- alignBottom -> <u>
1214+ -- first center y
1215+ , Child ("s:first-of-type." ++ classes.alignContainerCenterY)
1216+ [ Prop "flex-grow" "1"
1217+ , Child (dot classes.alignCenterY)
1218+ [ Prop "margin-top" "auto !important"
1219+ , Prop "margin-bottom" "0 !important"
1220+ ]
1221+ ]
1222+ , Child ("s:last-of-type." ++ classes.alignContainerCenterY)
1223+ [ Prop "flex-grow" "1"
1224+ , Child (dot classes.alignCenterY)
1225+ [ Prop "margin-bottom" "auto !important"
1226+ , Prop "margin-top" "0 !important"
1227+ ]
1228+ ]
1229+
1230+ -- lonley centerY
1231+ , Child ("s:only-of-type." ++ classes.alignContainerCenterY)
1232+ [ Prop "flex-grow" "1"
1233+ , Child (dot classes.alignCenterY)
1234+ [ Prop "margin-top" "auto !important"
1235+ , Prop "margin-bottom" "auto !important"
1236+ ]
1237+ ]
1238+
1239+ -- alignBottom's after a centerY should not grow
1240+ , Child ("s:last-of-type." ++ classes.alignContainerCenterY ++ " ~ u")
1241+ [ Prop "flex-grow" "0"
1242+ ]
1243+
1244+ -- centerY's after an alignBottom should be ignored
1245+ , Child ("u:first-of-type." ++ classes.alignContainerBottom ++ " ~ s." ++ classes.alignContainerCenterY)
1246+ -- Bottom alignment always overrides center alignment
1247+ [ Prop "flex-grow" "0"
1248+ ]
1249+ , describeAlignment <|
1250+ \alignment ->
1251+ case alignment of
1252+ Top ->
1253+ ( [ Prop "justify-content" "flex-start" ]
1254+ , [ Prop "margin-bottom" "auto" ]
1255+ )
1256+
1257+ Bottom ->
1258+ ( [ Prop "justify-content" "flex-end" ]
1259+ , [ Prop "margin-top" "auto" ]
1260+ )
1261+
1262+ Right ->
1263+ ( [ Prop "align-items" "flex-end" ]
1264+ , [ Prop "align-self" "flex-end" ]
1265+ )
1266+
1267+ Left ->
1268+ ( [ Prop "align-items" "flex-start" ]
1269+ , [ Prop "align-self" "flex-start" ]
1270+ )
1271+
1272+ CenterX ->
1273+ ( [ Prop "align-items" "center" ]
1274+ , [ Prop "align-self" "center"
1275+ ]
1276+ )
1277+
1278+ CenterY ->
1279+ ( [ Prop "justify-content" "center" ]
1280+ , []
1281+ )
1282+ , Child (dot classes.container)
1283+ [ Prop "flex-grow" "0"
1284+ , Prop "flex-basis" "auto"
1285+ , Prop "width" "100%"
1286+ , Prop "align-self" "stretch !important"
1287+ ]
1288+ , Descriptor (dot classes.spaceEvenly)
1289+ [ Prop "justify-content" "space-between"
1290+ ]
1291+ ]
1292+ , Descriptor (dot classes.grid)
1293+ [ Prop "display" "-ms-grid"
1294+ , Child ".gp"
1295+ [ Child (dot classes.any)
1296+ [ Prop "width" "100%"
1297+ ]
1298+ ]
1299+ , Supports ( "display", "grid" )
1300+ [ ( "display", "grid" )
1301+ ]
1302+ , gridAlignments <|
1303+ \alignment ->
1304+ case alignment of
1305+ Top ->
1306+ [ Prop "justify-content" "flex-start" ]
1307+
1308+ Bottom ->
1309+ [ Prop "justify-content" "flex-end" ]
1310+
1311+ Right ->
1312+ [ Prop "align-items" "flex-end" ]
1313+
1314+ Left ->
1315+ [ Prop "align-items" "flex-start" ]
1316+
1317+ CenterX ->
1318+ [ Prop "align-items" "center" ]
1319+
1320+ CenterY ->
1321+ [ Prop "justify-content" "center" ]
1322+ ]
1323+ , Descriptor (dot classes.page)
1324+ [ Prop "display" "block"
1325+ , Child (dot <| classes.any ++ ":first-child")
1326+ [ Prop "margin" "0 !important"
1327+ ]
1328+
1329+ -- clear spacing of any subsequent element if an element is float-left
1330+ , Child (dot <| classes.any ++ selfName (Self Left) ++ ":first-child + ." ++ classes.any)
1331+ [ Prop "margin" "0 !important"
1332+ ]
1333+ , Child (dot <| classes.any ++ selfName (Self Right) ++ ":first-child + ." ++ classes.any)
1334+ [ Prop "margin" "0 !important"
1335+ ]
1336+ , describeAlignment <|
1337+ \alignment ->
1338+ case alignment of
1339+ Top ->
1340+ ( []
1341+ , []
1342+ )
1343+
1344+ Bottom ->
1345+ ( []
1346+ , []
1347+ )
1348+
1349+ Right ->
1350+ ( []
1351+ , [ Prop "float" "right"
1352+ , Descriptor "::after"
1353+ [ Prop "content" "\"\""
1354+ , Prop "display" "table"
1355+ , Prop "clear" "both"
1356+ ]
1357+ ]
1358+ )
1359+
1360+ Left ->
1361+ ( []
1362+ , [ Prop "float" "left"
1363+ , Descriptor "::after"
1364+ [ Prop "content" "\"\""
1365+ , Prop "display" "table"
1366+ , Prop "clear" "both"
1367+ ]
1368+ ]
1369+ )
1370+
1371+ CenterX ->
1372+ ( []
1373+ , []
1374+ )
1375+
1376+ CenterY ->
1377+ ( []
1378+ , []
1379+ )
1380+ ]
1381+ , Descriptor (dot classes.inputMultiline)
1382+ [ Prop "white-space" "pre-wrap"
1383+ ]
1384+ , Descriptor (dot classes.paragraph)
1385+ [ Prop "display" "block"
1386+ , Prop "white-space" "normal"
1387+ , Descriptor (dot classes.hasBehind)
1388+ [ Prop "z-index" "0"
1389+ , Child (dot classes.behind)
1390+ [ Prop "z-index" "-1"
1391+ ]
1392+ ]
1393+ , Child (dot classes.text)
1394+ [ Prop "display" "inline"
1395+ , Prop "white-space" "normal"
1396+ ]
1397+ , Child (dot classes.single)
1398+ [ Prop "display" "inline"
1399+ , Prop "white-space" "normal"
1400+ , Descriptor (dot classes.inFront)
1401+ [ Prop "display" "flex"
1402+ ]
1403+ , Descriptor (dot classes.behind)
1404+ [ Prop "display" "flex"
1405+ ]
1406+ , Descriptor (dot classes.above)
1407+ [ Prop "display" "flex"
1408+ ]
1409+ , Descriptor (dot classes.below)
1410+ [ Prop "display" "flex"
1411+ ]
1412+ , Descriptor (dot classes.onRight)
1413+ [ Prop "display" "flex"
1414+ ]
1415+ , Descriptor (dot classes.onLeft)
1416+ [ Prop "display" "flex"
1417+ ]
1418+ , Child (dot classes.text)
1419+ [ Prop "display" "inline"
1420+ , Prop "white-space" "normal"
1421+ ]
1422+ ]
1423+ , Child (dot classes.row)
1424+ [ Prop "display" "inline-flex"
1425+ ]
1426+ , Child (dot classes.column)
1427+ [ Prop "display" "inline-flex"
1428+ ]
1429+ , Child (dot classes.grid)
1430+ [ Prop "display" "inline-grid"
1431+ ]
1432+ , describeAlignment <|
1433+ \alignment ->
1434+ case alignment of
1435+ Top ->
1436+ ( []
1437+ , []
1438+ )
1439+
1440+ Bottom ->
1441+ ( []
1442+ , []
1443+ )
1444+
1445+ Right ->
1446+ ( []
1447+ , [ Prop "float" "right" ]
1448+ )
1449+
1450+ Left ->
1451+ ( []
1452+ , [ Prop "float" "left" ]
1453+ )
1454+
1455+ CenterX ->
1456+ ( []
1457+ , []
1458+ )
1459+
1460+ CenterY ->
1461+ ( []
1462+ , []
1463+ )
1464+ ]
1465+ , Descriptor ".hidden"
1466+ [ Prop "display" "none"
1467+ ]
1468+ , Batch <|
1469+ (\fn -> List.map fn locations) <|
1470+ \loc ->
1471+ case loc of
1472+ Above ->
1473+ Descriptor (dot classes.above)
1474+ [ Prop "position" "absolute"
1475+ , Prop "bottom" "100%"
1476+ , Prop "left" "0"
1477+ , Prop "width" "100%"
1478+ , Prop "z-index" "10"
1479+ , Prop "margin" "0 !important"
1480+ , Child (dot classes.heightFill)
1481+ [ Prop "height" "auto"
1482+ ]
1483+ , Child (dot classes.widthFill)
1484+ [ Prop "width" "100%"
1485+ ]
1486+ , Prop "pointer-events" "none"
1487+ , Child (dot classes.any)
1488+ [ Prop "pointer-events" "auto"
1489+ ]
1490+ ]
1491+
1492+ Below ->
1493+ Descriptor (dot classes.below)
1494+ [ Prop "position" "absolute"
1495+ , Prop "bottom" "0"
1496+ , Prop "left" "0"
1497+ , Prop "height" "0"
1498+ , Prop "width" "100%"
1499+ , Prop "z-index" "10"
1500+ , Prop "margin" "0 !important"
1501+ , Prop "pointer-events" "auto"
1502+ , Child (dot classes.heightFill)
1503+ [ Prop "height" "auto"
1504+ ]
1505+ ]
1506+
1507+ OnRight ->
1508+ Descriptor (dot classes.onRight)
1509+ [ Prop "position" "absolute"
1510+ , Prop "left" "100%"
1511+ , Prop "top" "0"
1512+ , Prop "height" "100%"
1513+ , Prop "margin" "0 !important"
1514+ , Prop "z-index" "10"
1515+ , Prop "pointer-events" "auto"
1516+ ]
1517+
1518+ OnLeft ->
1519+ Descriptor (dot classes.onLeft)
1520+ [ Prop "position" "absolute"
1521+ , Prop "right" "100%"
1522+ , Prop "top" "0"
1523+ , Prop "height" "100%"
1524+ , Prop "margin" "0 !important"
1525+ , Prop "z-index" "10"
1526+ , Prop "pointer-events" "auto"
1527+ ]
1528+
1529+ Within ->
1530+ Descriptor (dot classes.inFront)
1531+ [ Prop "position" "absolute"
1532+ , Prop "width" "100%"
1533+ , Prop "height" "100%"
1534+ , Prop "left" "0"
1535+ , Prop "top" "0"
1536+ , Prop "margin" "0 !important"
1537+ , Prop "z-index" "10"
1538+ , Prop "pointer-events" "none"
1539+ , Child (dot classes.any)
1540+ [ Prop "pointer-events" "auto"
1541+ ]
1542+ ]
1543+
1544+ Behind ->
1545+ Descriptor (dot classes.behind)
1546+ [ Prop "position" "absolute"
1547+ , Prop "width" "100%"
1548+ , Prop "height" "100%"
1549+ , Prop "left" "0"
1550+ , Prop "top" "0"
1551+ , Prop "margin" "0 !important"
1552+ , Prop "z-index" "0"
1553+ , Prop "pointer-events" "none"
1554+ , Child (dot classes.any)
1555+ [ Prop "pointer-events" "auto"
1556+ ]
1557+ ]
1558+ , Descriptor (dot classes.textThin)
1559+ [ Prop "font-weight" "100"
1560+ ]
1561+ , Descriptor (dot classes.textExtraLight)
1562+ [ Prop "font-weight" "200"
1563+ ]
1564+ , Descriptor (dot classes.textLight)
1565+ [ Prop "font-weight" "300"
1566+ ]
1567+ , Descriptor (dot classes.textNormalWeight)
1568+ [ Prop "font-weight" "400"
1569+ ]
1570+ , Descriptor (dot classes.textMedium)
1571+ [ Prop "font-weight" "500"
1572+ ]
1573+ , Descriptor (dot classes.textSemiBold)
1574+ [ Prop "font-weight" "600"
1575+ ]
1576+ , Descriptor (dot classes.bold)
1577+ [ Prop "font-weight" "700"
1578+ ]
1579+ , Descriptor (dot classes.textExtraBold)
1580+ [ Prop "font-weight" "800"
1581+ ]
1582+ , Descriptor (dot classes.textHeavy)
1583+ [ Prop "font-weight" "900"
1584+ ]
1585+ , Descriptor (dot classes.italic)
1586+ [ Prop "font-style" "italic"
1587+ ]
1588+ , Descriptor (dot classes.strike)
1589+ [ Prop "text-decoration" "line-through"
1590+ ]
1591+ , Descriptor (dot classes.underline)
1592+ [ Prop "text-decoration" "underline"
1593+ , Prop "text-decoration-skip-ink" "auto"
1594+ , Prop "text-decoration-skip" "ink"
1595+ ]
1596+ , Descriptor (dot classes.underline ++ dot classes.strike)
1597+ [ Prop "text-decoration" "line-through underline"
1598+ , Prop "text-decoration-skip-ink" "auto"
1599+ , Prop "text-decoration-skip" "ink"
1600+ ]
1601+ , Descriptor (dot classes.textUnitalicized)
1602+ [ Prop "font-style" "normal"
1603+ ]
1604+ , Descriptor (dot classes.textJustify)
1605+ [ Prop "text-align" "justify"
1606+ ]
1607+ , Descriptor (dot classes.textJustifyAll)
1608+ [ Prop "text-align" "justify-all"
1609+ ]
1610+ , Descriptor (dot classes.textCenter)
1611+ [ Prop "text-align" "center"
1612+ ]
1613+ , Descriptor (dot classes.textRight)
1614+ [ Prop "text-align" "right"
1615+ ]
1616+ , Descriptor (dot classes.textLeft)
1617+ [ Prop "text-align" "left"
1618+ ]
1619+ , Descriptor ".modal"
1620+ [ Prop "position" "fixed"
1621+ , Prop "left" "0"
1622+ , Prop "top" "0"
1623+ , Prop "width" "100%"
1624+ , Prop "height" "100%"
1625+ , Prop "pointer-events" "none"
1626+ ]
1627+ ]
1628+ ]
tests/Tests/Basic.elmnew file+54
@@ -0,0 +1,54 @@
1+module Tests.Basic exposing (view)
2+
3+{-| -}
4+
5+import Element as Actual
6+import Html
7+import Testable
8+import Testable.Element as Element exposing (..)
9+import Testable.Element.Background as Background
10+import Testable.Element.Font as Font
11+import Testable.Runner
12+import Tests.Palette as Palette
13+
14+
15+{-| -}
16+main : Html.Html msg
17+main =
18+ Testable.Runner.show view
19+
20+
21+{-| -}
22+view : Testable.Element msg
23+view =
24+ row [ spacing 50, alignTop ]
25+ [ el
26+ [ width (px 200)
27+ , height (px 200)
28+ , Background.color Palette.blue
29+ , Font.color Palette.white
30+ ]
31+ (text "Hello!")
32+ , el
33+ [ width (px 200)
34+ , height (px 200)
35+ , Background.color Palette.blue
36+ , Font.color Palette.white
37+ ]
38+ (text "Hello!")
39+ , el
40+ [ width (px 200)
41+ , height (px 200)
42+ , Background.color (Actual.rgba 0 0 1 1)
43+ , Font.color Palette.white
44+ , below
45+ (el
46+ [ Background.color Palette.grey
47+ , width (px 50)
48+ , height (px 50)
49+ ]
50+ none
51+ )
52+ ]
53+ (text "Hello!")
54+ ]
tests/Tests/ColumnAlignment.elmnew file+176
@@ -0,0 +1,176 @@
1+module Tests.ColumnAlignment exposing (..)
2+
3+import Html
4+import Testable
5+import Testable.Element as Element exposing (..)
6+import Testable.Element.Background as Background
7+import Testable.Element.Font as Font
8+import Testable.Runner
9+import Tests.Palette as Palette exposing (..)
10+
11+
12+{-| -}
13+main : Html.Html msg
14+main =
15+ Testable.Runner.show view
16+
17+
18+box attrs =
19+ el
20+ ([ width (px 50)
21+ , height (px 50)
22+ , Background.color blue
23+ ]
24+ ++ attrs
25+ )
26+ none
27+
28+
29+container =
30+ el [ width (px 100), height (px 100) ]
31+
32+
33+view =
34+ let
35+ colContainer attrs children =
36+ column ([ Background.color lightGrey, spacing 20, width (px 100), height (px 500) ] ++ attrs) children
37+
38+ tallContainer attrs children =
39+ column ([ Background.color lightGrey, spacing 20, width (px 100), height fill ] ++ attrs) children
40+ in
41+ column
42+ [ width fill, spacing 20 ]
43+ [ el [] (text "Alignment Within a Column")
44+ , row [ spacing 20 ]
45+ [ colContainer []
46+ [ box [] ]
47+ , colContainer []
48+ [ box [ centerY ]
49+ ]
50+ , colContainer []
51+ [ box [ alignBottom, label "first" ]
52+ ]
53+ ]
54+ , row [ spacing 20 ]
55+ [ colContainer []
56+ [ box [] ]
57+ , colContainer []
58+ [ box []
59+ , box []
60+ , box []
61+ ]
62+ , colContainer []
63+ [ box [ alignLeft, label "first" ]
64+ , box [ centerX, label "second" ]
65+ , box [ alignRight, label "third" ]
66+ ]
67+ ]
68+ , row [ spacing 20 ]
69+ [ colContainer []
70+ [ box []
71+ , box []
72+ , box []
73+ ]
74+ , colContainer []
75+ [ box []
76+ , box []
77+ , box [ alignBottom, label "third" ]
78+ ]
79+ , colContainer []
80+ [ box []
81+ , box [ alignBottom, label "second" ]
82+ , box []
83+ ]
84+ , colContainer []
85+ [ box [ alignBottom, label "first" ]
86+ , box []
87+ , box []
88+ ]
89+ ]
90+ , text "centerY"
91+ , row [ spacing 20 ]
92+ [ colContainer [ height fill ]
93+ [ box [ centerY, label "solo" ]
94+ ]
95+ , colContainer []
96+ [ box []
97+ , box [ centerY, label "middle" ]
98+ , box []
99+ ]
100+ , colContainer []
101+ [ box []
102+ , box []
103+ , box [ centerY, label "last" ]
104+ ]
105+ ]
106+ , text "multiple centerY"
107+ , row [ height (px 800), spacing 20 ]
108+ [ tallContainer []
109+ [ box []
110+ , box [ centerY ]
111+ , box [ centerY ]
112+ , box [ centerY ]
113+ , box [ centerY ]
114+ , box [ alignBottom ]
115+ ]
116+ , tallContainer []
117+ [ box []
118+ , box [ centerY ]
119+ , box []
120+ ]
121+ , tallContainer []
122+ [ box []
123+ , box []
124+ , box [ centerY ]
125+ ]
126+ ]
127+ , text "top, center, bottom"
128+ , row [ spacing 20 ]
129+ [ colContainer []
130+ [ box [ alignTop, label "first" ]
131+ , box []
132+ , box [ alignBottom, label "last" ]
133+ ]
134+ , colContainer []
135+ [ box [ alignTop ]
136+ , box [ centerY ]
137+ , box [ alignBottom ]
138+ ]
139+ , colContainer []
140+ [ box [ alignLeft, alignTop ]
141+ , box [ centerX, centerY ]
142+ , box [ alignRight, alignBottom ]
143+ ]
144+ ]
145+ , el [ width fill ] (text "Column in a Row")
146+ , row [ width fill, spacing 20, label "row" ]
147+ [ box [ alignLeft, alignTop, label "box" ]
148+ , column
149+ [ alignLeft
150+ , alignTop
151+ , spacing 20
152+ , label "column"
153+ , Background.color lightGrey
154+ ]
155+ [ box []
156+ , box []
157+ , box []
158+ ]
159+ , column
160+ [ spacing 20
161+ , width (px 100)
162+ , alignLeft
163+ , alignTop
164+ , Background.color lightGrey
165+ ]
166+ [ box []
167+ , box []
168+ , box []
169+ ]
170+ , colContainer []
171+ [ box [ alignRight ]
172+ , box [ centerX ]
173+ , box [ alignLeft ]
174+ ]
175+ ]
176+ ]
tests/Tests/ColumnSpacing.elmnew file+68
@@ -0,0 +1,68 @@
1+module Tests.ColumnSpacing exposing (..)
2+
3+import Html
4+import Testable
5+import Testable.Element as Element exposing (..)
6+import Testable.Element.Background as Background
7+import Testable.Element.Font as Font
8+import Testable.Runner
9+import Tests.Palette as Palette exposing (..)
10+
11+
12+{-| -}
13+main : Html.Html msg
14+main =
15+ Testable.Runner.show view
16+
17+
18+box attrs =
19+ el
20+ ([ width (px 50)
21+ , height (px 50)
22+ , Background.color blue
23+ ]
24+ ++ attrs
25+ )
26+ none
27+
28+
29+tinyBox attrs =
30+ el
31+ ([ width (px 20)
32+ , height (px 20)
33+
34+ -- , centerY
35+ , Background.color darkCharcoal
36+ ]
37+ ++ attrs
38+ )
39+ none
40+
41+
42+container =
43+ el [ width (px 100), height (px 100) ]
44+
45+
46+view =
47+ let
48+ colContainer attrs children =
49+ column ([ spacing 20, width (px 100), height (px 500) ] ++ attrs) children
50+ in
51+ column
52+ []
53+ [ el [] (text "Spacing within a column")
54+ , row []
55+ [ colContainer []
56+ [ box [] ]
57+ , colContainer []
58+ [ box []
59+ , box []
60+ , box []
61+ ]
62+ , colContainer [ onRight (tinyBox []) ]
63+ [ box []
64+ , box []
65+ , box []
66+ ]
67+ ]
68+ ]
tests/Tests/ElementAlignment.elmnew file+111
@@ -0,0 +1,111 @@
1+module Tests.ElementAlignment exposing (..)
2+
3+import Generator
4+import Html
5+import Testable
6+import Testable.Element as Element exposing (..)
7+import Testable.Element.Background as Background
8+import Testable.Element.Font as Font
9+import Testable.Runner
10+import Tests.Palette as Palette exposing (..)
11+
12+
13+{-| -}
14+main : Html.Html msg
15+main =
16+ Testable.Runner.show view
17+
18+
19+box : List (Testable.Attr msg) -> Testable.Element msg
20+box attrs =
21+ el
22+ ([ width (px 50)
23+ , height (px 50)
24+ , Background.color blue
25+ ]
26+ ++ attrs
27+ )
28+ none
29+
30+
31+container : Testable.Element msg -> Testable.Element msg
32+container =
33+ el
34+ [ width (px 200)
35+ , height (px 200)
36+ , Background.color lightGrey
37+ ]
38+
39+
40+view : Testable.Element msg
41+view =
42+ column []
43+ [ el [] (text "Alignment Within an El")
44+ , container <|
45+ box []
46+ , text "alignLeft, centerX, alignRight"
47+ , column [ spacing 20 ] <|
48+ Generator.sizes
49+ (\resizeable ->
50+ row [ spacing 20 ]
51+ [ container <|
52+ resizeable [ Background.color blue, alignLeft ] none
53+ , container <|
54+ resizeable [ Background.color blue, centerX ] none
55+ , container <|
56+ resizeable [ Background.color blue, alignRight ] none
57+ ]
58+ )
59+ , text "top, centerY, bottom"
60+ , column [ spacing 20 ] <|
61+ Generator.sizes
62+ (\resizeable ->
63+ row [ spacing 20 ]
64+ [ container <|
65+ resizeable [ alignTop ] none
66+ , container <|
67+ resizeable [ centerY ] none
68+ , container <|
69+ resizeable [ alignBottom ] none
70+ ]
71+ )
72+ , text "align top ++ alignments"
73+ , column [ spacing 20 ] <|
74+ Generator.sizes
75+ (\resizeable ->
76+ row [ spacing 20 ]
77+ [ container <|
78+ resizeable [ alignTop, alignLeft ] none
79+ , container <|
80+ resizeable [ alignTop, centerX ] none
81+ , container <|
82+ resizeable [ alignTop, alignRight ] none
83+ ]
84+ )
85+ , text "centerY ++ alignments"
86+ , column [ spacing 20 ] <|
87+ Generator.sizes
88+ (\resizeable ->
89+ row [ spacing 20 ]
90+ [ container <|
91+ resizeable [ centerY, alignLeft ] none
92+ , container <|
93+ resizeable [ centerY, centerX ] none
94+ , container <|
95+ resizeable [ centerY, alignRight ] none
96+ ]
97+ )
98+ , text "alignBottom ++ alignments"
99+ , column [ spacing 20 ] <|
100+ Generator.sizes
101+ (\resizeable ->
102+ row [ spacing 20 ]
103+ [ container <|
104+ resizeable [ alignBottom, alignLeft ] none
105+ , container <|
106+ resizeable [ alignBottom, centerX ] none
107+ , container <|
108+ resizeable [ alignBottom, alignRight ] none
109+ ]
110+ )
111+ ]
tests/Tests/Manual/AlignedForm.elmnew file+286
@@ -0,0 +1,286 @@
1+module Main exposing (..)
2+
3+{-| -}
4+
5+import Tests.Palette as Palette exposing (..)
6+import Element exposing (..)
7+import Element.Background as Background
8+import Element.Border as Border
9+import Element.Font as Font
10+import Element.Input as Input
11+import Element.Region as Region
12+import Html
13+
14+
15+main =
16+ Html.program
17+ { init = ( init, Cmd.none )
18+ , view = view
19+ , update = update
20+ , subscriptions = \_ -> Sub.none
21+ }
22+
23+
24+init =
25+ { username = ""
26+ , password = ""
27+ , agreeTOS = False
28+ , comment = ""
29+ , lunch = Gyro
30+ }
31+
32+
33+type alias Form =
34+ { username : String
35+ , password : String
36+ , agreeTOS : Bool
37+ , comment : String
38+ , lunch : Lunch
39+ }
40+
41+
42+type Msg
43+ = Update Form
44+
45+
46+update msg model =
47+ case Debug.log "msg" msg of
48+ Update new ->
49+ ( new, Cmd.none )
50+
51+
52+type Lunch
53+ = Burrito
54+ | Taco
55+ | Gyro
56+
57+
58+view model =
59+ let
60+ label str =
61+ Input.labelLeft
62+ [ width (fillPortion 1)
63+ , Font.alignRight
64+ , paddingXY 12 7
65+ , Font.bold
66+ , focused
67+ [ Background.color blue
68+ , Font.size 40
69+ , Font.color white
70+ ]
71+ ]
72+ (text str)
73+
74+ testRadio =
75+ Input.radio
76+ [ width (fillPortion 4)
77+ , transparent True
78+ , spacing 15
79+ , focused
80+ [ transparent False
81+ ]
82+ ]
83+ { selected = Just model.lunch
84+ , onChange = Just (\new -> Update { model | lunch = new })
85+ , label =
86+ Input.labelAbove
87+ [ transparent True
88+ , focused
89+ [ transparent False
90+ ]
91+ ]
92+ (text "What would you like for lunch?")
93+ , options =
94+ [ Input.option Gyro (text "Gyro")
95+ , Input.option Burrito (text "Burrito")
96+ , Input.option Taco (text "Taco")
97+ ]
98+ }
99+ in
100+ Element.layout
101+ [ Font.size 20
102+ ]
103+ <|
104+ Element.column
105+ [ width (px 800)
106+ , height shrink
107+ , centerY
108+ , centerX
109+ , spacing 36
110+ , padding 10
111+ ]
112+ [ el
113+ [ Region.heading 1
114+ , alignLeft
115+ , Font.size 36
116+
117+ -- , above True
118+ -- [ focused
119+ -- [ transparent False ]
120+ -- ]
121+ -- (text "show me")
122+ -- , mouseOver
123+ -- [ Font.size 36
124+ -- , above True
125+ -- (el
126+ -- [ Font.color red
127+ -- , Font.size 14
128+ -- , alignLeft
129+ -- ]
130+ -- (text "This one is le wrong")
131+ -- )
132+ -- -- Decoration.backgroundColor Color.blue
133+ -- -- , Decoration.fontSize 40
134+ -- -- , Decoration.fontColor Color.white
135+ -- ]
136+ , above True
137+ (el
138+ [ Font.color red
139+ , Font.size 14
140+ , alignLeft
141+ ]
142+ (text "This one is le wrong")
143+ )
144+
145+ -- [ Decoration.fontSize 40
146+ -- , Decoration.backgroundColor Color.blue
147+ -- ]
148+ ]
149+ (text "Welcome to the Stylish Elephants Lunch Emporium")
150+
151+ -- , Input.radioRow [ width (fillPortion 4), spacing 15 ]
152+ -- { selected = Just model.lunch
153+ -- , onChange = Just (\new -> Update { model | lunch = new })
154+ -- , label = label "What would you like for lunch?"
155+ -- , options =
156+ -- [ Input.option Gyro (text "Gyro")
157+ -- , Input.option Burrito (text "Burrito")
158+ -- , Input.option Taco (text "Taco")
159+ -- ]
160+ -- }
161+ -- , Input.username
162+ -- [ width (fillPortion 4)
163+ -- , focused
164+ -- [ Decoration.backgroundColor Color.blue
165+ -- , Decoration.fontSize 40
166+ -- , Decoration.fontColor Color.white
167+ -- ]
168+ -- , below True
169+ -- (el
170+ -- [ Font.color red
171+ -- , Font.size 14
172+ -- , alignLeft
173+ -- ]
174+ -- (text "This one is le wrong")
175+ -- )
176+ -- ]
177+ -- { text = model.username
178+ -- , placeholder = Nothing --Just (Input.placeholder [] (text "Extra hot sauce?"))
179+ -- , onChange = Just (\new -> Update { model | username = new })
180+ -- , label =
181+ -- Input.labelAbove
182+ -- []
183+ -- (text "username")
184+ -- }
185+ -- , Input.username
186+ -- [ below True
187+ -- (el
188+ -- [ Font.color red
189+ -- , Font.size 14
190+ -- , alignLeft
191+ -- ]
192+ -- (text "This one is le wrong")
193+ -- )
194+ -- , width (fillPortion 4)
195+ -- -- , Decoration.focused
196+ -- -- [ Decoration.backgroundColor Color.blue
197+ -- -- , Decoration.fontSize 40
198+ -- -- , Decoration.fontColor Color.white
199+ -- -- ]
200+ -- ]
201+ -- { text = model.username
202+ -- , placeholder = Nothing --Just (Input.placeholder [] (text "Extra hot sauce?"))
203+ -- , onChange = Just (\new -> Update { model | username = new })
204+ -- , label =
205+ -- Input.labelAbove
206+ -- [ moveDown 30
207+ -- , Font.size 20
208+ -- , Decoration.focused
209+ -- [ Decoration.fontSize 15
210+ -- , Decoration.moveUp 500
211+ -- ]
212+ -- ]
213+ -- (text "Username")
214+ -- }
215+ -- , Input.currentPassword [ width (fillPortion 4) ]
216+ -- { text = model.password
217+ -- , placeholder = Nothing
218+ -- , onChange = Just (\new -> Update { model | password = new })
219+ -- , label = label "Password"
220+ -- , show = False
221+ -- }
222+ , Input.multiline
223+ [ height shrink
224+ , width (fillPortion 4)
225+ , below True
226+ (el
227+ [ Font.color red
228+ , Font.size 14
229+ , alignLeft
230+ ]
231+ (text "This one is le wrong")
232+ )
233+ ]
234+ { text = model.comment
235+ , placeholder = Just (Input.placeholder [] (text "Extra hot sauce?"))
236+ , onChange = Just (\new -> Update { model | comment = new })
237+ , label =
238+ Input.labelLeft
239+ [ width (fillPortion 1)
240+ , Font.alignRight
241+ , Font.bold
242+ , transparent True
243+ , paddingXY 12 7
244+ , focused
245+ [ Background.color blue
246+ , Font.size 40
247+ , Font.color white
248+ ]
249+ ]
250+ (text "Question")
251+ , spellcheck = False
252+ }
253+
254+ -- , testRadio
255+ , Element.row
256+ [ Font.bold
257+ , alignLeft
258+ , below True <|
259+ testRadio
260+ ]
261+ [ el [ alignLeft, Font.bold ] <| text "Selection"
262+ ]
263+
264+ -- , Element.row []
265+ -- [ Element.el [ Element.width Element.fill ] Element.none
266+ -- , Input.checkbox
267+ -- [ width (fillPortion 4) ]
268+ -- { checked = model.agreeTOS
269+ -- , onChange = Just (\new -> Update { model | agreeTOS = new })
270+ -- , icon = Nothing
271+ -- , label = Input.labelRight [] (text "Agree to Terms of Service")
272+ -- }
273+ -- ]
274+ -- , Input.button
275+ -- [ Background.color blue
276+ -- , Font.color white
277+ -- , Border.color darkBlue
278+ -- , paddingXY 15 5
279+ -- , Border.rounded 3
280+ -- , alignLeft
281+ -- -- , width fill
282+ -- ]
283+ -- { onPress = Nothing
284+ -- , label = Element.text "Place your lunch order!"
285+ -- }
286+ ]
tests/Tests/Manual/All.elmnew file+1263
@@ -0,0 +1,1263 @@
1+module Tests.Manual.All exposing (..)
2+
3+{-| A file for manually inspecting layouts.
4+-}
5+
6+import Tests.Palette as Palette exposing (..)
7+import Dom
8+import Element exposing (..)
9+import Element.Background as Background
10+import Element.Border as Border
11+import Element.Events as Events
12+import Element.Font as Font
13+import Element.Input as Input
14+import Element.Lazy
15+import Element.Region as Region
16+import Html
17+import Html.Attributes
18+import Task
19+
20+
21+main =
22+ Html.program
23+ { init = ( init, Cmd.none )
24+ , view = view
25+ , update = update
26+ , subscriptions = \_ -> Sub.none
27+ }
28+
29+
30+init =
31+ { username = ""
32+ , password = ""
33+ , agreeTOS = False
34+ , menuOpen = False
35+ , menuFocused = False
36+ , comment = ""
37+ , lunch = Gyro
38+ }
39+
40+
41+type alias Form =
42+ { username : String
43+ , password : String
44+ , agreeTOS : Bool
45+ , comment : String
46+ , menuOpen : Bool
47+ , menuFocused : Bool
48+ , lunch : Lunch
49+ }
50+
51+
52+type Msg
53+ = Update Form
54+ | Focus String
55+ | LoseFocus String
56+ | NoOp
57+
58+
59+update msg model =
60+ case Debug.log "msg" msg of
61+ NoOp ->
62+ ( model, Cmd.none )
63+
64+ Update new ->
65+ ( new, Cmd.none )
66+
67+ Focus id ->
68+ ( { model | menuFocused = True }
69+ , Task.attempt (always NoOp) (Dom.focus id)
70+ )
71+
72+ LoseFocus id ->
73+ ( { model | menuFocused = False }
74+ , Task.attempt (always NoOp) (Dom.blur id)
75+ )
76+
77+
78+type Lunch
79+ = Burrito
80+ | Taco
81+ | Gyro
82+
83+
84+view model =
85+ Element.layout
86+ [ htmlAttribute (Html.Attributes.class "wireframe")
87+ , Font.family
88+ [ Font.external
89+ { url = "https://fonts.googleapis.com/css?family=Source+Code+Pro"
90+ , name = "Source Code Pro"
91+ }
92+ ]
93+ ]
94+ <|
95+ column
96+ [ Background.color lightGrey
97+ , width (px 800)
98+ , centerX
99+ ]
100+ [ textElements
101+ , misc
102+ , widths
103+ , transparentElements
104+ , text "Lazy Time!"
105+ , Element.Lazy.lazy (always textElements) ()
106+ , Element.Lazy.lazy lazyWithNearby ()
107+ , Element.Lazy.lazy lazyWithoutNearby ()
108+ , singleAlignment
109+ , rowAlignment
110+ , columnAlignment
111+ , nearbyElements
112+ , columnSpacing
113+ , rowSpacing
114+ , tableElements
115+ , signInForm model
116+ , el [ height (px 200) ] none
117+ ]
118+
119+
120+misc =
121+ column [ spacing 32 ]
122+ [-- text "The following should be centered, with a fillBetween width"
123+ -- , row
124+ -- [ width
125+ -- (fillBetween
126+ -- { min = Nothing
127+ -- , max = Just 360
128+ -- }
129+ -- )
130+ -- , centerX
131+ -- , padding 15
132+ -- , Background.color Color.lightBlue
133+ -- , Font.bold
134+ -- ]
135+ -- [ el [ alignLeft ] (text "Logo")
136+ -- , el [ alignRight ] (text "Navigation")
137+ -- ]
138+ ]
139+
140+
141+widths =
142+ column [ spacing 32 ]
143+ [ el [ Background.color blue, height (px 200), width fill ] (text "width fill")
144+ , el [ Background.color blue, height (px 200), width (px 200) ] (text "width 200 px")
145+ , el [ Background.color blue, height (px 200), width shrink ] (text "width shrink")
146+ , text "rows"
147+ , row [ spacing 32 ]
148+ [ box []
149+ , box []
150+ , box []
151+ ]
152+ , row [ spacing 32, width fill ]
153+ [ box []
154+ , box []
155+ , box []
156+ ]
157+ , row [ spacing 32, width (px 500) ]
158+ [ box []
159+ , box []
160+ , box []
161+ ]
162+ , row [ spacing 32, width shrink ]
163+ [ box []
164+ , box []
165+ , box []
166+ ]
167+ , text "within a row"
168+ , row [ spacing 32 ]
169+ [ box [ width fill ]
170+ , box [ width (px 200) ]
171+ , el [ Background.color blue, width shrink, height (px 50) ] (text "shrink me")
172+ ]
173+ , row [ spacing 32 ]
174+ [ box [ width fill ]
175+ , box [ width (fillPortion 2) ]
176+ , box [ width (fillPortion 3) ]
177+ ]
178+ , text "columns"
179+ , column [ spacing 32 ]
180+ [ box []
181+ , box []
182+ , box []
183+ ]
184+ , column [ spacing 32, width fill ]
185+ [ box []
186+ , box []
187+ , box []
188+ ]
189+ , column [ spacing 32, width (px 500) ]
190+ [ box []
191+ , box []
192+ , box []
193+ ]
194+ , column [ spacing 32, width shrink ]
195+ [ box []
196+ , box []
197+ , box []
198+ ]
199+ , text "within a column"
200+ , column [ spacing 32 ]
201+ [ box [ width fill ]
202+ , box [ width (px 200) ]
203+ , el [ Background.color blue, width shrink, height (px 50) ] (text "shrink me")
204+ ]
205+ , column [ spacing 32 ]
206+ [ box [ width fill ]
207+ , box [ width (fillPortion 2) ]
208+ , box [ width (fillPortion 3) ]
209+ ]
210+
211+ -- , text "Width Fill Between"
212+ -- , text "Max at 200"
213+ -- , el [ width fill, height (px 60) ]
214+ -- (box
215+ -- [ width
216+ -- (fillBetween
217+ -- { min = Just 20
218+ -- , max = Just 200
219+ -- }
220+ -- )
221+ -- ]
222+ -- )
223+ -- , text "Fill between 20 and 200"
224+ -- , el [ width (px 150), height (px 60) ]
225+ -- (box
226+ -- [ width
227+ -- (fillBetween
228+ -- { min = Just 20
229+ -- , max = Just 200
230+ -- }
231+ -- )
232+ -- ]
233+ -- )
234+ -- , text "Min at 20"
235+ -- , el [ width (px 10), height (px 60) ]
236+ -- (box
237+ -- [ width
238+ -- (fillBetween
239+ -- { min = Just 20
240+ -- , max = Just 200
241+ -- }
242+ -- )
243+ -- ]
244+ -- )
245+ , text "widths for text layouts"
246+ , textColumn []
247+ [ box [ width fill ]
248+ , box [ width (px 200) ]
249+ , el [ Background.color blue, width shrink, height (px 50) ] (text "shrink me")
250+ , el [ Background.color blue ] (text "shrink me")
251+ , el [ Background.color blue ] (text "shrink me")
252+ , el [ Background.color blue ] (text "shrink me")
253+ ]
254+ , paragraph []
255+ [ text "unstyled"
256+ , box [ width fill ]
257+ , box [ width (px 200) ]
258+ , el [ Background.color blue, width shrink, height (px 50) ] (text "shrink me")
259+ , el [ Background.color blue ] (text "shrink me")
260+ , el [ Background.color blue ] (text "shrink me")
261+ , el [ Background.color blue ] (text "shrink me")
262+ ]
263+ ]
264+
265+
266+lazyWithNearby _ =
267+ column
268+ [ spacing 32
269+ , onRight (box [])
270+ ]
271+ [ box [], box [], box [] ]
272+
273+
274+lazyWithoutNearby _ =
275+ column
276+ [ spacing 32
277+ ]
278+ [ box [], box [], box [] ]
279+
280+
281+transparentElements =
282+ column [ paddingXY 0 100, spacing 16 ]
283+ [ text "transparency"
284+ , row [ spacing 16 ]
285+ [ box [ transparent True ]
286+ , box [ transparent False ]
287+ ]
288+ , text "transparency with hover"
289+ , row [ spacing 16 ]
290+ [ box
291+ [ transparent True
292+ , mouseOver [ Background.color green ]
293+ ]
294+ , box
295+ [ transparent False
296+ , mouseOver [ Background.color green ]
297+ ]
298+ ]
299+ , text "all opacities"
300+ , row [ spacing 16 ]
301+ [ box [ alpha 0 ]
302+ , box [ alpha 0.25 ]
303+ , box [ alpha 0.5 ]
304+ , box [ alpha 0.75 ]
305+ , box [ alpha 1.0 ]
306+ ]
307+ ]
308+
309+
310+textElements : Element msg
311+textElements =
312+ let
313+ txt =
314+ text "I'm a lumberjack and I'm ok"
315+
316+ short =
317+ text "lumber"
318+ in
319+ column [ width fill, spacing 50, paddingXY 0 100 ]
320+ [ txt
321+ , el [] txt
322+ , el [ width fill ] txt
323+ , row [ spacing 50 ]
324+ [ short, short, short ]
325+ , column [ spacing 50 ]
326+ [ txt, txt, txt ]
327+ , text "text layouts"
328+ , textColumn
329+ [ Background.color darkCharcoal
330+ , Font.color white
331+ , spacing 32
332+ , padding 64
333+ ]
334+ [ box [ alignRight ]
335+ , paragraph [] [ text lorem ]
336+ , box [ alignLeft ]
337+ , paragraph [] [ text lorem ]
338+ ]
339+ , textColumn
340+ [ Background.color darkCharcoal
341+ , Font.color white
342+ , spacingXY 32 64
343+ , padding 64
344+ ]
345+ [ box [ alignRight ]
346+ , paragraph [] [ text lorem ]
347+ , box [ alignLeft ]
348+ , paragraph [] [ text lorem ]
349+ ]
350+ , textColumn
351+ [ Background.color darkCharcoal
352+ , Font.color white
353+ , spacingXY 32 64
354+ , padding 64
355+ ]
356+ [ box [ alignRight ]
357+ , paragraph [] [ text lorem ]
358+ , box [ alignLeft ]
359+ , paragraph [] [ box [ alignLeft ], text lorem ]
360+ ]
361+ ]
362+
363+
364+lorem =
365+ """Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis volutpat libero condimentum sapien ullamcorper cursus. Phasellus placerat nisl eu arcu bibendum pharetra. Morbi at nulla velit. Suspendisse sit amet condimentum ante. Sed libero mauris, ultrices ut sapien sed, imperdiet malesuada urna. Mauris malesuada nec magna eu ornare. """
366+
367+
368+sentence =
369+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
370+
371+
372+box attrs =
373+ el ([ mouseOver [ Background.color red ], width (px 50), height (px 50), Background.color blue ] ++ attrs) none
374+
375+
376+tinyBox attrs =
377+ el
378+ ([ width (px 20)
379+ , height (px 20)
380+
381+ -- , centerY
382+ , Background.color darkCharcoal
383+ ]
384+ ++ attrs
385+ )
386+ none
387+
388+
389+container : Element msg -> Element msg
390+container =
391+ el [ width (px 100), height (px 100) ]
392+
393+
394+{-| Alignment of an el within an other el
395+-}
396+singleAlignment : Element msg
397+singleAlignment =
398+ column []
399+ [ el [] (text "Alignment Within an El")
400+ , container <|
401+ box []
402+ , text "alignLeft, centerX, alignRight"
403+ , row [ spacing 20 ]
404+ [ container <|
405+ box [ alignLeft ]
406+ , container <|
407+ box [ centerX ]
408+ , container <|
409+ box [ alignRight ]
410+ ]
411+ , text "top, centerY, bottom"
412+ , row [ spacing 20 ]
413+ [ container <|
414+ box [ alignTop ]
415+ , container <|
416+ box [ centerY ]
417+ , container <|
418+ box [ alignBottom ]
419+ ]
420+ , text "align top ++ alignments"
421+ , row [ spacing 20 ]
422+ [ container <|
423+ box [ alignTop, alignLeft ]
424+ , container <|
425+ box [ alignTop, centerX ]
426+ , container <|
427+ box [ alignTop, alignRight ]
428+ ]
429+ , text "centerY ++ alignments"
430+ , row [ spacing 20 ]
431+ [ container <|
432+ box [ centerY, alignLeft ]
433+ , container <|
434+ box [ centerY, centerX ]
435+ , container <|
436+ box [ centerY, alignRight ]
437+ ]
438+ , text "alignBottom ++ alignments"
439+ , row [ spacing 20 ]
440+ [ container <|
441+ box [ alignBottom, alignLeft ]
442+ , container <|
443+ box [ alignBottom, centerX ]
444+ , container <|
445+ box [ alignBottom, alignRight ]
446+ ]
447+ ]
448+
449+
450+rowAlignment =
451+ let
452+ rowContainer attrs children =
453+ row ([ spacing 20, height (px 100) ] ++ attrs) children
454+ in
455+ column [ width (px 500) ]
456+ [ el [] (text "Alignment Within a Row")
457+ , rowContainer []
458+ [ box [] ]
459+ , rowContainer []
460+ [ box []
461+ , box []
462+ , box []
463+ ]
464+ , rowContainer []
465+ [ box []
466+ , box []
467+ , box [ alignRight ]
468+ ]
469+ , rowContainer []
470+ [ box []
471+ , box [ alignRight ]
472+ , box []
473+ ]
474+ , rowContainer []
475+ [ box [ alignRight ]
476+ , box []
477+ , box []
478+ ]
479+ , text "center X"
480+ , rowContainer []
481+ [ box [ centerX ]
482+ , box []
483+ , box []
484+ ]
485+ , rowContainer []
486+ [ box []
487+ , box [ centerX ]
488+ , box []
489+ ]
490+ , rowContainer []
491+ [ box []
492+ , box []
493+ , box [ centerX ]
494+ ]
495+ , text "left x right"
496+ , rowContainer []
497+ [ box [ alignLeft ]
498+ , box []
499+ , box [ alignRight ]
500+ ]
501+ , text "left center right"
502+ , rowContainer []
503+ [ box [ alignLeft ]
504+ , box [ centerX ]
505+ , box [ alignRight ]
506+ ]
507+ , text "vertical alignment"
508+ , rowContainer []
509+ [ box [ alignTop ]
510+ , box [ centerY ]
511+ , box [ alignBottom ]
512+ ]
513+ , text "all alignments alignment"
514+ , rowContainer []
515+ [ box [ alignLeft, alignTop ]
516+ , box [ centerX, centerY ]
517+ , box [ alignRight, alignBottom ]
518+ ]
519+ ]
520+
521+
522+columnAlignment =
523+ let
524+ colContainer attrs children =
525+ column ([ spacing 20, width (px 100), height (px 500) ] ++ attrs) children
526+ in
527+ column
528+ [ width fill ]
529+ [ el [] (text "Alignment Within a Column")
530+ , row []
531+ [ colContainer []
532+ [ box [] ]
533+ , colContainer []
534+ [ box []
535+ , box []
536+ , box []
537+ ]
538+ , colContainer []
539+ [ box [ alignLeft ]
540+ , box [ centerX ]
541+ , box [ alignRight ]
542+ ]
543+ ]
544+ , row []
545+ [ colContainer []
546+ [ box []
547+ , box []
548+ , box []
549+ ]
550+ , colContainer []
551+ [ box []
552+ , box []
553+ , box [ alignBottom ]
554+ ]
555+ , colContainer []
556+ [ box []
557+ , box [ alignBottom ]
558+ , box []
559+ ]
560+ , colContainer []
561+ [ box [ alignBottom ]
562+ , box []
563+ , box []
564+ ]
565+ ]
566+ , text "with centerY override"
567+ , row []
568+ [ colContainer []
569+ [ box []
570+ , box []
571+ , box []
572+ ]
573+ , colContainer []
574+ [ box []
575+ , box []
576+ , box [ alignBottom ]
577+ ]
578+ , colContainer []
579+ [ box []
580+ , box [ alignBottom ]
581+ , box [ centerY ]
582+ ]
583+ , colContainer []
584+ [ box [ alignBottom ]
585+ , box [ centerY ]
586+ , box [ centerY ]
587+ ]
588+ ]
589+ , text "centerY"
590+ , row []
591+ [ colContainer [ height fill ]
592+ [ box [ centerY ]
593+ ]
594+ , colContainer []
595+ [ box []
596+ , box [ centerY ]
597+ , box []
598+ ]
599+ , colContainer []
600+ [ box []
601+ , box []
602+ , box [ centerY ]
603+ ]
604+ ]
605+ , text "multiple centerY"
606+ , row [ height (px 800) ]
607+ [ colContainer [ height fill ]
608+ [ box []
609+ , box [ centerY ]
610+ , box [ centerY ]
611+ , box [ centerY ]
612+ , box [ centerY ]
613+ , box [ alignBottom ]
614+ ]
615+ , colContainer [ height fill ]
616+ [ box []
617+ , box [ centerY ]
618+ , box []
619+ ]
620+ , colContainer [ height fill ]
621+ [ box []
622+ , box []
623+ , box [ centerY ]
624+ ]
625+ ]
626+ , text "top, center, bottom"
627+ , row []
628+ [ colContainer []
629+ [ box [ alignTop ]
630+ , box []
631+ , box [ alignBottom ]
632+ ]
633+ , colContainer []
634+ [ box [ alignTop ]
635+ , box [ centerY ]
636+ , box [ alignBottom ]
637+ ]
638+ , colContainer []
639+ [ box [ alignLeft, alignTop ]
640+ , box [ centerX, centerY ]
641+ , box [ alignRight, alignBottom ]
642+ ]
643+ ]
644+ , text "using text nodes"
645+ , row []
646+ [ colContainer []
647+ [ text "yup"
648+ ]
649+ , colContainer []
650+ [ text "yup"
651+ , text "yup"
652+ , text "yup"
653+ ]
654+ , colContainer []
655+ [ box [ centerY ]
656+ , text "yup"
657+ , text "yup"
658+ ]
659+ , colContainer []
660+ [ box [ alignBottom ]
661+ , text "yup"
662+ , text "yup"
663+ ]
664+ ]
665+ , el [ width fill ] (text "Column in a Row")
666+ , row [ width fill, height fill, spacing 20 ]
667+ [ box [ alignLeft, alignTop ]
668+ , column [ alignLeft, alignTop, spacing 20, width shrink ]
669+ [ box []
670+ , box []
671+ , box []
672+ ]
673+ , colContainer [ alignLeft, alignTop, height shrink ]
674+ [ box []
675+ , box []
676+ , box []
677+ ]
678+ , colContainer []
679+ [ box [ alignRight ]
680+ , box [ centerX ]
681+ , box [ alignLeft ]
682+ ]
683+ ]
684+ ]
685+
686+
687+nearbyElements : Element msg
688+nearbyElements =
689+ let
690+ transparentBox attrs =
691+ el
692+ ([ Font.color white
693+ , width (px 50)
694+ , height (px 50)
695+ , Background.color (rgba 52 101 164 0.8)
696+ ]
697+ ++ attrs
698+ )
699+ (text "hi")
700+
701+ nearby location name box =
702+ column [ spacing 32 ]
703+ [ text name
704+ , row [ height (px 100), width fill, spacing 50 ]
705+ [ box
706+ [ location
707+ (el
708+ [ width (px 20)
709+ , height (px 20)
710+ , Background.color darkCharcoal
711+ ]
712+ none
713+ )
714+ ]
715+ , box
716+ [ location
717+ (el
718+ [ width (px 20)
719+ , height (px 20)
720+ , alignLeft
721+ , Background.color darkCharcoal
722+ ]
723+ none
724+ )
725+ ]
726+ , box
727+ [ location
728+ (el
729+ [ width (px 20)
730+ , height (px 20)
731+ , centerX
732+ , Background.color darkCharcoal
733+ ]
734+ none
735+ )
736+ ]
737+ , box
738+ [ location
739+ (el
740+ [ width (px 20)
741+ , height (px 20)
742+ , alignRight
743+ , Background.color darkCharcoal
744+ ]
745+ none
746+ )
747+ ]
748+ , box
749+ [ location
750+ (el
751+ [ width (px 20)
752+ , height (px 20)
753+ , alignTop
754+ , Background.color darkCharcoal
755+ ]
756+ none
757+ )
758+ ]
759+ , box
760+ [ location
761+ (el
762+ [ width (px 20)
763+ , height (px 20)
764+ , alignBottom
765+ , Background.color darkCharcoal
766+ ]
767+ none
768+ )
769+ ]
770+ , box
771+ [ location
772+ (el
773+ [ width (px 20)
774+ , height (px 20)
775+ , centerY
776+ , Background.color darkCharcoal
777+ ]
778+ none
779+ )
780+ ]
781+ ]
782+ , text "widths/heights"
783+ , row [ height (px 100), width fill, spacing 50 ]
784+ [ box
785+ [ location
786+ (el
787+ [ width fill
788+ , height fill
789+ , Background.color darkCharcoal
790+ ]
791+ none
792+ )
793+ ]
794+ , box
795+ [ location
796+ (el
797+ [ width (px 20)
798+ , height fill
799+ , Background.color darkCharcoal
800+ ]
801+ none
802+ )
803+ ]
804+ , box
805+ [ location
806+ (el
807+ [ width fill
808+ , height (px 20)
809+ , Background.color darkCharcoal
810+ ]
811+ none
812+ )
813+ ]
814+ , box
815+ [ location
816+ (el
817+ [ width (px 20)
818+ , height shrink
819+ , Background.color darkCharcoal
820+ , Font.color white
821+ ]
822+ (text "h-shrink")
823+ )
824+ ]
825+ , box
826+ [ location
827+ (el
828+ [ width shrink
829+ , height (px 20)
830+ , Background.color darkCharcoal
831+ , Font.color white
832+ ]
833+ (text "w-shrink")
834+ )
835+ ]
836+ ]
837+ ]
838+ in
839+ column
840+ [ centerX ]
841+ [ el [] (text "Nearby Elements")
842+ , nearby above "above" box
843+ , nearby below "below" box
844+ , nearby inFront "inFront" box
845+ , nearby onRight "onRight" box
846+ , nearby onLeft "onLeft" box
847+ , nearby behind "behind" transparentBox
848+ ]
849+
850+
851+columnSpacing =
852+ let
853+ colContainer attrs children =
854+ column ([ spacing 20, width (px 100), height (px 500) ] ++ attrs) children
855+ in
856+ column
857+ []
858+ [ el [] (text "Spacing within a column")
859+ , row []
860+ [ colContainer []
861+ [ box [] ]
862+ , colContainer []
863+ [ box []
864+ , box []
865+ , box []
866+ ]
867+ , colContainer [ onRight (tinyBox []) ]
868+ [ box []
869+ , box []
870+ , box []
871+ ]
872+ ]
873+ ]
874+
875+
876+rowSpacing =
877+ let
878+ colContainer attrs children =
879+ row ([ spacing 20, width (px 500), height (px 120) ] ++ attrs) children
880+ in
881+ column
882+ []
883+ [ el [] (text "Spacing within a row")
884+ , column []
885+ [ box []
886+ , colContainer []
887+ [ box [] ]
888+ , colContainer []
889+ [ box []
890+ , box []
891+ , box []
892+ ]
893+ , colContainer [ below (tinyBox []) ]
894+ [ box []
895+ , box []
896+ , box []
897+ ]
898+ ]
899+ ]
900+
901+
902+tableElements =
903+ let
904+ data =
905+ [ { firstName = "David"
906+ , lastName = "Bowie"
907+ }
908+ , { firstName = "Florence"
909+ , lastName = "Welch"
910+ }
911+ ]
912+ in
913+ column [ spacing 20, width fill, paddingXY 1 30 ]
914+ [ text "Tables With Headers"
915+ , table []
916+ { data = data
917+ , columns =
918+ [ { header = text "First Name"
919+ , view =
920+ \row -> text row.firstName
921+ }
922+ , { header = text "Last Name"
923+ , view =
924+ \row -> text row.lastName
925+ }
926+ ]
927+ }
928+ , text "Without Headers"
929+ , table []
930+ { data = data
931+ , columns =
932+ [ { header = none
933+ , view =
934+ \row -> text row.firstName
935+ }
936+ , { header = none
937+ , view =
938+ \row -> text row.lastName
939+ }
940+ ]
941+ }
942+ , text "With Spacing and Styling"
943+ , table
944+ [ Background.color blue
945+ , spacing 20
946+ , padding 30
947+ ]
948+ { data = data
949+ , columns =
950+ [ { header =
951+ el [ Font.color white ] <|
952+ text "First Name"
953+ , view =
954+ \row -> el [ Background.color lightGrey ] <| text row.firstName
955+ }
956+ , { header = el [ Font.color white ] <| text "Last Name"
957+ , view =
958+ \row ->
959+ el [ Background.color lightGrey ] <|
960+ text row.lastName
961+ }
962+ ]
963+ }
964+ , text "Indexed Table With Spacing and Styling"
965+ , indexedTable
966+ [ Background.color blue
967+ , spacing 20
968+ , padding 30
969+ ]
970+ { data = data
971+ , columns =
972+ [ { header =
973+ el [ Font.color white ] <|
974+ text "Index"
975+ , view =
976+ \i row -> el [ Background.color lightGrey, width fill ] <| text (toString i)
977+ }
978+ , { header =
979+ el [ Font.color white ] <|
980+ text "First Name"
981+ , view =
982+ \i row -> el [ Background.color lightGrey, width fill ] <| text row.firstName
983+ }
984+ , { header = el [ Font.color white ] <| text "Last Name"
985+ , view =
986+ \i row ->
987+ el [ Background.color lightGrey, width fill ] <|
988+ text row.lastName
989+ }
990+ ]
991+ }
992+ ]
993+
994+
995+signInForm model =
996+ let
997+ label str =
998+ Input.labelLeft
999+ [ width (fillPortion 1)
1000+ , Font.alignRight
1001+ , paddingXY 12 7
1002+ , Font.bold
1003+ ]
1004+ (text str)
1005+ in
1006+ Element.column
1007+ [ width fill
1008+ , height shrink
1009+ , centerY
1010+ , centerX
1011+ , spacing 36
1012+ , padding 10
1013+ , Background.color white
1014+ ]
1015+ [ el
1016+ [ Region.heading 1
1017+ , alignLeft
1018+ , Font.size 36
1019+ ]
1020+ (text "Sign in and Choose Lunch!")
1021+ , Input.radioRow
1022+ [ width (fillPortion 4)
1023+ , spacing 15
1024+ ]
1025+ { selected = Just model.lunch
1026+ , onChange = Just (\new -> Update { model | lunch = new })
1027+ , label =
1028+ label "What would you like for lunch?"
1029+ , options =
1030+ [ Input.option Gyro (text "Gyro")
1031+ , Input.option Burrito (text "Burrito")
1032+ , Input.option Taco (text "Taco")
1033+ ]
1034+ }
1035+ , Input.radio [ width fill, spacing 15 ]
1036+ { selected = Just model.lunch
1037+ , onChange = Just (\new -> Update { model | lunch = new })
1038+ , label =
1039+ Input.labelAbove [ Font.bold ] (text "What would you like for lunch?")
1040+ , options =
1041+ [ Input.option Gyro (text "Gyro")
1042+ , Input.option Burrito (text "Burrito")
1043+ , Input.option Taco (text "Taco")
1044+ ]
1045+ }
1046+ , Input.username
1047+ [ width (fillPortion 4)
1048+ , focused
1049+ [ Border.color red
1050+ ]
1051+ , below
1052+ (el
1053+ [ Font.color red
1054+ , Font.size 14
1055+ , alignLeft
1056+ ]
1057+ (text "This one is le wrong")
1058+ )
1059+ ]
1060+ { text = model.username
1061+ , placeholder = Nothing --Just (Input.placeholder [] (text "Extra hot sauce?"))
1062+ , onChange = Just (\new -> Update { model | username = new })
1063+ , label =
1064+ label "username"
1065+ }
1066+ , Input.currentPassword
1067+ [ width (fillPortion 4)
1068+ , focused
1069+ [ Border.glow red 5
1070+ , Border.glow blue 5
1071+ ]
1072+ ]
1073+ { text = model.password
1074+ , placeholder = Nothing
1075+ , onChange = Just (\new -> Update { model | password = new })
1076+ , label = label "Password"
1077+ , show = False
1078+ }
1079+ , Input.multiline
1080+ [ height shrink
1081+ , width (fillPortion 4)
1082+ ]
1083+ { text = model.comment
1084+ , placeholder = Just (Input.placeholder [] (text "Leave a comment?"))
1085+ , onChange = Just (\new -> Update { model | comment = new })
1086+ , label =
1087+ Input.labelLeft
1088+ [ width (fillPortion 1)
1089+ , Font.alignRight
1090+ , Font.bold
1091+ , paddingXY 12 7
1092+ ]
1093+ (text "Question")
1094+ , spellcheck = False
1095+ }
1096+ , el
1097+ [ Events.onClick
1098+ (if not model.menuFocused then
1099+ Focus "my-select-menu"
1100+ else
1101+ LoseFocus "my-select-menu"
1102+ )
1103+ , below <|
1104+ Input.radio
1105+ [ width (fillPortion 4)
1106+ , htmlAttribute (Html.Attributes.id "my-select-menu")
1107+ , Events.onFocus (Update { model | menuFocused = True })
1108+ , Events.onLoseFocus (Update { model | menuFocused = False })
1109+ , Background.color white
1110+ , Border.color grey
1111+ , Border.width 1
1112+ , padding 15
1113+ , spacing 15
1114+ , pointer
1115+ , transparent True
1116+ , focused
1117+ [ transparent False
1118+ ]
1119+ ]
1120+ { selected = Just model.lunch
1121+ , onChange = Just (\new -> Update { model | lunch = new })
1122+ , label =
1123+ Input.labelAbove
1124+ [ transparent True
1125+ ]
1126+ (text "What would you like for lunch?")
1127+ , options =
1128+ [ Input.option Gyro (text "Gyro")
1129+ , Input.option Burrito (text "Burrito")
1130+ , Input.option Taco (text "Taco")
1131+ ]
1132+ }
1133+ ]
1134+ <|
1135+ el
1136+ [ alignLeft
1137+ , Font.bold
1138+ , pointer
1139+ ]
1140+ <|
1141+ case model.lunch of
1142+ Gyro ->
1143+ text "What would you like for lunch? - Gyro"
1144+
1145+ Burrito ->
1146+ text "What would you like for lunch? - Burrito"
1147+
1148+ Taco ->
1149+ text "What would you like for lunch? -Taco"
1150+ , Input.radio
1151+ [ width (fillPortion 4)
1152+ , Background.color white
1153+ , Border.color grey
1154+ , Border.width 1
1155+ , padding 15
1156+ , spacing 15
1157+ , pointer
1158+ , transparent True
1159+ , focused
1160+ [ transparent False
1161+ ]
1162+ ]
1163+ { selected = Just model.lunch
1164+ , onChange = Just (\new -> Update { model | lunch = new })
1165+ , label =
1166+ Input.labelAbove
1167+ []
1168+ <|
1169+ el [ alignLeft, Font.bold ] <|
1170+ case model.lunch of
1171+ Gyro ->
1172+ text "What would you like for lunch? - Gyro"
1173+
1174+ Burrito ->
1175+ text "What would you like for lunch? - Burrito"
1176+
1177+ Taco ->
1178+ text "What would you like for lunch? -Taco"
1179+ , options =
1180+ [ Input.option Gyro (text "Gyro")
1181+ , Input.option Burrito (text "Burrito")
1182+ , Input.option Taco (text "Taco")
1183+ ]
1184+ }
1185+ , Input.checkbox
1186+ [ Border.width 1
1187+ , focused
1188+ [ Border.glow red 5
1189+ ]
1190+ ]
1191+ { checked = model.agreeTOS
1192+ , onChange = Just (\new -> Update { model | agreeTOS = new })
1193+ , icon = Nothing
1194+ , label = Input.labelRight [] (text "Agree to Terms of Service")
1195+ }
1196+ , Input.multiline
1197+ [ height shrink
1198+ ]
1199+ { text = model.comment
1200+ , placeholder = Just (Input.placeholder [] (text "Leave a comment?"))
1201+ , onChange = Just (\new -> Update { model | comment = new })
1202+ , label =
1203+ Input.labelAbove
1204+ [ width (fillPortion 1)
1205+
1206+ -- , Font.alignRight
1207+ , Font.bold
1208+ , paddingXY 12 7
1209+ ]
1210+ (text "Question")
1211+ , spellcheck = False
1212+ }
1213+ , Input.button
1214+ [ Background.color blue
1215+ , Font.color white
1216+ , Border.color darkBlue
1217+ , paddingXY 15 5
1218+ , Border.rounded 3
1219+ , width (fillPortion 4)
1220+ ]
1221+ { onPress = Nothing
1222+ , label = el [] <| Element.text "Place your lunch order!"
1223+ }
1224+ , Input.button
1225+ [ Background.color blue
1226+ , Font.color white
1227+ , Border.color darkBlue
1228+ , paddingXY 15 5
1229+ , Border.rounded 3
1230+ , width (fillPortion 4)
1231+ , mouseDown
1232+ [ Background.color green
1233+ ]
1234+ ]
1235+ { onPress = Nothing
1236+ , label = el [] <| Element.text "Place your lunch order!"
1237+ }
1238+ ]
1239+
1240+
1241+
1242+-- , Element.row []
1243+-- [ Element.el [ Element.width Element.fill ] Element.none
1244+-- , Input.checkbox
1245+-- [ width (fillPortion 4) ]
1246+-- { checked = model.agreeTOS
1247+-- , onChange = Just (\new -> Update { model | agreeTOS = new })
1248+-- , icon = Nothing
1249+-- , label = Input.labelRight [] (text "Agree to Terms of Service")
1250+-- }
1251+-- ]
1252+-- , Input.button
1253+-- [ Background.color blue
1254+-- , Font.color white
1255+-- , Border.color darkBlue
1256+-- , paddingXY 15 5
1257+-- , Border.rounded 3
1258+-- , alignLeft
1259+-- -- , width fill
1260+-- ]
1261+-- { onPress = Nothing
1262+-- , label = Element.text "Place your lunch order!"
1263+-- }
tests/Tests/Manual/LazyPerformance.elmnew file+523
@@ -0,0 +1,523 @@
1+module Tests.Manual.LazyPerformance exposing (..)
2+
3+{-
4+
5+ We want to make sure that lazy is able to perform correctly in style-elements.
6+
7+ For the setup:
8+
9+ Render an expensive thing in html.
10+
11+ Rerender with Lazy.
12+
13+
14+
15+
16+
17+
18+-}
19+
20+import Element
21+import Element.Lazy
22+import Html exposing (Html)
23+import Html.Attributes
24+import Html.Events
25+import Html.Lazy
26+import Internal.Model
27+
28+
29+subscriptions : Model -> Sub Msg
30+subscriptions model =
31+ Sub.none
32+
33+
34+init : ( Model, Cmd Msg )
35+init =
36+ ( { renderAs = NothingPlease, count = 0 }, Cmd.none )
37+
38+
39+main : Program Never Model Msg
40+main =
41+ Html.program
42+ { init = init
43+ , view = view
44+ , update = update
45+ , subscriptions = subscriptions
46+ }
47+
48+
49+type alias Model =
50+ { renderAs : Render
51+ , count : Int
52+ }
53+
54+
55+type Render
56+ = HtmlPlease
57+ | StylePlease
58+ | NothingPlease
59+
60+
61+type Msg
62+ = RenderAs Render
63+
64+
65+update : Msg -> Model -> ( Model, Cmd Msg )
66+update msg model =
67+ case msg of
68+ RenderAs mode ->
69+ ( { model
70+ | renderAs = mode
71+ , count =
72+ if mode == model.renderAs then
73+ model.count + 1
74+ else
75+ 0
76+ }
77+ , Cmd.none
78+ )
79+
80+
81+view : Model -> Html Msg
82+view model =
83+ Html.div []
84+ [ Html.button [ Html.Events.onClick <| RenderAs HtmlPlease ] [ Html.text "Html, Please" ]
85+ , Html.button [ Html.Events.onClick <| RenderAs StylePlease ] [ Html.text "Style, Please" ]
86+ , Html.div [] [ Html.text (toString model.count) ]
87+ , case model.renderAs of
88+ HtmlPlease ->
89+ Html.div []
90+ [ staticStyleSheet
91+ , Html.Lazy.lazy viewHtml 10000
92+ ]
93+
94+ StylePlease ->
95+ Element.layout []
96+ (Element.Lazy.lazy viewStyle 10000)
97+
98+ NothingPlease ->
99+ Html.text "Nothing rendered.."
100+ ]
101+
102+
103+
104+-- on second render, js time goes down to single digit ms, small bit of updating layout, and updating layout tree.
105+
106+
107+viewHtml x =
108+ Html.div [ Html.Attributes.class "se column spacing-20-20 content-top height-fill width-fill" ]
109+ (List.repeat x (Html.div [ Html.Attributes.class "se el width-content height-content self-center-y self-center-x" ] [ Html.div [ Html.Attributes.class "se text width-fill" ] [ Html.text "hello!" ] ]))
110+
111+
112+viewStyle x =
113+ Element.column []
114+ (List.repeat x (Element.el [] (Element.text "hello!")))
115+
116+
117+staticStyleSheet =
118+ Html.node "style"
119+ []
120+ [ Html.text """
121+html {
122+ height: 100%;
123+}
124+
125+body {
126+ height: 100%;
127+}
128+
129+input {
130+ border: none;
131+}
132+
133+a {
134+ text-decoration: none;
135+ color: inherit;
136+}
137+
138+.style-elements {
139+ width: 100%;
140+ height: auto;
141+ min-height: 100%;
142+}
143+
144+.se {
145+ position: relative;
146+ display: flex;
147+ flex-direction: row;
148+ box-sizing: border-box;
149+ margin: 0;
150+ padding: 0;
151+ border-width: 0;
152+ border-style: solid;
153+ font: inherit;
154+}
155+
156+.se.above {
157+ position: absolute;
158+ display: block;
159+ top: 0;
160+ height: 0;
161+ z-index: 10;
162+}
163+
164+.se.above > .height-fill {
165+ height: auto;
166+}
167+
168+.se.below {
169+ position: absolute;
170+ display: block;
171+ bottom: 0;
172+ height: 0;
173+ z-index: 10;
174+}
175+
176+.se.below > .height-fill {
177+ height: auto;
178+}
179+
180+.se.bold {
181+ font-weight: 700;
182+}
183+
184+.se.border-dashed {
185+ border-style: dashed;
186+}
187+
188+.se.border-dotted {
189+ border-style: dotted;
190+}
191+
192+.se.border-none {
193+ border-width: 0;
194+}
195+
196+.se.border-solid {
197+ border-style: solid;
198+}
199+
200+.se.italic {
201+ font-style: italic;
202+}
203+
204+.se.on-left {
205+ position: absolute;
206+ display: block;
207+ right: 100%;
208+ width: 0;
209+ z-index: 10;
210+}
211+
212+.se.on-right {
213+ position: absolute;
214+ display: block;
215+ left: 100%;
216+ width: 0;
217+ z-index: 10;
218+}
219+
220+.se.overlay {
221+ position: absolute;
222+ display: block;
223+ left: 0;
224+ top: 0;
225+ z-index: 10;
226+}
227+
228+.se.strike {
229+ text-decoration: line-through;
230+}
231+
232+.se.text-center {
233+ text-align: center;
234+}
235+
236+.se.text-justify {
237+ text-align: justify;
238+}
239+
240+.se.text-justify-all {
241+ text-align: justify-all;
242+}
243+
244+.se.text-left {
245+ text-align: left;
246+}
247+
248+.se.text-light {
249+ font-weight: 300;
250+}
251+
252+.se.text-right {
253+ text-align: right;
254+}
255+
256+.se.underline {
257+ text-decoration: underline;
258+}
259+
260+.se.width-content {
261+ width: auto;
262+}
263+
264+.text {
265+ white-space: pre;
266+ display: inline-block;
267+}
268+
269+.spacer + .se {
270+ margin-top: 0;
271+ margin-left: 0;
272+}
273+
274+.el {
275+ display: flex;
276+ flex-direction: row;
277+}
278+
279+.el > .height-fill {
280+ height: 100%;
281+}
282+
283+.el > .se.self-bottom {
284+ align-self: flex-end;
285+}
286+
287+.el > .se.self-center-x {
288+ margin-left: auto;
289+ margin-right: auto;
290+}
291+
292+.el > .se.self-center-y {
293+ align-self: center;
294+}
295+
296+.el > .se.self-left {
297+ margin-right: auto;
298+}
299+
300+.el > .se.self-right {
301+ margin-left: auto;
302+}
303+
304+.el > .se.self-top {
305+ align-self: flex-start;
306+}
307+
308+.el > .width-fill {
309+ width: 100%;
310+}
311+
312+.el.content-bottom {
313+ align-items: flex-end;
314+}
315+
316+.el.content-center-x {
317+ justify-content: center;
318+}
319+
320+.el.content-center-y {
321+ align-items: center;
322+}
323+
324+.el.content-left {
325+ justify-content: flex-start;
326+}
327+
328+.el.content-right {
329+ justify-content: flex-end;
330+}
331+
332+.el.content-top {
333+ align-items: flex-start;
334+}
335+
336+.nearby {
337+ position: absolute;
338+ width: 100%;
339+ height: 100%;
340+ pointer-events: none;
341+}
342+
343+.row {
344+ display: flex;
345+ flex-direction: row;
346+}
347+
348+.row > .height-fill {
349+ height: 100%;
350+}
351+
352+.row > .se.self-bottom {
353+ align-self: flex-end;
354+}
355+
356+.row > .se.self-center-y {
357+ align-self: center;
358+}
359+
360+.row > .se.self-top {
361+ align-self: flex-start;
362+}
363+
364+.row > .width-fill {
365+ flex-grow: 1;
366+}
367+
368+.row.content-bottom {
369+ align-items: flex-end;
370+}
371+
372+.row.content-center-x {
373+ justify-content: center;
374+}
375+
376+.row.content-center-y {
377+ align-items: center;
378+}
379+
380+.row.content-left {
381+ justify-content: flex-start;
382+}
383+
384+.row.content-right {
385+ justify-content: flex-end;
386+}
387+
388+.row.content-top {
389+ align-items: flex-start;
390+}
391+
392+.row.space-evenly {
393+ justify-content: space-between;
394+}
395+
396+.column {
397+ display: flex;
398+ flex-direction: column;
399+}
400+
401+.column > .height-fill {
402+ flex-grow: 1;
403+}
404+
405+.column > .se.self-center-x {
406+ align-self: center;
407+}
408+
409+.column > .se.self-left {
410+ align-self: flex-start;
411+}
412+
413+.column > .se.self-right {
414+ align-self: flex-end;
415+}
416+
417+.column > .width-fill {
418+ width: 100%;
419+}
420+
421+.column.content-bottom {
422+ justify-content: flex-end;
423+}
424+
425+.column.content-center-x {
426+ align-items: center;
427+}
428+
429+.column.content-center-y {
430+ justify-content: center;
431+}
432+
433+.column.content-left {
434+ align-items: flex-start;
435+}
436+
437+.column.content-right {
438+ align-items: flex-end;
439+}
440+
441+.column.content-top {
442+ justify-content: flex-start;
443+}
444+
445+.page {
446+ display: block;
447+}
448+
449+.page > .se.self-left {
450+ float: left;
451+}
452+
453+.page > .se.self-left:after: {
454+ content: "";
455+ display: table;
456+ clear: both;
457+}
458+
459+.page > .se.self-left:first-child + .se {
460+ margin: 0 !important;
461+}
462+
463+.page > .se.self-right {
464+ float: right;
465+}
466+
467+.page > .se.self-right:after: {
468+ content: "";
469+ display: table;
470+ clear: both;
471+}
472+
473+.page > .se.self-right:first-child + .se {
474+ margin: 0 !important;
475+}
476+
477+.paragraph {
478+ display: block;
479+}
480+
481+.paragraph > .column {
482+ display: inline-flex;
483+}
484+
485+.paragraph > .el {
486+ display: inline-flex;
487+}
488+
489+.paragraph > .el > .text {
490+ display: inline;
491+ white-space: normal;
492+}
493+
494+.paragraph > .grid {
495+ display: inline-grid;
496+}
497+
498+.paragraph > .row {
499+ display: inline-flex;
500+}
501+
502+.paragraph > .se.self-left {
503+ float: left;
504+}
505+
506+.paragraph > .se.self-right {
507+ float: right;
508+}
509+
510+.paragraph > .text {
511+ display: inline;
512+ white-space: normal;
513+}
514+
515+.hidden {
516+ display: none;
517+}
518+
519+.bg-52-101-164-100{background-color:rgba(52,101,164,1)}
520+.text-color-255-255-255-100{color:rgba(255,255,255,1)}
521+.font-size-20{font-size:20px}
522+.font-opensansgeorgiaserif{font-family:"Open Sans", "georgia", serif}
523+""" ]
tests/Tests/Manual/OtherScrollbars.elmnew file+36
@@ -0,0 +1,36 @@
1+module Tests.Manual.OtherScrollbars exposing (main)
2+
3+import Element exposing (..)
4+import Element.Background as Bg
5+import Html exposing (Html)
6+import Tests.Palette as Palette
7+
8+
9+main : Html msg
10+main =
11+ layout [ height Element.shrink ] <|
12+ row
13+ [ height fill
14+ ]
15+ [ Element.el [ scrollbarY, width fill, height fill ] <|
16+ column
17+ [ height <| px 1000
18+ , Bg.color Color.red
19+ , width fill
20+ ]
21+ []
22+ , Element.el [ scrollbarY, width fill, height fill ] <|
23+ column
24+ [ height <| px 1500
25+ , Bg.color Color.green
26+ , width fill
27+ ]
28+ []
29+ , Element.el [ scrollbarY, width fill, height fill ] <|
30+ column
31+ [ height <| px 2000
32+ , Bg.color Color.blue
33+ , width fill
34+ ]
35+ []
36+ ]
tests/Tests/Manual/Overflow.elmnew file+16
@@ -0,0 +1,16 @@
1+module Main exposing (main)
2+
3+import Tests.Palette as Palette
4+import Element exposing (..)
5+import Element.Background as Bg
6+import Html exposing (Html)
7+
8+
9+main : Html msg
10+main =
11+ layout [ height Element.shrink ] <|
12+ row [ height fill ]
13+ [ Element.el [ scrollbarY, width fill, height fill ] <| column [ height <| px 1000, Bg.color Color.red ] []
14+ , Element.el [ scrollbarY, width fill, height fill ] <| column [ height <| px 1500, Bg.color Color.green ] []
15+ , Element.el [ scrollbarY, width fill, height fill ] <| column [ height <| px 2000, Bg.color Color.blue ] []
16+ ]
tests/Tests/Manual/Scrollbars.elmnew file+62
@@ -0,0 +1,62 @@
1+module Tests.Manual.Scrollbars exposing (main)
2+
3+import Tests.Palette as Palette
4+import Element as El exposing (Element)
5+import Element.Background as Background
6+import Element.Border as Border
7+import Element.Events as Events
8+import Element.Font as Font
9+import Html exposing (Html)
10+
11+
12+main : Html msg
13+main =
14+ El.layout [ El.height El.shrink ] <|
15+ El.column
16+ [ --El.height El.fill
17+ El.width El.fill
18+ ]
19+ [ header
20+ , content
21+ ]
22+
23+
24+header =
25+ El.el
26+ [ El.height (El.px 80)
27+ , Background.color Color.red
28+ , El.width El.fill
29+ ]
30+ El.none
31+
32+
33+content =
34+ El.column
35+ [ --El.height El.fill
36+ El.scrollbarY
37+ , El.width El.fill
38+ ]
39+ [ item
40+ , item
41+ , item
42+ , item
43+ , item
44+ , item
45+ , item
46+ , item
47+ ]
48+
49+
50+item =
51+ El.column
52+ [ El.alignLeft
53+ , Background.color Color.green
54+ , El.paddingXY 10 0
55+ , El.width El.fill
56+ ]
57+ [ El.el
58+ [ El.height (El.px 500)
59+ , El.width El.fill
60+ ]
61+ (El.text "some content")
62+ ]
tests/Tests/Manual/ScrollbarsPls.elmnew file+58
@@ -0,0 +1,58 @@
1+module Main exposing (main)
2+
3+import Tests.Palette as Palette
4+import Element as El exposing (Element)
5+import Element.Background as Background
6+import Element.Border as Border
7+import Element.Events as Events
8+import Element.Font as Font
9+import Html exposing (Html)
10+
11+
12+main : Html msg
13+main =
14+ El.layout [ El.height El.shrink ] <|
15+ El.column
16+ [--El.height El.fill
17+ ]
18+ [ header
19+ , content
20+ ]
21+
22+
23+header =
24+ El.el
25+ [ El.height (El.px 80)
26+ , Background.color Color.red
27+ , El.width El.fill
28+ ]
29+ El.none
30+
31+
32+content =
33+ El.column
34+ [ --El.height El.fill
35+ El.scrollbarY
36+ ]
37+ [ item
38+ , item
39+ , item
40+ , item
41+ , item
42+ , item
43+ , item
44+ , item
45+ ]
46+
47+
48+item =
49+ El.column
50+ [ El.alignLeft
51+ , Background.color Color.green
52+ , El.paddingXY 10 0
53+ ]
54+ [ El.el
55+ [ El.height (El.px 500)
56+ ]
57+ (El.text "some content")
58+ ]
tests/Tests/Manual/TextLayout.elmnew file+43
@@ -0,0 +1,43 @@
1+module Main exposing (main)
2+
3+import Tests.Palette as Palette exposing (..)
4+import Element exposing (..)
5+import Element.Background as Background
6+import Element.Font as Font
7+import Element.Input as Input
8+import Html exposing (Html)
9+
10+
11+main : Html msg
12+main =
13+ layout
14+ [ Font.size 20
15+ , Font.lineHeight 1.3
16+ , Font.color darkCharcoal
17+ ]
18+ <|
19+ textColumn
20+ [ spacing 56 ]
21+ [ paragraph []
22+ [ el
23+ [ alignLeft
24+ , Font.size 60
25+ , Font.lineHeight 0.8
26+ , moveUp 5
27+ , paddingEach
28+ { right = 5
29+ , top = 0
30+ , bottom = 0
31+ , left = 0
32+ }
33+ ]
34+ (text "S")
35+ , text "tylish elephants are on the loose. By day they prowl, by night they sleep."
36+ , text "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam dictum turpis sem, sed commodo felis blandit quis. Etiam gravida velit a felis accumsan, ut finibus risus bibendum. Donec eget augue lorem. Curabitur neque sem, fermentum sed nisl at, semper feugiat nibh. Morbi mollis tempor turpis. Suspendisse est urna, sodales sed molestie semper, rhoncus eu nisl. Fusce ultrices leo sit amet arcu maximus, in scelerisque ante egestas. Suspendisse dictum augue eu venenatis molestie. Duis ullamcorper magna ut ex placerat fringilla. In mollis efficitur tellus, sit amet tincidunt mauris accumsan non. Morbi vel dapibus velit. Nullam quam sem, mattis vel feugiat et, sagittis vulputate libero. Maecenas posuere dui semper mollis hendrerit. Sed sit amet dolor tempus, tristique enim in, porttitor sem. Ut lobortis egestas lorem ut ornare."
37+ ]
38+ , paragraph []
39+ [ text "Vivamus luctus ex eros, ac accumsan lectus ornare eget. Phasellus mattis dapibus tortor, ut rutrum dolor pulvinar eget. Sed vulputate metus vel sapien dignissim, ac ornare justo porta. Praesent dolor leo, varius id nisl ut, pretium dapibus purus. Curabitur at dolor et augue accumsan venenatis et non urna. Aenean rutrum augue nulla, a tempus sapien aliquam vel. Cras sodales nulla sed dolor mollis, ac consectetur lorem consectetur." ]
40+ , paragraph []
41+ [ text "Maecenas ultricies felis ipsum, quis rhoncus libero malesuada eget. Maecenas sagittis quis ipsum at convallis. Suspendisse potenti. Donec feugiat ligula nunc, id pellentesque erat tristique sed. In hac habitasse platea dictumst. Sed rutrum, urna vel efficitur pretium, lacus risus blandit turpis, sed finibus nisi odio pretium diam. Etiam elementum ante non nibh semper, quis gravida elit fermentum."
42+ ]
43+ ]
tests/Tests/Nearby.elmnew file+393
@@ -0,0 +1,393 @@
1+module Tests.Nearby exposing (view)
2+
3+{-| Testing nearby elements such as those defined with `above`, `below`, etc.
4+-}
5+
6+import Html
7+import Testable
8+import Testable.Element exposing (..)
9+import Testable.Element.Background as Background
10+import Testable.Element.Font as Font
11+import Testable.Runner
12+import Tests.Palette as Palette exposing (..)
13+
14+
15+box attrs =
16+ el
17+ ([ width (px 50)
18+ , height (px 50)
19+ , Background.color blue
20+ ]
21+ ++ attrs
22+ )
23+ none
24+
25+
26+p attrs =
27+ paragraph
28+ ([ Background.color blue
29+ , Font.color white
30+ , padding 20
31+ ]
32+ ++ attrs
33+ )
34+ [ text "Lorem Ipsum or something or other." ]
35+
36+
37+{-| -}
38+main : Html.Html msg
39+main =
40+ Testable.Runner.show view
41+
42+
43+{-| -}
44+view : Testable.Element msg
45+view =
46+ let
47+ transparentBox attrs =
48+ el
49+ ([ Font.color white
50+ , width (px 50)
51+ , height (px 50)
52+ , Background.color (rgba 52 101 164 0.8)
53+ ]
54+ ++ attrs
55+ )
56+ (text "hi")
57+
58+ -- single location name box =
59+ -- row [ height (px 100), width fill, spacing 50 ]
60+ -- [ box
61+ -- [ location
62+ -- (el
63+ -- [ width (px 20)
64+ -- , height (px 20)
65+ -- , Background.color darkCharcoal
66+ -- ]
67+ -- none
68+ -- )
69+ -- ]
70+ -- , box
71+ -- [ location
72+ -- (el
73+ -- [ width (px 20)
74+ -- , height (px 20)
75+ -- , alignLeft
76+ -- , Background.color darkCharcoal
77+ -- ]
78+ -- none
79+ -- )
80+ -- ]
81+ -- ]
82+ little name attrs =
83+ el
84+ ([ label name
85+ , width (px 5)
86+ , height (px 5)
87+ , Background.color darkCharcoal
88+ ]
89+ ++ attrs
90+ )
91+ none
92+
93+ nearby location name render =
94+ column [ spacing 32, label "column" ]
95+ [ el [ padding 20, Background.color green, Font.color white ] (text name)
96+ , row [ height (px 100), width fill, spacing 50 ]
97+ [ render
98+ [ location
99+ (el
100+ [ label name
101+ , width (px 20)
102+ , height (px 20)
103+ , Background.color darkCharcoal
104+ ]
105+ none
106+ )
107+ ]
108+ , render
109+ [ location
110+ (el
111+ [ label name
112+ , width (px 20)
113+ , height (px 20)
114+ , alignLeft
115+ , Background.color darkCharcoal
116+ ]
117+ none
118+ )
119+ ]
120+ , render
121+ [ location
122+ (el
123+ [ label name
124+ , width (px 20)
125+ , height (px 20)
126+ , centerX
127+ , Background.color darkCharcoal
128+ ]
129+ none
130+ )
131+ ]
132+ , render
133+ [ location
134+ (el
135+ [ label name
136+ , width (px 20)
137+ , height (px 20)
138+ , alignRight
139+ , Background.color darkCharcoal
140+ ]
141+ none
142+ )
143+ ]
144+ , render
145+ [ location
146+ (el
147+ [ label name
148+ , width (px 20)
149+ , height (px 20)
150+ , alignTop
151+ , Background.color darkCharcoal
152+ ]
153+ none
154+ )
155+ ]
156+ , render
157+ [ location
158+ (el
159+ [ label name
160+ , width (px 20)
161+ , height (px 20)
162+ , centerY
163+ , Background.color darkCharcoal
164+ ]
165+ none
166+ )
167+ ]
168+ , render
169+ [ location
170+ (el
171+ [ label name
172+ , width (px 20)
173+ , height (px 20)
174+ , alignBottom
175+ , Background.color darkCharcoal
176+ ]
177+ none
178+ )
179+ ]
180+ ]
181+ , text "widths/heights"
182+ , row [ height (px 100), width fill, spacing 50, label "Row" ]
183+ [ render
184+ [ location
185+ (el
186+ [ label name
187+ , width fill
188+ , height fill
189+ , Background.color darkCharcoal
190+ ]
191+ none
192+ )
193+ ]
194+ , render
195+ [ location
196+ (el
197+ [ label name
198+ , width (px 20)
199+ , height fill
200+ , Background.color darkCharcoal
201+ ]
202+ none
203+ )
204+ ]
205+ , render
206+ [ label "render"
207+ , location
208+ (el
209+ [ label name
210+ , width fill
211+ , height (px 20)
212+ , Background.color darkCharcoal
213+ ]
214+ none
215+ )
216+ ]
217+ , render
218+ [ location
219+ (el
220+ [ label name
221+ , width (px 20)
222+ , height shrink
223+ , Background.color darkCharcoal
224+ , Font.color white
225+ ]
226+ (text "h-shrink")
227+ )
228+ ]
229+ , render
230+ [ location
231+ (el
232+ [ label name
233+ , width shrink
234+ , height (px 20)
235+ , Background.color darkCharcoal
236+ , Font.color white
237+ ]
238+ (text "w-shrink")
239+ )
240+ ]
241+ ]
242+ , text "on paragraph"
243+ , row [ width fill, spacing 50, label "Row" ]
244+ [ p
245+ [ location
246+ (el
247+ [ label name
248+ , width (px 20)
249+ , height (px 20)
250+ , Background.color darkCharcoal
251+ ]
252+ none
253+ )
254+ ]
255+ , p
256+ [ location
257+ (el
258+ [ label name
259+ , width (px 20)
260+ , height (px 20)
261+ , alignLeft
262+ , Background.color darkCharcoal
263+ ]
264+ none
265+ )
266+ ]
267+ , p
268+ [ location
269+ (el
270+ [ label name
271+ , width (px 20)
272+ , height (px 20)
273+ , centerX
274+ , Background.color darkCharcoal
275+ ]
276+ none
277+ )
278+ ]
279+ , p
280+ [ location
281+ (el
282+ [ label name
283+ , width (px 20)
284+ , height (px 20)
285+ , alignRight
286+ , Background.color darkCharcoal
287+ ]
288+ none
289+ )
290+ ]
291+ , p
292+ [ location
293+ (el
294+ [ label name
295+ , width (px 20)
296+ , height (px 20)
297+ , alignTop
298+ , Background.color darkCharcoal
299+ ]
300+ none
301+ )
302+ ]
303+ , p
304+ [ location
305+ (el
306+ [ label name
307+ , width (px 20)
308+ , height (px 20)
309+ , centerY
310+ , Background.color darkCharcoal
311+ ]
312+ none
313+ )
314+ ]
315+ , p
316+ [ location
317+ (el
318+ [ label name
319+ , width (px 20)
320+ , height (px 20)
321+ , alignBottom
322+ , Background.color darkCharcoal
323+ ]
324+ none
325+ )
326+ ]
327+ ]
328+ ]
329+
330+ master =
331+ el [ padding 20 ] <|
332+ box
333+ [ above (little "above-left" [ alignLeft ])
334+ , above (little "above-center" [ centerX ])
335+ , above (little "above-right" [ alignRight ])
336+ , below (little "below-left" [ alignLeft ])
337+ , below (little "below-center" [ centerX ])
338+ , below (little "below-right" [ alignRight ])
339+ , onRight (little "onRight-left" [ alignTop ])
340+ , onRight (little "onRight-center" [ centerY ])
341+ , onRight (little "onRight-right" [ alignBottom ])
342+ , onLeft (little "onLeft-left" [ alignTop ])
343+ , onLeft (little "onLeft-center" [ centerY ])
344+ , onLeft (little "onLeft-right" [ alignBottom ])
345+ , inFront (little "infront-left-top" [ alignTop, alignLeft ])
346+ , inFront (little "infront-center-top" [ alignTop, centerX ])
347+ , inFront (little "infront-right-top" [ alignTop, alignRight ])
348+ , inFront (little "infront-left-center" [ centerY, alignLeft ])
349+ , inFront (little "infront-center-center" [ centerY, centerX ])
350+ , inFront (little "infront-right-center" [ centerY, alignRight ])
351+ , inFront (little "infront-left-bottom" [ alignBottom, alignLeft ])
352+ , inFront (little "infront-center-bottom" [ alignBottom, centerX ])
353+ , inFront (little "infront-right-bottom" [ alignBottom, alignRight ])
354+ ]
355+
356+ masterParagraph =
357+ el [ padding 20 ] <|
358+ p
359+ [ above (little "above-left" [ alignLeft ])
360+ , above (little "above-center" [ centerX ])
361+ , above (little "above-right" [ alignRight ])
362+ , below (little "below-left" [ alignLeft ])
363+ , below (little "below-center" [ centerX ])
364+ , below (little "below-right" [ alignRight ])
365+ , onRight (little "onRight-left" [ alignTop ])
366+ , onRight (little "onRight-center" [ centerY ])
367+ , onRight (little "onRight-right" [ alignBottom ])
368+ , onLeft (little "onLeft-left" [ alignTop ])
369+ , onLeft (little "onLeft-center" [ centerY ])
370+ , onLeft (little "onLeft-right" [ alignBottom ])
371+ , inFront (little "infront-left-top" [ alignTop, alignLeft ])
372+ , inFront (little "infront-center-top" [ alignTop, centerX ])
373+ , inFront (little "infront-right-top" [ alignTop, alignRight ])
374+ , inFront (little "infront-left-center" [ centerY, alignLeft ])
375+ , inFront (little "infront-center-center" [ centerY, centerX ])
376+ , inFront (little "infront-right-center" [ centerY, alignRight ])
377+ , inFront (little "infront-left-bottom" [ alignBottom, alignLeft ])
378+ , inFront (little "infront-center-bottom" [ alignBottom, centerX ])
379+ , inFront (little "infront-right-bottom" [ alignBottom, alignRight ])
380+ ]
381+ in
382+ column
383+ [ centerX, label "Nearby Elements", spacing 100 ]
384+ [ master
385+ , masterParagraph
386+ , nearby above "above" box
387+ , nearby below "below" box
388+ , nearby inFront "inFront" box
389+ , nearby onRight "onRight" box
390+ , nearby onLeft "onLeft" box
391+ , nearby behind "behind" transparentBox
392+ , text "all nearbys, all alignments"
393+ ]
tests/Tests/Palette.elmnew file+41
@@ -0,0 +1,41 @@
1+module Tests.Palette exposing (..)
2+
3+{-| -}
4+
5+import Element
6+
7+
8+rgba r g b a =
9+ Element.rgba (r / 255) (g / 255) (b / 255) a
10+
11+
12+darkGrey =
13+ Element.rgb 0.8 0.8 0.8
14+
15+
16+darkCharcoal =
17+ Element.rgb 0.9 0.9 0.9
18+
19+
20+lightGrey =
21+ Element.rgb 0.5 0.5 0.5
22+
23+
24+grey =
25+ darkGrey
26+
27+
28+red =
29+ Element.rgb 1 0 0
30+
31+
32+green =
33+ Element.rgb 0 1 0
34+
35+
36+white =
37+ Element.rgb 1 1 1
38+
39+
40+blue =
41+ Element.rgb 0 0 1
tests/Tests/RowAlignment.elmnew file+212
@@ -0,0 +1,212 @@
1+module Tests.RowAlignment exposing (..)
2+
3+import Generator
4+import Html
5+import Testable
6+import Testable.Element as Element exposing (..)
7+import Testable.Element.Background as Background
8+import Testable.Element.Font as Font
9+import Testable.Runner
10+import Tests.Palette as Palette exposing (..)
11+
12+
13+{-| -}
14+main : Html.Html msg
15+main =
16+ Testable.Runner.show view
17+
18+
19+box attrs =
20+ el
21+ ([ width (px 50)
22+ , height (px 50)
23+ , Background.color blue
24+ ]
25+ ++ attrs
26+ )
27+ none
28+
29+
30+view =
31+ let
32+ rowContainer attrs children =
33+ row
34+ ([ spacing 20
35+ , height (px 100)
36+ , Background.color lightGrey
37+ ]
38+ ++ attrs
39+ )
40+ children
41+ in
42+ column [ width (px 500), spacing 20 ]
43+ [ el [] (text "Alignment Within a Row")
44+ , column [ spacing 20 ] <|
45+ Generator.sizes <|
46+ \resizeable ->
47+ rowContainer
48+ [ label "single child" ]
49+ [ resizeable [] none ]
50+ , column [ spacing 20 ] <|
51+ Generator.sizes <|
52+ \resizeable ->
53+ rowContainer
54+ [ label "single child" ]
55+ [ resizeable [ centerX ] none ]
56+ , column [ spacing 20 ] <|
57+ Generator.sizes <|
58+ \resizeable ->
59+ rowContainer [ label "single child" ]
60+ [ resizeable [ alignRight ] none ]
61+ , column [ spacing 20 ] <|
62+ Generator.sizes <|
63+ \resizeable ->
64+ rowContainer []
65+ [ resizeable [] none
66+ , resizeable [] none
67+ , resizeable [] none
68+ ]
69+ , column [ spacing 20 ] <|
70+ Generator.sizes <|
71+ \resizeable ->
72+ rowContainer []
73+ [ resizeable [] none
74+ , resizeable [] none
75+ , resizeable [ alignRight, label "Right Child in Row" ] none
76+ ]
77+ , column [ spacing 20 ] <|
78+ Generator.sizes <|
79+ \resizeable ->
80+ rowContainer
81+ []
82+ [ resizeable [] none
83+ , resizeable [ alignRight, label "Middle Child in Row" ] none
84+ , resizeable [] none
85+ ]
86+ , column [ spacing 20 ] <|
87+ Generator.sizes <|
88+ \resizeable ->
89+ rowContainer []
90+ [ resizeable [ alignRight, label "Left Child in Row" ] none
91+ , resizeable [] none
92+ , resizeable [] none
93+ ]
94+ , text "center X"
95+ , column [ spacing 20 ] <|
96+ Generator.sizes <|
97+ \resizeable ->
98+ rowContainer []
99+ [ resizeable [ centerX, label "Left Child in Row" ] none
100+ , resizeable [] none
101+ , resizeable [] none
102+ ]
103+ , column [ spacing 20 ] <|
104+ Generator.sizes <|
105+ \resizeable ->
106+ rowContainer []
107+ [ resizeable [] none
108+ , resizeable [ centerX, label "Middle Child in Row" ] none
109+ , resizeable [] none
110+ ]
111+ , column [ spacing 20 ] <|
112+ Generator.sizes <|
113+ \resizeable ->
114+ rowContainer []
115+ [ resizeable [] none
116+ , resizeable [] none
117+ , resizeable [ centerX, label "Right Child in Row" ] none
118+ ]
119+ , column [ spacing 20 ] <|
120+ Generator.sizes <|
121+ \resizeable ->
122+ rowContainer []
123+ [ resizeable [] none
124+ , resizeable [] none
125+ , resizeable [ centerX, label "Middle-Right Child in Row" ] none
126+ , resizeable [] none
127+ ]
128+ , column [ spacing 20 ] <|
129+ Generator.sizes <|
130+ \resizeable ->
131+ rowContainer []
132+ [ resizeable [] none
133+ , resizeable [] none
134+ , resizeable [ centerX, label "Middle-Right Child in Row" ] none
135+ , resizeable [ centerX, label "Middle-Right Child in Row" ] none
136+ , resizeable [] none
137+ ]
138+ , column [ spacing 20 ] <|
139+ Generator.sizes <|
140+ \resizeable ->
141+ rowContainer []
142+ [ resizeable [] none
143+ , resizeable [] none
144+ , resizeable [ centerX, label "Middle-Right Child in Row" ] none
145+ , resizeable [ centerX, label "Middle-Right Child in Row" ] none
146+ , resizeable [ centerX, label "Middle-Right Child in Row" ] none
147+ , resizeable [] none
148+ ]
149+ , text "left x right"
150+ , column [ spacing 20 ] <|
151+ Generator.sizes <|
152+ \resizeable ->
153+ rowContainer []
154+ [ resizeable [ alignLeft, label "Left Child in Row" ] none
155+ , resizeable [] none
156+ , resizeable [ alignRight, label "Right Child in Row" ] none
157+ ]
158+ , text "left center right"
159+ , column [ spacing 20 ] <|
160+ Generator.sizes <|
161+ \resizeable ->
162+ rowContainer []
163+ [ resizeable [ alignLeft, label "Left Child in Row" ] none
164+ , resizeable [ centerX, label "Middle Child in Row" ] none
165+ , resizeable [ alignRight, label "Right Child in Row" ] none
166+ ]
167+ , text "vertical alignment"
168+ , column [ spacing 20 ] <|
169+ Generator.sizes <|
170+ \resizeable ->
171+ rowContainer []
172+ [ resizeable [ alignTop, label "Left Child in Row" ] none
173+ , resizeable [ centerY, label "Middle Child in Row" ] none
174+ , resizeable [ alignBottom, label "Right Child in Row" ] none
175+ ]
176+ , text "x and y alignments"
177+ , column [ spacing 20 ] <|
178+ Generator.sizes <|
179+ \resizeable ->
180+ rowContainer []
181+ [ resizeable [ alignLeft, alignTop, label "Left Child" ] none
182+ , resizeable [ centerX, centerY, label "Middle Child" ] none
183+ , resizeable [ alignRight, alignBottom, label "Right Child" ] none
184+ ]
185+ , text "align Top and X alignments "
186+ , column [ spacing 20 ] <|
187+ Generator.sizes <|
188+ \resizeable ->
189+ rowContainer []
190+ [ resizeable [ alignLeft, alignTop, label "Left Child" ] none
191+ , resizeable [ centerX, alignTop, label "Middle Child" ] none
192+ , resizeable [ alignRight, alignTop, label "Right Child" ] none
193+ ]
194+ , text "align Bottom and X alignments "
195+ , column [ spacing 20 ] <|
196+ Generator.sizes <|
197+ \resizeable ->
198+ rowContainer []
199+ [ resizeable [ alignLeft, alignBottom, label "Left Child" ] none
200+ , resizeable [ centerX, alignBottom, label "Middle Child" ] none
201+ , resizeable [ alignRight, alignBottom, label "Right Child" ] none
202+ ]
203+ , text "centerY and X alignments "
204+ , column [ spacing 20 ] <|
205+ Generator.sizes <|
206+ \resizeable ->
207+ rowContainer []
208+ [ resizeable [ alignLeft, centerY, label "Left Child" ] none
209+ , resizeable [ centerX, centerY, label "Middle Child" ] none
210+ , resizeable [ alignRight, centerY, label "Right Child" ] none
211+ ]
212+ ]
tests/Tests/RowSpacing.elmnew file+69
@@ -0,0 +1,69 @@
1+module Tests.RowSpacing exposing (..)
2+
3+import Html
4+import Testable
5+import Testable.Element as Element exposing (..)
6+import Testable.Element.Background as Background
7+import Testable.Element.Font as Font
8+import Testable.Runner
9+import Tests.Palette as Palette exposing (..)
10+
11+
12+{-| -}
13+main : Html.Html msg
14+main =
15+ Testable.Runner.show view
16+
17+
18+box attrs =
19+ el
20+ ([ width (px 50)
21+ , height (px 50)
22+ , Background.color blue
23+ ]
24+ ++ attrs
25+ )
26+ none
27+
28+
29+tinyBox attrs =
30+ el
31+ ([ width (px 20)
32+ , height (px 20)
33+
34+ -- , centerY
35+ , Background.color darkCharcoal
36+ ]
37+ ++ attrs
38+ )
39+ none
40+
41+
42+container =
43+ el [ width (px 100), height (px 100) ]
44+
45+
46+view =
47+ let
48+ colContainer attrs children =
49+ row ([ spacing 20, width (px 500), height (px 120) ] ++ attrs) children
50+ in
51+ column
52+ []
53+ [ el [] (text "Spacing within a row")
54+ , column []
55+ [ box []
56+ , colContainer []
57+ [ box [] ]
58+ , colContainer []
59+ [ box []
60+ , box []
61+ , box []
62+ ]
63+ , colContainer [ below (tinyBox []) ]
64+ [ box []
65+ , box []
66+ , box []
67+ ]
68+ ]
69+ ]
tests/Tests/Run.elmnew file+28
@@ -0,0 +1,28 @@
1+port module Tests.Run exposing (..)
2+
3+{-| -}
4+
5+import Testable.Runner
6+import Tests.Basic
7+import Tests.ColumnAlignment
8+import Tests.ColumnSpacing
9+import Tests.ElementAlignment
10+import Tests.Nearby
11+import Tests.RowAlignment
12+import Tests.RowSpacing
13+import Tests.Transparency
14+
15+
16+main : Testable.Runner.TestableProgram
17+main =
18+ Testable.Runner.program
19+ [ --Tuple.pair "Basic Element" Tests.Basic.view
20+ Tuple.pair "Nearby" Tests.Nearby.view
21+ , Tuple.pair "Element Alignment" Tests.ElementAlignment.view
22+ , Tuple.pair "Transparency" Tests.Transparency.view
23+ , Tuple.pair "Column Alignment" Tests.ColumnAlignment.view
24+
25+ -- , Tuple.pair "Row Alignment" Tests.RowAlignment.view
26+ , Tuple.pair "Column Spacing" Tests.ColumnSpacing.view
27+ , Tuple.pair "Row Spacing" Tests.RowSpacing.view
28+ ]
tests/Tests/Table.elmnew file+123
@@ -0,0 +1,123 @@
1+module Tests.Table exposing (..)
2+
3+import Tests.Palette as Palette exposing (..)
4+import Html
5+import Testable
6+import Testable.Element as Element exposing (..)
7+import Testable.Element.Background as Background
8+import Testable.Element.Font as Font
9+import Testable.Runner
10+
11+
12+{-| -}
13+main : Html.Html msg
14+main =
15+ Testable.Runner.show view
16+
17+
18+box attrs =
19+ el
20+ ([ width (px 50)
21+ , height (px 50)
22+ , Background.color blue
23+ ]
24+ ++ attrs
25+ )
26+ none
27+
28+
29+container =
30+ el [ width (px 100), height (px 100) ]
31+
32+
33+view =
34+ let
35+ data =
36+ [ { firstName = "David"
37+ , lastName = "Bowie"
38+ }
39+ , { firstName = "Florence"
40+ , lastName = "Welch"
41+ }
42+ ]
43+ in
44+ column [ spacing 20, width fill, paddingXY 1 30 ]
45+ [ text "Tables With Headers"
46+ , table []
47+ { data = data
48+ , columns =
49+ [ { header = text "First Name"
50+ , view =
51+ \row -> text row.firstName
52+ }
53+ , { header = text "Last Name"
54+ , view =
55+ \row -> text row.lastName
56+ }
57+ ]
58+ }
59+ , text "Without Headers"
60+ , table []
61+ { data = data
62+ , columns =
63+ [ { header = none
64+ , view =
65+ \row -> text row.firstName
66+ }
67+ , { header = none
68+ , view =
69+ \row -> text row.lastName
70+ }
71+ ]
72+ }
73+ , text "With Spacing and Styling"
74+ , table
75+ [ Background.color blue
76+ , spacing 20
77+ , padding 30
78+ ]
79+ { data = data
80+ , columns =
81+ [ { header =
82+ el [ Font.color white ] <|
83+ text "First Name"
84+ , view =
85+ \row -> el [ Background.color lightGrey ] <| text row.firstName
86+ }
87+ , { header = el [ Font.color white ] <| text "Last Name"
88+ , view =
89+ \row ->
90+ el [ Background.color lightGrey ] <|
91+ text row.lastName
92+ }
93+ ]
94+ }
95+ , text "Indexed Table With Spacing and Styling"
96+ , indexedTable
97+ [ Background.color blue
98+ , spacing 20
99+ , padding 30
100+ ]
101+ { data = data
102+ , columns =
103+ [ { header =
104+ el [ Font.color white ] <|
105+ text "Index"
106+ , view =
107+ \i row -> el [ Background.color lightGrey, width fill ] <| text (toString i)
108+ }
109+ , { header =
110+ el [ Font.color white ] <|
111+ text "First Name"
112+ , view =
113+ \i row -> el [ Background.color lightGrey, width fill ] <| text row.firstName
114+ }
115+ , { header = el [ Font.color white ] <| text "Last Name"
116+ , view =
117+ \i row ->
118+ el [ Background.color lightGrey, width fill ] <|
119+ text row.lastName
120+ }
121+ ]
122+ }
123+ ]
tests/Tests/Transparency.elmnew file+57
@@ -0,0 +1,57 @@
1+module Tests.Transparency exposing (..)
2+
3+import Html
4+import Testable
5+import Testable.Element exposing (..)
6+import Testable.Element.Background as Background
7+import Testable.Element.Font as Font
8+import Testable.Runner
9+import Tests.Palette as Palette exposing (..)
10+
11+
12+box attrs =
13+ el
14+ ([ width (px 50)
15+ , height (px 50)
16+ , Background.color blue
17+ ]
18+ ++ attrs
19+ )
20+ none
21+
22+
23+{-| -}
24+main : Html.Html msg
25+main =
26+ Testable.Runner.show view
27+
28+
29+view =
30+ column [ paddingXY 0 100, spacing 16 ]
31+ [ text "transparency"
32+ , row [ spacing 16 ]
33+ [ box [ transparent True ]
34+ , box [ transparent False ]
35+ ]
36+ , text "transparency with hover"
37+ , row [ spacing 16 ]
38+ [ box
39+ [ transparent True
40+
41+ -- , mouseOver [ Background.color green ]
42+ ]
43+ , box
44+ [ transparent False
45+
46+ -- , mouseOver [ Background.color green ]
47+ ]
48+ ]
49+ , text "all opacities"
50+ , row [ spacing 16 ]
51+ [ box [ alpha 0 ]
52+ , box [ alpha 0.25 ]
53+ , box [ alpha 0.5 ]
54+ , box [ alpha 0.75 ]
55+ , box [ alpha 1.0 ]
56+ ]
57+ ]
tests/Tests/WidthHeight.elmnew file+61
@@ -0,0 +1,61 @@
1+module Tests.Basic exposing (view)
2+
3+{-| -}
4+
5+import Html
6+import Testable
7+import Testable.Element as Element exposing (..)
8+import Testable.Element.Background as Background
9+import Testable.Element.Font as Font
10+import Testable.Runner
11+import Tests.Palette as Palette exposing (..)
12+
13+
14+{-| -}
15+main : Html.Html msg
16+main =
17+ Testable.Runner.show view
18+
19+
20+container attrs child =
21+ el
22+ (Background.color lightGrey
23+ :: attrs
24+ )
25+ child
26+
27+
28+box attrs =
29+ el (Background.color blue :: attrs) none
30+
31+
32+{-| -}
33+view : Testable.Element msg
34+view =
35+ column [ spacing 50, alignTop, Font.color white ]
36+ [ el [ Font.color black ] (text "fill")
37+ , el
38+ [ width (px 600)
39+ , Background.color lightGrey
40+ ]
41+ (el
42+ [ Background.color blue
43+ , width fill
44+ , height (px 200)
45+ ]
46+ none
47+ )
48+ , el [ Font.color black ] (text "shrink")
49+ , el
50+ [ width (px 600)
51+ , height (px 100)
52+ , Background.color lightGrey
53+ ]
54+ (el
55+ [ Background.color blue
56+ , width shrink
57+ , height shrink
58+ ]
59+ (text "Hello")
60+ )
61+ ]
tests/automation/selenium_test.pynew file+59
@@ -0,0 +1,59 @@
1+from selenium import webdriver
2+from selenium.webdriver.common.keys import Keys
3+from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
4+import os
5+import time
6+
7+
8+def browsers():
9+
10+ capabilities = [
11+ {'platform': "Mac OS X 10.9",
12+ 'browserName': "chrome",
13+ 'version': "31",
14+ }
15+ ]
16+ return capabilies
17+
18+
19+def run_local():
20+ driver = webdriver.Firefox()
21+ local_file = "file://" + os.getcwd() + "/gather-styles.html"
22+ run_test(driver, local_file)
23+
24+
25+def get_credentials():
26+ return {'username': os.environ['SAUCE_USERNAME'], 'access_key': os.environ['SAUCE_ACCESS_KEY']}
27+
28+
29+def run_remote(desired_cap):
30+ creds = get_credentials()
31+ driver = webdriver.Remote(
32+ command_executor='http://{username}:{key}@ondemand.saucelabs.com:80/wd/hub'.format(
33+ username=creds['username'],
34+ key=creds['access_key']),
35+ desired_capabilities=desired_cap)
36+ run_test(driver)
37+
38+
39+def run_test(driver, url):
40+ try:
41+ driver.get(local_file)
42+ for x in range(5):
43+ results = driver.execute_script('return test_results')
44+ if results != "waiting..":
45+ print(results)
46+ break
47+ time.sleep(1)
48+ except Exception as inst:
49+ print(type(inst))
50+ print(inst.args)
51+ print(inst)
52+ driver.quit()
53+ else:
54+ print("quitting browser")
55+ driver.quit()
56+
57+
58+if __name__ == "__main__":
59+ run_local()
tests/elm.jsonnew file+26
@@ -0,0 +1,26 @@
1+{
2+ "type": "application",
3+ "source-directories": [
4+ "src",
5+ "suite",
6+ "../src"
7+ ],
8+ "elm-version": "0.19.0",
9+ "dependencies": {
10+ "direct": {
11+ "elm/browser": "1.0.0",
12+ "elm/core": "1.0.0",
13+ "elm/html": "1.0.0",
14+ "elm/json": "1.0.0",
15+ "elm/virtual-dom": "1.0.0"
16+ },
17+ "indirect": {
18+ "elm/time": "1.0.0",
19+ "elm/url": "1.0.0"
20+ }
21+ },
22+ "test-dependencies": {
23+ "direct": {},
24+ "indirect": {}
25+ }
26+}
tests/gather-styles.htmlnew file+83
@@ -0,0 +1,83 @@
1+<!DOCTYPE HTML>
2+<html>
3+
4+<head>
5+ <meta charset="UTF-8">
6+ <title>Rendering Benchmark Viewer</title>
7+ <script src="elm.js"></script>
8+</head>
9+<style>
10+</style>
11+
12+<body id="root"></body>
13+
14+<script type="text/javascript">
15+
16+ //var app = Elm.Main.fullscreen();
17+ var node = document.getElementById('root');
18+ var app = Elm.Tests.Run.embed(node);
19+
20+ var test_results = "waiting.."
21+
22+ app.ports.report.subscribe(function (results) {
23+ test_results = results;
24+ })
25+
26+ app.ports.analyze.subscribe(function (ids) {
27+ // ids : List String
28+ var idsLength = ids.length;
29+ var results = [];
30+ for (var i = 0; i < idsLength; i++) {
31+ var id = ids[i];
32+ var element = document.getElementById(id);
33+ if (element == null) {
34+ console.log("id " + id + " not found");
35+ }
36+ var style = getStyle(element);
37+ var bbox = getBoundingBox(element);
38+ var result = { "bbox": bbox, "style": style, "id": id };
39+ results.push(result);
40+
41+ }
42+ app.ports.styles.send(results);
43+ });
44+
45+ function getStyle(element) {
46+ var props = []
47+ var style = window.getComputedStyle(element);
48+ for (var i = style.length; i--;) {
49+ var name = style.item(i);
50+ var value = style.getPropertyValue(name);
51+ props.push([name, value]);
52+ }
53+ return props;
54+ }
55+
56+ function getBoundingBox(element) {
57+ var bbox = element.getBoundingClientRect();
58+ var style = window.getComputedStyle(element, null);
59+
60+ var padding = {
61+ 'top': parseFloat(style.getPropertyValue('padding-top'))
62+ , 'bottom': parseFloat(style.getPropertyValue('padding-bottom'))
63+ , 'left': parseFloat(style.getPropertyValue('padding-left'))
64+ , 'right': parseFloat(style.getPropertyValue('padding-right'))
65+ };
66+
67+ return {
68+ 'top': bbox.top
69+ , 'bottom': bbox.bottom
70+ , 'left': bbox.left
71+ , 'right': bbox.right
72+ , 'width': bbox.width
73+ , 'height': bbox.height
74+ , 'padding': padding
75+ }
76+ }
77+
78+
79+
80+
81+</script>
82+
83+</html>
tests/live.shnew file+1
@@ -0,0 +1 @@
1+elm-live Main.elm --output=elm.js --open --pushstate
tests/src/Generator.elmnew file+53
@@ -0,0 +1,53 @@
1+module Generator exposing (..)
2+
3+{-| -}
4+
5+import Testable
6+import Testable.Element as Element exposing (..)
7+
8+
9+-- sizes : (List (Testable.Attr msg) -> Testable.Element msg -> Testable.Element msg) -> List (Testable.Element msg)
10+
11+
12+sizes render =
13+ List.concatMap
14+ (\( widthLen, heightLen ) ->
15+ [ text (Debug.toString ( widthLen, heightLen ))
16+ , render
17+ (\attrs children ->
18+ el
19+ (width widthLen
20+ :: height heightLen
21+ :: attrs
22+ )
23+ children
24+ )
25+ ]
26+ )
27+ allLengthPairs
28+
29+
30+allLengthPairs : List ( Length, Length )
31+allLengthPairs =
32+ let
33+ crossProduct len =
34+ List.map (Tuple.pair len) lengths
35+ in
36+ List.concatMap crossProduct lengths
37+
38+
39+lengths =
40+ [ px 50
41+ , fill
42+ , shrink
43+ , fill
44+ |> maximum 100
45+ , fill
46+ |> maximum 100
47+ |> minimum 50
48+ , shrink
49+ |> maximum 100
50+ , shrink
51+ |> maximum 100
52+ |> minimum 50
53+ ]
tests/src/Testable.elmnew file+730
@@ -0,0 +1,730 @@
1+module Testable exposing (..)
2+
3+{-| -}
4+
5+import Dict exposing (Dict)
6+import Element exposing (Color)
7+import Expect
8+import Html exposing (Html)
9+import Html.Attributes
10+import Internal.Model as Internal
11+import Random
12+import Test exposing (Test)
13+import Test.Runner
14+import Test.Runner.Failure
15+
16+
17+type Element msg
18+ = El (List (Attr msg)) (Element msg)
19+ | Row (List (Attr msg)) (List (Element msg))
20+ | Column (List (Attr msg)) (List (Element msg))
21+ | TextColumn (List (Attr msg)) (List (Element msg))
22+ | Paragraph (List (Attr msg)) (List (Element msg))
23+ | Text String
24+ | Empty
25+
26+
27+type Attr msg
28+ = Attr (Element.Attribute msg)
29+ | AttrTest (Surroundings -> Test)
30+ | Batch (List (Attr msg))
31+ | Spacing Int
32+ | Nearby
33+ { location : Location
34+ , element : Element msg
35+ , test : Surroundings -> () -> Expect.Expectation
36+ , label : String
37+ }
38+ | Label String
39+ | LabeledTest
40+ { test : Surroundings -> () -> Expect.Expectation
41+ , label : String
42+ , attr : Element.Attribute msg
43+ }
44+
45+
46+type Location
47+ = Above
48+ | Below
49+ | OnRight
50+ | OnLeft
51+ | InFront
52+ | Behind
53+
54+
55+type LayoutContext
56+ = IsNearby Location
57+ | InRow
58+ | InEl
59+ | InColumn
60+
61+
62+type alias Surroundings =
63+ { siblings : List Found
64+ , parent : Found
65+ , children : List Found
66+ , self : Found
67+
68+ -- These values are needed to perform some types of tests.
69+ , location : LayoutContext
70+ , parentSpacing : Int
71+ }
72+
73+
74+type alias Found =
75+ { bbox : BoundingBox
76+ , style : Style
77+ }
78+
79+
80+{-| -}
81+type alias Style =
82+ Dict String String
83+
84+
85+type alias BoundingBox =
86+ { width : Float
87+ , height : Float
88+ , left : Float
89+ , top : Float
90+ , right : Float
91+ , bottom : Float
92+ , padding :
93+ { left : Float
94+ , right : Float
95+ , top : Float
96+ , bottom : Float
97+ }
98+ }
99+
100+
101+
102+{- Retrieve Ids -}
103+
104+
105+getIds : Element msg -> List String
106+getIds el =
107+ "se-0" :: getElementId [ 0, 0 ] el
108+
109+
110+getElementId : List Int -> Element msg -> List String
111+getElementId level el =
112+ let
113+ id =
114+ levelToString level
115+
116+ attrID attrIndex attr =
117+ case attr of
118+ Nearby nearby ->
119+ getElementId (attrIndex :: -1 :: level) nearby.element
120+
121+ _ ->
122+ []
123+
124+ attributeIDs attrs =
125+ attrs
126+ |> List.indexedMap attrID
127+ |> List.concat
128+
129+ childrenIDs children =
130+ List.concat <| List.indexedMap (\i -> getElementId (i :: level)) children
131+ in
132+ case el of
133+ El attrs child ->
134+ id :: getElementId (0 :: level) child ++ attributeIDs attrs
135+
136+ Row attrs children ->
137+ id :: childrenIDs children ++ attributeIDs attrs
138+
139+ Column attrs children ->
140+ id :: childrenIDs children ++ attributeIDs attrs
141+
142+ TextColumn attrs children ->
143+ id :: childrenIDs children ++ attributeIDs attrs
144+
145+ Paragraph attrs children ->
146+ id :: childrenIDs children ++ attributeIDs attrs
147+
148+ Empty ->
149+ []
150+
151+ Text _ ->
152+ []
153+
154+
155+
156+{- Render as Html -}
157+
158+
159+render : Element msg -> Html msg
160+render el =
161+ Element.layout [ idAttr "0" ] <|
162+ renderElement [ 0, 0 ] el
163+
164+
165+idAttr : String -> Element.Attribute msg
166+idAttr id =
167+ Element.htmlAttribute (Html.Attributes.id ("se-" ++ id))
168+
169+
170+renderElement : List Int -> Element msg -> Element.Element msg
171+renderElement level el =
172+ let
173+ id =
174+ level
175+ |> List.map String.fromInt
176+ |> String.join "-"
177+ |> idAttr
178+
179+ makeAttributes attrs =
180+ attrs
181+ |> List.indexedMap (renderAttribute level)
182+ |> List.concat
183+ in
184+ case el of
185+ El attrs child ->
186+ Element.el
187+ (id :: makeAttributes attrs)
188+ (renderElement (0 :: level) child)
189+
190+ Row attrs children ->
191+ Element.row
192+ (id :: makeAttributes attrs)
193+ (List.indexedMap (\i -> renderElement (i :: level)) children)
194+
195+ Column attrs children ->
196+ Element.column
197+ (id :: makeAttributes attrs)
198+ (List.indexedMap (\i -> renderElement (i :: level)) children)
199+
200+ TextColumn attrs children ->
201+ Element.textColumn
202+ (id :: makeAttributes attrs)
203+ (List.indexedMap (\i -> renderElement (i :: level)) children)
204+
205+ Paragraph attrs children ->
206+ Element.paragraph
207+ (id :: makeAttributes attrs)
208+ (List.indexedMap (\i -> renderElement (i :: level)) children)
209+
210+ Empty ->
211+ Element.none
212+
213+ Text str ->
214+ Element.text str
215+
216+
217+renderAttribute : List Int -> Int -> Attr msg -> List (Element.Attribute msg)
218+renderAttribute level attrIndex attr =
219+ case attr of
220+ Attr attribute ->
221+ [ attribute ]
222+
223+ AttrTest _ ->
224+ []
225+
226+ Spacing _ ->
227+ []
228+
229+ Label _ ->
230+ []
231+
232+ Nearby { location, element } ->
233+ case location of
234+ Above ->
235+ [ Element.above (renderElement (attrIndex :: -1 :: level) element) ]
236+
237+ Below ->
238+ [ Element.below (renderElement (attrIndex :: -1 :: level) element) ]
239+
240+ OnRight ->
241+ [ Element.onRight (renderElement (attrIndex :: -1 :: level) element) ]
242+
243+ OnLeft ->
244+ [ Element.onLeft (renderElement (attrIndex :: -1 :: level) element) ]
245+
246+ InFront ->
247+ [ Element.inFront (renderElement (attrIndex :: -1 :: level) element) ]
248+
249+ Behind ->
250+ [ Element.behindContent (renderElement (attrIndex :: -1 :: level) element) ]
251+
252+ Batch batch ->
253+ List.indexedMap (renderAttribute (attrIndex :: level)) batch
254+ |> List.concat
255+
256+ LabeledTest tested ->
257+ [ tested.attr ]
258+
259+
260+
261+{- Convert to Test -}
262+
263+
264+toTest : String -> Dict String Found -> Element msg -> Test
265+toTest label harvested el =
266+ let
267+ maybeFound =
268+ Dict.get "se-0" harvested
269+ in
270+ case maybeFound of
271+ Nothing ->
272+ Test.describe label
273+ [ Test.test "Find Root" <|
274+ \_ -> Expect.fail "unable to find root"
275+ ]
276+
277+ Just root ->
278+ Test.describe label <|
279+ createTest
280+ { siblings = []
281+ , parent = root
282+ , cache = harvested
283+ , parentSpacing = 0
284+ , level = [ 0, 0 ]
285+ , element = el
286+ , location = InEl
287+ }
288+
289+
290+levelToString : List Int -> String
291+levelToString level =
292+ level
293+ |> List.map String.fromInt
294+ |> String.join "-"
295+ |> (\x -> "se-" ++ x)
296+
297+
298+createTest :
299+ { siblings : List Found
300+ , parent : Found
301+ , cache : Dict String Found
302+ , level : List Int
303+ , element : Element msg
304+ , location : LayoutContext
305+ , parentSpacing : Int
306+ }
307+ -> List Test
308+createTest { siblings, parent, cache, level, element, location, parentSpacing } =
309+ let
310+ spacing =
311+ getSpacing element
312+ |> Maybe.withDefault 0
313+
314+ id =
315+ -- Debug.log "create Test" <|
316+ levelToString level
317+
318+ testChildren : Found -> List (Element msg) -> List Test
319+ testChildren found children =
320+ let
321+ childrenFound =
322+ -- Should check taht this lookup doesn't fail.
323+ -- Thoug if it does, it'll fail when the element itself is tested
324+ List.filterMap
325+ (\x ->
326+ Dict.get (levelToString (x :: level)) cache
327+ )
328+ (List.range 0 (List.length children))
329+ in
330+ List.foldl (applyChildTest found)
331+ { index = 0
332+ , upcoming = childrenFound
333+ , previous = []
334+ , tests = []
335+ }
336+ children
337+ |> .tests
338+
339+ applyChildTest :
340+ Found
341+ -> Element msg
342+ ->
343+ { a
344+ | index : Int
345+ , previous : List Found
346+ , tests : List Test
347+ , upcoming : List Found
348+ }
349+ ->
350+ { index : Int
351+ , previous : List Found
352+ , tests : List Test
353+ , upcoming : List Found
354+ }
355+ applyChildTest found child childTest =
356+ -- { index, upcoming, previous, tests }
357+ let
358+ surroundingChildren =
359+ case childTest.upcoming of
360+ [] ->
361+ childTest.previous
362+
363+ x :: remaining ->
364+ remaining ++ childTest.previous
365+
366+ childrenTests =
367+ createTest
368+ { siblings = surroundingChildren
369+ , parent = found
370+ , cache = cache
371+ , level = childTest.index :: level
372+ , element = child
373+ , parentSpacing = spacing
374+ , location =
375+ case element of
376+ El _ _ ->
377+ InEl
378+
379+ Row _ _ ->
380+ InRow
381+
382+ Column _ _ ->
383+ InColumn
384+
385+ TextColumn _ _ ->
386+ InColumn
387+
388+ Paragraph _ _ ->
389+ InRow
390+
391+ Text _ ->
392+ InEl
393+
394+ Empty ->
395+ InEl
396+ }
397+ in
398+ { index = childTest.index + 1
399+ , tests = childTest.tests ++ childrenTests
400+ , previous =
401+ case childTest.upcoming of
402+ [] ->
403+ childTest.previous
404+
405+ x :: _ ->
406+ x :: childTest.previous
407+ , upcoming =
408+ case childTest.upcoming of
409+ [] ->
410+ []
411+
412+ _ :: rest ->
413+ rest
414+ }
415+
416+ tests : Found -> List (Attr msg) -> List (Element msg) -> List Test
417+ tests self attributes children =
418+ let
419+ findBBox elem ( i, gathered ) =
420+ case elem of
421+ Empty ->
422+ ( i + 1
423+ , gathered
424+ )
425+
426+ Text _ ->
427+ ( i + 1
428+ , gathered
429+ )
430+
431+ _ ->
432+ case Dict.get (levelToString (i :: level)) cache of
433+ Nothing ->
434+ let
435+ _ =
436+ Debug.log "el failed to find" elem
437+
438+ _ =
439+ Debug.log "Failed to find child" (levelToString (i :: level))
440+ in
441+ ( i + 1
442+ , gathered
443+ )
444+
445+ Just found ->
446+ ( i + 1
447+ , found :: gathered
448+ )
449+
450+ childrenFoundData =
451+ List.foldl findBBox ( 0, [] ) children
452+ |> Tuple.second
453+
454+ attributeTests =
455+ attributes
456+ |> applyLabels
457+ |> List.indexedMap
458+ -- Found -> Dict String Found -> List Int -> Int -> Surroundings -> Attr msg -> List Test
459+ (\i attr ->
460+ createAttributeTest self
461+ cache
462+ level
463+ i
464+ { siblings = siblings
465+ , parent = parent
466+ , self = self
467+ , children = childrenFoundData
468+ , location = location
469+ , parentSpacing = parentSpacing
470+ }
471+ attr
472+ )
473+ |> List.concat
474+ in
475+ attributeTests
476+ ++ testChildren self children
477+ in
478+ case Dict.get id cache of
479+ Nothing ->
480+ case element of
481+ Empty ->
482+ []
483+
484+ Text _ ->
485+ []
486+
487+ _ ->
488+ [ Test.test ("Unable to find " ++ id) (always <| Expect.fail "failed id lookup") ]
489+
490+ Just self ->
491+ case element of
492+ El attrs child ->
493+ tests self attrs [ child ]
494+
495+ Row attrs children ->
496+ tests self attrs children
497+
498+ Column attrs children ->
499+ tests self attrs children
500+
501+ TextColumn attrs children ->
502+ tests self attrs children
503+
504+ Paragraph attrs children ->
505+ tests self attrs children
506+
507+ Empty ->
508+ []
509+
510+ Text str ->
511+ []
512+
513+
514+applyLabels : List (Attr msg) -> List (Attr msg)
515+applyLabels attrs =
516+ let
517+ toLabel attr =
518+ case attr of
519+ Label label ->
520+ Just label
521+
522+ _ ->
523+ Nothing
524+
525+ newLabels =
526+ attrs
527+ |> List.filterMap toLabel
528+ |> String.join ", "
529+
530+ applyLabel newLabel attr =
531+ case attr of
532+ LabeledTest labeled ->
533+ LabeledTest
534+ { labeled
535+ | label =
536+ if newLabel == "" then
537+ labeled.label
538+ else
539+ newLabel ++ ", " ++ labeled.label
540+ }
541+
542+ x ->
543+ x
544+ in
545+ List.map (applyLabel newLabels) attrs
546+
547+
548+createAttributeTest : Found -> Dict String Found -> List Int -> Int -> Surroundings -> Attr msg -> List Test
549+createAttributeTest parent cache level attrIndex surroundings attr =
550+ let
551+ indexLabel =
552+ levelToString (attrIndex :: level)
553+ in
554+ case attr of
555+ Attr _ ->
556+ []
557+
558+ Label _ ->
559+ []
560+
561+ Spacing _ ->
562+ []
563+
564+ AttrTest test ->
565+ [ test surroundings
566+ ]
567+
568+ Nearby nearby ->
569+ createTest
570+ { siblings = []
571+ , parent = parent
572+ , cache = cache
573+ , parentSpacing = 0
574+ , level = attrIndex :: -1 :: level
575+ , location = IsNearby nearby.location
576+ , element = addAttribute (AttrTest (\context -> Test.test (nearby.label ++ " #" ++ indexLabel) (nearby.test context))) nearby.element
577+ }
578+
579+ Batch batch ->
580+ batch
581+ |> List.indexedMap (\i attribute -> createAttributeTest parent cache (attrIndex :: level) i surroundings attribute)
582+ |> List.concat
583+
584+ LabeledTest { label, test } ->
585+ [ Test.test (label ++ " #" ++ indexLabel) (test surroundings)
586+ ]
587+
588+
589+addAttribute : Attr msg -> Element msg -> Element msg
590+addAttribute attr el =
591+ case el of
592+ El attrs child ->
593+ El (attr :: attrs) child
594+
595+ Row attrs children ->
596+ Row (attr :: attrs) children
597+
598+ Column attrs children ->
599+ Column (attr :: attrs) children
600+
601+ TextColumn attrs children ->
602+ TextColumn (attr :: attrs) children
603+
604+ Paragraph attrs children ->
605+ Paragraph (attr :: attrs) children
606+
607+ Empty ->
608+ Empty
609+
610+ Text str ->
611+ Text str
612+
613+
614+runTests :
615+ Random.Seed
616+ -> Test
617+ ->
618+ List
619+ ( String
620+ , Maybe
621+ { given : Maybe String
622+ , description : String
623+ , reason : Test.Runner.Failure.Reason
624+ }
625+ )
626+runTests seed tests =
627+ let
628+ run runner =
629+ let
630+ ran =
631+ List.map Test.Runner.getFailureReason (runner.run ())
632+ in
633+ List.map2 Tuple.pair runner.labels ran
634+
635+ results =
636+ case Test.Runner.fromTest 100 seed tests of
637+ Test.Runner.Plain rnrs ->
638+ List.map run rnrs
639+
640+ Test.Runner.Only rnrs ->
641+ List.map run rnrs
642+
643+ Test.Runner.Skipping rnrs ->
644+ List.map run rnrs
645+
646+ Test.Runner.Invalid invalid ->
647+ let
648+ _ =
649+ Debug.log "Invalid tests" invalid
650+ in
651+ []
652+ in
653+ List.concat results
654+
655+
656+compareFormattedColor : Color -> String -> Bool
657+compareFormattedColor color expected =
658+ formatColor color == expected || formatColorWithAlpha color == expected
659+
660+
661+formatColorWithAlpha : Color -> String
662+formatColorWithAlpha (Internal.Rgba red green blue alpha) =
663+ if alpha == 1 then
664+ ("rgba(" ++ String.fromInt (round (red * 255)))
665+ ++ (", " ++ String.fromInt (round (green * 255)))
666+ ++ (", " ++ String.fromInt (round (blue * 255)))
667+ ++ ", 1"
668+ ++ ")"
669+ else
670+ ("rgba(" ++ String.fromInt (round (red * 255)))
671+ ++ (", " ++ String.fromInt (round (green * 255)))
672+ ++ (", " ++ String.fromInt (round (blue * 255)))
673+ ++ (", " ++ String.fromFloat alpha ++ ")")
674+
675+
676+formatColor : Color -> String
677+formatColor (Internal.Rgba red green blue alpha) =
678+ if alpha == 1 then
679+ ("rgb(" ++ String.fromInt (round (red * 255)))
680+ ++ (", " ++ String.fromInt (round (green * 255)))
681+ ++ (", " ++ String.fromInt (round (blue * 255)))
682+ ++ ")"
683+ else
684+ ("rgb(" ++ String.fromInt (round (red * 255)))
685+ ++ (", " ++ String.fromInt (round (green * 255)))
686+ ++ (", " ++ String.fromInt (round (blue * 255)))
687+ ++ ")"
688+
689+
690+getSpacing : Element msg -> Maybe Int
691+getSpacing el =
692+ let
693+ getSpacingAttr attr found =
694+ if found /= Nothing then
695+ found
696+ else
697+ case attr of
698+ Spacing i ->
699+ Just i
700+
701+ Batch attrs ->
702+ List.foldr getSpacingAttr Nothing attrs
703+
704+ _ ->
705+ Nothing
706+
707+ filterAttrs attrs =
708+ List.foldr getSpacingAttr Nothing attrs
709+ in
710+ case el of
711+ El attrs _ ->
712+ filterAttrs attrs
713+
714+ Row attrs _ ->
715+ filterAttrs attrs
716+
717+ Column attrs _ ->
718+ filterAttrs attrs
719+
720+ TextColumn attrs _ ->
721+ filterAttrs attrs
722+
723+ Paragraph attrs _ ->
724+ filterAttrs attrs
725+
726+ Empty ->
727+ Nothing
728+
729+ Text _ ->
730+ Nothing
tests/src/Testable/Element.elmnew file+1045
@@ -0,0 +1,1045 @@
1+module Testable.Element exposing (..)
2+
3+{-| This module should mirror the top level `Element` api, with one important distinction.
4+
5+The resulting `Element msg` structure can either be rendered to Html or transformed into a test suite that can be run.
6+
7+In order to run the test:
8+
9+ - render html
10+ - gather information from the browser
11+ - generate tests
12+
13+-}
14+
15+import Dict
16+import Element
17+import Expect
18+import Testable
19+
20+
21+text : String -> Testable.Element msg
22+text =
23+ Testable.Text
24+
25+
26+el : List (Testable.Attr msg) -> Testable.Element msg -> Testable.Element msg
27+el =
28+ Testable.El
29+
30+
31+row : List (Testable.Attr msg) -> List (Testable.Element msg) -> Testable.Element msg
32+row =
33+ Testable.Row
34+
35+
36+column : List (Testable.Attr msg) -> List (Testable.Element msg) -> Testable.Element msg
37+column =
38+ Testable.Column
39+
40+
41+none : Testable.Element msg
42+none =
43+ Testable.Empty
44+
45+
46+paragraph : List (Testable.Attr msg) -> List (Testable.Element msg) -> Testable.Element msg
47+paragraph =
48+ Testable.Paragraph
49+
50+
51+textColumn : List (Testable.Attr msg) -> List (Testable.Element msg) -> Testable.Element msg
52+textColumn =
53+ Testable.TextColumn
54+
55+
56+type Length
57+ = Px Int
58+ | Fill Int
59+ | Shrink
60+ | Minimum Int Length
61+ | Maximum Int Length
62+
63+
64+minimum =
65+ Minimum
66+
67+
68+maximum =
69+ Maximum
70+
71+
72+px : Int -> Length
73+px =
74+ Px
75+
76+
77+fill : Length
78+fill =
79+ Fill 1
80+
81+
82+shrink : Length
83+shrink =
84+ Shrink
85+
86+
87+fillPortion : Int -> Length
88+fillPortion =
89+ Fill
90+
91+
92+label : String -> Testable.Attr msg
93+label =
94+ Testable.Label
95+
96+
97+transparent : Bool -> Testable.Attr msg
98+transparent on =
99+ Testable.LabeledTest
100+ { label =
101+ if on then
102+ "transparent"
103+ else
104+ "opaque"
105+ , attr = Element.transparent on
106+ , test =
107+ \context _ ->
108+ let
109+ selfTransparency =
110+ context.self.style
111+ |> Dict.get "opacity"
112+ |> Maybe.withDefault "notfound"
113+
114+ value =
115+ if on then
116+ "0"
117+ else
118+ "1"
119+ in
120+ Expect.equal value selfTransparency
121+ }
122+
123+
124+{-| -}
125+alpha : Float -> Testable.Attr msg
126+alpha a =
127+ Testable.LabeledTest
128+ { label = "alpha-" ++ String.fromFloat a
129+ , attr = Element.alpha a
130+ , test =
131+ \context _ ->
132+ let
133+ selfTransparency =
134+ context.self.style
135+ |> Dict.get "opacity"
136+ |> Maybe.withDefault "notfound"
137+ in
138+ Expect.equal (String.fromFloat a) selfTransparency
139+ }
140+
141+
142+{-| -}
143+padding : Int -> Testable.Attr msg
144+padding pad =
145+ Testable.LabeledTest
146+ { label = "padding " ++ String.fromInt pad
147+ , attr = Element.padding pad
148+ , test =
149+ \found _ ->
150+ Expect.true ("Padding " ++ String.fromInt pad ++ " is present")
151+ (List.all ((==) pad)
152+ [ floor found.self.bbox.padding.left
153+ , floor found.self.bbox.padding.right
154+ , floor found.self.bbox.padding.top
155+ , floor found.self.bbox.padding.bottom
156+ ]
157+ )
158+ }
159+
160+
161+{-| -}
162+paddingXY : Int -> Int -> Testable.Attr msg
163+paddingXY x y =
164+ Testable.LabeledTest
165+ { label = "paddingXY " ++ String.fromInt x ++ ", " ++ String.fromInt y
166+ , attr = Element.paddingXY x y
167+ , test =
168+ \found _ ->
169+ Expect.true ("PaddingXY (" ++ String.fromInt x ++ ", " ++ String.fromInt y ++ ") is present")
170+ (List.all ((==) x)
171+ [ floor found.self.bbox.padding.left
172+ , floor found.self.bbox.padding.right
173+ ]
174+ && List.all ((==) y)
175+ [ floor found.self.bbox.padding.top
176+ , floor found.self.bbox.padding.bottom
177+ ]
178+ )
179+ }
180+
181+
182+widthHelper maybeMin maybeMax len =
183+ let
184+ addMin l =
185+ case maybeMin of
186+ Nothing ->
187+ l
188+
189+ Just minPx ->
190+ l |> Element.minimum minPx
191+
192+ addMax l =
193+ case maybeMax of
194+ Nothing ->
195+ l
196+
197+ Just maxPx ->
198+ l |> Element.maximum maxPx
199+
200+ minLabel =
201+ case maybeMin of
202+ Nothing ->
203+ ""
204+
205+ Just i ->
206+ " min:" ++ String.fromInt i
207+
208+ maxLabel =
209+ case maybeMax of
210+ Nothing ->
211+ ""
212+
213+ Just i ->
214+ " max:" ++ String.fromInt i
215+
216+ minMaxTest actualWidth =
217+ case ( maybeMin, maybeMax ) of
218+ ( Nothing, Nothing ) ->
219+ True
220+
221+ ( Just lower, Nothing ) ->
222+ lower <= actualWidth
223+
224+ ( Just lower, Just higher ) ->
225+ lower <= actualWidth && actualWidth <= higher
226+
227+ ( Nothing, Just higher ) ->
228+ actualWidth <= higher
229+ in
230+ case len of
231+ Minimum m newLen ->
232+ widthHelper (Just m) maybeMax newLen
233+
234+ Maximum m newLen ->
235+ widthHelper maybeMin (Just m) newLen
236+
237+ Px val ->
238+ -- Pixel values should ignore min and max?
239+ Testable.LabeledTest
240+ { label = "width " ++ String.fromInt val ++ "px" ++ minLabel ++ maxLabel
241+ , attr =
242+ Element.width
243+ (Element.px val
244+ |> addMin
245+ |> addMax
246+ )
247+ , test =
248+ \found _ ->
249+ Expect.true "exact width is exact"
250+ ((floor found.self.bbox.width == val)
251+ && minMaxTest (floor found.self.bbox.width)
252+ )
253+ }
254+
255+ Fill portion ->
256+ Testable.LabeledTest
257+ { label = "width fill-" ++ String.fromInt portion ++ minLabel ++ maxLabel
258+ , attr =
259+ Element.width
260+ (Element.fillPortion portion
261+ |> addMin
262+ |> addMax
263+ )
264+ , test =
265+ \context _ ->
266+ if List.member context.location [ Testable.IsNearby Testable.OnRight, Testable.IsNearby Testable.OnLeft ] then
267+ Expect.true "width fill doesn't apply to onright/onleft elements" True
268+ else
269+ let
270+ parentAvailableWidth =
271+ context.parent.bbox.width - (context.self.bbox.padding.left + context.self.bbox.padding.right)
272+ in
273+ case context.location of
274+ Testable.IsNearby _ ->
275+ Expect.true "Nearby Element has fill width"
276+ ((floor context.parent.bbox.width == floor context.self.bbox.width)
277+ || minMaxTest (floor context.self.bbox.width)
278+ )
279+
280+ Testable.InColumn ->
281+ Expect.true "Element within column has fill width"
282+ ((floor parentAvailableWidth == floor context.self.bbox.width)
283+ || minMaxTest (floor context.self.bbox.width)
284+ )
285+
286+ Testable.InEl ->
287+ Expect.true "Element within element has fill width" <|
288+ (floor parentAvailableWidth == floor context.self.bbox.width)
289+ || minMaxTest (floor context.self.bbox.width)
290+
291+ _ ->
292+ let
293+ spacePerPortion =
294+ parentAvailableWidth / toFloat (List.length context.siblings + 1)
295+ in
296+ Expect.true "element has fill width" <|
297+ (floor spacePerPortion == floor context.self.bbox.width)
298+ || minMaxTest (floor context.self.bbox.width)
299+ }
300+
301+ Shrink ->
302+ Testable.LabeledTest
303+ { label = "width shrink" ++ minLabel ++ maxLabel
304+ , attr =
305+ Element.width
306+ (Element.shrink
307+ |> addMin
308+ |> addMax
309+ )
310+ , test =
311+ \context _ ->
312+ let
313+ childWidth child =
314+ -- TODO: add margin values to widths
315+ child.bbox.width
316+
317+ totalChildren =
318+ context.children
319+ |> List.map childWidth
320+ |> List.sum
321+
322+ horizontalPadding =
323+ context.self.bbox.padding.left + context.self.bbox.padding.right
324+
325+ spacingValue =
326+ toFloat context.parentSpacing * (toFloat (List.length context.children) - 1)
327+ in
328+ if totalChildren == 0 then
329+ -- TODO: The issue is that we have a hard time measuring `text` elements
330+ -- So if a element has a text child, then it's width isn't going to show up in the system.
331+ expectRoundedEquality context.self.bbox.width context.self.bbox.width
332+ else
333+ -- This fails if this element is actually a column
334+ -- So we need to capture what this element is in order to do this calculation.
335+ expectRoundedEquality (totalChildren + horizontalPadding + spacingValue) context.self.bbox.width
336+ }
337+
338+
339+heightHelper maybeMin maybeMax len =
340+ let
341+ addMin l =
342+ case maybeMin of
343+ Nothing ->
344+ l
345+
346+ Just minPx ->
347+ l |> Element.minimum minPx
348+
349+ addMax l =
350+ case maybeMax of
351+ Nothing ->
352+ l
353+
354+ Just maxPx ->
355+ l |> Element.maximum maxPx
356+
357+ minLabel =
358+ case maybeMin of
359+ Nothing ->
360+ ""
361+
362+ Just i ->
363+ " min:" ++ String.fromInt i
364+
365+ maxLabel =
366+ case maybeMax of
367+ Nothing ->
368+ ""
369+
370+ Just i ->
371+ " max:" ++ String.fromInt i
372+
373+ minMaxTest actualheight =
374+ case ( maybeMin, maybeMax ) of
375+ ( Nothing, Nothing ) ->
376+ True
377+
378+ ( Just lower, Nothing ) ->
379+ lower <= actualheight
380+
381+ ( Just lower, Just higher ) ->
382+ lower <= actualheight && actualheight <= higher
383+
384+ ( Nothing, Just higher ) ->
385+ actualheight <= higher
386+ in
387+ case len of
388+ Minimum m newLen ->
389+ heightHelper (Just m) maybeMax newLen
390+
391+ Maximum m newLen ->
392+ heightHelper maybeMin (Just m) newLen
393+
394+ Px val ->
395+ -- Pixel values should ignore min and max?
396+ Testable.LabeledTest
397+ { label = "height " ++ String.fromInt val ++ "px" ++ minLabel ++ maxLabel
398+ , attr =
399+ Element.height
400+ (Element.px val
401+ |> addMin
402+ |> addMax
403+ )
404+ , test =
405+ \found _ ->
406+ Expect.true "exact height is exact"
407+ ((floor found.self.bbox.height == val)
408+ && minMaxTest (floor found.self.bbox.height)
409+ )
410+ }
411+
412+ Fill portion ->
413+ Testable.LabeledTest
414+ { label = "height fill-" ++ String.fromInt portion ++ minLabel ++ maxLabel
415+ , attr =
416+ Element.height
417+ (Element.fillPortion portion
418+ |> addMin
419+ |> addMax
420+ )
421+ , test =
422+ \context _ ->
423+ if List.member context.location [ Testable.IsNearby Testable.Above, Testable.IsNearby Testable.Below ] then
424+ Expect.true "height fill doesn't apply to above/below elements" True
425+ else
426+ let
427+ parentAvailableHeight =
428+ context.parent.bbox.height - (context.self.bbox.padding.top + context.self.bbox.padding.bottom)
429+ in
430+ case context.location of
431+ Testable.IsNearby _ ->
432+ Expect.true "Nearby Element has fill height"
433+ ((floor context.parent.bbox.height == floor context.self.bbox.height)
434+ || minMaxTest (floor context.self.bbox.height)
435+ )
436+
437+ Testable.InColumn ->
438+ Expect.true "Element within column has fill height"
439+ ((floor parentAvailableHeight == floor context.self.bbox.height)
440+ || minMaxTest (floor context.self.bbox.height)
441+ )
442+
443+ Testable.InEl ->
444+ Expect.true "Element within el has fill height" <|
445+ (floor parentAvailableHeight == floor context.self.bbox.height)
446+ || minMaxTest (floor context.self.bbox.height)
447+
448+ _ ->
449+ let
450+ spacePerPortion =
451+ parentAvailableHeight / toFloat (List.length context.siblings + 1)
452+ in
453+ Expect.true "el has fill height" <|
454+ (floor spacePerPortion == floor context.self.bbox.height)
455+ || minMaxTest (floor context.self.bbox.height)
456+ }
457+
458+ Shrink ->
459+ Testable.LabeledTest
460+ { label = "height shrink" ++ minLabel ++ maxLabel
461+ , attr =
462+ Element.height
463+ (Element.shrink
464+ |> addMin
465+ |> addMax
466+ )
467+ , test =
468+ \context _ ->
469+ let
470+ childWidth child =
471+ -- TODO: add margin values to heights
472+ child.bbox.height
473+
474+ totalChildren =
475+ context.children
476+ |> List.map childWidth
477+ |> List.sum
478+
479+ verticalPadding =
480+ context.self.bbox.padding.top + context.self.bbox.padding.bottom
481+
482+ spacingValue =
483+ toFloat context.parentSpacing * (toFloat (List.length context.children) - 1)
484+ in
485+ if totalChildren == 0 then
486+ -- TODO: The issue is that we have a hard time measuring `text` elements
487+ -- So if a element has a text child, then it's height isn't going to show up in the system.
488+ expectRoundedEquality context.self.bbox.height context.self.bbox.height
489+ else
490+ -- This fails if this element is actually a column
491+ -- So we need to capture what this element is in order to do this calculation.
492+ expectRoundedEquality (totalChildren + verticalPadding + spacingValue) context.self.bbox.height
493+ }
494+
495+
496+width : Length -> Testable.Attr msg
497+width len =
498+ widthHelper Nothing Nothing len
499+
500+
501+height : Length -> Testable.Attr msg
502+height len =
503+ heightHelper Nothing Nothing len
504+
505+
506+spacing : Int -> Testable.Attr msg
507+spacing space =
508+ Testable.Batch
509+ [ Testable.Spacing space
510+ , Testable.LabeledTest
511+ { label = "spacing: " ++ String.fromInt space
512+ , attr = Element.spacing space
513+ , test =
514+ \found _ ->
515+ let
516+ findDistance child total =
517+ List.concatMap
518+ (\otherChild ->
519+ let
520+ horizontal =
521+ round <|
522+ child.bbox.left
523+ - otherChild.bbox.right
524+
525+ vertical =
526+ round <|
527+ child.bbox.top
528+ - otherChild.bbox.bottom
529+ in
530+ [ if horizontal > 0 then
531+ horizontal
532+ else
533+ space
534+ , if vertical > 0 then
535+ vertical
536+ else
537+ space
538+ ]
539+ )
540+ found.children
541+ ++ total
542+
543+ distances =
544+ List.foldl findDistance [] found.children
545+
546+ allAreSpaced =
547+ List.foldl
548+ (\x wrong ->
549+ if not (x >= space) then
550+ x :: wrong
551+ else
552+ wrong
553+ )
554+ []
555+ distances
556+ in
557+ Expect.true
558+ ("All children are at least "
559+ ++ String.fromInt space
560+ ++ " pixels apart."
561+ ++ Debug.toString allAreSpaced
562+ ++ " are not though"
563+ )
564+ (allAreSpaced == [])
565+ }
566+ ]
567+
568+
569+{-| alignLeft needs to account for
570+
571+ - parent padding
572+ - elements to the left
573+ - spacing value
574+
575+in order to calculate the expected result.
576+
577+Also need parent rendering context if this is to work with wrapped rows in the future.
578+
579+All sibling elements
580+
581+-}
582+alignLeft : Testable.Attr msg
583+alignLeft =
584+ Testable.LabeledTest
585+ { label = "alignLeft"
586+ , attr = Element.alignLeft
587+ , test =
588+ \found _ ->
589+ if List.member found.location [ Testable.IsNearby Testable.OnLeft, Testable.IsNearby Testable.OnRight ] then
590+ Expect.true "alignLeft doesn't apply to elements that are onLeft or onRight" True
591+ else if
592+ List.member found.location
593+ [ Testable.IsNearby Testable.InFront
594+ , Testable.IsNearby Testable.Behind
595+ , Testable.IsNearby Testable.Above
596+ , Testable.IsNearby Testable.Below
597+ ]
598+ then
599+ expectRoundedEquality
600+ found.self.bbox.left
601+ found.parent.bbox.left
602+ else if List.length found.siblings == 0 then
603+ expectRoundedEquality
604+ found.self.bbox.left
605+ (found.parent.bbox.left + found.parent.bbox.padding.left)
606+ else
607+ case found.location of
608+ Testable.InRow ->
609+ let
610+ siblingsOnLeft =
611+ List.filter (\x -> x.bbox.right < found.self.bbox.left) found.siblings
612+
613+ spacings =
614+ toFloat (List.length siblingsOnLeft * found.parentSpacing)
615+
616+ widthsOnLeft =
617+ siblingsOnLeft
618+ |> List.map (.width << .bbox)
619+ |> List.sum
620+ in
621+ expectRoundedEquality
622+ found.self.bbox.left
623+ (found.parent.bbox.left + (found.parent.bbox.padding.left + widthsOnLeft + spacings))
624+
625+ _ ->
626+ expectRoundedEquality
627+ found.self.bbox.left
628+ (found.parent.bbox.left + found.parent.bbox.padding.left)
629+ }
630+
631+
632+{-| -}
633+centerX : Testable.Attr msg
634+centerX =
635+ Testable.LabeledTest
636+ { label = "centerX"
637+ , attr = Element.centerX
638+ , test =
639+ \found _ ->
640+ let
641+ selfCenter : Float
642+ selfCenter =
643+ found.self.bbox.left + (found.self.bbox.width / 2)
644+
645+ parentCenter : Float
646+ parentCenter =
647+ found.parent.bbox.left + (found.parent.bbox.width / 2)
648+ in
649+ if List.member found.location [ Testable.IsNearby Testable.OnRight, Testable.IsNearby Testable.OnLeft ] then
650+ Expect.true "centerX doesn't apply to elements that are onLeft or onRight" True
651+ else if List.length found.siblings == 0 then
652+ expectRoundedEquality
653+ selfCenter
654+ parentCenter
655+ else
656+ case found.location of
657+ Testable.InRow ->
658+ let
659+ siblingsOnLeft =
660+ List.filter (\x -> x.bbox.right < found.self.bbox.left) found.siblings
661+
662+ siblingsOnRight =
663+ List.filter (\x -> x.bbox.left > found.self.bbox.right) found.siblings
664+
665+ widthsOnLeft : Float
666+ widthsOnLeft =
667+ siblingsOnLeft
668+ |> List.map (.width << .bbox)
669+ |> List.sum
670+ |> (\x -> x + (toFloat (List.length siblingsOnLeft) * toFloat found.parentSpacing))
671+
672+ widthsOnRight =
673+ siblingsOnRight
674+ |> List.map (.width << .bbox)
675+ |> List.sum
676+ |> (\x -> x + (toFloat (List.length siblingsOnRight) * toFloat found.parentSpacing))
677+
678+ expectedCenter : Float
679+ expectedCenter =
680+ found.parent.bbox.left
681+ + widthsOnLeft
682+ + ((found.parent.bbox.width - (widthsOnRight + widthsOnLeft))
683+ / 2
684+ )
685+ in
686+ expectRoundedEquality
687+ selfCenter
688+ expectedCenter
689+
690+ _ ->
691+ expectRoundedEquality selfCenter parentCenter
692+ }
693+
694+
695+{-| -}
696+alignRight : Testable.Attr msg
697+alignRight =
698+ Testable.LabeledTest
699+ { label = "alignRight"
700+ , attr = Element.alignRight
701+ , test =
702+ \found _ ->
703+ if List.member found.location [ Testable.IsNearby Testable.OnLeft, Testable.IsNearby Testable.OnRight ] then
704+ Expect.true "alignRight doesn't apply to elements that are onLeft or onRight" True
705+ else if
706+ List.member found.location
707+ [ Testable.IsNearby Testable.InFront
708+ , Testable.IsNearby Testable.Behind
709+ , Testable.IsNearby Testable.Above
710+ , Testable.IsNearby Testable.Below
711+ ]
712+ then
713+ expectRoundedEquality
714+ found.self.bbox.right
715+ found.parent.bbox.right
716+ else if List.length found.siblings == 0 then
717+ expectRoundedEquality
718+ found.self.bbox.right
719+ (found.parent.bbox.right + found.parent.bbox.padding.right)
720+ else
721+ case found.location of
722+ Testable.InRow ->
723+ let
724+ siblingsOnRight =
725+ List.filter (\x -> x.bbox.left > found.self.bbox.right) found.siblings
726+
727+ spacings =
728+ toFloat (List.length siblingsOnRight * found.parentSpacing)
729+
730+ widthsOnRight =
731+ siblingsOnRight
732+ |> List.map (.width << .bbox)
733+ |> List.sum
734+ in
735+ expectRoundedEquality
736+ found.self.bbox.right
737+ (found.parent.bbox.right - (found.parent.bbox.padding.right + widthsOnRight + spacings))
738+
739+ _ ->
740+ expectRoundedEquality
741+ found.self.bbox.right
742+ (found.parent.bbox.right + found.parent.bbox.padding.right)
743+ }
744+
745+
746+{-| -}
747+alignTop : Testable.Attr msg
748+alignTop =
749+ Testable.LabeledTest
750+ { label = "alignTop"
751+ , attr = Element.alignTop
752+ , test =
753+ \found _ ->
754+ if List.member found.location [ Testable.IsNearby Testable.Above, Testable.IsNearby Testable.Below ] then
755+ Expect.true "alignTop doesn't apply to elements that are above or below" True
756+ else if
757+ List.member found.location
758+ [ Testable.IsNearby Testable.InFront
759+ , Testable.IsNearby Testable.Behind
760+ , Testable.IsNearby Testable.OnRight
761+ , Testable.IsNearby Testable.OnLeft
762+ ]
763+ then
764+ expectRoundedEquality
765+ found.self.bbox.top
766+ found.parent.bbox.top
767+ else if List.length found.siblings == 0 then
768+ expectRoundedEquality
769+ found.self.bbox.top
770+ (found.parent.bbox.top + found.parent.bbox.padding.top)
771+ else
772+ case found.location of
773+ Testable.InColumn ->
774+ let
775+ siblingsAbove =
776+ List.filter (\x -> x.bbox.bottom < found.self.bbox.top) found.siblings
777+
778+ spacings =
779+ toFloat (List.length siblingsAbove * found.parentSpacing)
780+
781+ heightsAbove =
782+ siblingsAbove
783+ |> List.map (.height << .bbox)
784+ |> List.sum
785+ in
786+ expectRoundedEquality
787+ found.self.bbox.top
788+ (found.parent.bbox.top + (found.parent.bbox.padding.top + heightsAbove + spacings))
789+
790+ _ ->
791+ expectRoundedEquality
792+ found.self.bbox.top
793+ found.parent.bbox.top
794+ }
795+
796+
797+{-| -}
798+alignBottom : Testable.Attr msg
799+alignBottom =
800+ Testable.LabeledTest
801+ { label = "alignBottom"
802+ , attr = Element.alignBottom
803+ , test =
804+ \found _ ->
805+ if List.member found.location [ Testable.IsNearby Testable.Above, Testable.IsNearby Testable.Below ] then
806+ Expect.true "alignBottom doesn't apply to elements that are above or below" True
807+ else if
808+ List.member found.location
809+ [ Testable.IsNearby Testable.InFront
810+ , Testable.IsNearby Testable.Behind
811+ , Testable.IsNearby Testable.OnRight
812+ , Testable.IsNearby Testable.OnLeft
813+ ]
814+ then
815+ expectRoundedEquality
816+ found.self.bbox.bottom
817+ found.parent.bbox.bottom
818+ else if List.length found.siblings == 0 then
819+ expectRoundedEquality
820+ found.self.bbox.bottom
821+ (found.parent.bbox.bottom + found.parent.bbox.padding.bottom)
822+ else
823+ case found.location of
824+ Testable.InColumn ->
825+ let
826+ siblingsBelow =
827+ List.filter (\x -> x.bbox.top > found.self.bbox.bottom) found.siblings
828+
829+ spacings =
830+ toFloat (List.length siblingsBelow * found.parentSpacing)
831+
832+ heightsBelow =
833+ siblingsBelow
834+ |> List.map (.height << .bbox)
835+ |> List.sum
836+ in
837+ expectRoundedEquality
838+ found.self.bbox.bottom
839+ (found.parent.bbox.bottom - (found.parent.bbox.padding.bottom + heightsBelow + spacings))
840+
841+ _ ->
842+ expectRoundedEquality
843+ found.self.bbox.bottom
844+ (found.parent.bbox.bottom + found.parent.bbox.padding.bottom)
845+ }
846+
847+
848+expectRoundedEquality x y =
849+ Expect.true ("within 1 of each other " ++ String.fromFloat x ++ ":" ++ String.fromFloat y)
850+ (abs (x - y) < 1)
851+
852+
853+{-| -}
854+centerY : Testable.Attr msg
855+centerY =
856+ Testable.LabeledTest
857+ { label = "centerY"
858+ , attr = Element.centerY
859+ , test =
860+ \found _ ->
861+ let
862+ selfCenter =
863+ found.self.bbox.top + (found.self.bbox.height / 2)
864+
865+ parentCenter =
866+ found.parent.bbox.top + (found.parent.bbox.height / 2)
867+ in
868+ if List.member found.location [ Testable.IsNearby Testable.Above, Testable.IsNearby Testable.Below ] then
869+ Expect.true "centerY doesn't apply to elements that are above or below" True
870+ else if List.length found.siblings == 0 then
871+ expectRoundedEquality
872+ selfCenter
873+ parentCenter
874+ else
875+ case found.location of
876+ Testable.InColumn ->
877+ let
878+ siblingsOnTop =
879+ List.filter (\x -> x.bbox.bottom < found.self.bbox.top) found.siblings
880+
881+ siblingsBelow =
882+ List.filter (\x -> x.bbox.top > found.self.bbox.bottom) found.siblings
883+
884+ heightsAbove : Float
885+ heightsAbove =
886+ siblingsOnTop
887+ |> List.map (.height << .bbox)
888+ |> List.sum
889+ |> (\x -> x + (toFloat (List.length siblingsOnTop) * toFloat found.parentSpacing))
890+
891+ heightsBelow =
892+ siblingsBelow
893+ |> List.map (.height << .bbox)
894+ |> List.sum
895+ |> (\x -> x + (toFloat (List.length siblingsBelow) * toFloat found.parentSpacing))
896+
897+ expectedCenter : Float
898+ expectedCenter =
899+ found.parent.bbox.top
900+ + heightsAbove
901+ + ((found.parent.bbox.height - (heightsBelow + heightsAbove))
902+ / 2
903+ )
904+ in
905+ expectRoundedEquality
906+ selfCenter
907+ expectedCenter
908+
909+ _ ->
910+ expectRoundedEquality selfCenter parentCenter
911+ }
912+
913+
914+{-| -}
915+above : Testable.Element msg -> Testable.Attr msg
916+above element =
917+ Testable.Nearby
918+ { location = Testable.Above
919+ , element = element
920+ , label = "above"
921+ , test =
922+ \found _ ->
923+ expectRoundedEquality found.self.bbox.bottom found.parent.bbox.top
924+ }
925+
926+
927+{-| -}
928+below : Testable.Element msg -> Testable.Attr msg
929+below element =
930+ Testable.Nearby
931+ { location = Testable.Below
932+ , element = element
933+ , label = "below"
934+ , test =
935+ \found _ ->
936+ expectRoundedEquality found.self.bbox.top found.parent.bbox.bottom
937+ }
938+
939+
940+{-| -}
941+onRight : Testable.Element msg -> Testable.Attr msg
942+onRight element =
943+ Testable.Nearby
944+ { location = Testable.OnRight
945+ , element = element
946+ , label = "onRight"
947+ , test =
948+ \found _ ->
949+ expectRoundedEquality found.self.bbox.left found.parent.bbox.right
950+ }
951+
952+
953+{-| -}
954+onLeft : Testable.Element msg -> Testable.Attr msg
955+onLeft element =
956+ Testable.Nearby
957+ { location = Testable.OnLeft
958+ , element = element
959+ , label = "onLeft"
960+ , test =
961+ \found _ ->
962+ expectRoundedEquality found.self.bbox.right found.parent.bbox.left
963+ }
964+
965+
966+{-| -}
967+inFront : Testable.Element msg -> Testable.Attr msg
968+inFront element =
969+ Testable.Nearby
970+ { location = Testable.InFront
971+ , element = element
972+ , label = "inFront"
973+ , test =
974+ \found _ ->
975+ let
976+ horizontalCheck =
977+ if found.self.bbox.width > found.parent.bbox.width then
978+ [ (found.self.bbox.right <= found.parent.bbox.right)
979+ || (found.self.bbox.left >= found.parent.bbox.left)
980+ ]
981+ else
982+ [ found.self.bbox.right <= found.parent.bbox.right
983+ , found.self.bbox.left >= found.parent.bbox.left
984+ ]
985+
986+ verticalCheck =
987+ if found.self.bbox.width > found.parent.bbox.width then
988+ [ (found.self.bbox.top >= found.parent.bbox.top)
989+ || (found.self.bbox.bottom <= found.parent.bbox.bottom)
990+ ]
991+ else
992+ [ found.self.bbox.top >= found.parent.bbox.top
993+ , found.self.bbox.bottom <= found.parent.bbox.bottom
994+ ]
995+ in
996+ Expect.true "within the confines of the parent"
997+ (List.all ((==) True)
998+ (List.concat
999+ [ horizontalCheck
1000+ , verticalCheck
1001+ ]
1002+ )
1003+ )
1004+ }
1005+
1006+
1007+{-| -}
1008+behindContent : Testable.Element msg -> Testable.Attr msg
1009+behindContent element =
1010+ Testable.Nearby
1011+ { location = Testable.Behind
1012+ , element = element
1013+ , label = "behind"
1014+ , test =
1015+ \found _ ->
1016+ let
1017+ horizontalCheck =
1018+ if found.self.bbox.width > found.parent.bbox.width then
1019+ [ (found.self.bbox.right <= found.parent.bbox.right)
1020+ || (found.self.bbox.left >= found.parent.bbox.left)
1021+ ]
1022+ else
1023+ [ found.self.bbox.right <= found.parent.bbox.right
1024+ , found.self.bbox.left >= found.parent.bbox.left
1025+ ]
1026+
1027+ verticalCheck =
1028+ if found.self.bbox.width > found.parent.bbox.width then
1029+ [ (found.self.bbox.top >= found.parent.bbox.top)
1030+ || (found.self.bbox.bottom <= found.parent.bbox.bottom)
1031+ ]
1032+ else
1033+ [ found.self.bbox.top >= found.parent.bbox.top
1034+ , found.self.bbox.bottom <= found.parent.bbox.bottom
1035+ ]
1036+ in
1037+ Expect.true "within the confines of the parent"
1038+ (List.all ((==) True)
1039+ (List.concat
1040+ [ horizontalCheck
1041+ , verticalCheck
1042+ ]
1043+ )
1044+ )
1045+ }
tests/src/Testable/Element/Background.elmnew file+27
@@ -0,0 +1,27 @@
1+module Testable.Element.Background exposing (color)
2+
3+{-| -}
4+
5+import Dict
6+import Element exposing (Color)
7+import Element.Background as Background
8+import Expect
9+import Testable
10+
11+
12+color : Color -> Testable.Attr msg
13+color clr =
14+ Testable.LabeledTest
15+ { label = "background color-" ++ Testable.formatColor clr
16+ , attr = Background.color clr
17+ , test =
18+ \context _ ->
19+ let
20+ selfBackgroundColor =
21+ context.self.style
22+ |> Dict.get "background-color"
23+ |> Maybe.withDefault "notfound"
24+ in
25+ Expect.true ("Color Match - " ++ (Testable.formatColor clr ++ " vs " ++ selfBackgroundColor))
26+ (Testable.compareFormattedColor clr selfBackgroundColor)
27+ }
tests/src/Testable/Element/Font.elmnew file+48
@@ -0,0 +1,48 @@
1+module Testable.Element.Font exposing (color)
2+
3+{-| -}
4+
5+import Dict
6+import Element exposing (Color)
7+import Element.Font as Font
8+import Expect
9+import Testable
10+
11+
12+color : Color -> Testable.Attr msg
13+color clr =
14+ Testable.LabeledTest
15+ { label = "font color-" ++ Testable.formatColor clr
16+ , attr = Font.color clr
17+ , test =
18+ \context _ ->
19+ let
20+ selfFontColor =
21+ context.self.style
22+ |> Dict.get "color"
23+ |> Maybe.withDefault "notfound"
24+ in
25+ Expect.true ("Color Match - " ++ (Testable.formatColor clr ++ " vs " ++ selfFontColor))
26+ (Testable.compareFormattedColor clr selfFontColor)
27+ }
28+
29+
30+size : Int -> Testable.Attr msg
31+size i =
32+ Testable.LabeledTest
33+ { label = "font size-" ++ String.fromInt i
34+ , attr = Font.size i
35+ , test =
36+ \context _ ->
37+ let
38+ selfFontSize =
39+ context.self.style
40+ |> Dict.get "fontsize"
41+ |> Maybe.withDefault "notfound"
42+
43+ formattedInt =
44+ String.fromInt i
45+ in
46+ Expect.true ("Size Match - " ++ (formattedInt ++ " vs " ++ selfFontSize))
47+ (formattedInt == selfFontSize)
48+ }
tests/src/Testable/FuzzMain.elmnew file+629
@@ -0,0 +1,629 @@
1+port module Main exposing (..)
2+
3+{-| -}
4+
5+import AnimationFrame
6+import Color
7+import Dict exposing (Dict)
8+import Element
9+import Element.Background as Background
10+import Element.Border as Border
11+import Element.Font as Font
12+import Expect
13+import Fuzz
14+import Html exposing (Html)
15+import List.Extra
16+import Random
17+import Test
18+import Test.Runner
19+import Test.Runner.Failure
20+import Testable
21+import Tests exposing (Testable)
22+import Time exposing (Time)
23+
24+
25+init : ( Model Msg, Cmd Msg )
26+init =
27+ ( { current = Nothing
28+ , upcoming =
29+ Tests.tests
30+
31+ -- generateTests (Random.initialSeed 227852860) 1
32+ , finished = []
33+ , stage = BeginRendering
34+ }
35+ , Cmd.none
36+ )
37+
38+
39+main : Program Never (Model Msg) Msg
40+main =
41+ Html.program
42+ { init = init
43+ , view = view
44+ , update = update
45+ , subscriptions = subscriptions
46+ }
47+
48+
49+type alias Model msg =
50+ { current : Maybe (Testable msg)
51+ , upcoming : List (Testable msg)
52+ , finished : List (Testable msg)
53+ , stage : Stage
54+ }
55+
56+
57+type Msg
58+ = NoOp
59+ | Tick Time
60+ | RefreshBoundingBox
61+ (List
62+ { id : String
63+ , bbox : Testable.BoundingBox
64+ , style : Testable.Style
65+ }
66+ )
67+
68+
69+type Stage
70+ = Rendered
71+ | BeginRendering
72+ | GatherData
73+ | Finished
74+
75+
76+runTest : Dict String Testable.Found -> Testable msg -> Testable msg
77+runTest boxes test =
78+ let
79+ tests =
80+ Testable.toTest test.label boxes test.element
81+
82+ seed =
83+ Random.initialSeed 227852860
84+
85+ results =
86+ Testable.runTests seed (Debug.log "created Tests" tests)
87+ |> Debug.log "run tests"
88+ in
89+ { test | results = Just results }
90+
91+
92+generateTests : Random.Seed -> Int -> List (Testable msg)
93+generateTests seed num =
94+ if num <= 0 then
95+ []
96+ else
97+ let
98+ ( testable, newSeed ) =
99+ generateTest seed
100+ in
101+ testable :: generateTests newSeed (num - 1)
102+
103+
104+generateTest : Random.Seed -> ( Testable msg, Random.Seed )
105+generateTest seed =
106+ testableLayout
107+ |> Test.Runner.fuzz
108+ |> flip Random.step seed
109+ |> (\( ( val, shrinker ), newSeed ) -> ( val, seed ))
110+
111+
112+update : Msg -> Model Msg -> ( Model Msg, Cmd Msg )
113+update msg model =
114+ case msg of
115+ NoOp ->
116+ ( model, Cmd.none )
117+
118+ RefreshBoundingBox boxes ->
119+ case model.current of
120+ Nothing ->
121+ ( { model | stage = Finished }
122+ , Cmd.none
123+ )
124+
125+ Just current ->
126+ let
127+ toTuple box =
128+ ( box.id, { style = box.style, bbox = box.bbox } )
129+
130+ foundData =
131+ boxes
132+ |> List.map toTuple
133+ |> Dict.fromList
134+
135+ currentResults =
136+ runTest foundData current
137+ in
138+ case model.upcoming of
139+ [] ->
140+ ( { model
141+ | stage = Finished
142+ , current = Nothing
143+ , finished = currentResults :: model.finished
144+ }
145+ , Cmd.none
146+ )
147+
148+ newCurrent :: remaining ->
149+ ( { model
150+ | finished = currentResults :: model.finished
151+ , stage = BeginRendering
152+ }
153+ , Cmd.none
154+ )
155+
156+ Tick time ->
157+ case model.stage of
158+ BeginRendering ->
159+ case model.upcoming of
160+ [] ->
161+ ( { model | stage = Rendered }
162+ , Cmd.none
163+ )
164+
165+ current :: remaining ->
166+ ( { model
167+ | stage = Rendered
168+ , upcoming = remaining
169+ , current = Just current
170+ }
171+ , Cmd.none
172+ )
173+
174+ Rendered ->
175+ case model.current of
176+ Nothing ->
177+ ( { model | stage = Finished }
178+ , Cmd.none
179+ )
180+
181+ Just test ->
182+ ( { model | stage = GatherData }
183+ , analyze (Testable.getIds test.element)
184+ )
185+
186+ _ ->
187+ ( { model | stage = Rendered }
188+ , Cmd.none
189+ )
190+
191+
192+subscriptions : { a | stage : Stage } -> Sub Msg
193+subscriptions model =
194+ Sub.batch
195+ [ styles RefreshBoundingBox
196+ , case model.stage of
197+ BeginRendering ->
198+ AnimationFrame.times Tick
199+
200+ Rendered ->
201+ AnimationFrame.times Tick
202+
203+ _ ->
204+ Sub.none
205+ ]
206+
207+
208+port analyze : List String -> Cmd msg
209+
210+
211+port styles : (List { id : String, bbox : Testable.BoundingBox, style : Testable.Style } -> msg) -> Sub msg
212+
213+
214+view : Model Msg -> Html Msg
215+view model =
216+ case model.current of
217+ Nothing ->
218+ if model.stage == Finished then
219+ Element.layout [] <|
220+ Element.column
221+ [ Element.spacing 20
222+ , Element.padding 20
223+ , Element.width (Element.px 800)
224+
225+ -- , Background.color Color.grey
226+ ]
227+ (List.map viewResult model.finished)
228+ else
229+ Html.text "running?"
230+
231+ Just current ->
232+ Testable.render current.element
233+
234+
235+viewResult : Testable Msg -> Element.Element Msg
236+viewResult testable =
237+ let
238+ viewSingle result =
239+ case result of
240+ ( label, Nothing ) ->
241+ Element.el
242+ [ Background.color Color.green
243+ , Font.color Color.white
244+ , Element.paddingXY 20 10
245+ , Element.alignLeft
246+ , Border.rounded 3
247+ ]
248+ <|
249+ Element.text (label ++ " - " ++ "Success!")
250+
251+ ( label, Just { given, description, reason } ) ->
252+ Element.row
253+ [ Background.color Color.red
254+ , Font.color Color.white
255+ , Element.paddingXY 20 10
256+ , Element.alignLeft
257+ , Element.spacing 25
258+ , Border.rounded 3
259+ ]
260+ [ Element.el [ Element.width Element.fill ] <| Element.text label
261+
262+ -- , Element.el [ Element.width Element.fill ] <| Element.text description
263+ , Element.el [ Element.width Element.fill ] <| Element.text (toString reason)
264+ ]
265+ in
266+ Element.column
267+ [ Border.width 1
268+ , Border.color Color.lightGrey
269+ , Element.padding 20
270+ , Element.height Element.shrink
271+ , Element.alignLeft
272+ ]
273+ [ Element.el [ Font.bold ] (Element.text testable.label)
274+ , Element.column [ Element.alignLeft, Element.spacing 20 ]
275+ (case testable.results of
276+ Nothing ->
277+ [ Element.text "no results" ]
278+
279+ Just results ->
280+ List.map viewSingle results
281+ )
282+ ]
283+
284+
285+{--}
286+type alias Testable msg =
287+ { element : Testable.Element msg
288+ , label : String
289+ , results :
290+ Maybe
291+ (List
292+ ( String
293+ , Maybe
294+ { given : Maybe String
295+ , description : String
296+ , reason : Test.Runner.Failure.Reason
297+ }
298+ )
299+ )
300+ }
301+
302+
303+attr tag label attr test =
304+ Testable.Batch
305+ [ Testable.Attr attr
306+ , Testable.AttrTest
307+ (\found ->
308+ Test.test (tag ++ ": " ++ label) <|
309+ \_ -> test found
310+ )
311+ ]
312+
313+
314+widthFill tag =
315+ attr tag
316+ "width fill"
317+ (Element.width Element.fill)
318+ (\context ->
319+ let
320+ spacePerPortion =
321+ context.parent.bbox.width / toFloat (List.length context.siblings + 1)
322+ in
323+ Expect.equal spacePerPortion context.self.bbox.width
324+ )
325+
326+
327+heightFill tag =
328+ attr tag
329+ "height fill"
330+ (Element.height Element.fill)
331+ (\context ->
332+ let
333+ spacePerPortion =
334+ context.parent.bbox.height / toFloat (List.length context.siblings + 1)
335+ in
336+ Expect.equal spacePerPortion context.self.bbox.height
337+ )
338+
339+
340+heightPx px tag =
341+ attr tag
342+ ("height at " ++ toString px ++ "px")
343+ (Element.height (Element.px (round px)))
344+ (\found ->
345+ Expect.equal found.self.bbox.height px
346+ )
347+
348+
349+widthPx px tag =
350+ attr tag
351+ ("width at " ++ toString px ++ "px")
352+ (Element.width (Element.px (round px)))
353+ (\found ->
354+ Expect.equal found.self.bbox.width px
355+ )
356+
357+
358+type alias PossibleAttributes msg =
359+ { width : String -> Testable.Attr msg
360+ , height : String -> Testable.Attr msg
361+ }
362+
363+
364+possibleToList str { width, height } =
365+ width str :: height str :: []
366+
367+
368+availableAttributes : { width : Float, height : Float } -> Fuzz.Fuzzer (PossibleAttributes msg)
369+availableAttributes bounds =
370+ Fuzz.map2 PossibleAttributes
371+ (Fuzz.oneOf
372+ (List.map Fuzz.constant (widths bounds.width))
373+ )
374+ (Fuzz.oneOf
375+ (List.map Fuzz.constant (heights bounds.height))
376+ )
377+
378+
379+widths : Float -> List (String -> Testable.Attr msg)
380+widths width =
381+ [ \tag ->
382+ attr tag
383+ (" with width at " ++ toString width ++ "px")
384+ (Element.width (Element.px (round width)))
385+ (\found ->
386+ Expect.equal found.self.bbox.width width
387+ )
388+ , \tag ->
389+ attr tag
390+ " with width fill"
391+ (Element.width Element.fill)
392+ (\context ->
393+ let
394+ -- {-|
395+ -- Calculation is
396+ -- totalparentwidth - (paddingLeft ++ paddingRight)
397+ -- minus widths of all the other children
398+ -- -}
399+ otherWidth =
400+ -- Debug.log "width" <|
401+ List.sum <|
402+ -- Debug.log "widths" <|
403+ List.map (\child -> child.bbox.width) context.siblings
404+
405+ spacePerPortion =
406+ context.parent.bbox.width - otherWidth
407+ in
408+ Expect.equal spacePerPortion context.self.bbox.width
409+ )
410+ ]
411+
412+
413+heights : Float -> List (String -> Testable.Attr msg)
414+heights height =
415+ [ \tag ->
416+ attr tag
417+ (" with height at " ++ toString height ++ "px")
418+ (Element.height (Element.px (round height)))
419+ (\found ->
420+ Expect.equal found.self.bbox.height height
421+ )
422+ , \tag ->
423+ attr tag
424+ " with height fill"
425+ (Element.height Element.fill)
426+ (\context ->
427+ let
428+ -- {-|
429+ -- Calculation is
430+ -- totalparentwidth - (paddingLeft ++ paddingRight)
431+ -- minus widths of all the other children
432+ -- -}
433+ otherHeight =
434+ List.sum <|
435+ List.map (\child -> child.bbox.height) context.siblings
436+
437+ spacePerPortion =
438+ context.parent.bbox.height - otherHeight
439+ in
440+ Expect.equal spacePerPortion context.self.bbox.height
441+ )
442+ ]
443+
444+
445+content : Testable.Element msg
446+content =
447+ Testable.El
448+ [ Testable.Attr <| Element.width (Element.px 20)
449+ , Testable.Attr <| Element.height (Element.px 20)
450+ ]
451+ Testable.Empty
452+
453+
454+tests : List (Testable msg)
455+tests =
456+ [ { label = "Width/Height 200"
457+ , results = Nothing
458+ , element =
459+ Testable.El
460+ [ widthPx 200 "single element"
461+ , heightPx 200 "single element"
462+ ]
463+ Testable.Empty
464+ }
465+ , { label = "Width Fill/Height Fill - 1"
466+ , results = Nothing
467+ , element =
468+ Testable.El
469+ [ widthFill "single element"
470+ , heightFill "single element"
471+ ]
472+ <|
473+ content
474+ }
475+ , { label = "Width Fill/Height Fill - 2"
476+ , results = Nothing
477+ , element =
478+ Testable.El
479+ [ widthFill "top element"
480+ , heightFill "top element"
481+ ]
482+ <|
483+ Testable.El
484+ [ widthFill "embedded element"
485+ , heightFill "embedded element"
486+ ]
487+ <|
488+ content
489+ }
490+ , { label = "Row Width Fill/Height Fill"
491+ , results = Nothing
492+ , element =
493+ Testable.Row
494+ [ widthFill "on row"
495+ , heightFill "on row"
496+ ]
497+ [ Testable.El
498+ [ widthFill "row - 1"
499+ , heightFill "row - 1"
500+ ]
501+ content
502+ , Testable.El
503+ [ widthFill "row - 2"
504+ , heightFill "row - 2"
505+ ]
506+ content
507+ , Testable.El
508+ [ widthFill "row - 3"
509+ , heightFill "row - 3"
510+ ]
511+ content
512+ ]
513+ }
514+ ]
515+
516+
517+
518+{- Generating A Complete Test Suite
519+
520+-}
521+
522+
523+type TestableElement
524+ = El
525+ | Row
526+ | Column
527+
528+
529+allIndividualTestables : List (Fuzz.Fuzzer TestableElement)
530+allIndividualTestables =
531+ [ Fuzz.constant El
532+ , Fuzz.constant Row
533+ , Fuzz.constant Column
534+ ]
535+
536+
537+testableLayout : Fuzz.Fuzzer (Testable msg)
538+testableLayout =
539+ let
540+ asTestable layout =
541+ { element = layout
542+ , label = "A Sweet Test"
543+ , results = Nothing
544+ }
545+ in
546+ Fuzz.map asTestable layout
547+
548+
549+layout : Fuzz.Fuzzer (Testable.Element msg)
550+layout =
551+ Fuzz.map4 createContainer
552+ (Fuzz.tuple3
553+ ( availableAttributes { width = 20, height = 20 }
554+ , availableAttributes { width = 20, height = 20 }
555+ , availableAttributes { width = 20, height = 20 }
556+ )
557+ )
558+ (availableAttributes { width = 200, height = 200 })
559+ (Fuzz.oneOf allIndividualTestables)
560+ (Fuzz.oneOf allIndividualTestables)
561+
562+
563+createElement : String -> TestableElement -> PossibleAttributes msg -> Testable.Element msg
564+createElement tag layout possibleAttrs =
565+ let
566+ attrs =
567+ possibleToList tag possibleAttrs
568+ in
569+ case layout of
570+ El ->
571+ Testable.El attrs content
572+
573+ Row ->
574+ Testable.Row attrs
575+ [ content
576+ , content
577+ , content
578+ ]
579+
580+ Column ->
581+ Testable.Column attrs
582+ [ content
583+ , content
584+ , content
585+ ]
586+
587+
588+createContainer : ( PossibleAttributes msg, PossibleAttributes msg, PossibleAttributes msg ) -> PossibleAttributes msg -> TestableElement -> TestableElement -> Testable.Element msg
589+createContainer ( a1, a2, a3 ) possibleAttrs parent child =
590+ let
591+ attrs =
592+ possibleToList "0" possibleAttrs
593+ in
594+ case parent of
595+ El ->
596+ Testable.El attrs (createElement "0-0" child a1)
597+
598+ Row ->
599+ Testable.Row attrs
600+ [ createElement "0-0" child a1
601+ , createElement "0-1" child a2
602+ , createElement "0-2" child a3
603+ ]
604+
605+ Column ->
606+ Testable.Column attrs
607+ [ createElement "0-0" child a1
608+ , createElement "0-1" child a2
609+ , createElement "0-2" child a3
610+ ]
611+
612+
613+{-| Given a list of a list of possibilities,
614+
615+choose once from each list.
616+
617+ [ [width (px 100), width fill]
618+ , [height (px 100), height fill]
619+ ]
620+ ->
621+
622+ [ [width (px 100), height (px 100)]
623+ , [width (px 100), height fill]
624+ ]
625+
626+-}
627+allPossibilities : List (List a) -> List (List a)
628+allPossibilities =
629+ List.Extra.cartesianProduct
tests/src/Testable/Runner.elmnew file+444
@@ -0,0 +1,444 @@
1+port module Testable.Runner exposing (TestableProgram, program, show)
2+
3+{-| -}
4+
5+import Browser
6+import Char
7+import Dict exposing (Dict)
8+import Element
9+import Element.Background as Background
10+import Element.Border as Border
11+import Element.Font as Font
12+import Html exposing (Html)
13+import Parser exposing ((|.), (|=))
14+import Process
15+import Random
16+import Set
17+import Task
18+import Test.Runner
19+import Test.Runner.Failure as Failure
20+import Testable
21+import Time
22+
23+
24+show : Testable.Element msg -> Html msg
25+show =
26+ Testable.render
27+
28+
29+type alias TestableProgram =
30+ Program () (Model Msg) Msg
31+
32+
33+palette =
34+ { white = Element.rgb 1 1 1
35+ , red = Element.rgb 1 0 0
36+ , green = Element.rgb 0 1 0
37+ , lightGrey = Element.rgb 0.7 0.7 0.7
38+ }
39+
40+
41+program : List ( String, Testable.Element Msg ) -> TestableProgram
42+program tests =
43+ let
44+ ( current, upcoming ) =
45+ case tests of
46+ [] ->
47+ ( Nothing, [] )
48+
49+ cur :: remaining ->
50+ ( Just cur, remaining )
51+ in
52+ Browser.embed
53+ { init =
54+ always
55+ ( { current = current
56+ , upcoming = upcoming
57+ , finished = []
58+ }
59+ , Task.perform (always Analyze)
60+ (Process.sleep 32
61+ |> Task.andThen
62+ (always Time.now)
63+ )
64+ )
65+ , view = view
66+ , update = update
67+ , subscriptions = subscriptions
68+ }
69+
70+
71+subscriptions : Model Msg -> Sub Msg
72+subscriptions model =
73+ Sub.batch
74+ [ styles RefreshBoundingBox
75+ ]
76+
77+
78+type alias Model msg =
79+ { current : Maybe ( String, Testable.Element msg )
80+ , upcoming : List ( String, Testable.Element msg )
81+ , finished : List (WithResults (Testable.Element msg))
82+ }
83+
84+
85+type alias WithResults thing =
86+ { element : thing
87+ , label : String
88+ , results :
89+ List
90+ ( String
91+ , Maybe
92+ { given : Maybe String
93+ , description : String
94+ , reason : Failure.Reason
95+ }
96+ )
97+ }
98+
99+
100+prepareResults :
101+ List (WithResults (Testable.Element msg))
102+ ->
103+ List
104+ { label : String
105+ , results :
106+ List
107+ ( String
108+ , Maybe
109+ { given : Maybe String
110+ , description : String
111+ }
112+ )
113+ }
114+prepareResults withResults =
115+ let
116+ prepareNode ( x, maybeResult ) =
117+ ( x
118+ , case maybeResult of
119+ Nothing ->
120+ Nothing
121+
122+ Just res ->
123+ Just
124+ { given = res.given
125+ , description = res.description
126+ }
127+ )
128+
129+ prepare { label, results } =
130+ { label = label
131+ , results = List.map prepareNode results
132+ }
133+ in
134+ List.map prepare withResults
135+
136+
137+type Msg
138+ = NoOp
139+ | Analyze
140+ | RefreshBoundingBox
141+ (List
142+ { id : String
143+ , bbox : Testable.BoundingBox
144+ , style : List ( String, String )
145+ }
146+ )
147+
148+
149+runTest : Dict String Testable.Found -> String -> Testable.Element msg -> WithResults (Testable.Element msg)
150+runTest boxes label element =
151+ let
152+ tests =
153+ Testable.toTest label boxes element
154+
155+ seed =
156+ Random.initialSeed 227852860
157+
158+ results =
159+ Testable.runTests seed tests
160+ in
161+ { element = element
162+ , label = label
163+ , results = results
164+ }
165+
166+
167+update : Msg -> Model Msg -> ( Model Msg, Cmd Msg )
168+update msg model =
169+ case msg of
170+ NoOp ->
171+ ( model, Cmd.none )
172+
173+ RefreshBoundingBox boxes ->
174+ case model.current of
175+ Nothing ->
176+ ( model
177+ , Cmd.none
178+ )
179+
180+ Just ( label, current ) ->
181+ let
182+ toTuple box =
183+ ( box.id, { style = Dict.fromList box.style, bbox = box.bbox } )
184+
185+ foundData =
186+ boxes
187+ |> List.map toTuple
188+ |> Dict.fromList
189+
190+ currentResults =
191+ runTest foundData label current
192+ in
193+ case model.upcoming of
194+ [] ->
195+ ( { model
196+ | current = Nothing
197+ , finished = model.finished ++ [ currentResults ]
198+ }
199+ , report (prepareResults (currentResults :: model.finished))
200+ )
201+
202+ newCurrent :: remaining ->
203+ ( { model
204+ | finished = model.finished ++ [ currentResults ]
205+ , current = Just newCurrent
206+ , upcoming = remaining
207+ }
208+ , Task.perform (always Analyze)
209+ (Process.sleep 32
210+ |> Task.andThen
211+ (always Time.now)
212+ )
213+ )
214+
215+ Analyze ->
216+ case model.current of
217+ Nothing ->
218+ ( model
219+ , Cmd.none
220+ )
221+
222+ Just ( label, current ) ->
223+ ( model
224+ , analyze (Testable.getIds current)
225+ )
226+
227+
228+view : Model Msg -> Html Msg
229+view model =
230+ case model.current of
231+ Nothing ->
232+ if List.isEmpty model.upcoming then
233+ -- (List.map viewResult model.finished)
234+ case model.finished of
235+ [] ->
236+ Element.layout [] <|
237+ Element.column
238+ [ Element.spacing 20
239+ , Element.padding 20
240+ , Element.width (Element.px 800)
241+
242+ -- , Background.color Color.grey
243+ ]
244+ [ Element.none ]
245+
246+ finished :: remaining ->
247+ if False then
248+ viewResultsInline finished
249+ else
250+ Element.layout [] <|
251+ Element.column
252+ [ Element.spacing 20
253+ , Element.padding 20
254+ , Element.width (Element.px 800)
255+ ]
256+ (List.map viewResult (finished :: remaining))
257+ else
258+ Html.text "running?"
259+
260+ Just ( label, current ) ->
261+ Testable.render current
262+
263+
264+viewResultsInline : WithResults (Testable.Element Msg) -> Html Msg
265+viewResultsInline testable =
266+ Html.div
267+ []
268+ [ viewResultsAnnotationStylesheet testable.results
269+ , Testable.render testable.element
270+ ]
271+
272+
273+{-| Our ID is part of our label. This could be fixed farther down the chain, but I think it'd be pretty involved.
274+
275+So, now we can just parse the id out of the label.
276+
277+-}
278+parseId str =
279+ str
280+ |> Parser.run
281+ (Parser.succeed identity
282+ |. Parser.chompWhile (\c -> c /= '#')
283+ |= Parser.variable
284+ { start = \c -> c == '#'
285+ , inner = \c -> Char.isAlphaNum c || c == '-'
286+ , reserved = Set.empty
287+ }
288+ )
289+ |> Result.toMaybe
290+
291+
292+viewResultsAnnotationStylesheet results =
293+ let
294+ toStyleClass ( label, maybeFailure ) =
295+ case maybeFailure of
296+ Nothing ->
297+ ""
298+
299+ Just failure ->
300+ case parseId label of
301+ Nothing ->
302+ Debug.log "NO ID FOUND" label
303+
304+ Just id ->
305+ id ++ " { background-color:red; outline: dashed; };"
306+
307+ styleSheet =
308+ results
309+ |> List.map toStyleClass
310+ |> String.join ""
311+ in
312+ Html.node "style"
313+ []
314+ [ Html.text styleSheet
315+ ]
316+
317+
318+viewResult : WithResults (Testable.Element Msg) -> Element.Element Msg
319+viewResult testable =
320+ let
321+ isPassing result =
322+ case Tuple.second result of
323+ Nothing ->
324+ True
325+
326+ Just _ ->
327+ False
328+
329+ ( passing, failing ) =
330+ List.partition isPassing testable.results
331+
332+ viewSingle result =
333+ case result of
334+ ( label, Nothing ) ->
335+ Element.el
336+ [ Background.color palette.green
337+ , Font.color palette.white
338+ , Element.paddingXY 20 10
339+ , Element.alignLeft
340+ , Border.rounded 3
341+ ]
342+ <|
343+ Element.text ("Success! - " ++ label)
344+
345+ ( label, Just ({ given, description } as reason) ) ->
346+ Element.column
347+ [ Background.color palette.red
348+ , Font.color palette.white
349+ , Element.paddingXY 20 10
350+ , Element.alignLeft
351+ , Element.width Element.shrink
352+
353+ -- , Element.spacing 25
354+ , Border.rounded 3
355+ ]
356+ [ Element.el [ Element.width Element.fill ] <| Element.text label
357+ , Element.el [ Element.width Element.fill ] <| Element.text (viewReason reason)
358+ ]
359+ in
360+ Element.column
361+ [ Border.width 1
362+ , Border.color palette.lightGrey
363+ , Element.padding 20
364+ , Element.height Element.shrink
365+ , Element.alignLeft
366+ , Element.spacing 16
367+ ]
368+ [ Element.el [ Font.bold, Font.size 64 ] (Element.text testable.label)
369+ , Element.column [ Element.alignLeft, Element.spacing 20 ]
370+ (failing
371+ |> List.map viewSingle
372+ )
373+ , Element.el
374+ [ Element.alignLeft
375+ , Element.spacing 20
376+ , Background.color palette.green
377+ , Font.color palette.white
378+ , Element.paddingXY 20 10
379+ , Element.alignLeft
380+ , Border.rounded 3
381+ ]
382+ (Element.text (String.fromInt (List.length passing) ++ " tests passing!"))
383+ ]
384+
385+
386+viewReason { description, reason } =
387+ case reason of
388+ Failure.Custom ->
389+ description
390+
391+ Failure.Equality one two ->
392+ description ++ " " ++ one ++ " " ++ two
393+
394+ Failure.Comparison one two ->
395+ description ++ " " ++ one ++ " " ++ two
396+
397+ Failure.ListDiff expected actual ->
398+ "expected\n"
399+ ++ String.join " \n" expected
400+ ++ "actual\n"
401+ ++ String.join " \n" actual
402+
403+ Failure.CollectionDiff { expected, actual, extra, missing } ->
404+ String.join "\n"
405+ [ formatKeyValue "expected" expected
406+ , formatKeyValue "actual" actual
407+ , formatKeyValue "extra" (String.join ", " extra)
408+ , formatKeyValue "missing" (String.join ", " missing)
409+ ]
410+
411+ Failure.TODO ->
412+ description
413+
414+ Failure.Invalid _ ->
415+ description
416+
417+
418+formatKeyValue : String -> String -> String
419+formatKeyValue key val =
420+ key ++ ": " ++ val
421+
422+
423+port report :
424+ List
425+ { label : String
426+ , results :
427+ List
428+ ( String
429+ , Maybe
430+ { given : Maybe String
431+ , description : String
432+ }
433+ )
434+ }
435+ -> Cmd msg
436+
437+
438+port test : String -> Cmd msg
439+
440+
441+port analyze : List String -> Cmd msg
442+
443+
444+port styles : (List { id : String, bbox : Testable.BoundingBox, style : List ( String, String ) } -> msg) -> Sub msg
tests/suite/ClassNames.elmnew file+158
@@ -0,0 +1,158 @@
1+module Main exposing (..)
2+
3+import Html
4+import Internal.Flag as Flag
5+import Internal.Style
6+
7+
8+main =
9+ Html.div []
10+ [ Html.text "The following names collide"
11+ , Html.div []
12+ (List.map viewPair onlyDuplicates)
13+ ]
14+
15+
16+viewPair ( name, description ) =
17+ Html.div []
18+ [ Html.text name
19+ , Html.text ": "
20+ , Html.text description
21+ ]
22+
23+
24+onlyDuplicates =
25+ List.filter findDuplicates allClassNames
26+
27+
28+findDuplicates ( name, description ) =
29+ List.any
30+ (\( checkName, checkDescription ) ->
31+ checkName == name && description /= checkDescription
32+ )
33+ allClassNames
34+
35+
36+allClassNames =
37+ List.map (Tuple.mapFirst (\fn -> fn Internal.Style.classes)) allClassNameFns
38+
39+
40+allClassNameFns =
41+ [ ( .root, "root" )
42+ , ( .any, "any" )
43+ , ( .single, "single" )
44+ , ( .row, "row" )
45+ , ( .column, "column" )
46+ , ( .page, "page" )
47+ , ( .paragraph, "paragraph" )
48+ , ( .text, "text" )
49+ , ( .grid, "grid" )
50+ , ( .imageContainer, "imageContainer" )
51+
52+ -- widhts/heights
53+ , ( .widthFill, "widthFill" )
54+ , ( .widthContent, "widthContent" )
55+ , ( .widthExact, "widthExact" )
56+ , ( .widthFillPortion, "widthFillPortion" )
57+ , ( .heightFill, "heightFill" )
58+ , ( .heightContent, "heightContent" )
59+ , ( .heightFillPortion, "heightFillPortion" )
60+ , ( .seButton, "seButton" )
61+
62+ -- nearby elements
63+ , ( .above, "above" )
64+ , ( .below, "below" )
65+ , ( .onRight, "onRight" )
66+ , ( .onLeft, "onLeft" )
67+ , ( .inFront, "inFront" )
68+ , ( .behind, "behind" )
69+
70+ -- alignments
71+ , ( .alignTop, "alignTop" )
72+ , ( .alignBottom, "alignBottom" )
73+ , ( .alignRight, "alignRight" )
74+ , ( .alignLeft, "alignLeft" )
75+ , ( .alignCenterX, "alignCenterX" )
76+ , ( .alignCenterY, "alignCenterY" )
77+ , ( .alignedHorizontally, "alignedHorizontally" )
78+ , ( .alignedVertically, "alignedVertically" )
79+
80+ -- space evenly
81+ , ( .spaceEvenly, "spaceEvenly" )
82+ , ( .container, "container" )
83+ , ( .alignContainerRight, "alignContainerRight" )
84+ , ( .alignContainerBottom, "alignContainerBottom" )
85+ , ( .alignContainerCenterX, "alignContainerCenterX" )
86+ , ( .alignContainerCenterY, "alignContainerCenterY" )
87+
88+ -- content alignments
89+ , ( .contentTop, "contentTop" )
90+ , ( .contentBottom, "contentBottom" )
91+ , ( .contentRight, "contentRight" )
92+ , ( .contentLeft, "contentLeft" )
93+ , ( .contentCenterX, "contentCenterX" )
94+ , ( .contentCenterY, "contentCenterY" )
95+
96+ -- selection
97+ , ( .noTextSelection, "noTextSelection" )
98+ , ( .cursorPointer, "cursorPointer" )
99+ , ( .cursorText, "cursorText" )
100+
101+ -- pointer events
102+ , ( .passPointerEvents, "passPointerEvents" )
103+ , ( .capturePointerEvents, "capturePointerEvents" )
104+ , ( .transparent, "transparent" )
105+ , ( .opaque, "opaque" )
106+ , ( .overflowHidden, "overflowHidden" )
107+
108+ -- special state classes
109+ , ( .hover, "hover" )
110+
111+ -- , ( .hoverOpaque, "hoverOpaque" )
112+ , ( .focus, "focus" )
113+
114+ -- , ( .focusOpaque, "focusOpaque" )
115+ , ( .active, "active" )
116+
117+ -- , ( .activeOpaque, "activeOpaque" )
118+ --scrollbars
119+ , ( .scrollbars, "scrollbars" )
120+ , ( .scrollbarsX, "scrollbarsX" )
121+ , ( .scrollbarsY, "scrollbarsY" )
122+ , ( .clip, "clip" )
123+ , ( .clipX, "clipX" )
124+ , ( .clipY, "clipY" )
125+
126+ -- borders
127+ , ( .borderNone, "borderNone" )
128+ , ( .borderDashed, "borderDashed" )
129+ , ( .borderDotted, "borderDotted" )
130+ , ( .borderSolid, "borderSolid" )
131+
132+ -- text weight
133+ , ( .textThin, "textThin" )
134+ , ( .textExtraLight, "textExtraLight" )
135+ , ( .textLight, "textLight" )
136+ , ( .textNormalWeight, "textNormalWeight" )
137+ , ( .textMedium, "textMedium" )
138+ , ( .textSemiBold, "textSemiBold" )
139+ , ( .bold, "bold" )
140+ , ( .textExtraBold, "textExtraBold" )
141+ , ( .textHeavy, "textHeavy" )
142+ , ( .italic, "italic" )
143+ , ( .strike, "strike" )
144+ , ( .underline, "underline" )
145+ , ( .textUnitalicized, "textUnitalicized" )
146+
147+ -- text alignment
148+ , ( .textJustify, "textJustify" )
149+ , ( .textJustifyAll, "textJustifyAll" )
150+ , ( .textCenter, "textCenter" )
151+ , ( .textRight, "textRight" )
152+ , ( .textLeft, "textLeft" )
153+ , ( .transition, "transition" )
154+
155+ -- inputText
156+ , ( .inputText, "inputText" )
157+ , ( .inputMultiline, "inputMultiline" )
158+ ]
tests/suite/Flags.elmnew file+89
@@ -0,0 +1,89 @@
1+module Main exposing (..)
2+
3+import Html
4+import Internal.Flag as Flag
5+
6+
7+main =
8+ Html.div []
9+ [ Html.text "Verify All Flags invalidate themselves"
10+ , Html.div []
11+ (List.indexedMap invalidateSelf allFlags)
12+ , Html.text "Verify All Flags don't interfere with other flags"
13+ , Html.div []
14+ (List.indexedMap doesntInvalidateOthers allFlags)
15+ ]
16+
17+
18+invalidateSelf i flag =
19+ if Flag.present flag (Flag.add flag Flag.none) then
20+ Html.text ""
21+ else
22+ Html.div [] [ Html.text (toString (Flag.value flag) ++ " at index " ++ toString i ++ " does not invalidate itself") ]
23+
24+
25+doesntInvalidateOthers i flag =
26+ let
27+ withFlag =
28+ Flag.none
29+ |> Flag.add flag
30+
31+ passing =
32+ List.all identity <|
33+ List.indexedMap
34+ (\j otherFlag ->
35+ Flag.present otherFlag (Flag.add otherFlag withFlag)
36+ )
37+ allFlags
38+ in
39+ if passing then
40+ Html.text ""
41+ else
42+ Html.div []
43+ [ Html.text (toString (Flag.value flag) ++ " at index " ++ toString i ++ " invalidates other flags!")
44+ ]
45+
46+
47+allFlags =
48+ [ Flag.transparency
49+ , Flag.padding
50+ , Flag.spacing
51+ , Flag.fontSize
52+ , Flag.fontFamily
53+ , Flag.width
54+ , Flag.height
55+ , Flag.bgColor
56+ , Flag.bgImage
57+ , Flag.bgGradient
58+ , Flag.borderStyle
59+ , Flag.fontAlignment
60+ , Flag.fontWeight
61+ , Flag.fontColor
62+ , Flag.wordSpacing
63+ , Flag.letterSpacing
64+ , Flag.borderRound
65+ , Flag.shadows
66+ , Flag.overflow
67+ , Flag.cursor
68+ , Flag.scale
69+ , Flag.rotate
70+ , Flag.moveX
71+ , Flag.moveY
72+ , Flag.borderWidth
73+ , Flag.borderColor
74+ , Flag.yAlign
75+ , Flag.xAlign
76+ , Flag.focus
77+ , Flag.active
78+ , Flag.hover
79+ , Flag.gridTemplate
80+ , Flag.gridPosition
81+ , Flag.heightContent
82+ , Flag.heightFill
83+ , Flag.widthContent
84+ , Flag.widthFill
85+ , Flag.alignRight
86+ , Flag.alignBottom
87+ , Flag.centerX
88+ , Flag.centerY
89+ ]
tests/suite/elm-package.jsonnew file+16
@@ -0,0 +1,16 @@
1+{
2+ "version": "1.0.0",
3+ "summary": "helpful summary of your project, less than 80 characters",
4+ "repository": "https://github.com/user/project.git",
5+ "license": "BSD3",
6+ "source-directories": [
7+ ".",
8+ "../../src"
9+ ],
10+ "exposed-modules": [],
11+ "dependencies": {
12+ "elm-lang/core": "5.1.1 <= v < 6.0.0",
13+ "elm-lang/html": "2.0.0 <= v < 3.0.0"
14+ },
15+ "elm-version": "0.18.0 <= v < 0.19.0"
16+}