├── .gitignore ├── LICENSE ├── README.md ├── elm.json └── src └── Element ├── WithContext.elm └── WithContext ├── Background.elm ├── Border.elm ├── Events.elm ├── Font.elm ├── Input.elm ├── Internal.elm ├── Keyed.elm ├── Lazy.elm └── Region.elm /.gitignore: -------------------------------------------------------------------------------- 1 | elm-stuff 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020, Matthew Griffith 2 | Copyright (c) 2021, Leonardo Taglialegne 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | * Neither the name of Elm UI nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Elm-ui-with-context 2 | This library wraps [`mdgriffith/elm-ui`](https://package.elm-lang.org/packages/mdgriffith/elm-ui/latest/) to provide a global context available while building the view. If you're not familiar with `elm-ui`, you should try it and only come back to this library when you have a problem to solve. 3 | 4 | A context is a global, *constant or mostly constant* object. It can be used to store those things that you will need *almost everywhere* in your `view` but don't change often, or at all. 5 | 6 | Examples of things you could want to put in the context: 7 | 1. theme (dark/light/custom) - this is needed almost everwhere for colors, and styles, and changes very rarely; 8 | 2. language - this is needed for every single label for localization, and changes rarely or never; 9 | 3. timezone - this is needed to display local times for the user, and mostly doesn't change; 10 | 4. responsive class (phone/tablet/desktop) - this doesn't usually change (unless the user dramatically resizes the window); 11 | 5. user permissions (to disable buttons or inputs) - this changes very rarely and is needed in a lot of places. 12 | 13 | Example of things that you do *not* want in the context: 14 | 1. time - this changes constantly; 15 | 2. window size - on desktop, this can change a lot while resizing; 16 | 3. user ID - this should be part of your regular model. 17 | 18 | A good test for inclusion is to think of this: does it make sense to completely redraw the user interface when the value changes? In particular, changing anything in the context will force the recalculation of all the `lazy` nodes. 19 | 20 | ## How to use it 21 | 1. Define a `Context` type (it will usually be a type alias); 22 | 2. replace any `import Element` and any `import Element.X as X` with: 23 | ```elm 24 | import Element.WithContext as Element 25 | import Element.WithContext.X as X 26 | ``` 27 | 3. don't expose `Element` or `Attribute` in the `import`, but instead define your type aliases: 28 | ```elm 29 | type Element msg = 30 | Element.Element Context msg 31 | 32 | type Attribute msg = 33 | Element.Attribute Context msg 34 | 35 | type Attr decorative msg = 36 | Element.Attr Context decorative msg 37 | ``` 38 | 4. pass the context to `Element.layout`; 39 | 5. everything should work as before, but now you can use `with` and `withAttribute` to access your context. 40 | 41 | 42 | ## Example: localization 43 | A nice way to do localization is to completely avoid exposing `text` from `Element.WithContext`, and instead defining your custom one like this: 44 | 45 | ```elm 46 | type Language 47 | = En 48 | | It 49 | | Fr 50 | 51 | 52 | type alias L10N a = 53 | { en : a 54 | , it : a 55 | , fr : a 56 | } 57 | 58 | 59 | text : L10N String -> Element { a | language : Language } msg 60 | text { en, it, fr } = 61 | Element.with 62 | (\{ language } -> 63 | case language of 64 | En -> 65 | en 66 | 67 | It -> 68 | it 69 | 70 | Fr -> 71 | fr 72 | ) 73 | Element.text 74 | ``` 75 | 76 | So that you can use it like this: `text { en = "Hello", it = "Ciao", fr = "Bonjour" }` (you should also probably move all the localized strings into a `Localization` package). 77 | 78 | This has the advantage of keeping a nice API while making it (almost) impossible to have untranslated labels. 79 | 80 | Notice how `text` simply requires a context that includes a `language` field, so is very generic. 81 | 82 | This tecnique can be adapted for image sources, title texts, and anything that needs localization. 83 | 84 | Strings with placeholders can be represented as `L10N (a -> b -> String)` and used by defining an `apply : L10N (a -> b) -> a -> L10N b`. Beware: different languages can have very different rules on plurals, genders, special cases, ... 85 | 86 | 87 | ## Example: Theme 88 | If you have a field `theme : Theme` in your context then you can replace any color constants in your code with `Theme -> Color` functions, and use them like this: 89 | 90 | 91 | ```elm 92 | type Theme 93 | = Light 94 | | Dark 95 | 96 | 97 | fontColor : Theme -> Color 98 | fontColor theme = 99 | case theme of 100 | Light -> 101 | rgb 0.2 0.2 0.2 102 | 103 | Dark -> 104 | rgb 0.8 0.8 0.8 105 | 106 | 107 | someViewFunction = 108 | el 109 | [ Element.withAttribute 110 | (\{theme} -> fontColor theme) 111 | Font.color 112 | ] 113 | (text "Hello") 114 | ``` 115 | 116 | (`\{theme} -> fontColor theme` can be replaced by `.theme >> fontColor`, depending on taste). 117 | 118 | This also has the advantage that you can "force" a particular theme in places that need it, like the theme picker, by just doing `fontColor Light`. 119 | 120 | ## API differences from the original `elm-ui` 121 | 122 | 1. `Element msg` becomes `Element context msg` and `Attribute msg` becomes `Attribute context msg`, 123 | 2. `layout` requires a `context` parameter, 124 | 3. you have access to the `with` and `withAttribute` functions. 125 | 126 | --- 127 | 128 | You should also have a look at [the original README](https://package.elm-lang.org/packages/mdgriffith/elm-ui/latest/). 129 | -------------------------------------------------------------------------------- /elm.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "package", 3 | "name": "miniBill/elm-ui-with-context", 4 | "summary": "An augmentation of elm-ui with a global context.", 5 | "license": "BSD-3-Clause", 6 | "version": "2.0.0", 7 | "exposed-modules": [ 8 | "Element.WithContext", 9 | "Element.WithContext.Input", 10 | "Element.WithContext.Events", 11 | "Element.WithContext.Background", 12 | "Element.WithContext.Border", 13 | "Element.WithContext.Font", 14 | "Element.WithContext.Lazy", 15 | "Element.WithContext.Keyed", 16 | "Element.WithContext.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 | "mdgriffith/elm-ui": "1.1.0 <= v < 2.0.0" 23 | }, 24 | "test-dependencies": {} 25 | } 26 | -------------------------------------------------------------------------------- /src/Element/WithContext.elm: -------------------------------------------------------------------------------- 1 | module Element.WithContext exposing 2 | ( with, withAttribute, withDecoration, layout, layoutWith, element, attribute, attr 3 | , Element, none, text, el 4 | , row, wrappedRow, column 5 | , paragraph, textColumn 6 | , Column, table, IndexedColumn, indexedTable 7 | , Attribute, width, height, Length, px, shrink, fill, fillPortion, maximum, minimum 8 | , explain 9 | , padding, paddingXY, paddingEach 10 | , spacing, spacingXY, spaceEvenly 11 | , centerX, centerY, alignLeft, alignRight, alignTop, alignBottom 12 | , transparent, alpha, pointer 13 | , moveUp, moveDown, moveRight, moveLeft, rotate, scale 14 | , clip, clipX, clipY 15 | , scrollbars, scrollbarX, scrollbarY 16 | , Option, noStaticStyleSheet, forceHover, noHover, focusStyle, FocusStyle 17 | , link, newTabLink, download, downloadAs 18 | , image 19 | , Color, rgba, rgb, rgb255, rgba255, fromRgb, fromRgb255, toRgb 20 | , above, below, onRight, onLeft, inFront, behindContent 21 | , Attr, Decoration, mouseOver, mouseDown, focused 22 | , Device, DeviceClass(..), Orientation(..), classifyDevice 23 | , modular 24 | , map, mapAttribute 25 | , html, htmlAttribute 26 | , withContext, withContextAttribute, withContextDecoration 27 | ) 28 | 29 | {-| 30 | 31 | 32 | # `elm-ui-with-context` specific functions 33 | 34 | @docs with, withAttribute, withDecoration, layout, layoutWith, element, attribute, attr 35 | 36 | 37 | # Basic Elements 38 | 39 | @docs Element, none, text, el 40 | 41 | 42 | # Rows and Columns 43 | 44 | When we want more than one child on an element, we want to be _specific_ about how they will be laid out. 45 | 46 | So, the common ways to do that would be `row` and `column`. 47 | 48 | @docs row, wrappedRow, column 49 | 50 | 51 | # Text Layout 52 | 53 | Text layout needs some specific considerations. 54 | 55 | @docs paragraph, textColumn 56 | 57 | 58 | # Data Table 59 | 60 | @docs Column, table, IndexedColumn, indexedTable 61 | 62 | 63 | # Size 64 | 65 | @docs Attribute, width, height, Length, px, shrink, fill, fillPortion, maximum, minimum 66 | 67 | 68 | # Debugging 69 | 70 | @docs explain 71 | 72 | 73 | # Padding and Spacing 74 | 75 | There's no concept of margin in `elm-ui`, instead we have padding and spacing. 76 | 77 | Padding is the distance between the outer edge and the content, and spacing is the space between children. 78 | 79 | So, if we have the following row, with some padding and spacing. 80 | 81 | Element.row [ padding 10, spacing 7 ] 82 | [ Element.el [] none 83 | , Element.el [] none 84 | , Element.el [] none 85 | ] 86 | 87 | Here's what we can expect: 88 | 89 | ![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) 90 | 91 | **Note** `spacing` set on a `paragraph`, will set the pixel spacing between lines. 92 | 93 | @docs padding, paddingXY, paddingEach 94 | 95 | @docs spacing, spacingXY, spaceEvenly 96 | 97 | 98 | # Alignment 99 | 100 | Alignment can be used to align an `Element` within another `Element`. 101 | 102 | Element.el [ centerX, alignTop ] (text "I'm centered and aligned top!") 103 | 104 | 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. 105 | 106 | Element.row [] 107 | [ Element.el [] Element.none 108 | , Element.el [ alignLeft ] Element.none 109 | , Element.el [ centerX ] Element.none 110 | , Element.el [ alignRight ] Element.none 111 | ] 112 | 113 | will result in a layout like 114 | 115 | |-|-| |-| |-| 116 | 117 | Where there are two elements on the left, one on the right, and one in the center of the space between the elements on the left and right. 118 | 119 | **Note** For text alignment, check out `Element.Font`! 120 | 121 | @docs centerX, centerY, alignLeft, alignRight, alignTop, alignBottom 122 | 123 | 124 | # Transparency 125 | 126 | @docs transparent, alpha, pointer 127 | 128 | 129 | # Adjustment 130 | 131 | @docs moveUp, moveDown, moveRight, moveLeft, rotate, scale 132 | 133 | 134 | # Clipping and Scrollbars 135 | 136 | Clip the content if it overflows. 137 | 138 | @docs clip, clipX, clipY 139 | 140 | Add a scrollbar if the content is larger than the element. 141 | 142 | @docs scrollbars, scrollbarX, scrollbarY 143 | 144 | 145 | # Rendering 146 | 147 | @docs Option, noStaticStyleSheet, forceHover, noHover, focusStyle, FocusStyle 148 | 149 | 150 | # Links 151 | 152 | @docs link, newTabLink, download, downloadAs 153 | 154 | 155 | # Images 156 | 157 | @docs image 158 | 159 | 160 | # Color 161 | 162 | In order to use attributes like `Font.color` and `Background.color`, you'll need to make some colors! 163 | 164 | @docs Color, rgba, rgb, rgb255, rgba255, fromRgb, fromRgb255, toRgb 165 | 166 | 167 | # Nearby Elements 168 | 169 | 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_. 170 | 171 | Element.row [] 172 | [ Element.el 173 | [ Element.below (Element.text "I'm below!") 174 | ] 175 | (Element.text "I'm normal!") 176 | ] 177 | 178 | This will result in 179 | 180 | |- I'm normal! -| 181 | I'm below 182 | 183 | Where `"I'm Below"` doesn't change the size of `Element.row`. 184 | 185 | This is very useful for things like dropdown menus or tooltips. 186 | 187 | @docs above, below, onRight, onLeft, inFront, behindContent 188 | 189 | 190 | # Temporary Styling 191 | 192 | @docs Attr, Decoration, mouseOver, mouseDown, focused 193 | 194 | 195 | # Responsiveness 196 | 197 | The main technique for responsiveness is to store window size information in your model. 198 | 199 | 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). 200 | 201 | You'll also need to retrieve the initial window size. You can either use [`Browser.Dom.getViewport`](https://package.elm-lang.org/packages/elm/browser/latest/Browser-Dom#getViewport) or pass in `window.innerWidth` and `window.innerHeight` as flags to your program, which is the preferred way. This requires minor setup on the JS side, but allows you to avoid the state where you don't have window info. 202 | 203 | @docs Device, DeviceClass, Orientation, classifyDevice 204 | 205 | 206 | # Scaling 207 | 208 | @docs modular 209 | 210 | 211 | ## Mapping 212 | 213 | @docs map, mapAttribute 214 | 215 | 216 | ## Compatibility 217 | 218 | @docs html, htmlAttribute 219 | 220 | 221 | ## Advanced 222 | 223 | Sometimes it's more convenient to just access the whole context while building your view. This functions allow you do just that. 224 | 225 | @docs withContext, withContextAttribute, withContextDecoration 226 | 227 | -} 228 | 229 | import Element 230 | import Element.WithContext.Internal as Internal exposing (Attr(..), Attribute, Element(..), attr, attribute, attributes, run, runAttr, wrapAttrs, wrapContainer) 231 | import Html exposing (Html) 232 | 233 | 234 | {-| -} 235 | type alias Color = 236 | Internal.Color 237 | 238 | 239 | {-| Provide the red, green, and blue channels for the color. 240 | 241 | Each channel takes a value between 0 and 1. 242 | 243 | -} 244 | rgb : Float -> Float -> Float -> Color 245 | rgb = 246 | Element.rgb 247 | 248 | 249 | {-| -} 250 | rgba : Float -> Float -> Float -> Float -> Color 251 | rgba = 252 | Element.rgba 253 | 254 | 255 | {-| Provide the red, green, and blue channels for the color. 256 | 257 | Each channel takes a value between 0 and 255. 258 | 259 | -} 260 | rgb255 : Int -> Int -> Int -> Color 261 | rgb255 = 262 | Element.rgb255 263 | 264 | 265 | {-| -} 266 | rgba255 : Int -> Int -> Int -> Float -> Color 267 | rgba255 = 268 | Element.rgba255 269 | 270 | 271 | {-| Create a color from an RGB record. 272 | -} 273 | fromRgb : 274 | { red : Float 275 | , green : Float 276 | , blue : Float 277 | , alpha : Float 278 | } 279 | -> Color 280 | fromRgb = 281 | Element.fromRgb 282 | 283 | 284 | {-| -} 285 | fromRgb255 : 286 | { red : Int 287 | , green : Int 288 | , blue : Int 289 | , alpha : Float 290 | } 291 | -> Color 292 | fromRgb255 = 293 | Element.fromRgb255 294 | 295 | 296 | {-| Deconstruct a `Color` into its rgb channels. 297 | -} 298 | toRgb : 299 | Color 300 | -> 301 | { red : Float 302 | , green : Float 303 | , blue : Float 304 | , alpha : Float 305 | } 306 | toRgb = 307 | Element.toRgb 308 | 309 | 310 | {-| The basic building block of your layout. 311 | 312 | howdy : Element context msg 313 | howdy = 314 | Element.el [] (Element.text "Howdy!") 315 | 316 | -} 317 | type alias Element context msg = 318 | Internal.Element context msg 319 | 320 | 321 | {-| An attribute that can be attached to an `Element` 322 | -} 323 | type alias Attribute context msg = 324 | Internal.Attribute context msg 325 | 326 | 327 | {-| This is a special attribute that counts as both a `Attribute context msg` and a `Decoration context`. 328 | -} 329 | type alias Attr context decorative msg = 330 | Internal.Attr context decorative msg 331 | 332 | 333 | {-| Only decorations 334 | -} 335 | type alias Decoration context = 336 | Internal.Decoration context 337 | 338 | 339 | {-| -} 340 | html : Html msg -> Element context msg 341 | html child = 342 | element <| Element.html child 343 | 344 | 345 | {-| -} 346 | htmlAttribute : Html.Attribute msg -> Attribute context msg 347 | htmlAttribute child = 348 | attribute <| Element.htmlAttribute child 349 | 350 | 351 | {-| Embed an element from the original elm-ui library. This is useful for interop with existing code, like `lemol/ant-design-icons-elm-ui`. 352 | -} 353 | element : Element.Element msg -> Element context msg 354 | element elem = 355 | Element <| \_ -> elem 356 | 357 | 358 | {-| Embed an attribute from the original elm-ui library. This is useful for interop with existing code. 359 | -} 360 | attribute : Element.Attribute msg -> Attribute context msg 361 | attribute elem = 362 | Attribute <| \_ -> elem 363 | 364 | 365 | {-| Embed an attribute from the original elm-ui library. This is useful for interop with existing code. 366 | -} 367 | attr : Element.Attr decorative msg -> Attr context decorative msg 368 | attr elem = 369 | Attribute <| \_ -> elem 370 | 371 | 372 | {-| -} 373 | map : (msg -> msg1) -> Element context msg -> Element context msg1 374 | map f (Element g) = 375 | Element (g >> Element.map f) 376 | 377 | 378 | {-| -} 379 | mapAttribute : (msg -> msg1) -> Attribute context msg -> Attribute context msg1 380 | mapAttribute f (Attribute g) = 381 | Attribute (g >> Element.mapAttribute f) 382 | 383 | 384 | {-| Use a property from the context to build an `Element`. Have a look at the README for examples. 385 | -} 386 | with : (context -> property) -> (property -> Element context msg) -> Element context msg 387 | with selector f = 388 | Element <| \context -> run context <| f <| selector context 389 | 390 | 391 | {-| Use a property from the context to build an `Attribute`. Have a look at the README for examples. 392 | -} 393 | withAttribute : (context -> property) -> (property -> Attribute context msg) -> Attribute context msg 394 | withAttribute selector f = 395 | Attribute <| \context -> runAttr context <| f <| selector context 396 | 397 | 398 | {-| Use a property from the context to build a `Decoration`. Have a look at the README for examples. 399 | -} 400 | withDecoration : (context -> property) -> (property -> Decoration context) -> Decoration context 401 | withDecoration selector f = 402 | Attribute <| \context -> runAttr context <| f <| selector context 403 | 404 | 405 | {-| Use the context to build an `Element`. Have a look at the README for examples. 406 | -} 407 | withContext : (context -> Element context msg) -> Element context msg 408 | withContext f = 409 | Element <| \context -> run context <| f context 410 | 411 | 412 | {-| Use the context to build an `Attribute`. Have a look at the README for examples. 413 | -} 414 | withContextAttribute : (context -> Attribute context msg) -> Attribute context msg 415 | withContextAttribute f = 416 | Attribute <| \context -> runAttr context <| f context 417 | 418 | 419 | {-| Use the context to build a `Decoration`. Have a look at the README for examples. 420 | -} 421 | withContextDecoration : (context -> Decoration context) -> Decoration context 422 | withContextDecoration f = 423 | Attribute <| \context -> runAttr context <| f context 424 | 425 | 426 | {-| -} 427 | type alias Length = 428 | Element.Length 429 | 430 | 431 | {-| -} 432 | px : Int -> Length 433 | px = 434 | Element.px 435 | 436 | 437 | {-| Shrink an element to fit its contents. 438 | -} 439 | shrink : Length 440 | shrink = 441 | Element.shrink 442 | 443 | 444 | {-| Fill the available space. The available space will be split evenly between elements that have `width fill`. 445 | -} 446 | fill : Length 447 | fill = 448 | Element.fill 449 | 450 | 451 | {-| Similarly you can set a minimum boundary. 452 | 453 | el 454 | [ height 455 | (fill 456 | |> maximum 300 457 | |> minimum 30 458 | ) 459 | 460 | ] 461 | (text "I will stop at 300px") 462 | 463 | -} 464 | minimum : Int -> Length -> Length 465 | minimum = 466 | Element.minimum 467 | 468 | 469 | {-| Add a maximum to a length. 470 | 471 | el 472 | [ height 473 | (fill 474 | |> maximum 300 475 | ) 476 | ] 477 | (text "I will stop at 300px") 478 | 479 | -} 480 | maximum : Int -> Length -> Length 481 | maximum = 482 | Element.maximum 483 | 484 | 485 | {-| 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. 486 | 487 | 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. 488 | 489 | **Also:** `fill == fillPortion 1` 490 | 491 | -} 492 | fillPortion : Int -> Length 493 | fillPortion = 494 | Element.fillPortion 495 | 496 | 497 | {-| This is your top level node where you can turn `Element` into `Html`. 498 | -} 499 | layout : context -> List (Attribute context msg) -> Element context msg -> Html msg 500 | layout context attrs (Element f) = 501 | Element.layout (attributes context attrs) (f context) 502 | 503 | 504 | {-| -} 505 | layoutWith : context -> { options : List Option } -> List (Attribute context msg) -> Element context msg -> Html msg 506 | layoutWith context options attrs (Element f) = 507 | Element.layoutWith options (attributes context attrs) (f context) 508 | 509 | 510 | {-| -} 511 | type alias Option = 512 | Element.Option 513 | 514 | 515 | {-| Elm UI embeds two StyleSheets, one that is constant, and one that changes dynamically based on styles collected from the elements being rendered. 516 | 517 | This option will stop the static/constant stylesheet from rendering. 518 | 519 | If you're embedding multiple elm-ui `layout` elements, you need to guarantee that only one is rendering the static style sheet and that it's above all the others in the DOM tree. 520 | 521 | -} 522 | noStaticStyleSheet : Option 523 | noStaticStyleSheet = 524 | Element.noStaticStyleSheet 525 | 526 | 527 | {-| -} 528 | type alias FocusStyle = 529 | { borderColor : Maybe Color 530 | , backgroundColor : Maybe Color 531 | , shadow : 532 | Maybe 533 | { color : Color 534 | , offset : ( Int, Int ) 535 | , blur : Int 536 | , size : Int 537 | } 538 | } 539 | 540 | 541 | {-| -} 542 | focusStyle : FocusStyle -> Option 543 | focusStyle = 544 | Element.focusStyle 545 | 546 | 547 | {-| Disable all `mouseOver` styles. 548 | -} 549 | noHover : Option 550 | noHover = 551 | Element.noHover 552 | 553 | 554 | {-| Any `hover` styles, aka attributes with `mouseOver` in the name, will be always turned on. 555 | 556 | This is useful for when you're targeting a platform that has no mouse, such as mobile. 557 | 558 | -} 559 | forceHover : Option 560 | forceHover = 561 | Element.forceHover 562 | 563 | 564 | {-| When you want to render exactly nothing. 565 | -} 566 | none : Element context msg 567 | none = 568 | element Element.none 569 | 570 | 571 | {-| Create some plain text. 572 | 573 | text "Hello, you stylish developer!" 574 | 575 | **Note** text does not wrap by default. In order to get text to wrap, check out `paragraph`! 576 | 577 | -} 578 | text : String -> Element context msg 579 | text content = 580 | element <| Element.text content 581 | 582 | 583 | {-| The basic building block of your layout. 584 | 585 | You can think of an `el` as a `div`, but it can only have one child. 586 | 587 | If you want multiple children, you'll need to use something like `row` or `column` 588 | 589 | import Element.WithContext as Element exposing (Element, rgb) 590 | import Element.WithContext.Background as Background 591 | import Element.WithContext.Border as Border 592 | 593 | myElement : Element context msg 594 | myElement = 595 | Element.el 596 | [ Background.color (rgb 0 0.5 0) 597 | , Border.color (rgb 0 0.7 0) 598 | ] 599 | (Element.text "You've made a stylish element!") 600 | 601 | -} 602 | el : List (Attribute context msg) -> Element context msg -> Element context msg 603 | el = 604 | wrapAttrs Element.el run 605 | 606 | 607 | {-| -} 608 | row : List (Attribute context msg) -> List (Element context msg) -> Element context msg 609 | row = 610 | wrapContainer Element.row 611 | 612 | 613 | {-| -} 614 | column : List (Attribute context msg) -> List (Element context msg) -> Element context msg 615 | column = 616 | wrapContainer Element.column 617 | 618 | 619 | {-| Same as `row`, but will wrap if it takes up too much horizontal space. 620 | -} 621 | wrappedRow : List (Attribute context msg) -> List (Element context msg) -> Element context msg 622 | wrappedRow = 623 | wrapContainer Element.wrappedRow 624 | 625 | 626 | {-| This is just an alias for `Debug.todo` 627 | -} 628 | type alias Todo = 629 | String -> Never 630 | 631 | 632 | {-| 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! 633 | 634 | **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. 635 | 636 | el 637 | [ Element.explain Debug.todo 638 | ] 639 | (text "Help, I'm being debugged!") 640 | 641 | -} 642 | explain : Todo -> Attribute context msg 643 | explain t = 644 | attribute <| Element.explain t 645 | 646 | 647 | {-| -} 648 | type alias Column context record msg = 649 | { header : Element context msg 650 | , width : Length 651 | , view : record -> Element context msg 652 | } 653 | 654 | 655 | {-| Show some tabular data. 656 | 657 | Start with a list of records and specify how each column should be rendered. 658 | 659 | So, if we have a list of `persons`: 660 | 661 | type alias Person = 662 | { firstName : String 663 | , lastName : String 664 | } 665 | 666 | persons : List Person 667 | persons = 668 | [ { firstName = "David" 669 | , lastName = "Bowie" 670 | } 671 | , { firstName = "Florence" 672 | , lastName = "Welch" 673 | } 674 | ] 675 | 676 | We could render it using 677 | 678 | Element.table [] 679 | { data = persons 680 | , columns = 681 | [ { header = Element.text "First Name" 682 | , width = fill 683 | , view = 684 | \person -> 685 | Element.text person.firstName 686 | } 687 | , { header = Element.text "Last Name" 688 | , width = fill 689 | , view = 690 | \person -> 691 | Element.text person.lastName 692 | } 693 | ] 694 | } 695 | 696 | **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`. 697 | 698 | -} 699 | table : 700 | List (Attribute context msg) 701 | -> 702 | { data : List records 703 | , columns : List (Column context records msg) 704 | } 705 | -> Element context msg 706 | table = 707 | wrapAttrs Element.table <| 708 | \context config -> 709 | { data = config.data 710 | , columns = 711 | List.map 712 | (\col -> 713 | { header = run context col.header 714 | , width = col.width 715 | , view = run context << col.view 716 | } 717 | ) 718 | config.columns 719 | } 720 | 721 | 722 | {-| -} 723 | type alias IndexedColumn context record msg = 724 | { header : Element context msg 725 | , width : Length 726 | , view : Int -> record -> Element context msg 727 | } 728 | 729 | 730 | {-| Same as `Element.table` except the `view` for each column will also receive the row index as well as the record. 731 | -} 732 | indexedTable : 733 | List (Attribute context msg) 734 | -> 735 | { data : List records 736 | , columns : List (IndexedColumn context records msg) 737 | } 738 | -> Element context msg 739 | indexedTable = 740 | wrapAttrs Element.indexedTable <| 741 | \context config -> 742 | { data = config.data 743 | , columns = 744 | List.map 745 | (\col -> 746 | { header = run context col.header 747 | , width = col.width 748 | , view = \i -> run context << col.view i 749 | } 750 | ) 751 | config.columns 752 | } 753 | 754 | 755 | {-| A paragraph will layout all children as wrapped, inline elements. 756 | 757 | import Element exposing (el, paragraph, text) 758 | import Element.Font as Font 759 | 760 | view = 761 | paragraph [] 762 | [ text "lots of text ...." 763 | , el [ Font.bold ] (text "this is bold") 764 | , text "lots of text ...." 765 | ] 766 | 767 | 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. 768 | 769 | 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). 770 | 771 | This makes it particularly easy to do something like a [dropped capital](https://en.wikipedia.org/wiki/Initial). 772 | 773 | import Element exposing (alignLeft, el, padding, paragraph, text) 774 | import Element.Font as Font 775 | 776 | view = 777 | paragraph [] 778 | [ el 779 | [ alignLeft 780 | , padding 5 781 | ] 782 | (text "S") 783 | , text "o much text ...." 784 | ] 785 | 786 | Which will look something like 787 | 788 | ![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) 789 | 790 | **Note** `spacing` on a paragraph will set the pixel spacing between lines. 791 | 792 | -} 793 | paragraph : List (Attribute context msg) -> List (Element context msg) -> Element context msg 794 | paragraph = 795 | wrapContainer Element.paragraph 796 | 797 | 798 | {-| Now that we have a paragraph, we need some way to attach a bunch of paragraph's together. 799 | 800 | To do that we can use a `textColumn`. 801 | 802 | 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. 803 | 804 | In the following example, we have a `textColumn` where one child has `alignLeft`. 805 | 806 | Element.textColumn [ spacing 10, padding 10 ] 807 | [ paragraph [] [ text "lots of text ...." ] 808 | , el [ alignLeft ] none 809 | , paragraph [] [ text "lots of text ...." ] 810 | ] 811 | 812 | Which will result in something like: 813 | 814 | ![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) 815 | 816 | -} 817 | textColumn : List (Attribute context msg) -> List (Element context msg) -> Element context msg 818 | textColumn = 819 | wrapContainer Element.textColumn 820 | 821 | 822 | {-| Both a source and a description are required for images. 823 | 824 | The description is used for people using screen readers. 825 | 826 | 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. 827 | 828 | So, take a moment to describe your image as you would to someone who has a harder time seeing. 829 | 830 | -} 831 | image : List (Attribute context msg) -> { src : String, description : String } -> Element context msg 832 | image = 833 | wrapAttrs Element.image (always identity) 834 | 835 | 836 | {-| 837 | 838 | link [] 839 | { url = "http://fruits.com" 840 | , label = text "A link to my favorite fruit provider." 841 | } 842 | 843 | -} 844 | link : 845 | List (Attribute context msg) 846 | -> 847 | { url : String 848 | , label : Element context msg 849 | } 850 | -> Element context msg 851 | link = 852 | wrapAttrs Element.link 853 | (\context { url, label } -> 854 | { url = url 855 | , label = run context label 856 | } 857 | ) 858 | 859 | 860 | {-| -} 861 | newTabLink : 862 | List (Attribute context msg) 863 | -> 864 | { url : String 865 | , label : Element context msg 866 | } 867 | -> Element context msg 868 | newTabLink = 869 | wrapAttrs Element.newTabLink 870 | (\context { url, label } -> 871 | { url = url 872 | , label = run context label 873 | } 874 | ) 875 | 876 | 877 | {-| A link to download a file. 878 | -} 879 | download : 880 | List (Attribute context msg) 881 | -> 882 | { url : String 883 | , label : Element context msg 884 | } 885 | -> Element context msg 886 | download = 887 | wrapAttrs Element.download 888 | (\context { url, label } -> 889 | { url = url 890 | , label = run context label 891 | } 892 | ) 893 | 894 | 895 | {-| A link to download a file, but you can specify the filename. 896 | -} 897 | downloadAs : 898 | List (Attribute context msg) 899 | -> 900 | { label : Element context msg 901 | , filename : String 902 | , url : String 903 | } 904 | -> Element context msg 905 | downloadAs = 906 | wrapAttrs Element.downloadAs 907 | (\context { url, label, filename } -> 908 | { url = url 909 | , filename = filename 910 | , label = run context label 911 | } 912 | ) 913 | 914 | 915 | 916 | {- NEARBYS -} 917 | 918 | 919 | createNearby : (Element.Element msg -> Element.Attribute msg) -> Element context msg -> Attribute context msg 920 | createNearby toAttr (Element f) = 921 | Attribute (f >> toAttr) 922 | 923 | 924 | {-| -} 925 | below : Element context msg -> Attribute context msg 926 | below = 927 | createNearby Element.below 928 | 929 | 930 | {-| -} 931 | above : Element context msg -> Attribute context msg 932 | above = 933 | createNearby Element.above 934 | 935 | 936 | {-| -} 937 | onRight : Element context msg -> Attribute context msg 938 | onRight = 939 | createNearby Element.onRight 940 | 941 | 942 | {-| -} 943 | onLeft : Element context msg -> Attribute context msg 944 | onLeft = 945 | createNearby Element.onLeft 946 | 947 | 948 | {-| This will place an element in front of another. 949 | 950 | **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. 951 | 952 | -} 953 | inFront : Element context msg -> Attribute context msg 954 | inFront = 955 | createNearby Element.inFront 956 | 957 | 958 | {-| This will place an element between the background and the content of an element. 959 | -} 960 | behindContent : Element context msg -> Attribute context msg 961 | behindContent = 962 | createNearby Element.behindContent 963 | 964 | 965 | {-| -} 966 | width : Length -> Attribute context msg 967 | width l = 968 | attribute <| Element.width l 969 | 970 | 971 | {-| -} 972 | height : Length -> Attribute context msg 973 | height l = 974 | attribute <| Element.height l 975 | 976 | 977 | {-| -} 978 | scale : Float -> Attr context decorative msg 979 | scale n = 980 | attr <| Element.scale n 981 | 982 | 983 | {-| Angle is given in radians. [Here are some conversion functions if you want to use another unit.](https://package.elm-lang.org/packages/elm/core/latest/Basics#degrees) 984 | -} 985 | rotate : Float -> Attr context decorative msg 986 | rotate angle = 987 | attr <| Element.rotate angle 988 | 989 | 990 | {-| -} 991 | moveUp : Float -> Attr context decorative msg 992 | moveUp y = 993 | attr <| Element.moveUp y 994 | 995 | 996 | {-| -} 997 | moveDown : Float -> Attr context decorative msg 998 | moveDown y = 999 | attr <| Element.moveDown y 1000 | 1001 | 1002 | {-| -} 1003 | moveRight : Float -> Attr context decorative msg 1004 | moveRight x = 1005 | attr <| Element.moveRight x 1006 | 1007 | 1008 | {-| -} 1009 | moveLeft : Float -> Attr context decorative msg 1010 | moveLeft x = 1011 | attr <| Element.moveLeft x 1012 | 1013 | 1014 | {-| -} 1015 | padding : Int -> Attribute context msg 1016 | padding x = 1017 | attribute <| Element.padding x 1018 | 1019 | 1020 | {-| Set horizontal and vertical padding. 1021 | -} 1022 | paddingXY : Int -> Int -> Attribute context msg 1023 | paddingXY x y = 1024 | attribute <| Element.paddingXY x y 1025 | 1026 | 1027 | {-| If you find yourself defining unique paddings all the time, you might consider defining 1028 | 1029 | edges = 1030 | { top = 0 1031 | , right = 0 1032 | , bottom = 0 1033 | , left = 0 1034 | } 1035 | 1036 | And then just do 1037 | 1038 | paddingEach { edges | right = 5 } 1039 | 1040 | -} 1041 | paddingEach : { top : Int, right : Int, bottom : Int, left : Int } -> Attribute context msg 1042 | paddingEach paddings = 1043 | attribute <| Element.paddingEach paddings 1044 | 1045 | 1046 | {-| -} 1047 | centerX : Attribute context msg 1048 | centerX = 1049 | attribute Element.centerX 1050 | 1051 | 1052 | {-| -} 1053 | centerY : Attribute context msg 1054 | centerY = 1055 | attribute Element.centerY 1056 | 1057 | 1058 | {-| -} 1059 | alignTop : Attribute context msg 1060 | alignTop = 1061 | attribute Element.alignTop 1062 | 1063 | 1064 | {-| -} 1065 | alignBottom : Attribute context msg 1066 | alignBottom = 1067 | attribute Element.alignBottom 1068 | 1069 | 1070 | {-| -} 1071 | alignLeft : Attribute context msg 1072 | alignLeft = 1073 | attribute Element.alignLeft 1074 | 1075 | 1076 | {-| -} 1077 | alignRight : Attribute context msg 1078 | alignRight = 1079 | attribute Element.alignRight 1080 | 1081 | 1082 | {-| -} 1083 | spaceEvenly : Attribute context msg 1084 | spaceEvenly = 1085 | attribute Element.spaceEvenly 1086 | 1087 | 1088 | {-| -} 1089 | spacing : Int -> Attribute context msg 1090 | spacing x = 1091 | attribute <| Element.spacing x 1092 | 1093 | 1094 | {-| In the majority of cases you'll just need to use `spacing`, which will work as intended. 1095 | 1096 | However for some layouts, like `textColumn`, you may want to set a different spacing for the x axis compared to the y axis. 1097 | 1098 | -} 1099 | spacingXY : Int -> Int -> Attribute context msg 1100 | spacingXY x y = 1101 | attribute <| Element.spacingXY x y 1102 | 1103 | 1104 | {-| Make an element transparent and have it ignore any mouse or touch events, though it will stil take up space. 1105 | -} 1106 | transparent : Bool -> Attr context decorative msg 1107 | transparent on = 1108 | attr <| Element.transparent on 1109 | 1110 | 1111 | {-| A capped value between 0.0 and 1.0, where 0.0 is transparent and 1.0 is fully opaque. 1112 | 1113 | Semantically equivalent to html opacity. 1114 | 1115 | -} 1116 | alpha : Float -> Attr context decorative msg 1117 | alpha o = 1118 | attr <| Element.alpha o 1119 | 1120 | 1121 | {-| -} 1122 | scrollbars : Attribute context msg 1123 | scrollbars = 1124 | attribute Element.scrollbars 1125 | 1126 | 1127 | {-| -} 1128 | scrollbarY : Attribute context msg 1129 | scrollbarY = 1130 | attribute Element.scrollbarY 1131 | 1132 | 1133 | {-| -} 1134 | scrollbarX : Attribute context msg 1135 | scrollbarX = 1136 | attribute Element.scrollbarX 1137 | 1138 | 1139 | {-| -} 1140 | clip : Attribute context msg 1141 | clip = 1142 | attribute Element.clip 1143 | 1144 | 1145 | {-| -} 1146 | clipY : Attribute context msg 1147 | clipY = 1148 | attribute Element.clipY 1149 | 1150 | 1151 | {-| -} 1152 | clipX : Attribute context msg 1153 | clipX = 1154 | attribute Element.clipX 1155 | 1156 | 1157 | {-| Set the cursor to be a pointing hand when it's hovering over this element. 1158 | -} 1159 | pointer : Attribute context msg 1160 | pointer = 1161 | attribute Element.pointer 1162 | 1163 | 1164 | {-| -} 1165 | type alias Device = 1166 | { class : DeviceClass 1167 | , orientation : Orientation 1168 | } 1169 | 1170 | 1171 | {-| -} 1172 | type DeviceClass 1173 | = Phone 1174 | | Tablet 1175 | | Desktop 1176 | | BigDesktop 1177 | 1178 | 1179 | {-| -} 1180 | type Orientation 1181 | = Portrait 1182 | | Landscape 1183 | 1184 | 1185 | {-| Takes in a Window.Size and returns a device profile which can be used for responsiveness. 1186 | 1187 | If you have more detailed concerns around responsiveness, it probably makes sense to copy this function into your codebase and modify as needed. 1188 | 1189 | -} 1190 | classifyDevice : { window | height : Int, width : Int } -> Device 1191 | classifyDevice size = 1192 | let 1193 | { class, orientation } = 1194 | Element.classifyDevice size 1195 | in 1196 | { class = 1197 | case class of 1198 | Element.Phone -> 1199 | Phone 1200 | 1201 | Element.Tablet -> 1202 | Tablet 1203 | 1204 | Element.Desktop -> 1205 | Desktop 1206 | 1207 | Element.BigDesktop -> 1208 | BigDesktop 1209 | , orientation = 1210 | case orientation of 1211 | Element.Portrait -> 1212 | Portrait 1213 | 1214 | Element.Landscape -> 1215 | Landscape 1216 | } 1217 | 1218 | 1219 | {-| When designing it's nice to use a modular scale to set spacial rythms. 1220 | 1221 | scaled = 1222 | Element.modular 16 1.25 1223 | 1224 | A modular scale starts with a number, and multiplies it by a ratio a number of times. 1225 | Then, when setting font sizes you can use: 1226 | 1227 | Font.size (scaled 1) -- results in 16 1228 | 1229 | Font.size (scaled 2) -- 16 * 1.25 results in 20 1230 | 1231 | Font.size (scaled 4) -- 16 * 1.25 ^ (4 - 1) results in 31.25 1232 | 1233 | We can also provide negative numbers to scale below 16px. 1234 | 1235 | Font.size (scaled -1) -- 16 * 1.25 ^ (-1) results in 12.8 1236 | 1237 | -} 1238 | modular : Float -> Float -> Int -> Float 1239 | modular = 1240 | Element.modular 1241 | 1242 | 1243 | {-| -} 1244 | mouseOver : List (Decoration context) -> Attribute context msg 1245 | mouseOver decs = 1246 | Attribute <| \context -> Element.mouseOver <| List.map (\(Attribute f) -> f context) decs 1247 | 1248 | 1249 | {-| -} 1250 | mouseDown : List (Decoration context) -> Attribute context msg 1251 | mouseDown decs = 1252 | Attribute <| \context -> Element.mouseDown <| List.map (\(Attribute f) -> f context) decs 1253 | 1254 | 1255 | {-| -} 1256 | focused : List (Decoration context) -> Attribute context msg 1257 | focused decs = 1258 | Attribute <| \context -> Element.focused <| List.map (\(Attribute f) -> f context) decs 1259 | -------------------------------------------------------------------------------- /src/Element/WithContext/Background.elm: -------------------------------------------------------------------------------- 1 | module Element.WithContext.Background exposing 2 | ( color, gradient 3 | , image, uncropped, tiled, tiledX, tiledY 4 | ) 5 | 6 | {-| 7 | 8 | @docs color, gradient 9 | 10 | 11 | # Images 12 | 13 | @docs image, uncropped, tiled, tiledX, tiledY 14 | 15 | **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.behindContent`. 16 | 17 | -} 18 | 19 | import Element.Background as Background 20 | import Element.WithContext exposing (Attr, Attribute, Color) 21 | import Element.WithContext.Internal exposing (attr, attribute) 22 | 23 | 24 | {-| -} 25 | color : Color -> Attr context decorative msg 26 | color clr = 27 | attr <| Background.color clr 28 | 29 | 30 | {-| Resize the image to fit the containing element while maintaining proportions and cropping the overflow. 31 | -} 32 | image : String -> Attribute context msg 33 | image src = 34 | attribute <| Background.image src 35 | 36 | 37 | {-| A centered background image that keeps its natural proportions, but scales to fit the space. 38 | -} 39 | uncropped : String -> Attribute context msg 40 | uncropped src = 41 | attribute <| Background.uncropped src 42 | 43 | 44 | {-| Tile an image in the x and y axes. 45 | -} 46 | tiled : String -> Attribute context msg 47 | tiled src = 48 | attribute <| Background.tiled src 49 | 50 | 51 | {-| Tile an image in the x axis. 52 | -} 53 | tiledX : String -> Attribute context msg 54 | tiledX src = 55 | attribute <| Background.tiledX src 56 | 57 | 58 | {-| Tile an image in the y axis. 59 | -} 60 | tiledY : String -> Attribute context msg 61 | tiledY src = 62 | attribute <| Background.tiledY src 63 | 64 | 65 | {-| A linear gradient. 66 | 67 | First you need to specify what direction the gradient is going by providing an angle in radians. `0` is up and `pi` is down. 68 | 69 | The colors will be evenly spaced. 70 | 71 | -} 72 | gradient : 73 | { angle : Float 74 | , steps : List Color 75 | } 76 | -> Attr context decorative msg 77 | gradient config = 78 | attr <| Background.gradient config 79 | -------------------------------------------------------------------------------- /src/Element/WithContext/Border.elm: -------------------------------------------------------------------------------- 1 | module Element.WithContext.Border exposing 2 | ( color 3 | , width, widthXY, widthEach 4 | , solid, dashed, dotted 5 | , rounded, roundEach 6 | , glow, innerGlow, shadow, innerShadow 7 | ) 8 | 9 | {-| 10 | 11 | @docs color 12 | 13 | 14 | ## Border Widths 15 | 16 | @docs width, widthXY, widthEach 17 | 18 | 19 | ## Border Styles 20 | 21 | @docs solid, dashed, dotted 22 | 23 | 24 | ## Rounded Corners 25 | 26 | @docs rounded, roundEach 27 | 28 | 29 | ## Shadows 30 | 31 | @docs glow, innerGlow, shadow, innerShadow 32 | 33 | -} 34 | 35 | import Element.Border as Border 36 | import Element.WithContext exposing (Attr, Attribute, Color) 37 | import Element.WithContext.Internal exposing (attr, attribute) 38 | 39 | 40 | {-| -} 41 | color : Color -> Attr context decorative msg 42 | color clr = 43 | attr <| Border.color clr 44 | 45 | 46 | {-| -} 47 | width : Int -> Attribute context msg 48 | width v = 49 | attribute <| Border.width v 50 | 51 | 52 | {-| Set horizontal and vertical borders. 53 | -} 54 | widthXY : Int -> Int -> Attribute context msg 55 | widthXY x y = 56 | attribute <| Border.widthXY x y 57 | 58 | 59 | {-| -} 60 | widthEach : 61 | { bottom : Int 62 | , left : Int 63 | , right : Int 64 | , top : Int 65 | } 66 | -> Attribute context msg 67 | widthEach edges = 68 | attribute <| Border.widthEach edges 69 | 70 | 71 | 72 | -- {-| No Borders 73 | -- -} 74 | -- none : Attribute context msg 75 | -- none = 76 | -- Class "border" "border-none" 77 | 78 | 79 | {-| -} 80 | solid : Attribute context msg 81 | solid = 82 | attribute Border.solid 83 | 84 | 85 | {-| -} 86 | dashed : Attribute context msg 87 | dashed = 88 | attribute Border.dashed 89 | 90 | 91 | {-| -} 92 | dotted : Attribute context msg 93 | dotted = 94 | attribute Border.dotted 95 | 96 | 97 | {-| Round all corners. 98 | -} 99 | rounded : Int -> Attribute context msg 100 | rounded radius = 101 | attribute <| Border.rounded radius 102 | 103 | 104 | {-| -} 105 | roundEach : 106 | { topLeft : Int 107 | , topRight : Int 108 | , bottomLeft : Int 109 | , bottomRight : Int 110 | } 111 | -> Attribute context msg 112 | roundEach edges = 113 | attribute <| Border.roundEach edges 114 | 115 | 116 | {-| A simple glow by specifying the color and size. 117 | -} 118 | glow : Color -> Float -> Attr context decorative msg 119 | glow clr size = 120 | attr <| Border.glow clr size 121 | 122 | 123 | {-| -} 124 | innerGlow : Color -> Float -> Attr context decorative msg 125 | innerGlow clr size = 126 | attr <| Border.innerGlow clr size 127 | 128 | 129 | {-| -} 130 | shadow : 131 | { offset : ( Float, Float ) 132 | , size : Float 133 | , blur : Float 134 | , color : Color 135 | } 136 | -> Attr context decorative msg 137 | shadow almostShade = 138 | attr <| Border.shadow almostShade 139 | 140 | 141 | {-| -} 142 | innerShadow : 143 | { offset : ( Float, Float ) 144 | , size : Float 145 | , blur : Float 146 | , color : Color 147 | } 148 | -> Attr context decorative msg 149 | innerShadow almostShade = 150 | attr <| Border.innerShadow almostShade 151 | -------------------------------------------------------------------------------- /src/Element/WithContext/Events.elm: -------------------------------------------------------------------------------- 1 | module Element.WithContext.Events exposing 2 | ( onClick, onDoubleClick, onMouseDown, onMouseUp, onMouseEnter, onMouseLeave, onMouseMove 3 | , onFocus, onLoseFocus 4 | ) 5 | 6 | {-| 7 | 8 | 9 | ## Mouse Events 10 | 11 | @docs onClick, onDoubleClick, onMouseDown, onMouseUp, onMouseEnter, onMouseLeave, onMouseMove 12 | 13 | 14 | ## Focus Events 15 | 16 | @docs onFocus, onLoseFocus 17 | 18 | -} 19 | 20 | import Element.Events as Events 21 | import Element.WithContext exposing (Attribute) 22 | import Element.WithContext.Internal exposing (attribute) 23 | 24 | 25 | 26 | -- MOUSE EVENTS 27 | 28 | 29 | {-| -} 30 | onMouseDown : msg -> Attribute context msg 31 | onMouseDown msg = 32 | attribute <| Events.onMouseDown msg 33 | 34 | 35 | {-| -} 36 | onMouseUp : msg -> Attribute context msg 37 | onMouseUp msg = 38 | attribute <| Events.onMouseUp msg 39 | 40 | 41 | {-| -} 42 | onClick : msg -> Attribute context msg 43 | onClick msg = 44 | attribute <| Events.onClick msg 45 | 46 | 47 | {-| -} 48 | onDoubleClick : msg -> Attribute context msg 49 | onDoubleClick msg = 50 | attribute <| Events.onDoubleClick msg 51 | 52 | 53 | {-| -} 54 | onMouseEnter : msg -> Attribute context msg 55 | onMouseEnter msg = 56 | attribute <| Events.onMouseEnter msg 57 | 58 | 59 | {-| -} 60 | onMouseLeave : msg -> Attribute context msg 61 | onMouseLeave msg = 62 | attribute <| Events.onMouseLeave msg 63 | 64 | 65 | {-| -} 66 | onMouseMove : msg -> Attribute context msg 67 | onMouseMove msg = 68 | attribute <| Events.onMouseMove msg 69 | 70 | 71 | 72 | -- FOCUS EVENTS 73 | 74 | 75 | {-| -} 76 | onLoseFocus : msg -> Attribute context msg 77 | onLoseFocus msg = 78 | attribute <| Events.onLoseFocus msg 79 | 80 | 81 | {-| -} 82 | onFocus : msg -> Attribute context msg 83 | onFocus msg = 84 | attribute <| Events.onFocus msg 85 | -------------------------------------------------------------------------------- /src/Element/WithContext/Font.elm: -------------------------------------------------------------------------------- 1 | module Element.WithContext.Font exposing 2 | ( color, size 3 | , family, Font, typeface, serif, sansSerif, monospace 4 | , external 5 | , alignLeft, alignRight, center, justify, letterSpacing, wordSpacing 6 | , underline, strike, italic, unitalicized 7 | , heavy, extraBold, bold, semiBold, medium, regular, light, extraLight, hairline 8 | , Variant, variant, variantList, smallCaps, slashedZero, ligatures, ordinal, tabularNumbers, stackedFractions, diagonalFractions, swash, feature, indexed 9 | , glow, shadow 10 | ) 11 | 12 | {-| 13 | 14 | import Element.WithContext as Element 15 | import Element.WithContext.Font as Font 16 | 17 | view = 18 | Element.el 19 | [ Font.color (Element.rgb 0 0 1) 20 | , Font.size 18 21 | , Font.family 22 | [ Font.typeface "Open Sans" 23 | , Font.sansSerif 24 | ] 25 | ] 26 | (Element.text "Woohoo, I'm stylish text") 27 | 28 | **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. 29 | 30 | **Other Note:** If you're looking for something like `line-height`, it's handled by `Element.spacing` on a `paragraph`. 31 | 32 | @docs color, size 33 | 34 | 35 | ## Typefaces 36 | 37 | @docs family, Font, typeface, serif, sansSerif, monospace 38 | 39 | @docs external 40 | 41 | 42 | ## Alignment and Spacing 43 | 44 | @docs alignLeft, alignRight, center, justify, letterSpacing, wordSpacing 45 | 46 | 47 | ## Font Styles 48 | 49 | @docs underline, strike, italic, unitalicized 50 | 51 | 52 | ## Font Weight 53 | 54 | @docs heavy, extraBold, bold, semiBold, medium, regular, light, extraLight, hairline 55 | 56 | 57 | ## Variants 58 | 59 | @docs Variant, variant, variantList, smallCaps, slashedZero, ligatures, ordinal, tabularNumbers, stackedFractions, diagonalFractions, swash, feature, indexed 60 | 61 | 62 | ## Shadows 63 | 64 | @docs glow, shadow 65 | 66 | -} 67 | 68 | import Element.Font as Font 69 | import Element.WithContext exposing (Attr, Attribute, Color) 70 | import Element.WithContext.Internal exposing (attr, attribute) 71 | 72 | 73 | {-| -} 74 | type alias Font = 75 | Font.Font 76 | 77 | 78 | {-| -} 79 | color : Color -> Attr context decorative msg 80 | color fontColor = 81 | attr <| Font.color fontColor 82 | 83 | 84 | {-| 85 | 86 | import Element.WithContext as Element 87 | import Element.WithContext.Font as Font 88 | 89 | myElement = 90 | Element.el 91 | [ Font.family 92 | [ Font.typeface "Helvetica" 93 | , Font.sansSerif 94 | ] 95 | ] 96 | (text "") 97 | 98 | -} 99 | family : List Font -> Attribute context msg 100 | family families = 101 | attribute <| Font.family families 102 | 103 | 104 | {-| -} 105 | serif : Font 106 | serif = 107 | Font.serif 108 | 109 | 110 | {-| -} 111 | sansSerif : Font 112 | sansSerif = 113 | Font.sansSerif 114 | 115 | 116 | {-| -} 117 | monospace : Font 118 | monospace = 119 | Font.monospace 120 | 121 | 122 | {-| -} 123 | typeface : String -> Font 124 | typeface name = 125 | Font.typeface name 126 | 127 | 128 | {-| **Note** it's likely that `Font.external` will cause a flash on your page on loading. 129 | 130 | To bypass this, import your fonts using a separate stylesheet and just use `Font.typeface`. 131 | 132 | It's likely that `Font.external` will be removed or redesigned in the future to avoid the flashing. 133 | 134 | `Font.external` can be used to import font files. Let's say you found a neat font on : 135 | 136 | import Element 137 | import Element.Font as Font 138 | 139 | view = 140 | Element.el 141 | [ Font.family 142 | [ Font.external 143 | { name = "Roboto" 144 | , url = "https://fonts.googleapis.com/css?family=Roboto" 145 | } 146 | , Font.sansSerif 147 | ] 148 | ] 149 | (Element.text "Woohoo, I'm stylish text") 150 | 151 | -} 152 | external : { url : String, name : String } -> Font 153 | external = 154 | Font.external 155 | 156 | 157 | {-| Font sizes are always given as `px`. 158 | -} 159 | size : Int -> Attr context decorative msg 160 | size i = 161 | attr <| Font.size i 162 | 163 | 164 | {-| In `px`. 165 | -} 166 | letterSpacing : Float -> Attribute context msg 167 | letterSpacing offset = 168 | attribute <| Font.letterSpacing offset 169 | 170 | 171 | {-| In `px`. 172 | -} 173 | wordSpacing : Float -> Attribute context msg 174 | wordSpacing offset = 175 | attribute <| Font.wordSpacing offset 176 | 177 | 178 | {-| Align the font to the left. 179 | -} 180 | alignLeft : Attribute context msg 181 | alignLeft = 182 | attribute Font.alignLeft 183 | 184 | 185 | {-| Align the font to the right. 186 | -} 187 | alignRight : Attribute context msg 188 | alignRight = 189 | attribute Font.alignRight 190 | 191 | 192 | {-| Center align the font. 193 | -} 194 | center : Attribute context msg 195 | center = 196 | attribute Font.center 197 | 198 | 199 | {-| -} 200 | justify : Attribute context msg 201 | justify = 202 | attribute Font.justify 203 | 204 | 205 | 206 | -- {-| -} 207 | -- justifyAll : Attribute context msg 208 | -- justifyAll = 209 | -- Internal.class classesTextJustifyAll 210 | 211 | 212 | {-| -} 213 | underline : Attribute context msg 214 | underline = 215 | attribute Font.underline 216 | 217 | 218 | {-| -} 219 | strike : Attribute context msg 220 | strike = 221 | attribute Font.strike 222 | 223 | 224 | {-| -} 225 | italic : Attribute context msg 226 | italic = 227 | attribute Font.italic 228 | 229 | 230 | {-| -} 231 | bold : Attribute context msg 232 | bold = 233 | attribute Font.bold 234 | 235 | 236 | {-| -} 237 | light : Attribute context msg 238 | light = 239 | attribute Font.light 240 | 241 | 242 | {-| -} 243 | hairline : Attribute context msg 244 | hairline = 245 | attribute Font.hairline 246 | 247 | 248 | {-| -} 249 | extraLight : Attribute context msg 250 | extraLight = 251 | attribute Font.extraLight 252 | 253 | 254 | {-| -} 255 | regular : Attribute context msg 256 | regular = 257 | attribute Font.regular 258 | 259 | 260 | {-| -} 261 | semiBold : Attribute context msg 262 | semiBold = 263 | attribute Font.semiBold 264 | 265 | 266 | {-| -} 267 | medium : Attribute context msg 268 | medium = 269 | attribute Font.medium 270 | 271 | 272 | {-| -} 273 | extraBold : Attribute context msg 274 | extraBold = 275 | attribute Font.extraBold 276 | 277 | 278 | {-| -} 279 | heavy : Attribute context msg 280 | heavy = 281 | attribute Font.heavy 282 | 283 | 284 | {-| This will reset bold and italic. 285 | -} 286 | unitalicized : Attribute context msg 287 | unitalicized = 288 | attribute Font.unitalicized 289 | 290 | 291 | {-| -} 292 | shadow : 293 | { offset : ( Float, Float ) 294 | , blur : Float 295 | , color : Color 296 | } 297 | -> Attr context decorative msg 298 | shadow shade = 299 | attr <| Font.shadow shade 300 | 301 | 302 | {-| A glow is just a simplified shadow. 303 | -} 304 | glow : Color -> Float -> Attr context decorative msg 305 | glow clr i = 306 | attr <| Font.glow clr i 307 | 308 | 309 | 310 | {- Variants -} 311 | 312 | 313 | {-| -} 314 | type alias Variant = 315 | Font.Variant 316 | 317 | 318 | {-| You can use this to set a single variant on an element itself such as: 319 | 320 | el 321 | [ Font.variant Font.smallCaps 322 | ] 323 | (text "rendered with smallCaps") 324 | 325 | **Note** These will **not** stack. If you want multiple variants, you should use `Font.variantList`. 326 | 327 | -} 328 | variant : Variant -> Attribute context msg 329 | variant var = 330 | attribute <| Font.variant var 331 | 332 | 333 | {-| -} 334 | variantList : List Variant -> Attribute context msg 335 | variantList vars = 336 | attribute <| Font.variantList vars 337 | 338 | 339 | {-| [Small caps](https://en.wikipedia.org/wiki/Small_caps) are rendered using uppercase glyphs, but at the size of lowercase glyphs. 340 | -} 341 | smallCaps : Variant 342 | smallCaps = 343 | Font.smallCaps 344 | 345 | 346 | {-| Add a slash when rendering `0` 347 | -} 348 | slashedZero : Variant 349 | slashedZero = 350 | Font.slashedZero 351 | 352 | 353 | {-| -} 354 | ligatures : Variant 355 | ligatures = 356 | Font.ligatures 357 | 358 | 359 | {-| Oridinal markers like `1st` and `2nd` will receive special glyphs. 360 | -} 361 | ordinal : Variant 362 | ordinal = 363 | Font.ordinal 364 | 365 | 366 | {-| Number figures will each take up the same space, allowing them to be easily aligned, such as in tables. 367 | -} 368 | tabularNumbers : Variant 369 | tabularNumbers = 370 | Font.tabularNumbers 371 | 372 | 373 | {-| Render fractions with the numerator stacked on top of the denominator. 374 | -} 375 | stackedFractions : Variant 376 | stackedFractions = 377 | Font.stackedFractions 378 | 379 | 380 | {-| Render fractions 381 | -} 382 | diagonalFractions : Variant 383 | diagonalFractions = 384 | Font.diagonalFractions 385 | 386 | 387 | {-| -} 388 | swash : Int -> Variant 389 | swash = 390 | Font.swash 391 | 392 | 393 | {-| Set a feature by name and whether it should be on or off. 394 | 395 | Feature names are four-letter names as defined in the [OpenType specification](https://docs.microsoft.com/en-us/typography/opentype/spec/featurelist). 396 | 397 | -} 398 | feature : String -> Bool -> Variant 399 | feature = 400 | Font.feature 401 | 402 | 403 | {-| A font variant might have multiple versions within the font. 404 | 405 | In these cases we need to specify the index of the version we want. 406 | 407 | -} 408 | indexed : String -> Int -> Variant 409 | indexed = 410 | Font.indexed 411 | -------------------------------------------------------------------------------- /src/Element/WithContext/Input.elm: -------------------------------------------------------------------------------- 1 | module Element.WithContext.Input exposing 2 | ( focusedOnLoad 3 | , button 4 | , checkbox, defaultCheckbox 5 | , text, multiline 6 | , Placeholder, placeholder 7 | , username, newPassword, currentPassword, email, search, spellChecked 8 | , slider, Thumb, thumb, defaultThumb 9 | , radio, radioRow, Option, option, optionWith, OptionState(..) 10 | , Label, labelAbove, labelBelow, labelLeft, labelRight, labelHidden 11 | ) 12 | 13 | {-| Input elements have a lot of constraints! 14 | 15 | We want all of our input elements to: 16 | 17 | - _Always be accessible_ 18 | - _Behave intuitively_ 19 | - _Be completely restyleable_ 20 | 21 | While these three goals may seem pretty obvious, Html and CSS have made it surprisingly difficult to achieve! 22 | 23 | And incredibly difficult for developers to remember all the tricks necessary to make things work. If you've every tried to make a `