Commits: 8
Make CartesianCoordinates example parametrized
This required changes to CartesianPlane (graph and axes).
Also refactor code for other examples to use pipes and limit the width of layout (in some cases).
index 5fce82e..91b6b4e 100644
--- a/content/day-1.txt
+++ b/content/day-1.txt
@@ -254,6 +254,12 @@ Reload the browser to confirm your hypothesis. Voila! The SVG space now fills th
= cy=0
=
=It should now be possible to place our dot in the center of the screen. But, how exactly will we achieve it?
+| Row
+ | CartesianCoordinates
+ minX = -10
+ minY = -10
+ maxX = 300
+ maxY = 200
=
=Let's return to the idea we introduced earlier, that the center of the dot is at a point called the {Code|origin} or {Code|(0, 0)}. What exactly does this mean?
=
@@ -261,7 +267,6 @@ Let's return to the idea we introduced earlier, that the center of the dot is at
=
=Adjust the sliders below to change the x and y coordinates of the dot. It's like moving an object on a table by describing how far left, right, up or down it should be.
=
-| CartesianCoordinates
=
=Knowing that the center of the dot is at the {Code|origin} or point {Code|(0,0)} of our plain, we can see that by default the origin of an SVG space is at the upper left corner. If we want to help our dot get out of hiding, we'll have to place the center of the dot at a point with positive {Code|x} and {Code|y} values.
=index 6029a39..a162a84 100644
--- a/src/CartesianPlane.elm
+++ b/src/CartesianPlane.elm
@@ -11,24 +11,39 @@ import Triangle2d
=import Vector2d
=
=
-graph : Float -> List (Svg msg) -> Html msg
-graph size shapes =
+type alias Config =
+ { minX : Float
+ , minY : Float
+ , maxX : Float
+ , maxY : Float
+ }
+
+
+graph : Config -> List (Svg msg) -> Html msg
+graph config shapes =
= let
+ size =
+ Basics.max (config.maxX - config.minX) (config.maxY - config.minY)
+
= hairline =
- size / 1000
+ 20 / size
=
= fontsize =
- size / 200
+ 100 / size
=
= background =
= axes
= [ stroke "gray"
= , fill "gray"
= ]
- size
+ config
= in
= svg
- [ [ negate size / 2, negate size / 2, size, size ]
+ [ [ config.minX
+ , config.minY
+ , config.maxX - config.minX
+ , config.maxY - config.minY
+ ]
= |> List.map String.fromFloat
= |> String.join " "
= |> viewBox
@@ -40,22 +55,17 @@ graph size shapes =
= (background :: shapes)
=
=
-axes attributes size =
+axes : List (Attribute msg) -> Config -> Svg msg
+axes attributes config =
= let
- max =
- size / 2
-
- min =
- negate max
-
= xAxis =
- { start = Point2d.fromCoordinates ( min, 0 )
- , end = Point2d.fromCoordinates ( max, 0 )
+ { start = Point2d.fromCoordinates ( config.minX, 0 )
+ , end = Point2d.fromCoordinates ( config.maxX, 0 )
= }
=
= yAxis =
- { start = Point2d.fromCoordinates ( 0, min )
- , end = Point2d.fromCoordinates ( 0, max )
+ { start = Point2d.fromCoordinates ( 0, config.minY )
+ , end = Point2d.fromCoordinates ( 0, config.maxY )
= }
= in
= g []
@@ -67,9 +77,21 @@ axes attributes size =
= attributes
= yAxis.start
= yAxis.end
- , label [ fontSize "8", color "gray" ] (xAxis.end |> Point2d.translateIn (Direction2d.fromAngle (degrees -135)) 10) "x"
- , label [ fontSize "8", color "gray" ] (yAxis.end |> Point2d.translateIn (Direction2d.fromAngle (degrees -45)) 10) "y"
- , label [ fontSize "8", color "gray" ] (Point2d.origin |> Point2d.translateIn (Direction2d.fromAngle (degrees 135)) 10) "O"
+ , label [ fontSize "8", color "gray" ]
+ (xAxis.end
+ |> Point2d.translateIn (Direction2d.fromAngle (degrees -135)) 10
+ )
+ "x"
+ , label [ fontSize "8", color "gray" ]
+ (yAxis.end
+ |> Point2d.translateIn (Direction2d.fromAngle (degrees -45)) 10
+ )
+ "y"
+ , label [ fontSize "8", color "gray" ]
+ (Point2d.origin
+ |> Point2d.translateIn (Direction2d.fromAngle (degrees 135)) 10
+ )
+ "O"
= ]
=
=index 1c4266c..b8b1723 100644
--- a/src/Examples/CartesianCoordinates.elm
+++ b/src/Examples/CartesianCoordinates.elm
@@ -1,5 +1,6 @@
=module Examples.CartesianCoordinates exposing
- ( Model
+ ( Config
+ , Model
= , Msg
= , init
= , main
@@ -19,6 +20,23 @@ import Svg exposing (..)
=import Svg.Attributes exposing (..)
=
=
+type alias Config =
+ { minX : Float
+ , minY : Float
+ , maxX : Float
+ , maxY : Float
+ }
+
+
+defaults : Config
+defaults =
+ { minX = -150
+ , minY = -150
+ , maxX = 150
+ , maxY = 150
+ }
+
+
=main : Program () Model Msg
=main =
= Browser.sandbox
@@ -46,11 +64,16 @@ init =
=
=view : Model -> Html Msg
=view model =
- Element.layout
- [ Element.height Element.fill
- , Element.width Element.fill
- ]
- (ui model)
+ model
+ |> ui defaults
+ |> Element.el
+ [ Element.width (Element.maximum 600 Element.fill)
+ , Element.centerX
+ ]
+ |> Element.layout
+ [ Element.height Element.fill
+ , Element.width Element.fill
+ ]
=
=
=update : Msg -> Model -> Model
@@ -63,42 +86,48 @@ update msg model =
= { model | y = y }
=
=
-ui : Model -> Element Msg
-ui model =
+ui : Config -> Model -> Element Msg
+ui config model =
+ let
+ graph =
+ [ circle
+ [ cx <| String.fromFloat model.x
+ , cy <| String.fromFloat model.y
+ , r "2"
+ , fill "magenta"
+ ]
+ []
+ , text_
+ [ x <| String.fromFloat (model.x + 5)
+ , y <| String.fromFloat (model.y + 5)
+ , fontSize "6"
+ , fontFamily "Source Code Pro, monospace"
+ , fill "gray"
+ , dominantBaseline "central"
+ ]
+ [ text coordinates ]
+ ]
+ |> CartesianPlane.graph config
+ |> Element.html
+ |> Element.el
+ [ Element.height Element.fill
+ , Element.width Element.fill
+ ]
+
+ coordinates =
+ "{ x = "
+ ++ String.fromFloat model.x
+ ++ ", y = "
+ ++ String.fromFloat model.y
+ ++ "}"
+ in
= Element.column
= [ Element.width Element.fill
= , Element.centerX
= , Element.spacing 30
= , Element.padding 30
= ]
- [ Element.el
- [ Element.height Element.fill
- , Element.width Element.fill
- ]
- <|
- Element.html <|
- CartesianPlane.graph 300
- [ circle
- [ cx <| String.fromFloat model.x
- , cy <| String.fromFloat model.y
- , r "2"
- , fill "magenta"
- ]
- []
- , text_
- [ x <| String.fromFloat (model.x + 0.03)
- , y <| String.fromFloat model.y
- , fontSize "0.05"
- , dominantBaseline "central"
- ]
- [ text <|
- "("
- ++ String.fromFloat model.x
- ++ ", "
- ++ String.fromFloat model.y
- ++ ")"
- ]
- ]
+ [ graph
= , Input.slider
= [ Element.behindContent
= (Element.el
@@ -114,9 +143,9 @@ ui model =
= { onChange = SetX
= , label =
= Input.labelBelow [ Element.centerX ] <|
- Element.text ("x value: " ++ String.fromFloat model.x)
- , min = -150
- , max = 150
+ Element.text ("x = " ++ String.fromFloat model.x)
+ , min = config.minX
+ , max = config.maxX
= , value = model.x
= , thumb = Input.defaultThumb
= , step = Just 0.01
@@ -136,9 +165,9 @@ ui model =
= { onChange = SetY
= , label =
= Input.labelBelow [ Element.centerX ] <|
- Element.text ("y value: " ++ String.fromFloat model.y)
- , min = -150
- , max = 150
+ Element.text ("y = " ++ String.fromFloat model.y)
+ , min = config.minY
+ , max = config.maxY
= , value = model.y
= , thumb = Input.defaultThumb
= , step = Just 0.01index 35baf6c..a79414c 100644
--- a/src/Examples/Circle.elm
+++ b/src/Examples/Circle.elm
@@ -33,11 +33,15 @@ type alias Config =
=
=main : Html.Html msg
=main =
- Element.layout
- [ Element.width Element.fill
- , Element.height Element.fill
- ]
- (ui defaults)
+ ui defaults
+ |> Element.el
+ [ Element.width (Element.maximum 600 Element.fill)
+ , Element.centerX
+ ]
+ |> Element.layout
+ [ Element.width Element.fill
+ , Element.height Element.fill
+ ]
=
=
=palette : List Stringindex 46899d3..11dbad5 100644
--- a/src/Examples/LineTypedTransformations.elm
+++ b/src/Examples/LineTypedTransformations.elm
@@ -34,10 +34,6 @@ type Transformation
= | Rotate Float
=
=
-
--- transform : List Transformation -> Svg.Attribute msg
-
-
=transform transformations =
= let
= toString : Transformation -> Stringindex b1bc6f5..f4a9dbf 100644
--- a/src/Examples/NestedTransformations.elm
+++ b/src/Examples/NestedTransformations.elm
@@ -154,7 +154,12 @@ ui model =
= in
= shape
= |> List.singleton
- |> CartesianPlane.graph 300
+ |> CartesianPlane.graph
+ { minX = -150
+ , minY = -150
+ , maxX = 150
+ , maxY = 150
+ }
= |> wrapper
=
=
@@ -441,5 +446,12 @@ grid attributes unit size =
= |> List.map LineSegment2d.fromEndpoints
= |> List.map (Geometry.Svg.lineSegment2d attributes)
= )
- |> (::) (CartesianPlane.axes attributes (size * unit))
+ |> (::)
+ (CartesianPlane.axes attributes
+ { minX = size * unit * -0.5
+ , minY = size * unit * -0.5
+ , maxX = size * unit * 0.5
+ , maxY = size * unit * 0.5
+ }
+ )
= |> g []index 4fd1e93..8f552f0 100644
--- a/src/Examples/PolarCoordinates.elm
+++ b/src/Examples/PolarCoordinates.elm
@@ -70,7 +70,12 @@ ui model =
= ]
= <|
= Element.html <|
- CartesianPlane.graph 300
+ CartesianPlane.graph
+ { minX = -150
+ , minY = -150
+ , maxX = 150
+ , maxY = 150
+ }
= [ circle
= [ cx <| String.fromFloat point.x
= , cy <| String.fromFloat point.yindex 3321826..15d42cf 100644
--- a/src/Examples/Transformations.elm
+++ b/src/Examples/Transformations.elm
@@ -57,11 +57,16 @@ init =
=
=view : Model -> Html Msg
=view model =
- Element.layout
- [ Element.height Element.fill
- , Element.width Element.fill
- ]
- (ui model)
+ model
+ |> ui
+ |> Element.el
+ [ Element.width (Element.maximum 600 Element.fill)
+ , Element.centerX
+ ]
+ |> Element.layout
+ [ Element.height Element.fill
+ , Element.width Element.fill
+ ]
=
=
=ui : Model -> Element Msg
@@ -72,8 +77,7 @@ ui model =
=
= wrapper element =
= Element.column
- [ Element.width (Element.maximum 600 Element.fill)
- , Element.centerX
+ [ Element.width Element.fill
= , Element.spacing 20
= ]
= [ Element.el
@@ -113,7 +117,12 @@ ui model =
= in
= shape
= |> List.singleton
- |> CartesianPlane.graph 300
+ |> CartesianPlane.graph
+ { minX = -150
+ , minY = -150
+ , maxX = 150
+ , maxY = 150
+ }
= |> wrapper
=
=
@@ -339,5 +348,12 @@ grid attributes unit size =
= |> List.map LineSegment2d.fromEndpoints
= |> List.map (Geometry.Svg.lineSegment2d attributes)
= )
- |> (::) (CartesianPlane.axes attributes (size * unit))
+ |> (::)
+ (CartesianPlane.axes attributes
+ { minX = -150
+ , minY = -150
+ , maxX = 150
+ , maxY = 150
+ }
+ )
= |> g []index 222da7a..576f5e8 100644
--- a/src/Examples/ViewBox.elm
+++ b/src/Examples/ViewBox.elm
@@ -47,8 +47,8 @@ init : Model
=init =
= { left = 0
= , top = 0
- , width = 400
- , height = 400
+ , width = 50
+ , height = 50
= , preserveAspectRatio = False
= }
=
@@ -95,7 +95,7 @@ ui model =
= let
= viewbox =
= svg
- [ viewBox "-500 -500 1000 1000"
+ [ viewBox "-100 -100 200 200"
= ]
= [ g [] world
= , rect
@@ -187,8 +187,8 @@ ui model =
= , label =
= Input.labelBelow [ Element.centerX ] <|
= Element.text ("left: " ++ String.fromFloat model.left)
- , min = -500
- , max = 500
+ , min = -100
+ , max = 100
= , value = model.left
= , thumb = Input.defaultThumb
= , step = Just 1
@@ -209,8 +209,8 @@ ui model =
= , label =
= Input.labelBelow [ Element.centerX ] <|
= Element.text ("top: " ++ String.fromFloat model.top)
- , min = -500
- , max = 500
+ , min = -100
+ , max = 100
= , value = model.top
= , thumb = Input.defaultThumb
= , step = Just 1
@@ -232,7 +232,7 @@ ui model =
= Input.labelBelow [ Element.centerX ] <|
= Element.text ("width: " ++ String.fromFloat model.width)
= , min = 1
- , max = 500
+ , max = 100
= , value = model.width
= , thumb = Input.defaultThumb
= , step = Just 1
@@ -254,7 +254,7 @@ ui model =
= Input.labelBelow [ Element.centerX ] <|
= Element.text ("height: " ++ String.fromFloat model.height)
= , min = 1
- , max = 500
+ , max = 100
= , value = model.height
= , thumb = Input.defaultThumb
= , step = Just 1
@@ -270,7 +270,20 @@ ui model =
=
=world : List (Svg Msg)
=world =
- [ circle [ cx "400", cy "300", r "250", fill "purple" ] []
- , circle [ cx "200", cy "350", r "50", fill "yellow" ] []
- , circle [ cx "0", cy "0", r "20", fill "red" ] []
+ [ CartesianPlane.axes
+ [ stroke "gray"
+ , fill "gray"
+ ]
+ { minX = -100
+ , minY = -100
+ , maxX = 100
+ , maxY = 100
+ }
+ , circle
+ [ cx "0"
+ , cy "0"
+ , r "2"
+ , fill "magenta"
+ ]
+ []
= ]index 9a9da15..782b5a7 100644
--- a/src/Main.elm
+++ b/src/Main.elm
@@ -209,10 +209,10 @@ view model =
= "Expecting a block name " ++ name
=
= Mark.ExpectingInlineName name ->
- "Expecting an inline name" ++ name
+ "Expecting an inline name " ++ name
=
= Mark.ExpectingFieldName name ->
- "Expecting a field name" ++ name
+ "Expecting a field name " ++ name
=
= Mark.NonMatchingFields { expecting, found } ->
= "Fields don't match. Expecting one of [ "
@@ -640,13 +640,23 @@ document =
= cartesianCoordinates : Mark.Block (Examples.Model -> Element Msg)
= cartesianCoordinates =
= let
- render model =
+ render :
+ Examples.CartesianCoordinates.Config
+ -> Examples.Model
+ -> Element Msg
+ render config model =
= model.cartesianCoordinates
- |> Examples.CartesianCoordinates.ui
+ |> Examples.CartesianCoordinates.ui config
= |> Element.map Examples.CartesianCoordinatesMsg
= |> Element.map ExamplesMsg
= in
- Mark.stub "CartesianCoordinates" render
+ Mark.record4 "CartesianCoordinates"
+ Examples.CartesianCoordinates.Config
+ (Mark.field "minX" Mark.float)
+ (Mark.field "minY" Mark.float)
+ (Mark.field "maxX" Mark.float)
+ (Mark.field "maxY" Mark.float)
+ |> Mark.map render
=
= polarCoordinates : Mark.Block (Examples.Model -> Element Msg)
= polarCoordinates =Small changes to Terminal and Code blocks
Font size, icons, fix background color not bleeding to the edge in Chromium.
index 73c5f76..c047718 100644
--- a/src/Mark/Custom.elm
+++ b/src/Mark/Custom.elm
@@ -105,7 +105,7 @@ code =
= , Background.color colors.charcoal
= , Font.color colors.gray
= ]
- [ FeatherIcons.file
+ [ FeatherIcons.fileText
= |> FeatherIcons.toHtml []
= |> Element.html
= |> Element.el
@@ -184,6 +184,7 @@ terminal =
= [ Border.width 3
= , Border.rounded 5
= , Border.color colors.charcoal
+ , Background.color colors.charcoal
= , css "page-break-inside" "avoid"
= , Font.family
= [ sourceCodePro
@@ -193,7 +194,6 @@ terminal =
= ]
= [ Element.row
= [ Element.width Element.fill
- , Background.color colors.charcoal
= , Font.color colors.gray
= ]
= [ FeatherIcons.terminal
@@ -226,7 +226,7 @@ terminal =
= , bottomLeft = 3
= , bottomRight = 3
= }
- , Font.size 18
+ , Font.size 14
= , Font.family
= [ sourceCodePro
= , Font.monospaceMake Icon size dependant on the font size of the parent
Use em units instead of px.
index c047718..0a42947 100644
--- a/src/Mark/Custom.elm
+++ b/src/Mark/Custom.elm
@@ -396,8 +396,8 @@ icon =
= |> Dict.get name
= |> Maybe.map
= (FeatherIcons.toHtml
- [ Html.Attributes.height 14
- , Html.Attributes.width 14
+ [ Html.Attributes.style "height" "1em"
+ , Html.Attributes.style "width" "1em"
= ]
= )
= |> Maybe.map Element.htmlRe-draft day 1
Back to single dot (it's hard enough for one day).
Move all semantics discussion to day 3 (not redacted yet).
index 91b6b4e..d1fc7a3 100644
--- a/content/day-1.txt
+++ b/content/day-1.txt
@@ -2,7 +2,7 @@
= Day 1
=
=| Emphasize
- Let's Make Some Dots!
+ Let's Make a Dot!
=
=
=| Note
@@ -22,7 +22,7 @@
=As mentioned before, programs are represented as text (called the /source code/). The source code is stored in files {Icon|name=file} and files are organized in directories {Icon|name=folder}.
=
=
-So the first step is to create a directory for our new program. Let's call it "software-garden".
+So the first step is to create a directory for our new program. Let's call it {Code|software-garden}.
=
=In the terminal type:
=
@@ -50,12 +50,34 @@ We can easily create a new program by entering a following command:
=| Terminal
= elm init
=
-Then to create the file which will contain our source code, type:
+You should see something similar to this:
+
+| Terminal
+ Hello! Elm projects always start with an elm.json file. I can create them!
+
+ Now you may be wondering, what will be in this file? How do I add Elm files to
+ my project? How do I see it in the browser? How will my code grow? Do I need
+ more directories? What about tests? Etc.
+
+ Check out <https://elm-lang.org/0.19.0/init> for all the answers!
+
+ Knowing all that, would you like me to create an elm.json file now? [Y/n]:
+
+
+Just press {Key|enter} once again to proceed. Then to create the file which will contain our source code, type:
=
=| Terminal
= atom src/Main.elm
=
-It may take few seconds, but eventually this command should open a new Atom editor window with an empty text file. If you prefer to use different editor, feel free to do so. Once the editor is ready, type the code belowand save the file.
+| Note
+ Notice that the word {Code|Main} is capitalized. It's important! You can enter uppercase letter {Code|M} with {Key|shift} + {Key|m}, which means:
+
+ | List
+ - Press and hold {Key|shift}
+ - While holding press the {Key|m} button once.
+ - Finally release the {Key|shift}.
+
+It may take few seconds, but eventually this command should open a new Atom editor window with an empty text file. If you prefer to use different editor, feel free to do so. Once the editor is ready, type the code below and save the file.
=
=| Code
= module Main exposing (main)
@@ -66,7 +88,7 @@ It may take few seconds, but eventually this command should open a new Atom edit
= main =
= Html.text "Hello, Tree"
=
-Then enter the following command in the terminal:
+Then switch back to the terminal and enter the following command:
=
=| Terminal
= elm reactor
@@ -135,8 +157,17 @@ We are going to use a technology called *SVG (Scalable Vector Graphics)*. It's a
= - While holding press the {Key|c} button once.
= - Finally release the {Key|ctrl}.
=
+In response you should see something like this:
=
-Now that we have {Code|elm//svg} installed, we can write the code that will draw a dot for us. Change the code to look like this:
+| Terminal
+ Here is my plan:
+
+ Add:
+ elm/svg 1.0.1
+
+ Would you like me to update your elm.json accordingly? [Y/n]:
+
+Press {Key|enter} to proceed with the installation. Now that we have {Code|elm//svg} installed, we can write the code that will draw a dot for us. Change the code to look like this:
=
=| Code
= module Main exposing (main)
@@ -168,12 +199,9 @@ Reload the browser. You should see something like this:
=
=At this point we have a dot on the screen - it's at the top left corner of the viewport (the area of the browser window where the content is displayed). We can see only a quarter of it - the rest is outside of the viewport.
=
-| Note
- TODO: Picture // example showing the viewport
-
=It's a good start, but the dot is not in the center of the screen yet. Let's try to understand why it is where it is.
=
-First we have to realize that the dot is inside an SVG element that itself has a position, width and height. We can make the background of the SVG element pink to see where it is:
+First we have to realize that the dot is inside an {Code|svg} element that itself has a position, width and height. We can make the background of the {Code|svg} element pink to see where it is:
=
=| Code
= module Main exposing (main)
@@ -189,33 +217,21 @@ First we have to realize that the dot is inside an SVG element that itself has a
= []
= ]
=
-Realoading the browser should reveal something like this:
+Reloading the browser should reveal something like this:
=
=| Window
= | Dot
= background=pink
= fill=black
=
-Since the dot is inside the SVG element, and the SVG element doesn't cover the center of the screen, it's impossible to place the dot at the center. Before anything else, let's make the SVG fill entire {Definition|term=viewport|definiens=the area of the browser window where the content is displayed}.
-
-There are two reasons. First, because the center of the dot is at a point called the {Code|origin} or {Code|(0, 0)}. We'll explore exactly what this means later.
-
-Second, it's because the SVG space doesn't fill the browser window.
-
-
-When you reload the browser, you'll see this:
+Since the dot is inside the {Code|svg} element, and the {Code|svg} element doesn't cover the center of the screen, it's impossible to place the dot at the center. Before anything else, let's make the {Code|svg} fill entire {Definition|term=viewport|definiens=the area of the browser window where the content is displayed}.
=
+To do this we will use a package called {Code|mdgriffith//elm-ui}. It makes laying out elements relatively easy. Install it with the terminal. Press {Key|ctrl} + {Key|c} to stop the elm reactor and enter the command
=
+| Terminal
+ elm install mdgriffith/elm-ui
=
-Now we can clearly see that our SVG element (which is pink) does not fill the screen.
-
-We can easily correct this with the help of a very handy elm package, {Code|mdgriffith//elm-ui}.
-
-Install it with the terminal.
-
-Press {Key|ctrl} + {Key|c} to stop the elm-reactor, type {Code|elm install mdgriffith//elm-ui} and press {Key|enter}.
-
-Then in {Code|src//Main.elm} add the necessary import {Code|import Element} and change {Code|Main} to look like this:
+As before, press {Key|enter} to follow the plan. Now let's use the package. Change the code to look like this:
=
=| Code
= module Main exposing (main)
@@ -226,25 +242,33 @@ Then in {Code|src//Main.elm} add the necessary import {Code|import Element} and
=
=
= main =
- Element.layout
- [ Element.width Element.fill
- , Element.height Element.fill
+ Svg.svg
+ [ Svg.Attributes.style "background: pink"
+ , Svg.Attributes.height "100%"
+ , Svg.Attributes.width "100%"
= ]
- (Element.html
- (Svg.svg
- [ Svg.Attributes.height "100%"
- , Svg.Attributes.width "100%"
- , Svg.Attributes.style "background: pink"
- ]
- [ Svg.circle
- [ Svg.Attributes.r "10" ] []
- ]
- )
- )
-
-Take a wild guess: what do you think {Code|Element.height Element.fill}, {Code|Svg.Attributes.height "100%"}, {Code|Element.width Element.fill} and {Code|Svg.Attributes.width "100%"} do?
-
-Reload the browser to confirm your hypothesis. Voila! The SVG space now fills the screen.
+ [ Svg.circle
+ [ Svg.Attributes.r "10"
+ ]
+ []
+ ]
+ |> Element.html
+ |> Element.layout
+ [ Element.width Element.fill
+ , Element.height Element.fill
+ ]
+
+
+| Note
+ Don't worry if it all looks like black magic now. We will discuss what it all means on {Link|Day 3|url=day-3.html} of the workshop.
+
+ For now let's just note that on line 3 we have added a new {Code|import} statement for {Code|Element} module (similar to how we did it for {Code|Svg} and {Code|Svg.Attributes} before).
+
+ After imports our code looks the same at first: {Code|main} is an {Code|svg} element containing the {Code|circle}, but we have added two new attributes {Code|width} an {Code|height}, both set to {Code|"100%"}. We will talk more about attributes later today.
+
+ Then there are some new things on lines 19 - 23. First is the pipe operator ({Code|\|>}). We use it to pass the {Code|svg} element together with the {Code|circle} inside it into `Element.html` function (we need to convert it into an {Code|Element}) and then to pass this {Code|Element} into an {Code|Element.layout} which will fill the viewport with its contents. Again, more about the pipe and functions on day 3.
+
+Reload the browser. The SVG space now fills the entire viewport. Even the small white margin is gone!
=
=| Window
= | DotInElement
@@ -253,7 +277,22 @@ Reload the browser to confirm your hypothesis. Voila! The SVG space now fills th
= cx=0
= cy=0
=
-It should now be possible to place our dot in the center of the screen. But, how exactly will we achieve it?
+It should now be possible to place our dot in the center of the screen. Currently the dot is in the top left corner of the viewport, but at least the parent {Code|svg} element is covering it, so the center of the viewport is somewher within the {Code|svg} element.
+
+| Header
+ The Coordinates
+
+We will now explore how child elements (like our {Code|circle}) are positioned inside the parent {Code|svg} element.
+
+One method is to use the *cartesian coordinates system*. To get some intuition about it, imagine that you are a villain in an old James Bond movie. For some sinister purpose you need to tell somebody exactly where a flower pot is located on a large table. The problem is that you can only communicate through an old fashioned phone {Icon|name=phone}. To fool mr. Bond you need to be very precise, so saying something like "it's roughly in the middle" won't work.
+
+| Note
+ TODO: Image of the table, the flower pot and the villain
+
+However, if you had a ruler, you could say: "it's 63.3cm from the top edge and 27.1cm from the left edge". To do that you would have previously agree which edge is left and which is top - not so obvious in case of a table. Fortunately in SVG we have this sort of agreement made for us. We alsways measure from the top and left edge of the viewport.
+
+The distance from the left edge is called {Code|x} and the distance from the top is called {Code|y}. The bigger the {Code|x} the more to the right goes the element. The bigger the {Code|y} the more it goes down. Try it out:
+
=| Row
= | CartesianCoordinates
= minX = -10
@@ -261,16 +300,11 @@ It should now be possible to place our dot in the center of the screen. But, how
= maxX = 300
= maxY = 200
=
-Let's return to the idea we introduced earlier, that the center of the dot is at a point called the {Code|origin} or {Code|(0, 0)}. What exactly does this mean?
-
-* a picture of a vase on a table should be here *
-
-Adjust the sliders below to change the x and y coordinates of the dot. It's like moving an object on a table by describing how far left, right, up or down it should be.
-
+Adjust the sliders below to change the {Code|x} and {Code|y} coordinates of the dot. It's like moving the flower pot on the table by describing how far left, right, up or down it should be.
=
-Knowing that the center of the dot is at the {Code|origin} or point {Code|(0,0)} of our plain, we can see that by default the origin of an SVG space is at the upper left corner. If we want to help our dot get out of hiding, we'll have to place the center of the dot at a point with positive {Code|x} and {Code|y} values.
+Initially the center of the dot is at a point called /the origin/ or {Code|\{ x = 0, y = 0\\}}. The origin is in the top left corner of the viewport. That's why only quarter of the dot is visible. If we want to help our dot get out of hiding, we'll have to place the center of the dot at a point with positive {Code|x} and {Code|y} values.
=
-To change the position of the dot, we can use the {Code|cx} and {Code|cy} attributes of the {Code|circle} element.
+To change the position of the dot, we can use the {Code|cx} and {Code|cy} attributes of the {Code|circle} element, like this:
=
=| Code
= module Main exposing (main)
@@ -281,25 +315,23 @@ To change the position of the dot, we can use the {Code|cx} and {Code|cy} attrib
=
=
= main =
- Element.layout
+ [ Svg.circle
+ [ Svg.Attributes.r "10"
+ , Svg.Attributes.cx "100"
+ , Svg.Attributes.cy "50"
+ ]
+ []
+ ]
+ |> Svg.svg
+ [ Svg.Attributes.height "100%"
+ , Svg.Attributes.width "100%"
+ , Svg.Attributes.style "background: pink"
+ ]
+ |> Element.html
+ |> Element.layout
= [ Element.width Element.fill
= , Element.height Element.fill
= ]
- (Element.html
- (Svg.svg
- [ Svg.Attributes.height "100%"
- , Svg.Attributes.width "100%"
- , Svg.Attributes.style "background: pink"
- ]
- [ Svg.circle
- [ Svg.Attributes.r "10"
- , Svg.Attributes.cx "100"
- , Svg.Attributes.cy "50"
- ]
- []
- ]
- )
- )
=
=| Window
= | DotInElement
@@ -308,30 +340,49 @@ To change the position of the dot, we can use the {Code|cx} and {Code|cy} attrib
= cx=100
= cy=50
=
-Now, if we want our dot to be exactly in the center of the screen, we'll have to set {Code|cx} and {Code|cy} to values equal to exactly half of the {Code|width} and {Code|height} of the SVG space.
+| Note
+ The {Code|cx} stands for /center x/ and {Code|cy} for /center y/, where center refers to the circle. We will discuss what the center of the circle is in more exact terms on day 2, but you probably get the concept.
+
=
-We can calculate the position of the circle as:
+Now the dot is not in the corner anymore, but it's not in the center yet. If we want our dot to be exactly in the center of the screen, we'll have to set {Code|cx} and {Code|cy} to values equal to exactly half of the {Code|width} and {Code|height} of the SVG viewport. We could calculate the position of the circle as:
=
=| Monospace
- cx = width/2
- cy = height/2
+ cx = width / 2
+ cy = height / 2
=
-There is a problem though! We don't actually know the width and height of the SVG space. All we know is that out {Code|layout's} {Code|width} and {Code|height} will {Code|fill} the viewport, and the {Code|svg} space's {Code|width} and {Code|height} are {Code|100%} of the {Code|layout}. We don't know the exact size of either (because the {Code|layout} will scale to the browser window).
-☹️
+There is a problem though! We don't actually know the width and height of the SVG viewport. All we know is that it will fill all the available space, but this can be different on different devices: {Icon|name=smartphone} {Icon|name=tablet} {Icon|name=monitor}
=
-Fortunately, we don't need to know the width and height of the SVG space!
+Fortunately, we don't need to know the width and height of the {Code|svg} element! That's why it's called *scalable*. We can work with it without knowing the actual scale on the screen.
=
-This is a bit tricky. Instead of moving the dot in the SVG space, we can set the boundaries of the {Code|scene} so that the dot is at the center using a property called {Code|Viewbox}.
+| Header
+ The ViewBox
+
+You can imagine an SVG element as an infinite surface on which elements are placed. Which elements you see, depends on where you look. And where you look is called the *viewbox*. It has four properties: {Code|top}, {Code|left}, {Code|width} and {Code|height}.
+
+It works a little bit like a photo camera pointing directly at the surface. You can move it up, down (along the {Code|y} axis) and left and right (along the {Code|x} axis). You can also change it's focal length to cover more or less area. Unlike a lens of the camera you set the width and height separately. If you set it so that the dot is in the middle of your frame, then no matter how big the print of the picture will be, the dot will always be in the center. Increasing the {Code|width} and {Code|height} of the viewbox will make more of the surrounding surface visible and the dot will become smaller, but it's position won't change. Let's see it in action:
+
+| Row
+ | ViewBox
=
-To use an anology, imagine you've decided to build a holiday home with your family or friends in the Alps. Sounds pretty nice, right? Now, your budget isn't unlimited, so you've had to make a few compromises. You're in a beautiful area with a lovely view of the mountains, but yours isn't the only house in the area, and some of the others aren't the nicest to look at. Of course, you don't want your view of the beautiful snow-capped mountains to be blocked by a big pile of concrete. So what are you going to do about this? You can move your neighbours' homes, and the mountains while you're at it, so they are directly in front of your windows. Or, you can move the windows as you design your house so that you get a full view of the beautiful surroundings. Imagine you could move the windows around, as you decide the best place for them. As the window moves, you it will look as though the surroundings as moving, to the right, the left, up and down, as you shift the window. But in reality it is only your perspective that is changing.
=
-A videwbox is like a window into an SVG space. It allows us to change the point of view of the scene.
+On the left and the right side we show the same set of elements: a dot at the origin and two axes ({Code|x} going from left to right and {Code|y} going top - down). The pink rectangle represents the viewbox. Normally it is not visible, but we display it here to help you understand how it works.
+
+On the right there is a window with a viewport inside. Everything covered by the viewbox will be displayed in the viewport. Their sizes are independent, so the shapes will be scaled to fit. You can also set the option to preserve the aspect ratio to prevent the shapes from being stretched.
+
+You cannot control the size of the window or the viewport. All you can do is play with the viewbox. Try calibrating the {Code|left}, {Code|top}, {Code|width} and {Code|height} properties of the viewbox so that the dot is in the center of the viewport.
+
+| Note
+ Here is how I would do it. First pick any number for the {Code|width}, say {Code|20}. Then you have to move the left edge half way ({Code|20 // 2 = 10}) to the left. Once you click on a slider you can use {Key|◀} and {Key|▶} keys to precisely set the value. Moving to the left means making the value lesser, and since we start from {Code|0} we will end up at {Code|-10}. Do the same for {Code|height} and {Code|top}. The dot should be exactly in the center of the viewbox and the viewport.
+
+The formula is simple:
=
-| ViewBox
+| Monospace
+ left = - (width / 2)
+ top = - (height / 2)
=
-In this example, we can set the width and height of the viewbox to an arbitrary value. As we see in the interactive example, the width and height of the viewbox will effect how big the dot appears, but that's not so important to us at the moment. Let's set them both to 600.
+So instead of moving the dot in the SVG plane, we change where we look by moving the viewbox. By setting it that way, we know that the distance from the left and right edge of the screen to the center of the dot will be the same. We don't know exactly what it will be (because we don't control the size of the viewport), but it will always be equal. That's the definition of being in the center!
=
-The top and left values of the viewbox are a bit trickier. We saw ealier that if we knew the width and height of our SVG scene, we could have set the cx and cy values of the dot to half the width and height of the scene to center the dot. We will have to use similar reasoning here. If we set the top and left values of the viewbox to 0, the dot will still still appear in the top left corner of the scene. If we want to see the dot moved to the right and downward within the scene, we will need the top and left values of the viewbox to be negative. If we want the origin of the SVG space to appear directly in the center of the scene, we will need to set the top and left values of the viewbox to {Code|- height//2} and {Code|- width//2}. In this case, they will both have the value 300.
+In the code we set the viewbox as an attribute of the {Code|svg} element, like this:
=
=| Code
= module Main exposing (main)
@@ -342,240 +393,84 @@ The top and left values of the viewbox are a bit trickier. We saw ealier that if
=
=
= main =
- Element.layout
+ [ Svg.circle
+ [ Svg.Attributes.r "10"
+ , Svg.Attributes.cx "0"
+ , Svg.Attributes.cy "0"
+ ]
+ []
+ ]
+ |> Svg.svg
+ [ Svg.Attributes.height "100%"
+ , Svg.Attributes.width "100%"
+ , Svg.Attributes.style "background: pink"
+ , Svg.Attributes.viewBox "-300 -300 600 600"
+ ]
+ |> Element.html
+ |> Element.layout
= [ Element.width Element.fill
= , Element.height Element.fill
= ]
- (Element.html
- (Svg.svg
- [ Svg.Attributes.viewBox "-300 -300 600 600" ]
- [ Svg.circle [ Svg.Attributes.r "10" ] [] ]
- )
- )
=
-Refresh the broswer. We should now see the dot in the center of the screen.
+There are only two changes here. First we reset the {Code|cx} and {Code|cy} attributes of the {Code|circle} back to {Code|"0"} (on lines 11 and 12). We want the dot to be back at origin. Then we set up the position of the viewbox on line 20. Try experimenting with different values. If you set them right and refresh the broswer the dot should be in the center of the screen.
=
=| Window
= | DotWithViewBox
- background=none
+ background=pink
= fill=black
= viewBox=-300 -300 600 600
=
+You can also remove line 19 to remove the pink background, or set it to some other color.
=
-Now that we've centered our dot, we can customize it by giving it a color! Let's take a deeper look at how we give attributes to an SVG element.
-
-We've already seen a few example of SVG attributes. The radius of our dot ({Code|circle}) is an attribute.
-
-| Code
- Svg.circle [ Svg.Attributes.r "10" ] []
-
-Try giving it a new value to see your dot grow or shrink.
-
-Our viewBox is also an attribute. It's an attribute of the {Code|svg} element.
-
-| Code
- Svg.svg
- [ Svg.Attributes.viewBox "-300 -300 600 600" ]
- [ Svg.circle [ Svg.Attributes.r "10" ] [] ]
-
-Again, try giving it new values. As we saw earlier, the first two values (which represent the left and top of the viewBox) must be equal to the negative of the half of the last two values (which correspond to width and height). Try other combinations of values to adjust the placement of the dot. If the behavior of the viewBox is difficult to understand, refer again to the interactive tutorial above.
-
-To color our dot, we'll have to give it a second attribute. Many SVG elements take a {Code|List} of attributes. A {Code|List} is a very important concept in software development. {Code|Lists} allow us to group an arbitary number of similar values. In this case, we'll be using a {Code|List} to group {Code|Attribute a} values. Whenever you see {Code|[...]} in an Elm source code file, you are dealing with a list.
-
-Before we give a color to our dot, try to identify all the lists in the code we're writen already. There are 5. Hint: It is possible for a list to have only 1 or 0 items ({Code|[]}).
-
-| Code
- Svg.circle [ Svg.Attributes.r "10" ] []
-
-When we take a look at the code responsible for creating our dot, we see that it is followed by two lists. We'll only need to worry about the first one at the moment. As we've discussed, the first list already contains the {Code|r} attribute of the circle, representing the radius. We'll need to add a second attribute to the circle. We use a comma ({Code|,}) to seperate elements in a list.
-
-| Code
- Svg.circle [ Svg.Attributes.r "10", ... ] []
+| Header
+ Color
=
-To color our circle, we'll use the {Code|fill} attribute.
+Speaking of colors, now that we've centered our dot, it's time to give it a color! We do it by adding an {Code|fill} attribute to the {Code|circle} element, like that:
=
=| Code
- Svg.circle
- [ Svg.Attributes.r "10"
- , Svg.Attributes.fill "blue"
- ]
- []
-
-You should see this result:
-
-| Window
- | DotWithViewBox
- background=none
- fill=blue
- viewBox=-300 -300 600 600
-
+ module Main exposing (main)
=
-Let's use our new understanding of lists to do something a bit more interesting. Let's draw multiple dots!
+ import Element
+ import Svg
+ import Svg.Attributes
=
-We can start by taking a look at our {Code|main} value:
=
-| Code
= main =
- Element.layout
+ [ Svg.circle
+ [ Svg.Attributes.r "10"
+ , Svg.Attributes.cx "0"
+ , Svg.Attributes.cy "0"
+ , Svg.Attributes.fill "skyblue"
+ ]
+ []
+ ]
+ |> Svg.svg
+ [ Svg.Attributes.height "100%"
+ , Svg.Attributes.width "100%"
+ , Svg.Attributes.viewBox "-300 -300 600 600"
+ ]
+ |> Element.html
+ |> Element.layout
= [ Element.width Element.fill
= , Element.height Element.fill
= ]
- (Element.html
- (Svg.svg
- [ Svg.Attributes.viewBox "-500 -500 1000 1000"
- ]
- [ Svg.circle
- [ Svg.Attributes.r "10"
- , Svg.Attributes.fill "blue"
- ]
- []
- ]
- )
- )
-
-Where is our list of circles? To find it, first find the circle.
-
-| Code
- Svg.circle
- [ Svg.Attributes.r "10"
- , Svg.Attributes.fill "blue"
- ]
- []
=
-Next, identify the first open square bracket {Code|[} before the circle.
-
-| Code
- [ Svg.circle
- [ Svg.Attributes.r "10"
- , Svg.Attributes.fill "blue"
- ]
- []
- ...
-
-This is where the list begins.
-
-Finally, identify the matching closing brakcet {Code|]}. This is a bit tricky because there are two lists within this list. The first is the list of attributes to the circle we saw earlier. The second is an empty list. We'll see exactly what they do in a moment. Ignore both of these for now. The matching closing bracket should be on the same indentation level as the opening bracket. Your text editor should be able to help you here. When you move your cursor before the opening bracket, the closing bracket should be highlighted.
-
-| Code
- [ Svg.circle
- [ Svg.Attributes.r "10"
- , Svg.Attributes.fill "blue"
- ]
- []
- ]
-
-Right now, our list of circles has one value. We want to give it a second value. The second value will look identical to the first. What is the first value?
-
-| Code
- Svg.circle
- [ Svg.Attributes.r "10"
- , Svg.Attributes.fill "blue"
- ]
- []
-
-It might not seem obvious at first that this is all one value. But it is. {Code|Svg.circle} takes two lists as arguments. We'll see what an argument is later. For now, we will need to understand the difference between an attribute and a child.
-
-Imagine a box of colored easter eggs. One egg is painted yellow, another red, another blue. Moreover, they don't all come from the same bird. One is a small robin's egg. Another is a medium chicken egg. Another is a huge ostrich egg.
-
-Some of the elements in our image are 'things'. A box is a thing. Each egg is also a thing. Some of the elements in our image are not things. Blueness is not a thing. Medium-sizeness is not a thing. You can see blueness. You can see medium-sizeness. But they are not things. They are attributes of a thing. A thing can be blue. It can be medium-sized.
-
-It is a convention in many programming languages, including Elm and SVG, to distinguish between the attributes and children of an element. If we wanted to represent our box in SVG, we would give our box several attributes. It would have a color (maybe brown). It would have a width, length, depth. We would also give it several children. Each of the eggs within the box would be a child of the box. A box with 6 eggs would have 6 children. Each of these children (the eggs) would have different attributes: blue, red, medium-sized, etc. The children of an element are the 'things' it contains. The attributes of a thing are the elements that describe it.
-
-The first list following {Code|Svg.circle} is:
-
-| Code
- [ Svg.Attributes.r "10"
- , Svg.Attributes.fill "blue"
- ]
-
-This is a list of attributes. A radius is not a 'thing'. A fill color is not a 'thing'. They are elements that describe a thing. They describe our circle.
-
-The second list is:
-
-| Code
- []
-
-It is an empty list. Nevertheless, it is a list of children. Our circles don't have any children.
-
-Is the circle itself an attribute or a thing?
-
-From Elm's perspective, this entire code block is a circle:
-
-| Code
- Svg.circle
- [ Svg.Attributes.r "10"
- , Svg.Attributes.fill "blue"
- ]
- []
-
-This is not a circle:
-
-| Code
- Svg.circle
- [ Svg.Attributes.r "10"
- , Svg.Attributes.fill "blue"
- ]
-
-Nor is this:
-
-| Code
- Svg.circle
-
-To create a list with two circles, we'll want to duplicate the properly formed circle value. You can give the second dot a different color from the first.
-
-| Code
- [ Svg.circle
- [ Svg.Attributes.r "10"
- , Svg.Attributes.fill "blue"
- ]
- []
- , Svg.circle
- [ Svg.Attributes.r "10"
- , Svg.Attributes.fill "red"
- ]
- []
- ]
-
-Make add the second circle to the list of circles in your source code and refresh the page. What do you see?
+The only change is on line 13. Reload the browser and see this:
=
=| Window
- | TwoDots
+ | DotWithViewBox
= background=none
+ fill=skyblue
= viewBox=-300 -300 600 600
- firstFill=blue
- secondFill=red
- firstCX=0
- secondCX=0
-
-Only one dot! We see the second one, but what happened to the first dot? It's under the second... Both are at the origin (0, 0), so they are both in the same spot. If we want to see both dots, we will have to change their positions. To do this we'll have to use their {Code|cx} or {Code|cy} values.
=
-| Code
- [ Svg.circle
- [ Svg.Attributes.r "10"
- , Svg.Attributes.cx "-10"
- , Svg.Attributes.fill "blue"
- ]
- []
- , Svg.circle
- [ Svg.Attributes.r "10"
- , Svg.Attributes.cx "10"
- , Svg.Attributes.fill "red"
- ]
- []
- ]
+| Emphasize
+ Wow!
=
-| Window
- | TwoDots
- background=none
- viewBox=-300 -300 600 600
- firstFill=blue
- secondFill=red
- firstCX=-100
- secondCX=100
=
-As an exercise, try adding an {Code|Svg.Attribute.cy} attribute to the circles to adjust their vertical position.
+That's exactly what we were trying to achieve. Scroll up to the problem section to compare.
=
-For a more advanced exercise, try adding a new circle. Why not draw 5 circles on the screen? Give them each a unique color and position.
+| Emphasize
+ Congratulations!
=
=| Emphasize
= {Icon|name=award}index 1ab9957..5780836 100644
--- a/content/day-3.txt
+++ b/content/day-3.txt
@@ -4,9 +4,8 @@
=| Emphasize
= Connecting the Dots
=
-
=| Note
- Today we are going to lear about
+ So far we have been writing the code of our program without thinking too much about about what it means. Today we are going to learn about
=
= | List
= - Values and names
@@ -15,10 +14,400 @@
= - Types
= - SVG gradients
=
-| Header
- The Problem
+Before we begin, let's reflect on the following.
+
+| Emphasize
+ Every Elm program is composed of three basic building blocks: {Code|values}, {Code|names} and {Code|types}.
+
+For efficiency we are going to play with these concepts in REPL (the Read - Evaluate - Print Loop). Go to the terminal. If you have Elm reactor running, then stop it with {Key|ctrl} + {Key|c}. Then start the REPL by entering
+
+| Terminal
+ elm repl
+
+It should show something like this
+
+| Terminal
+ ---- Elm 0.19.0 ----------------------------------------------------------------
+ Read <https://elm-lang.org/0.19.0/repl> to learn more: exit, help, imports, etc.
+ --------------------------------------------------------------------------------
+ >
+
+Here you can enter snippets of Elm code and see it evaluated.
+
+| Note
+ To /evaluate/ means to compute the value of a given expression, e.g. expression {Code|2 + 2} has a value of {Code|4}. The process of figuring it out is called evaluation. That's what computer programs do!
+
+Let's start with values. The simplest way to create a value is to literally type it in our code. Try it (type things after {Code|>} and expect to see what follows):
+
+| Terminal
+ > 2
+ 2 : number
+ > "Hello"
+ "Hello!" : String
+ >
+
+These expressions ({Code|2} and {Code|"Hello!"}) are called /literals/, because their values are literally what they look like. The value of {Code|2} is simply {Code|2}! When you enter them, the REPL evaluates and prints their value (which in this case is very easy) and type (the text after {Code|:}). We will get to types later.
+
+Some value literals are simple, like numbers and strings. On day 1 we discussed lists. You can also type them literally, like this:
+
+| Terminal
+ > [ 1, 2, 5, 77, 2 ]
+ [1,2,5,77.6,2] : List number
+
+First notice that a list literal starts with {Code|[} and ends with {Code|]}. Inside there are values called /elements of the list/. Each element is separated from others with a {Code|,} (comma). Then notice that each element is a value on it's own, but the whole list is also a value.
+
+| Note
+ It can be called a /composite value/. It's like the box of eggs. Each egg is a thing, but the box of eggs is also a thing (composed of the box and the eggs).
+
+List can be empty:
+
+| Terminal
+ > []
+ [] : List a
+
+or contain only one element:
+
+| Terminal
+ > [ "Hello" ]
+ ["Hello"] : List String
+
+| Note
+ Notice that a list with one element is not the same as the element itself, just like the box with one egg is not the same as an egg alone.
+
+Now let's focus our attention to *names*. You can give any value a name.
+
+| Terminal
+ > kittens = 2
+ 2 : number
+ > greeting = "Hello!"
+ "Hello!" : String
+ >
+
+From now on you can refer to this value either literally or by it's name:
+
+| Terminal
+ > kittens
+ 2 : number
+ > 2
+ 2 : number
+
+The effect will be exactly the same - you will get a value back. So we see that there are at least two ways of getting values: by literally expressing them or refering to them by names given to them previously. In a composite value of a list, you can mix the two ways together:
+
+| Terminal
+ > [ 1, kittens, 3 ]
+ [1,2,3] : List number
+
+A function is a special kind of a value. Let's create a named function like this:
+
+| Terminal
+ > fun something = something ++ " is fun!"
+ <function> : String -> String
+
+Here we gave a name {Code|fun} to a function, that given {Code|something} will produce a string saying that this {Code|something} is fun! Let's see how you can use it:
+
+| Terminal
+ > fun "Learning Elm"
+ "Learning Elm is fun!" : String
+
+Above we applied the function (by calling it's name: {Code|fun}) to a literal value: {Code|"Learning Elm"}. In return the function produced another value: {Code|"Learning Elm is fun!"}. So a function takes a value and gives a value. But function itself is also a value.
+
+| Note
+ Imagine a machine that paints eggs. You put an egg into the machine and on the other side you get a painted egg. The egg is a thing and the painted egg is also a thing. But so is the machine!
+
+Now we see that we can get a value in three ways: (1) by literally expressing it, (2) by calling it's name and (3) by applying a function to another value.
+
+Not every function can be applied to any value. You can add two numbers (like {Code|2} and {Code|5} in {Code|2 + 5}), but you can't a number to a string ({Code|2 + "Hello!"} - try it and see an error!). The system that governs what function can be applied to what value is called a *type system* and it plays important role in Elm programming.
+
+| Note
+ Again, imagine a machine that paints eggs. If you try to put a carrot in that machine, the dumb machine would make a mess of your carrot and probably jam. But a smart machine would sound a low pitch buzz and simply refuse to take the carrot. That way a smart machine avoids making a mess and possibly damaging itself. The Elm's type system is what makes your functions smart. It stops you from putting a carrot into the egg painting machine!
+
+Every value in Elm has a type. You can see the type in the REPL - after evaluating the expression it shows it's value and the type, like here:
+
+| Terminal
+ > kittens
+ 2 : number
+
+Above {Code|2} is the value and `number` is the type. Similar here:
+
+| Terminal
+ > fun "Learning Elm"
+ "Learning Elm is fun!" : String
+
+The value is {Code|"Learning Elm is fun!"} and the type is {Code|String}.
+
+Composite values like lists have composite types. For example here the type is {Code|List String}.
+
+| Terminal
+ > [ "Hello", "Good bye" ]
+ ["Hello","Good bye"] : List String
+
+This means that it's a list of strings. That means that all the elements of this list are of type {Code|String}. When talking about lists you have to tell that it's a list and what is the type of it's element. Only this can be considered a fully specified type. Compare:
+
+| Terminal
+ > [ 1, 2, 3 ]
+ [1,2,3] : List number
+
+This is a list of numbers. It's easy to see - all the elements are numbers, just as previously they were strings. All the elements of a list must be of the same type. If you try mixing the types, Elm will refuse to build your program. Try:
+
+| Terminal
+ > [ 1, 2, 3, "Hello" ]
+ -- TYPE MISMATCH ----------------------------------------------------------- elm
+
+ The 4th element of this list does not match all the previous elements:
+
+ 7| [ 1, 2, 3, "Hello" ]
+ ^^^^^^^
+ The 4th element is a string of type:
+
+ String
+
+ But all the previous elements in the list are:
+
+ number
+
+ Hint: Everything in the list needs to be the same type of value. This way you
+ never run into unexpected values partway through. To mix different types in a
+ single list, create a "union type" as described in:
+ <http://guide.elm-lang.org/types/union_types.html>
+
+
+What's the type of an empty list? Let's see:
+
+| Terminal
+ > []
+ [] : List a
+
+A list of {Code|a}. Obviously {Code|a} is not a type. This means that the type of the elements cannot be determine yet. This is signaled by the lowercase term where the type should be: {Code|a} instead of the capitalized {Code|String}.
+
+| Note
+ Notice that the {Code|number} (type of value {Code|15} for example} is also lowercase. That's because it's also not a concrete type! Elm has two concrete types for numbers: {Code|Int} that can represent an integer number (1, 2, 3, 0, -122 etc.) and {Code|Float} that can represent a number with a fraction, (like 1.2, -3.5, 44.2). Because fraction part can be 0 (e.g. 1.0, 2.0, 0.0, -122.0 - so called whole numbers), it's not possible to tell if {Code|2} is an {Code|Int} or a {Code|Float}. We will see some of the implications of this later.
+
+Above I said that every value has a type and also that a function is a value on it's own. Let's try the following in REPL:
+
+| Terminal
+ > fun
+ <function> : String -> String
+
+We have asked the REPL to evaluate the value referenced by name {Code|fun}. In response the value is display as {Code|<function>}. That's because there is no simple way to represent the value of a function as text. Let's focus our attention on the type (part after the colon {Code|:}). It is {Code|String -> String}. This means that the type is a function (we recognize it by the arrow symbol). What's before the arrow is a type of the value that goes into the function (called the /argument of the function/). The type of the argument is {Code|String}. That shouldn't be a surprise. Remember how we have used the function before to produce the value:
+
+| Terminal
+ > fun "Learning Elm"
+ "Learning Elm is fun!" : String
+
+The part expressed as {Code|"Learning Elm"} is a {Code|String} (we recognize it by the quotation around it) and that's exactly what the function needs.
+
+The part after the arrow symbol is the type that the function returns. It is also {Code|String}. That also should not surprise us, since {Code|"Learning Elm is fun!"} is also a {Code|String}.
+
+Not every function returns the same type as it takes. For example function {Code|String.fromInt} takes a value of type {Code|Int} and returns a value of type {Code|String}. It's type is therefor {Code|Int -> String}. We read it as: /type of String.fromInt is a function from Int to String/. Let's see it in action:
+
+| Terminal
+ > String.fromInt 10
+ "10" : String
+
+That's right! The value {Code|10} is an {Code|Int}, the value {Code|"10"} is a {Code|String}.
+
+| Terminal
+ > String.fromInt
+ <function> : Int -> String
+
+But what if we want to say that a certain number (say, {Code|232}) is fun? We have our {Code|fun} function, but it takes a {Code|String} and what we have is an {Code|Int}. We can use the very same {Code|String.fromInt} to first convert it into the {Code|String} and then pass the value to the {Code|fun} function, like this:
+
+| Terminal
+ > fun (String.fromInt 232)
+ "232 is fun!" : String
+
+We had to put the {Code|String.fromInt 232} expression in parentheses, so that Elm understands that value {Code|232} is to be passed to {Code|String.fromInt} function, instead of the {Code|String.fromInt} being passed to {Code|fun} (which would not work anyway, since {Code|fun} takes values of type {Code|String}, not {Code|Int -> String}).
+
+Alternatively we can use the pipe operator which looks like this: {Code|\|>}. It has the advantage that the code that the transformations that happen first are on the left side and ones that happen later on the right, so we can read our code left to right instead of inside out. Here is how it would look like:
+
+| Terminal
+ > 232 |> String.fromInt |> fun
+ "232 is fun!" : String
+
+Now it's more apparent that the value {Code|232} is passed to the function {Code|String.fromInt}, and whatever comes on the other end is passed to the {Code|fun} function. We don't need parentheses in this case.
+
+| Note
+ TODO: But where does the functions come from? Modules.
+
+
+
+
+
=
=| Note
= *We are still working on this content.*
=
= Stay tuned {Icon|name=radio}
+
+Let's take a deeper look at how we give attributes to an SVG element.
+
+We've already seen a few example of SVG attributes. The radius of our dot ({Code|circle}) is an attribute.
+
+| Code
+ Svg.circle [ Svg.Attributes.r "10" ] []
+
+Try giving it a new value to see your dot grow or shrink.
+
+Our viewBox is also an attribute. It's an attribute of the {Code|svg} element.
+
+| Code
+ Svg.svg
+ [ Svg.Attributes.viewBox "-300 -300 600 600" ]
+ [ Svg.circle [ Svg.Attributes.r "10" ] [] ]
+
+Again, try giving it new values. As we saw earlier, the first two values (which represent the left and top of the viewBox) must be equal to the negative of the half of the last two values (which correspond to width and height). Try other combinations of values to adjust the placement of the dot. If the behavior of the viewBox is difficult to understand, refer again to the interactive tutorial above.
+
+To color our dot, we'll have to give it a second attribute. Many SVG elements take a {Code|List} of attributes. A {Code|List} is a very important concept in software development. {Code|Lists} allow us to group an arbitary number of similar values. In this case, we'll be using a {Code|List} to group {Code|Attribute a} values. Whenever you see {Code|[...]} in an Elm source code file, you are dealing with a list.
+
+Before we give a color to our dot, try to identify all the lists in the code we're writen already. There are 5. Hint: It is possible for a list to have only 1 or 0 items ({Code|[]}).
+
+| Code
+ Svg.circle [ Svg.Attributes.r "10" ] []
+
+When we take a look at the code responsible for creating our dot, we see that it is followed by two lists. We'll only need to worry about the first one at the moment. As we've discussed, the first list already contains the {Code|r} attribute of the circle, representing the radius. We'll need to add a second attribute to the circle. We use a comma ({Code|,}) to separate elements in a list.
+
+| Code
+ Svg.circle [ Svg.Attributes.r "10", ... ] []
+
+To color our circle, we'll use the {Code|fill} attribute.
+
+| Code
+ Svg.circle
+ [ Svg.Attributes.r "10"
+ , Svg.Attributes.fill "blue"
+ ]
+ []
+
+You should see this result:
+
+| Window
+ | DotWithViewBox
+ background=none
+ fill=blue
+ viewBox=-300 -300 600 600
+
+Let's use our new understanding of lists to do something a bit more interesting. Let's draw multiple dots!
+
+We can start by taking a look at our {Code|main} value:
+
+| Code
+ main =
+ Element.layout
+ [ Element.width Element.fill
+ , Element.height Element.fill
+ ]
+ (Element.html
+ (Svg.svg
+ [ Svg.Attributes.viewBox "-500 -500 1000 1000"
+ ]
+ [ Svg.circle
+ [ Svg.Attributes.r "10"
+ , Svg.Attributes.fill "blue"
+ ]
+ []
+ ]
+ )
+ )
+
+Where is our list of circles? To find it, first find the circle.
+
+| Code
+ Svg.circle
+ [ Svg.Attributes.r "10"
+ , Svg.Attributes.fill "blue"
+ ]
+ []
+
+Next, identify the first open square bracket {Code|[} before the circle.
+
+| Code
+ [ Svg.circle
+ [ Svg.Attributes.r "10"
+ , Svg.Attributes.fill "blue"
+ ]
+ []
+ ...
+
+This is where the list begins.
+
+Finally, identify the matching closing brakcet {Code|]}. This is a bit tricky because there are two lists within this list. The first is the list of attributes to the circle we saw earlier. The second is an empty list. We'll see exactly what they do in a moment. Ignore both of these for now. The matching closing bracket should be on the same indentation level as the opening bracket. Your text editor should be able to help you here. When you move your cursor before the opening bracket, the closing bracket should be highlighted.
+
+| Code
+ [ Svg.circle
+ [ Svg.Attributes.r "10"
+ , Svg.Attributes.fill "blue"
+ ]
+ []
+ ]
+
+Right now, our list of circles has one value. We want to give it a second value. The second value will look identical to the first. What is the first value?
+
+| Code
+ Svg.circle
+ [ Svg.Attributes.r "10"
+ , Svg.Attributes.fill "blue"
+ ]
+ []
+
+It might not seem obvious at first that this is all one value. But it is. {Code|Svg.circle} takes two lists as arguments. We'll see what an argument is later. For now, we will need to understand the difference between an attribute and a child.
+
+Imagine a box of colored easter eggs. One egg is painted yellow, another red, another blue. Moreover, they don't all come from the same bird. One is a small robin's egg. Another is a medium chicken egg. Another is a huge ostrich egg.
+
+Some of the elements in our image are 'things'. A box is a thing. Each egg is also a thing. Some of the elements in our image are not things. Blueness is not a thing. Medium-sizeness is not a thing. You can see blueness. You can see medium-sizeness. But they are not things. They are attributes of a thing. A thing can be blue. It can be medium-sized.
+
+It is a convention in many programming languages, including Elm and SVG, to distinguish between the attributes and children of an element. If we wanted to represent our box in SVG, we would give our box several attributes. It would have a color (maybe brown). It would have a width, length, depth. We would also give it several children. Each of the eggs within the box would be a child of the box. A box with 6 eggs would have 6 children. Each of these children (the eggs) would have different attributes: blue, red, medium-sized, etc. The children of an element are the 'things' it contains. The attributes of a thing are the elements that describe it.
+
+The first list following {Code|Svg.circle} is:
+
+| Code
+ [ Svg.Attributes.r "10"
+ , Svg.Attributes.fill "blue"
+ ]
+
+This is a list of attributes. A radius is not a 'thing'. A fill color is not a 'thing'. They are elements that describe a thing. They describe our circle.
+
+The second list is:
+
+| Code
+ []
+
+It is an empty list. Nevertheless, it is a list of children. Our circles don't have any children.
+
+Is the circle itself an attribute or a thing?
+
+From Elm's perspective, this entire code block is a circle:
+
+| Code
+ Svg.circle
+ [ Svg.Attributes.r "10"
+ , Svg.Attributes.fill "blue"
+ ]
+ []
+
+This is not a circle:
+
+| Code
+ Svg.circle
+ [ Svg.Attributes.r "10"
+ , Svg.Attributes.fill "blue"
+ ]
+
+Nor is this:
+
+| Code
+ Svg.circle
+
+To create a list with two circles, we'll want to duplicate the properly formed circle value. You can give the second dot a different color from the first.
+
+| Code
+ [ Svg.circle
+ [ Svg.Attributes.r "10"
+ , Svg.Attributes.fill "blue"
+ ]
+ []
+ , Svg.circle
+ [ Svg.Attributes.r "10"
+ , Svg.Attributes.fill "red"
+ ]
+ []
+ ]index c2832b6..f181d18 100644
--- a/content/index.txt
+++ b/content/index.txt
@@ -13,7 +13,7 @@ Below is material through which we will be going during the workshop. Only the f
=
=| List
= # {Link|Before the course begins|url=/preparation.html}
- # {Link|Day 1 - Let's Make some Dots|url=/day-1.html}
+ # {Link|Day 1 - Let's Make a Dot|url=/day-1.html}
= # {Link|Day 2 - Let's Place the Dots in a Circle|url=/day-2.html}
= # {Link|Day 3 - Connecting the Dots|url=/day-3.html}
= # {Link|Day 4 - Let's Make a Tree|url=/day-4.html}Temporary fix for #9
In capture.coffee preplace all p tags with div.
Upon closer inspection it seems that p tag is never targeted in css sheet. Only classes are targeted. so replacing it with div doesn't affect any styling.
I consider this a temporary solution. Permanent one probably requires changes to github.com/mdgriffith/elm-ui
index bdbd6c6..d630128 100644
--- a/src/capture.coffee
+++ b/src/capture.coffee
@@ -38,13 +38,16 @@ do () =>
= await page.goto url, waitUntil: "networkidle2"
=
= html =
- (await page.content ``).replace "</body>", """
- <script src="/assets/index.js"></script>
- <script>
- Elm.Main.init()
- </script>
- </body>
- """
+ (await page.content ``)
+ .replace /<p\b/gi, "<div"
+ .replace /<\/p\b/gi, "</div"
+ .replace "</body>", """
+ <script src="/assets/index.js"></script>
+ <script>
+ Elm.Main.init()
+ </script>
+ </body>
+ """
=
= await new Promise (resolve, reject) ->
= FS.writeFile path, html, (error) ->Use external stylesheet to load Google fonts to avoid FOAC
Use Montserrat font for all texts except Monospace, Code and Terminal.
Justify text in paragraphs.
index fb384fe..1f43711 100644
--- a/container.html
+++ b/container.html
@@ -3,10 +3,13 @@
= <head>
= <meta charset="utf-8">
= <title>Software Garden</title>
+ <style>
+ @import url('https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Source+Code+Pro:300,400,700');
+ </style>
= </head>
= <body>
= <!-- <div id="app-container"></div> -->
- <script src="/assets/index.js"></script>
+ <script src="/built/index.js"></script>
= <script>
= Elm.Main.init()
= </script>index 1330ef5..b3c69f2 100755
--- a/scripts/build
+++ b/scripts/build
@@ -14,7 +14,7 @@ npx elm make src/Main.elm --output built/index.js
=
=cp -r content/ public/content/
=cp -r assets/ public/assets/
-cp -r built/* public/assets/
+cp -r built/ public/built/
=cp -r .well-known/ public/.well-known/
=
=index 987f698..d3e4036 100755
--- a/scripts/develop
+++ b/scripts/develop
@@ -9,4 +9,10 @@ do
= npx elm make --output "public/${example}.html" "${example}"
=done
=
-npx elm-live src/Main.elm --pushstate --port 8001 -- --debug
+npx elm-live src/Main.elm \
+ --pushstate \
+ --start-page container.html \
+ --port 8001 \
+ -- \
+ --output built/index.js \
+ --debugindex 782b5a7..64059a0 100644
--- a/src/Main.elm
+++ b/src/Main.elm
@@ -302,6 +302,7 @@ view model =
= [ -- Below is a hack that enables static site generation
= Element.htmlAttribute (Html.Attributes.id "app-container")
= , Element.htmlAttribute (Html.Attributes.lang "en")
+ , Font.family [ Font.typeface "Montserrat", Font.sansSerif ]
= ]
= content.body
= ]index 0a42947..18fef27 100644
--- a/src/Mark/Custom.elm
+++ b/src/Mark/Custom.elm
@@ -55,6 +55,7 @@ paragraph =
= [ Element.paddingXY 0 10
= , Element.spacing 12
= , css "hyphens" "auto"
+ , Font.justify
= ]
= (content model)
= in
@@ -71,7 +72,7 @@ monospace =
= , Font.size 16
= , Font.color colors.maroon
= , Font.family
- [ sourceCodePro
+ [ Font.typeface "Source Code Pro"
= , Font.monospace
= ]
= , Element.scrollbarY
@@ -95,7 +96,7 @@ code =
= , Border.color colors.charcoal
= , css "page-break-inside" "avoid"
= , Font.family
- [ sourceCodePro
+ [ Font.typeface "Source Code Pro"
= , Font.monospace
= ]
= , css "page-break-inside" "avoid"
@@ -116,7 +117,7 @@ code =
= [ Element.width Element.fill
= , Font.size 16
= , Font.family
- [ sourceCodePro
+ [ Font.typeface "Source Code Pro"
= , Font.monospace
= ]
= ]
@@ -187,7 +188,7 @@ terminal =
= , Background.color colors.charcoal
= , css "page-break-inside" "avoid"
= , Font.family
- [ sourceCodePro
+ [ Font.typeface "Source Code Pro"
= , Font.monospace
= ]
= , css "page-break-inside" "avoid"
@@ -207,7 +208,7 @@ terminal =
= [ Element.width Element.fill
= , Font.size 16
= , Font.family
- [ sourceCodePro
+ [ Font.typeface "Source Code Pro"
= , Font.monospace
= ]
= ]
@@ -228,7 +229,7 @@ terminal =
= }
= , Font.size 14
= , Font.family
- [ sourceCodePro
+ [ Font.typeface "Source Code Pro"
= , Font.monospace
= ]
= , Font.color colors.gray
@@ -312,9 +313,10 @@ note =
=emphasize : Mark.Block (model -> Element msg)
=emphasize =
= let
- render element model =
+ render elements model =
= model
- |> element
+ |> elements
+ |> Element.paragraph []
= |> Element.el
= [ Element.spacing 30
= , Font.bold
@@ -326,7 +328,7 @@ emphasize =
= in
= Mark.block "Emphasize"
= render
- paragraph
+ text
=
=
=image : Mark.Block (model -> Element msg)
@@ -375,7 +377,7 @@ text =
= , drop
= ]
= , code =
- [ Font.family [ sourceCodePro, Font.monospace ]
+ [ Font.family [ Font.typeface "Source Code Pro", Font.monospace ]
= , css "font-size" "0.8em"
= , Font.color colors.maroon
= , Element.paddingEach
@@ -500,14 +502,6 @@ css property value =
= )
=
=
-sourceCodePro : Font.Font
-sourceCodePro =
- Font.external
- { url = "https://fonts.googleapis.com/css?family=Source+Code+Pro:200,400,700&subset=latin-ext"
- , name = "Source Code Pro"
- }
-
-
=colors =
= { maroon = Element.rgb 0.7 0 0
= , gray = Element.rgb 0.8 0.8 0.8index bdbd6c6..ae3b622 100644
--- a/src/capture.coffee
+++ b/src/capture.coffee
@@ -39,7 +39,7 @@ do () =>
=
= html =
= (await page.content ``).replace "</body>", """
- <script src="/assets/index.js"></script>
+ <script src="/built/index.js"></script>
= <script>
= Elm.Main.init()
= </script>Upgrade feathericons/elm-feather to 1.3.0
index b321d9d..439720e 100644
--- a/elm.json
+++ b/elm.json
@@ -19,7 +19,7 @@
= "elm-community/maybe-extra": "5.0.0",
= "elm-community/result-extra": "2.2.1",
= "elm-explorations/markdown": "1.0.0",
- "feathericons/elm-feather": "1.2.0",
+ "feathericons/elm-feather": "1.3.0",
= "ianmackenzie/elm-geometry": "1.2.1",
= "ianmackenzie/elm-geometry-svg": "1.0.2",
= "mdgriffith/elm-markup": "2.0.5",Correct the vertical alignment of Key, Icon and inline Code
After changing the font they were a little bit off.
index 18fef27..b49f7d3 100644
--- a/src/Mark/Custom.elm
+++ b/src/Mark/Custom.elm
@@ -377,8 +377,10 @@ text =
= , drop
= ]
= , code =
- [ Font.family [ Font.typeface "Source Code Pro", Font.monospace ]
- , css "font-size" "0.8em"
+ [ Font.family
+ [ Font.typeface "Source Code Pro"
+ , Font.monospace
+ ]
= , Font.color colors.maroon
= , Element.paddingEach
= { top = 3
@@ -409,8 +411,7 @@ icon =
= )
= |> Element.el
= [ Element.padding 4
-
- -- , Element.width (Element.px 12)
+ , css "vertical-align" "middle"
= ]
= )
= |> Mark.inlineString "name"
@@ -427,8 +428,8 @@ key =
= )
= |> Element.row
= [ Element.paddingEach
- { top = 6
- , bottom = 4
+ { top = 7
+ , bottom = 7
= , right = 8
= , left = 8
= }
@@ -436,7 +437,7 @@ key =
= , Border.rounded 3
= , Font.variant Font.smallCaps
= , Font.size 12
- , Element.moveUp 2
+ , Element.moveUp 4
= , Font.bold
= , Background.color colors.charcoal
= , Font.color colors.white