diff --git a/intro/part3/README.md b/intro/part3/README.md new file mode 100644 index 0000000..bd69f36 --- /dev/null +++ b/intro/part3/README.md @@ -0,0 +1,21 @@ +# Part 3 + +This is just like last time, except first we'll install the `elm/browser` package so we can create an interactive app in the browser, instead of static HTML. + +To install the package, `cd` into the `part3/` directory and run: + +```shell +elm install elm/browser +``` + +Then build everything the same way as last time: + +```shell +elm make src/Main.elm --output elm.js +``` + +Finally, open `index.html` in your browser. + +## Exercise + +Open `src/Main.elm` in your editor and resolve the TODOs there. diff --git a/intro/part3/elm.json b/intro/part3/elm.json new file mode 100644 index 0000000..dd41cae --- /dev/null +++ b/intro/part3/elm.json @@ -0,0 +1,24 @@ +{ + "type": "application", + "source-directories": [ + "src" + ], + "elm-version": "0.19.0", + "dependencies": { + "direct": { + "elm/browser": "1.0.0", + "elm/core": "1.0.0", + "elm/html": "1.0.0" + }, + "indirect": { + "elm/json": "1.0.0", + "elm/time": "1.0.0", + "elm/url": "1.0.0", + "elm/virtual-dom": "1.0.0" + } + }, + "test-dependencies": { + "direct": {}, + "indirect": {} + } +} \ No newline at end of file diff --git a/intro/part3/index.html b/intro/part3/index.html new file mode 100644 index 0000000..3860a04 --- /dev/null +++ b/intro/part3/index.html @@ -0,0 +1,15 @@ + + + + + Elm Workshop + + + + +
+ + + diff --git a/intro/part3/src/Article.elm b/intro/part3/src/Article.elm new file mode 100644 index 0000000..833e956 --- /dev/null +++ b/intro/part3/src/Article.elm @@ -0,0 +1,21 @@ +module Article exposing (feed, tags) + +-- For now, this module only holds hardcoded data. +-- +-- In future exercises, it will read data from the server! + + +tags = + [ "elm" + , "fun" + , "programming" + , "dragons" + ] + + +feed = + [ { title = "Elm is fun!", description = "Elm", body = "I've really been enjoying it!", tags = [ "elm", "fun" ], slug = "elm-is-fun--zb6nba" } + , { title = "Who says undefined isn't a function anyway?", description = "Functions", body = "Quite frankly I think undefined can be anything it wants to be,if it believes in itself.", slug = "who-says-undefined-isnt-a-function-anyway-t39ope", tags = [ "programming" ] } + , { title = "This compiler is pretty neat", description = "Elm", body = "It tells me about problems in my code. How neat is that?", tags = [ "compilers", "elm" ], slug = "this-compiler-is-pretty-neat-9ycui8" } + , { title = "Are dragons real?", description = "dragons", body = "Do Komodo Dragons count? I think they should. It's right there in the name!", tags = [ "dragons" ], slug = "are-dragons-real-467lsh" } + ] diff --git a/intro/part3/src/Main.elm b/intro/part3/src/Main.elm new file mode 100644 index 0000000..9b9d1c8 --- /dev/null +++ b/intro/part3/src/Main.elm @@ -0,0 +1,132 @@ +module Main exposing (main) + +-- 👇 You can see our new `Article` module in `src/Article.elm` + +import Article +import Browser +import Html exposing (..) +import Html.Attributes exposing (..) +import Html.Events exposing (onClick) + + +-- MODEL + + +initialModel = + { tags = Article.tags + , selectedTag = "elm" + , allArticles = Article.feed + } + + + +-- UPDATE + + +update msg model = + {- 👉 TODO: If `msg.description` is "ClickedTag", then + set the model's `selectedTag` field to be `msg.data` + + 💡 HINT 1: record update syntax looks like this: + + { model | foo = bar } + + 💡 HINT 2: Don't forget, every `if` must have an `else`! + + -} + model + + + +-- VIEW + + +view model = + let + {- 👉 TODO: Filter the articles down to onl the ones + that include the currently selected tag. + + 💡 HINT: Replace `True` below with something involving + `List.member`, `article.tags`, and `model.selectedTag` + + Docs for List.member: http://package.elm-lang.org/packages/elm-lang/core/latest/List#member + -} + articles = + List.filter (\article -> True) + model.allArticles + + feed = + List.map viewArticle articles + in + div [ class "home-page" ] + [ viewBanner + , div [ class "container page" ] + [ div [ class "row" ] + [ div [ class "col-md-9" ] feed + , div [ class "col-md-3" ] + [ div [ class "sidebar" ] + [ p [] [ text "Popular Tags" ] + , viewTags model + ] + ] + ] + ] + ] + + +viewArticle article = + div [ class "article-preview" ] + [ h1 [] [ text article.title ] + , p [] [ text article.description ] + , span [] [ text "Read more..." ] + ] + + +viewBanner = + div [ class "banner" ] + [ div [ class "container" ] + [ h1 [ class "logo-font" ] [ text "conduit" ] + , p [] [ text "A place to share your knowledge." ] + ] + ] + + +viewTag selectedTagName tagName = + let + otherClass = + if tagName == selectedTagName then + "tag-selected" + else + "tag-default" + in + button + [ class ("tag-pill " ++ otherClass) + + {- 👉 TODO: Add an `onClick` handler which sends a msg + that our `update` function above will use + to set the currently selected tag to `tagName`. + + 💡 HINT: It should look something like this: + + , onClick { description = … , data = … } + + 👆 Don't forget to add a comma before `onClick`! + -} + ] + [ text tagName ] + + +viewTags model = + div [ class "tag-list" ] (List.map (viewTag model.selectedTag) model.tags) + + + +-- MAIN + + +main = + Browser.sandbox + { init = initialModel + , view = view + , update = update + }