Commits: 8
More progress on Day 1
index 8359570..beac6da 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,4 @@
=/public/
=/built/
=/node_modules/
+/src/Playground.elmindex 83cc9bf..9cf6974 100644
--- a/content/day-1.txt
+++ b/content/day-1.txt
@@ -85,7 +85,11 @@ Note that the same address was printed by the Elm reactor.
=
=We want to display a dot at the center of the screen, like this:
=
-| DotAtTheCenterOfTheScreen
+| DotWithViewBox
+ background=none
+ fill=black
+ viewBox=-300 -300 600 600
+
=
=Below is the complete code to draw a dot like this, but don't type it in your editor yet! We are going to recreate it together, step by step.
=
@@ -128,9 +132,8 @@ Its all about drawing shapes on the screen. Let's install an Elm Package to help
=Now that we have {Code|elm//svg} installed, we can write the code that will draw our dot:
=
=| Code
- module Simplest exposing (main)
+ module Main exposing (main)
=
- import Element
= import Svg
= import Svg.Attributes
=
@@ -138,9 +141,7 @@ Now that we have {Code|elm//svg} installed, we can write the code that will draw
= main =
= Svg.svg []
= [ Svg.circle
- [ Svg.Attributes.r "10"
- ]
- []
+ [ Svg.Attributes.r "10" ] []
= ]
=
=Start the reactor again:
@@ -153,7 +154,7 @@ Start the reactor again:
=
=Reload the browser. You should see something like this:
=
-| Simplest
+| Dot
= background=none
= fill=black
=
@@ -163,15 +164,22 @@ There are two reasons. First, because the center of the dot is at a point called
=
=Second, it's because the SVG space doesn't fill the browser window. Don't believe me? We can see that the space doesn't fill the window by changing the background color of the SVG element.
=
-| Note
- Code sample for simplest with pink background should be here.
+| Code
+ module Main exposing (main)
=
-When you reload the browser, you'll see this:
+ import Svg
+ import Svg.Attributes
=
-| Note
- Example of simplest with pink background should be here.
=
-| Simplest
+ main =
+ Svg.svg [ Svg.Attributes.style "background: pink" ]
+ [ Svg.circle
+ [ Svg.Attributes.r "10" ] []
+ ]
+
+When you reload the browser, you'll see this:
+
+| Dot
= background=pink
= fill=black
=
@@ -185,11 +193,42 @@ Press {Code|CTRL + C} to stop the elm-reactor, type {Code|elm install mdgriffith
=
=Then in {Code|src//Main.elm} add the necessary import {Code|import Element} and change {Code|Main} to look like this:
=
-Main = * the code of Element.layout should be here*
+| Code
+ module Main exposing (main)
=
-Take a wild guess: what do you think {Code|Element.height Element.fill} and {Code|Element.height Element.width} do?
+ import Element
+ import Svg
+ import Svg.Attributes
=
-Reload the browser to confirm your hypothesis. Voila! The SVG space now fills the screen. It should now be possible to place our dot in the center of the screen. But, how exactly will we achieve it?
+
+ main =
+ 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" ] []
+ ]
+ )
+ )
+
+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.
+
+| DotInElement
+ background=pink
+ fill=black
+ 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?
=
=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 eactly does this mean?
=
@@ -203,7 +242,40 @@ Knowing that the center of the dot is at the {Code|origin} or point {Code|(0,0)}
=
=To change the position of the dot, we can use the {Code|cx} and {Code|cy} attributes of the {Code|circle} element.
=
-* simplest with cx and cy should be here *
+| Code
+ module Main exposing (main)
+
+ import Element
+ import Svg
+ import Svg.Attributes
+
+
+ main =
+ 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"
+ ]
+ []
+ ]
+ )
+ )
+
+| DotInElement
+ background=pink
+ fill=black
+ 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.
=
@@ -213,7 +285,7 @@ We can calculate the position of the circle as:
= 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 its {Code|width} and {Code|height} will {Code|fill} the viewport, whatever its size.
+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).
=☹️
=
=Fortunately, we don't need to know the width and height of the SVG space!
@@ -226,12 +298,37 @@ A videwbox is like a window into an SVG space. It allows us to change the point
=
=| ViewBox
=
-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 1000.
+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.
+
+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.
+
+| Code
+ module Main exposing (main)
+
+ import Element
+ import Svg
+ import Svg.Attributes
+
=
-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 500.
+ main =
+ 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.
=
+| DotWithViewBox
+ background=none
+ fill=black
+ viewBox=-300 -300 600 600
=
=Give a color to the dot
=similarity index 91%
rename from src/Simplest.elm
rename to src/Dot.elm
index 2e65fa4..0f6eec4 100644
--- a/src/Simplest.elm
+++ b/src/Dot.elm
@@ -1,4 +1,4 @@
-module Simplest exposing (Config, defaults, ui)
+module Dot exposing (Config, defaults, ui)
=
=import Html exposing (Html)
=import Svgdeleted file mode 100644
index 83f0d1d..0000000
--- a/src/DotAtTheCenterOfTheScreen.elm
+++ /dev/null
@@ -1,27 +0,0 @@
-module DotAtTheCenterOfTheScreen exposing (main, ui)
-
-import Element
-import Svg
-import Svg.Attributes
-
-
-main =
- Element.layout
- [ Element.width Element.fill
- , Element.height Element.fill
- ]
- ui
-
-
-ui =
- Svg.svg
- [ Svg.Attributes.viewBox "-100 -100 200 200"
- ]
- [ Svg.circle
- [ Svg.Attributes.r "10"
- , Svg.Attributes.cx "0"
- , Svg.Attributes.cy "0"
- ]
- []
- ]
- |> Element.htmlnew file mode 100644
index 0000000..a3e3397
--- /dev/null
+++ b/src/DotInElement.elm
@@ -0,0 +1,44 @@
+module DotInElement exposing (Config, main, ui)
+
+import Element exposing (Element)
+import Svg
+import Svg.Attributes
+
+
+type alias Config =
+ { background : String
+ , fill : String
+ , cx : String
+ , cy : String
+ }
+
+
+defaults : Config
+defaults =
+ Config "pink" "black" "0" "0"
+
+
+main =
+ Element.layout
+ [ Element.width Element.fill
+ , Element.height Element.fill
+ ]
+ (ui defaults)
+
+
+ui : Config -> Element msg
+ui { background, fill, cx, cy } =
+ Svg.svg
+ [ Svg.Attributes.height "100%"
+ , Svg.Attributes.width "100%"
+ , Svg.Attributes.style <| "background: " ++ background
+ ]
+ [ Svg.circle
+ [ Svg.Attributes.r "10"
+ , Svg.Attributes.fill fill
+ , Svg.Attributes.cx cx
+ , Svg.Attributes.cy cy
+ ]
+ []
+ ]
+ |> Element.htmlsimilarity index 66%
rename from src/CenteredDot.elm
rename to src/DotWithViewBox.elm
index 59fb692..d26753c 100644
--- a/src/CenteredDot.elm
+++ b/src/DotWithViewBox.elm
@@ -1,4 +1,9 @@
-module CenteredDot exposing (main)
+module DotWithViewBox exposing (Config, main, ui)
+
+import Element
+import Svg
+import Svg.Attributes
+
=
={-| This program demonstrates how to center an element within an SVG viewport. The idea is to make sure that (0, 0) point (so called origin) should be in the middle of the ViewBox. We can achive that, by making the top-left corner of the ViewBox same distance from the (0, 0) as the bottom right.
=
@@ -18,23 +23,35 @@ In fact it doesnt matter how wide and high the viewBox is. As long as the top wi
=That's the trick to position an object (like a dot) exactly in the center of the SVG viewport
=
=-}
+type alias Config =
+ { background : String
+ , fill : String
+ , viewBox : String
+ }
=
-import Element
-import Svg
-import Svg.Attributes
+
+defaults : Config
+defaults =
+ Config "white" "black" "-500 -500 1000 1000"
=
=
=main =
+ Element.layout
+ [ Element.width Element.fill
+ , Element.height Element.fill
+ ]
+ (ui defaults)
+
+
+ui { background, fill, viewBox } =
= Svg.svg
- [ Svg.Attributes.style "background: pink"
- , Svg.Attributes.width "300"
- , Svg.Attributes.height "150"
- , Svg.Attributes.viewBox "-100 -100 200 200"
+ [ Svg.Attributes.viewBox viewBox
+ , Svg.Attributes.style <| "background: " ++ background
= ]
= [ Svg.circle
= [ Svg.Attributes.r "10"
- , Svg.Attributes.cx "0"
- , Svg.Attributes.cy "0"
+ , Svg.Attributes.fill fill
= ]
= []
= ]
+ |> Element.htmldeleted file mode 100644
index 56f88d6..0000000
--- a/src/FillTheScreen.elm
+++ /dev/null
@@ -1,28 +0,0 @@
-module FillTheScreen exposing (main, ui)
-
-import Element
-import Svg
-import Svg.Attributes
-
-
-main =
- Element.layout
- [ Element.width Element.fill
- , Element.height Element.fill
- ]
- ui
-
-
-ui =
- Svg.svg
- [ Svg.Attributes.style "background: pink"
- , Svg.Attributes.viewBox "-100 -100 200 200"
- ]
- [ Svg.circle
- [ Svg.Attributes.r "10"
- , Svg.Attributes.cx "0"
- , Svg.Attributes.cy "0"
- ]
- []
- ]
- |> Element.htmlindex 019ba58..87b6396 100644
--- a/src/Main.elm
+++ b/src/Main.elm
@@ -11,10 +11,11 @@ import Browser
=import Browser.Navigation as Navigation
=import BrowserWindow
=import CartesianCoordinates
-import CenteredDot
=import Counter
=import Dict
-import DotAtTheCenterOfTheScreen
+import Dot
+import DotInElement
+import DotWithViewBox
=import Element exposing (Element)
=import Element.Background as Background
=import Element.Border as Border
@@ -22,7 +23,6 @@ import Element.Events
=import Element.Font as Font
=import Element.Input as Input
=import FeatherIcons exposing (icons)
-import FillTheScreen
=import Gradient
=import Html exposing (Html)
=import Html.Attributes
@@ -38,7 +38,6 @@ import PolarCoordinates
=import Result.Extra as Result
=import RosetteTypedTransformations
=import Routes exposing (Route)
-import Simplest
=import Spiral
=import Transformations
=import Tree
@@ -447,10 +446,9 @@ document =
=
= -- Embeded programs
= , counter
- , simplest
- , fillTheScreen
- , dotAtTheCenterOfTheScreen
- , centeredDot
+ , dot
+ , dotInElement
+ , dotWithViewBox
= , line
= , gradient
= , transformations
@@ -484,66 +482,54 @@ document =
= in
= Mark.stub "Counter" render
=
- simplest : Mark.Block (Model -> Element Msg)
- simplest =
+ dot : Mark.Block (Model -> Element Msg)
+ dot =
= let
- render : Simplest.Config -> Model -> Element Msg
+ render : Dot.Config -> Model -> Element Msg
= render config model =
- Simplest.ui config
+ Dot.ui config
= |> Element.html
- |> Element.el []
= |> Element.el
- [ Element.height (Element.px 400)
- , Element.width Element.fill
+ [ Element.width (Element.px 300)
= , Element.padding 5
= ]
- |> BrowserWindow.window []
+ |> BrowserWindow.window [ Element.height (Element.px 300) ]
= in
- Mark.record2 "Simplest"
- Simplest.Config
+ Mark.record2 "Dot"
+ Dot.Config
= (Mark.field "background" Mark.string)
= (Mark.field "fill" Mark.string)
= |> Mark.map render
=
- fillTheScreen : Mark.Block (Model -> Element Msg)
- fillTheScreen =
- let
- render model =
- FillTheScreen.ui
- |> Element.el
- [ Element.height (Element.px 400)
- , Element.width Element.fill
- ]
- |> BrowserWindow.window []
- in
- Mark.stub "FillTheScreen" render
-
- dotAtTheCenterOfTheScreen : Mark.Block (Model -> Element Msg)
- dotAtTheCenterOfTheScreen =
+ dotInElement : Mark.Block (Model -> Element Msg)
+ dotInElement =
= let
- render model =
- DotAtTheCenterOfTheScreen.ui
- |> Element.el
- [ Element.height (Element.px 400)
- , Element.width Element.fill
- ]
- |> BrowserWindow.window []
+ render config model =
+ DotInElement.ui config
+ |> BrowserWindow.window [ Element.height (Element.px 300) ]
= in
- Mark.stub "DotAtTheCenterOfTheScreen" render
+ Mark.record4 "DotInElement"
+ DotInElement.Config
+ (Mark.field "background" Mark.string)
+ (Mark.field "fill" Mark.string)
+ (Mark.field "cx" Mark.string)
+ (Mark.field "cy" Mark.string)
+ |> Mark.map render
=
- centeredDot : Mark.Block (Model -> Element Msg)
- centeredDot =
+ dotWithViewBox : Mark.Block (Model -> Element Msg)
+ dotWithViewBox =
= let
- render model =
- CenteredDot.main
- |> Element.html
- |> Element.el
- [ Element.height (Element.px 400)
- , Element.width Element.fill
- ]
+ render config model =
+ DotWithViewBox.ui config
+ |> Element.el [ Element.height (Element.px 300) ]
= |> BrowserWindow.window []
= in
- Mark.stub "CenteredDot" render
+ Mark.record3 "DotWithViewBox"
+ DotWithViewBox.Config
+ (Mark.field "background" Mark.string)
+ (Mark.field "fill" Mark.string)
+ (Mark.field "viewBox" Mark.string)
+ |> Mark.map render
=
= line : Mark.Block (Model -> Element Msg)
= line =Merge branch 'multiple-pages' into review-day-one
Fix bad imports with merge
index 0f6eec4..5581546 100644
--- a/src/Examples/Dot.elm
+++ b/src/Examples/Dot.elm
@@ -1,4 +1,4 @@
-module Dot exposing (Config, defaults, ui)
+module Examples.Dot exposing (Config, defaults, ui)
=
=import Html exposing (Html)
=import Svgindex a3e3397..ee127b3 100644
--- a/src/Examples/DotInElement.elm
+++ b/src/Examples/DotInElement.elm
@@ -1,4 +1,4 @@
-module DotInElement exposing (Config, main, ui)
+module Examples.DotInElement exposing (Config, main, ui)
=
=import Element exposing (Element)
=import Svgindex d26753c..6f91dc6 100644
--- a/src/Examples/DotWithViewBox.elm
+++ b/src/Examples/DotWithViewBox.elm
@@ -1,4 +1,4 @@
-module DotWithViewBox exposing (Config, main, ui)
+module Examples.DotWithViewBox exposing (Config, main, ui)
=
=import Element
=import Svgindex 6451e35..56ef197 100644
--- a/src/Main.elm
+++ b/src/Main.elm
@@ -10,12 +10,7 @@ import Basics.Extra exposing (curry)
=import Browser
=import Browser.Navigation as Navigation
=import BrowserWindow
-import CartesianCoordinates
-import Counter
=import Dict
-import Dot
-import DotInElement
-import DotWithViewBox
=import Element exposing (Element)
=import Element.Background as Background
=import Element.Border as Border
@@ -23,23 +18,21 @@ import Element.Events
=import Element.Font as Font
=import Element.Input as Input
=import Examples.CartesianCoordinates
-import Examples.CenteredDot
=import Examples.Counter
-import Examples.DotAtTheCenterOfTheScreen
-import Examples.FillTheScreen
+import Examples.Dot as Dot
+import Examples.DotInElement as DotInElement
+import Examples.DotWithViewBox as DotWithViewBox
=import Examples.Gradient
=import Examples.Line
=import Examples.NestedTransformations
=import Examples.PolarCoordinates
=import Examples.RosetteTypedTransformations
-import Examples.Simplest
=import Examples.Spiral
=import Examples.Transformations
=import Examples.TransformationsCircle
=import Examples.Tree
=import Examples.ViewBox
=import FeatherIcons exposing (icons)
-import Gradient
=import Html exposing (Html)
=import Html.Attributes
=import Http
@@ -50,9 +43,7 @@ import Parser
=import Parser.Advanced
=import Result.Extra as Result
=import Routes exposing (Route)
-import Spiral
=import Transformations
-import Tree
=import Url exposing (Url)
=
=Finish day 1
Create multiple dots example
index 9cf6974..b9a8560 100644
--- a/content/day-1.txt
+++ b/content/day-1.txt
@@ -318,8 +318,7 @@ The top and left values of the viewbox are a bit trickier. We saw ealier that if
= (Element.html
= (Svg.svg
= [ Svg.Attributes.viewBox "-300 -300 600 600" ]
- [ Svg.circle [ Svg.Attributes.r "10" ] []
- ]
+ [ Svg.circle [ Svg.Attributes.r "10" ] [] ]
= )
= )
=
@@ -330,29 +329,215 @@ Refresh the broswer. We should now see the dot in the center of the screen.
= fill=black
= viewBox=-300 -300 600 600
=
-Give a color to the dot
=
+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" ] []
=
-| List
- - Explain SVG attributes ( svg elements take a list of attributes)
- - Everyone can pick a color
+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.
=
-Multiple dots
+| 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.
=
-| List
- -> Introduce a problem: We want to have two dots on the screen
- -> Explain a list, and show there is already a list there (with one item)
+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|[]}).
=
-| Header
- Do you see any other lists?
+| 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", ... ] []
+
+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:
+
+| 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"
+ ]
+ []
+ ]
+
+Make add the second circle to the list of circles in your source code and refresh the page. What do you see?
+
+| TwoDots
+ background=none
+ 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"
+ ]
+ []
+ ]
+
+| 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.
=
-| List
- -> Element takes a list of children
- -> Mention , how can we have a list with one or zero elements? Shopping list with one item. Empty spoon drawer( list of spoons)
- -> Give your new dot a color and different coordinates
- -> Show the code for 2 dots
- -> Exercise: make 3 more dots (5 total)
+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.new file mode 100644
index 0000000..b0625da
--- /dev/null
+++ b/src/Examples/MultipleDots.elm
@@ -0,0 +1,41 @@
+module Examples.MultipleDots exposing (Config, main, ui)
+
+import Element
+import Svg exposing (Attribute)
+import Svg.Attributes
+
+
+type alias Config msg =
+ { background : String
+ , viewBox : String
+ , dotsAttributes : List (List (Attribute msg))
+ }
+
+
+defaults : Config msg
+defaults =
+ Config "white" "-500 -500 1000 1000" [ [ Svg.Attributes.r "10" ] ]
+
+
+main =
+ Element.layout
+ [ Element.width Element.fill
+ , Element.height Element.fill
+ ]
+ (ui defaults)
+
+
+ui { background, viewBox, dotsAttributes } =
+ Svg.svg
+ [ Svg.Attributes.viewBox viewBox
+ , Svg.Attributes.style <| "background: " ++ background
+ ]
+ (dotsAttributes
+ |> List.map
+ (\attributes ->
+ Svg.circle
+ attributes
+ []
+ )
+ )
+ |> Element.htmlindex 56ef197..5f855b9 100644
--- a/src/Main.elm
+++ b/src/Main.elm
@@ -24,6 +24,7 @@ import Examples.DotInElement as DotInElement
=import Examples.DotWithViewBox as DotWithViewBox
=import Examples.Gradient
=import Examples.Line
+import Examples.MultipleDots as MultipleDots
=import Examples.NestedTransformations
=import Examples.PolarCoordinates
=import Examples.RosetteTypedTransformations
@@ -43,6 +44,7 @@ import Parser
=import Parser.Advanced
=import Result.Extra as Result
=import Routes exposing (Route)
+import Svg.Attributes
=import Transformations
=import Url exposing (Url)
=
@@ -463,6 +465,7 @@ document =
= , dot
= , dotInElement
= , dotWithViewBox
+ , twoDots
= , transformationsCircle
= , line
= , gradient
@@ -546,6 +549,36 @@ document =
= (Mark.field "viewBox" Mark.string)
= |> Mark.map render
=
+ twoDots : Mark.Block (Model -> Element Msg)
+ twoDots =
+ let
+ render config model =
+ MultipleDots.ui config
+ |> Element.el [ Element.height (Element.px 300) ]
+ |> BrowserWindow.window []
+ in
+ Mark.record6 "TwoDots"
+ (\background viewBoxConf firstFill secondFill firstCX secondCX ->
+ MultipleDots.Config background
+ viewBoxConf
+ [ [ Svg.Attributes.r "10"
+ , Svg.Attributes.fill firstFill
+ , Svg.Attributes.cx firstCX
+ ]
+ , [ Svg.Attributes.r "10"
+ , Svg.Attributes.fill secondFill
+ , Svg.Attributes.cx secondCX
+ ]
+ ]
+ )
+ (Mark.field "background" Mark.string)
+ (Mark.field "viewBox" Mark.string)
+ (Mark.field "firstFill" Mark.string)
+ (Mark.field "secondFill" Mark.string)
+ (Mark.field "firstCX" Mark.string)
+ (Mark.field "secondCX" Mark.string)
+ |> Mark.map render
+
= transformationsCircle : Mark.Block (Model -> Element Msg)
= transformationsCircle =
= letRemove duplicated ViewBox module
deleted file mode 100644
index b3f0a44..0000000
--- a/src/ViewBox.elm
+++ /dev/null
@@ -1,276 +0,0 @@
-module ViewBox exposing
- ( Model
- , Msg
- , init
- , main
- , ui
- , update
- , view
- )
-
-import Browser
-import BrowserWindow
-import CartesianPlane exposing (graph)
-import Element
-import Element.Background as Background
-import Element.Border as Border
-import Element.Input as Input
-import Html exposing (Html)
-import Svg exposing (..)
-import Svg.Attributes exposing (..)
-
-
-main =
- Browser.sandbox
- { init = init
- , view = view
- , update = update
- }
-
-
-type alias Model =
- { left : Float
- , top : Float
- , width : Float
- , height : Float
- , preserveAspectRatio : Bool
- }
-
-
-type Msg
- = Move Float Float
- | Resize Float Float
- | PreserveAspectRatio Bool
-
-
-init : Model
-init =
- { left = 0
- , top = 0
- , width = 400
- , height = 400
- , preserveAspectRatio = False
- }
-
-
-view : Model -> Html.Html Msg
-view model =
- let
- wrapper element =
- Element.el
- [ Element.width (Element.maximum 1200 Element.fill)
- , Element.height Element.fill
- , Element.centerX
- ]
- element
- in
- ui model
- |> wrapper
- |> Element.layout
- [ Element.height Element.fill
- , Element.width Element.fill
- ]
-
-
-update : Msg -> Model -> Model
-update msg model =
- case msg of
- Move left top ->
- { model
- | left = left
- , top = top
- }
-
- Resize width height ->
- { model
- | width = width
- , height = height
- }
-
- PreserveAspectRatio value ->
- { model | preserveAspectRatio = value }
-
-
-ui model =
- let
- viewbox =
- svg
- [ viewBox "-500 -500 1000 1000"
- ]
- [ g [] world
- , rect
- [ x (String.fromFloat model.left)
- , y (String.fromFloat model.top)
- , width (String.fromFloat model.width)
- , height (String.fromFloat model.height)
- , opacity "0.3"
- , stroke "white"
- , fill "pink"
- ]
- []
- ]
- |> Element.html
- |> Element.el
- [ Element.centerX
- , Element.centerY
- , Element.width Element.fill
- , Element.height Element.fill
- ]
-
- viewport =
- svg
- [ [ model.left, model.top, model.width, model.height ]
- |> List.map String.fromFloat
- |> String.join " "
- |> viewBox
- , preserveAspectRatio <|
- case model.preserveAspectRatio of
- True ->
- "xMidYMid meet"
-
- False ->
- "none"
- , Svg.Attributes.style "width: 100%; height: 100%; flex-grow: 1; flex-basis: 0"
- ]
- [ g [] world
- , rect
- [ x (String.fromFloat model.left)
- , y (String.fromFloat model.top)
- , width (String.fromFloat model.width)
- , height (String.fromFloat model.height)
- , opacity "0.3"
- , stroke "white"
- , fill "pink"
- ]
- []
- ]
- |> Element.html
- |> Element.el
- [ Element.width Element.fill
- , Element.height Element.fill
- ]
- |> BrowserWindow.window
- [ Element.centerX
- , Element.centerY
- , Element.width Element.fill
- , Element.height Element.fill
- ]
- in
- Element.column
- [ Element.width Element.fill
- , Element.height Element.fill
- , Element.spacing 30
- , Element.padding 30
- ]
- [ Element.row
- [ Element.width Element.fill
- , Element.height Element.fill
- , Element.centerX
- , Element.spacing 30
- ]
- [ viewbox
- , viewport
- ]
- , Input.slider
- [ Element.behindContent
- (Element.el
- [ Element.width Element.fill
- , Element.height (Element.px 2)
- , Element.centerY
- , Background.color <| Element.rgb 0.7 0.7 0.7
- , Border.rounded 2
- ]
- Element.none
- )
- ]
- { onChange = \left -> Move left model.top
- , label =
- Input.labelBelow [ Element.centerX ] <|
- Element.text ("left: " ++ String.fromFloat model.left)
- , min = -500
- , max = 500
- , value = model.left
- , thumb = Input.defaultThumb
- , step = Just 1
- }
- , Input.slider
- [ Element.behindContent
- (Element.el
- [ Element.width Element.fill
- , Element.height (Element.px 2)
- , Element.centerY
- , Background.color <| Element.rgb 0.7 0.7 0.7
- , Border.rounded 2
- ]
- Element.none
- )
- ]
- { onChange = \top -> Move model.left top
- , label =
- Input.labelBelow [ Element.centerX ] <|
- Element.text ("top: " ++ String.fromFloat model.top)
- , min = -500
- , max = 500
- , value = model.top
- , thumb = Input.defaultThumb
- , step = Just 1
- }
- , Input.slider
- [ Element.behindContent
- (Element.el
- [ Element.width Element.fill
- , Element.height (Element.px 2)
- , Element.centerY
- , Background.color <| Element.rgb 0.7 0.7 0.7
- , Border.rounded 2
- ]
- Element.none
- )
- ]
- { onChange = \width -> Resize width model.height
- , label =
- Input.labelBelow [ Element.centerX ] <|
- Element.text ("width: " ++ String.fromFloat model.width)
- , min = 1
- , max = 500
- , value = model.width
- , thumb = Input.defaultThumb
- , step = Just 1
- }
- , Input.slider
- [ Element.behindContent
- (Element.el
- [ Element.width Element.fill
- , Element.height (Element.px 2)
- , Element.centerY
- , Background.color <| Element.rgb 0.7 0.7 0.7
- , Border.rounded 2
- ]
- Element.none
- )
- ]
- { onChange = \height -> Resize model.width height
- , label =
- Input.labelBelow [ Element.centerX ] <|
- Element.text ("height: " ++ String.fromFloat model.height)
- , min = 1
- , max = 500
- , value = model.height
- , thumb = Input.defaultThumb
- , step = Just 1
- }
- , Input.checkbox []
- { checked = model.preserveAspectRatio
- , icon = Input.defaultCheckbox
- , label = Input.labelRight [] (Element.text "Preserve aspect ratio")
- , onChange = PreserveAspectRatio
- }
- ]
-
-
-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" ] []
- ]Separate Window block from example blocks
Now each example can be nested in a Window, but doesn't have to be.
index 83cc9bf..bda2078 100644
--- a/content/day-1.txt
+++ b/content/day-1.txt
@@ -85,7 +85,8 @@ Note that the same address was printed by the Elm reactor.
=
=We want to display a dot at the center of the screen, like this:
=
-| DotAtTheCenterOfTheScreen
+| Window
+ | DotAtTheCenterOfTheScreen
=
=Below is the complete code to draw a dot like this, but don't type it in your editor yet! We are going to recreate it together, step by step.
=
@@ -153,9 +154,10 @@ Start the reactor again:
=
=Reload the browser. You should see something like this:
=
-| Simplest
- background=none
- fill=black
+| Window
+ | Simplest
+ background=none
+ fill=black
=
=Something isn't quite right here. Why is the dot in the corner?
=
@@ -171,9 +173,10 @@ When you reload the browser, you'll see this:
=| Note
= Example of simplest with pink background should be here.
=
-| Simplest
- background=pink
- fill=black
+| Window
+ | Simplest
+ background=pink
+ fill=black
=
=Now we can clearly see that our SVG element (which is pink) does not fill the screen.
=index 99d1276..e6bd250 100644
--- a/src/Main.elm
+++ b/src/Main.elm
@@ -475,6 +475,28 @@ document =
= , spiral
= , tree
= , viewBox
+
+ -- Window block can contain any embeded program
+ , Mark.Custom.window
+ (Mark.oneOf
+ [ counter
+ , simplest
+ , fillTheScreen
+ , dotAtTheCenterOfTheScreen
+ , centeredDot
+ , circle
+ , line
+ , gradient
+ , transformations
+ , nestedTransformations
+ , cartesianCoordinates
+ , polarCoordinates
+ , rosette
+ , spiral
+ , tree
+ , viewBox
+ ]
+ )
= ]
= )
=
@@ -489,11 +511,6 @@ document =
= [ Element.centerX
= , Element.centerY
= ]
- |> Element.el
- [ Element.height (Element.px 400)
- , Element.width Element.fill
- ]
- |> BrowserWindow.window []
= |> Element.map CounterMsg
= in
= Mark.stub "Counter" render
@@ -505,13 +522,12 @@ document =
= render config model =
= Examples.Simplest.ui config
= |> Element.html
- |> Element.el []
= |> Element.el
- [ Element.height (Element.px 400)
- , Element.width Element.fill
- , Element.padding 5
+ [ Element.width (Element.px 300)
+ , Element.height (Element.px 150)
= ]
- |> BrowserWindow.window []
+ |> Element.el
+ [ Element.padding 5 ]
= in
= Mark.record2 "Simplest"
= Examples.Simplest.Config
@@ -537,11 +553,6 @@ document =
= let
= render model =
= Examples.DotAtTheCenterOfTheScreen.ui
- |> Element.el
- [ Element.height (Element.px 400)
- , Element.width Element.fill
- ]
- |> BrowserWindow.window []
= in
= Mark.stub "DotAtTheCenterOfTheScreen" render
=
@@ -565,12 +576,6 @@ document =
= render : Examples.TransformationsCircle.Config -> Model -> Element Msg
= render config model =
= Examples.TransformationsCircle.ui config
- |> Element.el
- [ Element.height (Element.px 400)
- , Element.width Element.fill
- , Element.padding 5
- ]
- |> BrowserWindow.window []
= in
= Mark.record4 "TransformationsCircle"
= Examples.TransformationsCircle.Config
@@ -640,10 +645,6 @@ document =
= render model =
= model.cartesianCoordinates
= |> Examples.CartesianCoordinates.ui
- |> Element.el
- [ Element.width Element.fill
- ]
- |> BrowserWindow.window []
= |> Element.map CartesianCoordinatesMsg
= in
= Mark.stub "CartesianCoordinates" renderindex 3e50354..ca76c37 100644
--- a/src/Mark/Custom.elm
+++ b/src/Mark/Custom.elm
@@ -1,5 +1,21 @@
-module Mark.Custom exposing (code, colors, css, emphasize, header, icon, image, list, monospace, note, paragraph, text, title)
-
+module Mark.Custom exposing
+ ( code
+ , colors
+ , css
+ , emphasize
+ , header
+ , icon
+ , image
+ , list
+ , monospace
+ , note
+ , paragraph
+ , text
+ , title
+ , window
+ )
+
+import BrowserWindow
=import Dict
=import Element exposing (Element)
=import Element.Background as Background
@@ -107,6 +123,24 @@ code =
= Mark.multiline
=
=
+window :
+ Mark.Block (a -> Element msg)
+ -> Mark.Block (a -> Element msg)
+window block =
+ let
+ render child model =
+ child model
+ |> Element.el
+ [ Element.height (Element.px 400)
+ , Element.width Element.fill
+ ]
+ |> BrowserWindow.window []
+ in
+ Mark.block "Window"
+ render
+ block
+
+
=note : Mark.Block (model -> Element msg)
=note =
= letMake develop script print the modules it compiles to HTML
Otherwise it just prints a lot of "Success" which looks dumb.
index 3b0e90e..bf51dda 100755
--- a/scripts/develop
+++ b/scripts/develop
@@ -5,6 +5,7 @@ set -euo pipefail
=# Build examples
=for example in src/Examples/*.elm
=do
+ echo "Compiling ${example} to public/${example}.html"
= npx elm make --output "public/${example}.html" "${example}"
=done
=Remove calls to Debug.log from Main
index e6bd250..072cef8 100644
--- a/src/Main.elm
+++ b/src/Main.elm
@@ -275,11 +275,10 @@ view model =
=
=update : Msg -> Model -> ( Model, Cmd Msg )
=update msg model =
- case Debug.log "update" msg of
+ case msg of
= UrlRequested (Browser.Internal url) ->
= ( model
= , url
- |> Debug.log "New URL"
= |> Url.toString
= |> Navigation.pushUrl model.key
= )
@@ -376,7 +375,7 @@ subscriptions model =
=
=loadContent : Route -> Cmd Msg
=loadContent route =
- case Debug.log "Route" route of
+ case route of
= Routes.Home ->
= Http.get
= { url = "/content/index.txt"