diff --git a/.travis.yml b/.travis.yml
index 912f7fd..9314eb6 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,7 +2,8 @@ sudo: false
cache:
directories:
- - elm-stuff/build-artifacts
+ - elm-stuff/0.19.1
+ - ~/.elm/0.19.1
- sysconfcpus
os:
@@ -24,17 +25,12 @@ before_install:
cd ..;
fi
-
install:
- nvm install $TARGET_NODE_VERSION
- nvm use $TARGET_NODE_VERSION
- node --version
- npm --version
- - npm install -g elm@0.18.0
- - mv $(npm config get prefix)/bin/elm-make $(npm config get prefix)/bin/elm-make-old
- - printf '%s\n\n' '#!/bin/bash' 'echo "Running elm-make with sysconfcpus -n 2"' '$TRAVIS_BUILD_DIR/sysconfcpus/bin/sysconfcpus -n 2 elm-make-old "$@"' > $(npm config get prefix)/bin/elm-make
- - chmod +x $(npm config get prefix)/bin/elm-make
- - travis_retry elm-package install --yes
+ - npm install -g elm@latest-0.19.1
script:
- - elm-make src/Main.elm --output=/dev/null --yes
+ - $TRAVIS_BUILD_DIR/sysconfcpus/bin/sysconfcpus -n 2 elm make src/Main.elm --output=/dev/null
diff --git a/build.sh b/build.sh
index 20b7414..dbc3d94 100755
--- a/build.sh
+++ b/build.sh
@@ -1,4 +1,3 @@
#!/bin/bash
-elm-css src/Stylesheets.elm
-elm-make src/Main.elm --output build/main.js
-uglifyjs ./build/main.js --output ./build/main.js --screw-ie8 --compress dead_code,pure_getters,negate_iife,cascade,hoist_funs --mangle toplevel=true
+elm make src/Main.elm --output build/main.js
+uglifyjs ./build/main.js --compress "pure_funcs=[F2,F3,F4,F5,F6,F7,F8,F9,A2,A3,A4,A5,A6,A7,A8,A9],pure_getters,keep_fargs=false,unsafe_comps,unsafe" | uglifyjs --mangle --output ./build/main.js
diff --git a/elm-package.json b/elm-package.json
deleted file mode 100644
index 1fe9c54..0000000
--- a/elm-package.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
- "version": "1.0.0",
- "summary": "A list of projects and apps built with Elm.",
- "repository": "https://github.com/elm-community/builtwithelm.git",
- "license": "BSD3",
- "source-directories": [
- "./src"
- ],
- "exposed-modules": [],
- "dependencies": {
- "elm-lang/core": "5.0.0 <= v < 6.0.0",
- "elm-lang/html": "2.0.0 <= v < 3.0.0",
- "elm-lang/http": "1.0.0 <= v < 2.0.0",
- "elm-lang/navigation": "2.0.1 <= v < 3.0.0",
- "evancz/url-parser": "2.0.1 <= v < 3.0.0",
- "lukewestby/elm-http-builder": "4.0.0 <= v < 5.0.0",
- "rtfeldman/elm-css": "7.0.0 <= v < 8.0.0",
- "rtfeldman/elm-css-helpers": "2.0.0 <= v < 3.0.0"
- },
- "elm-version": "0.18.0 <= v < 0.19.0"
-}
diff --git a/elm.json b/elm.json
new file mode 100644
index 0000000..a5437a0
--- /dev/null
+++ b/elm.json
@@ -0,0 +1,31 @@
+{
+ "type": "application",
+ "source-directories": [
+ "src"
+ ],
+ "elm-version": "0.19.1",
+ "dependencies": {
+ "direct": {
+ "NoRedInk/elm-json-decode-pipeline": "1.0.0",
+ "elm/browser": "1.0.1",
+ "elm/core": "1.0.2",
+ "elm/html": "1.0.0",
+ "elm/http": "2.0.0",
+ "elm/json": "1.1.3",
+ "elm/url": "1.0.0",
+ "rtfeldman/elm-css": "16.0.1"
+ },
+ "indirect": {
+ "Skinney/murmur3": "2.0.8",
+ "elm/bytes": "1.0.8",
+ "elm/file": "1.0.5",
+ "elm/time": "1.0.0",
+ "elm/virtual-dom": "1.0.2",
+ "rtfeldman/elm-hex": "1.0.0"
+ }
+ },
+ "test-dependencies": {
+ "direct": {},
+ "indirect": {}
+ }
+}
\ No newline at end of file
diff --git a/index.html b/index.html
index d5f1544..3e40b6b 100644
--- a/index.html
+++ b/index.html
@@ -1,23 +1,26 @@
-
- Built With Elm
-
-
-
-
-
-
-
-
+
+ Built With Elm
+
+
+
+
+
+
+
diff --git a/package.json b/package.json
index a8bbb22..dd65657 100644
--- a/package.json
+++ b/package.json
@@ -4,13 +4,14 @@
"description": "A list of projects and apps built with Elm.",
"main": "build/main.js",
"dependencies": {
- "elm": "^0.18.0",
- "elm-css": "^0.6.0",
- "uglifyjs": "^2.4.10"
+ "elm": "latest-0.19.1"
+ },
+ "devDependencies": {
+ "elm-live": "^4.0.1"
},
- "devDependencies": {},
"scripts": {
- "build": "bash build.sh"
+ "build": "bash build.sh",
+ "dev": "elm-live src/Main.elm --open --start-page=index.html -- --output=build/main.js"
},
"repository": {
"type": "git",
diff --git a/src/Cmds.elm b/src/Cmds.elm
index 3886e28..5f96cec 100644
--- a/src/Cmds.elm
+++ b/src/Cmds.elm
@@ -1,25 +1,23 @@
port module Cmds exposing (loadProjects, newPage, notifyOffsetChanged)
+import Browser.Navigation exposing (Key)
import Http
-import HttpBuilder exposing (withExpect)
-import Json.Decode as Decode
+import Json.Decode exposing (Decoder)
import Model exposing (Project, decodeProject)
-import Navigation
import Query
-import Task
-projectsDecoder : Decode.Decoder (List Project)
+projectsDecoder : Decoder (List Project)
projectsDecoder =
- Decode.list decodeProject
+ Json.Decode.list decodeProject
port notifyOffsetChangedRaw : () -> Cmd msg
-newPage : Int -> Cmd msg
-newPage =
- Navigation.newUrl << Query.pageUrl
+newPage : Key -> Int -> Cmd msg
+newPage key page =
+ Browser.Navigation.pushUrl key (Query.pageUrl page)
notifyOffsetChanged : Cmd msg
@@ -29,6 +27,7 @@ notifyOffsetChanged =
loadProjects : (Result Http.Error (List Project) -> msg) -> Cmd msg
loadProjects msg =
- HttpBuilder.get "data/projects.json"
- |> withExpect (Http.expectJson projectsDecoder)
- |> HttpBuilder.send msg
+ Http.get
+ { url = "data/projects.json"
+ , expect = Http.expectJson msg (Json.Decode.list decodeProject)
+ }
diff --git a/src/Main.elm b/src/Main.elm
index 9b8099a..00d45f6 100644
--- a/src/Main.elm
+++ b/src/Main.elm
@@ -1,18 +1,18 @@
module Main exposing (..)
+import Browser
import Model exposing (Model)
-import Navigation
-import Query exposing (parsePage)
-import Update exposing (Msg(NewPage), update, init)
+import Update exposing (Msg(..), init, update)
import View exposing (view)
-main : Program Never Model Msg
+main : Program () Model Msg
main =
- Navigation.program
- (NewPage << parsePage)
- { init = init << parsePage
+ Browser.application
+ { init = init
, view = view
, update = update
, subscriptions = always Sub.none
+ , onUrlRequest = OnUrlRequest
+ , onUrlChange = OnUrlChange
}
diff --git a/src/Model.elm b/src/Model.elm
index 917d47a..e75fb59 100644
--- a/src/Model.elm
+++ b/src/Model.elm
@@ -1,6 +1,8 @@
-module Model exposing (Project, Model, decodeProject)
+module Model exposing (Model, Project, decodeProject)
-import Json.Decode as Decode exposing (field)
+import Browser.Navigation exposing (Key)
+import Json.Decode exposing (Decoder)
+import Json.Decode.Pipeline exposing (required)
type alias Project =
@@ -13,7 +15,8 @@ type alias Project =
type alias Model =
- { projects : List Project
+ { key : Key
+ , projects : List Project
, isLoading : Bool
, loadFailed : Bool
, page : Int
@@ -22,16 +25,11 @@ type alias Model =
}
-(|:) : Decode.Decoder (a -> b) -> Decode.Decoder a -> Decode.Decoder b
-(|:) =
- Decode.map2 (<|)
-
-
-decodeProject : Decode.Decoder Project
+decodeProject : Decoder Project
decodeProject =
- Decode.succeed Project
- |: (field "previewImageUrl" Decode.string)
- |: (field "name" Decode.string)
- |: (field "primaryUrl" Decode.string)
- |: (field "description" Decode.string)
- |: (field "repositoryUrl" (Decode.maybe Decode.string))
+ Json.Decode.succeed Project
+ |> required "previewImageUrl" Json.Decode.string
+ |> required "name" Json.Decode.string
+ |> required "primaryUrl" Json.Decode.string
+ |> required "description" Json.Decode.string
+ |> required "repositoryUrl" (Json.Decode.nullable Json.Decode.string)
diff --git a/src/Query.elm b/src/Query.elm
index 5ee0b0b..7c20973 100644
--- a/src/Query.elm
+++ b/src/Query.elm
@@ -1,17 +1,18 @@
module Query exposing (pageUrl, parsePage)
-import Navigation exposing (Location)
-import UrlParser exposing (..)
+import Url exposing (Url)
+import Url.Parser exposing ((>), Parser)
+import Url.Parser.Query
pageParser : Parser (Maybe Int -> a) a
pageParser =
- map (\_ n -> n) <| string > intParam "page"
+ Url.Parser.top > Url.Parser.Query.int "page"
-parsePage : Location -> Int
+parsePage : Url -> Int
parsePage =
- parsePath pageParser
+ Url.Parser.parse pageParser
>> Maybe.withDefault Nothing
>> Maybe.withDefault 0
@@ -20,5 +21,6 @@ pageUrl : Int -> String
pageUrl page =
if page == 0 then
"/"
+
else
- "?page=" ++ (toString page)
+ "?page=" ++ String.fromInt page
diff --git a/src/Styles.elm b/src/Styles.elm
index f211ea1..39a0d06 100644
--- a/src/Styles.elm
+++ b/src/Styles.elm
@@ -1,32 +1,289 @@
-module Styles exposing (CssClasses(..), builtWithElmNamespace)
-
-
-builtWithElmNamespace : String
-builtWithElmNamespace =
- "builtwithelm-"
-
-
-type CssClasses
- = Container
- | Sidebar
- | SidebarHeader
- | SidebarLogoContainer
- | Content
- | ListContainer
- | Paging
- | Button
- | Dropdown
- | Link
- | GithubLogo
- | Project
- | ProjectScreenshotShell
- | ProjectImage
- | ProjectHeader
- | Logo
- | BuiltWithText
- | BuiltWithLink
- | SubmitProject
- | SubmitProjectHeader
- | BuiltBy
- | SearchContainer
- | SearchInput
+module Styles exposing (..)
+
+import Css exposing (..)
+import Css.Global exposing (Snippet, descendants, everything, selector)
+import Css.Media exposing (MediaQuery)
+import Html.Styled exposing (Attribute, Html)
+import Html.Styled.Attributes exposing (css)
+
+
+mediumScreen : List MediaQuery
+mediumScreen =
+ [ Css.Media.only Css.Media.screen [ Css.Media.maxWidth (px 768) ] ]
+
+
+smallScreen : List MediaQuery
+smallScreen =
+ [ Css.Media.only Css.Media.screen [ Css.Media.maxWidth (px 570) ] ]
+
+
+p : List (Attribute msg) -> List (Html msg) -> Html msg
+p attrs children =
+ Html.Styled.p (css [ textAlign justify ] :: attrs) children
+
+
+h1 : List (Attribute msg) -> List (Html msg) -> Html msg
+h1 attrs children =
+ Html.Styled.h1
+ (css
+ [ property "font-weight" "normal"
+ , textAlign center
+ , paddingBottom (px 20)
+ , borderBottom3 (px 1) solid (hex "999999")
+ , Css.Media.withMedia mediumScreen
+ [ flex (int 1)
+ , borderStyle none
+ , property "display" "flex"
+ , flexDirection column
+ , property "justify-content" "center"
+ , fontSize (em 3)
+ , paddingBottom (px 0)
+ ]
+ , Css.Media.withMedia smallScreen [ fontSize (em 1.8) ]
+ ]
+ :: attrs
+ )
+ children
+
+
+h2 : List (Attribute msg) -> List (Html msg) -> Html msg
+h2 attrs children =
+ Html.Styled.h2 (css [ margin (px 0) ] :: attrs) children
+
+
+a : List (Attribute msg) -> List (Html msg) -> Html msg
+a attrs children =
+ Html.Styled.a (css [ property "color" "inherit" ] :: attrs) children
+
+
+body : Snippet
+body =
+ selector "body"
+ [ margin (px 0)
+ , height (pct 100)
+ , descendants
+ [ everything
+ [ boxSizing borderBox
+ ]
+ ]
+ ]
+
+
+container : List Style
+container =
+ [ property "display" "flex"
+ , height (pct 100)
+ , position relative
+ , fontFamilies [ qt "Source Sans Pro" ]
+ , Css.Media.withMedia mediumScreen [ flexDirection column ]
+ ]
+
+
+content : List Style
+content =
+ [ marginLeft (px 240)
+ , property "width" "calc(100% - 240px)"
+ , paddingTop (px 20)
+ , paddingRight (px 0)
+ , paddingBottom (px 20)
+ , paddingLeft (px 20)
+ , Css.Media.withMedia mediumScreen
+ [ marginLeft auto
+ , width auto
+ , paddingLeft (px 0)
+ ]
+ ]
+
+
+listContainer : List Style
+listContainer =
+ [ padding4 (px 0) (px 20) (px 0) (px 20)
+ , maxWidth (px 920)
+ , marginTop (px 0)
+ , marginBottom (px 0)
+ , marginLeft auto
+ , marginRight auto
+ ]
+
+
+paging : List Style
+paging =
+ [ property "display" "flex"
+ , flexDirection row
+ , property "justify-content" "center"
+ , property "align-items" "center"
+ , width (pct 50)
+ , margin auto
+ ]
+
+
+button : List Style
+button =
+ [ fontFamilies [ "Helvetica" ]
+ , fontSize (em 1.6)
+ , fontWeight (int 400)
+ , color (hex "5cb5cd")
+ , backgroundColor (rgb 255 255 255)
+ , property "border-width" "2px"
+ , borderStyle solid
+ , borderColor (hex "e5e5e5")
+ , margin (px 1)
+ , borderRadius (px 5)
+ , padding (px 3)
+ , flexBasis (pct 50)
+ , minWidth (px 100)
+ , property "outline" "0"
+ , property "cursor" "pointer"
+ , disabled
+ [ color (hex "e5e5e5")
+ ]
+ ]
+
+
+dropdown : List Style
+dropdown =
+ [ fontFamilies [ "Helvetica" ]
+ , backgroundColor (rgb 255 255 255)
+ , margin (px 1)
+ , padding (px 3)
+ , flexBasis (pct 50)
+ , minWidth (px 100)
+ , property "outline" "0"
+ ]
+
+
+link : List Style
+link =
+ [ property "color" "inherit"
+ ]
+
+
+githubLogo : List Style
+githubLogo =
+ [ width (em 2)
+ ]
+
+
+project : List Style
+project =
+ [ marginBottom (px 40)
+ ]
+
+
+projectHeader : List Style
+projectHeader =
+ [ property "display" "flex"
+ , property "justify-content" "space-between"
+ ]
+
+
+projectScreenshotShell : List Style
+projectScreenshotShell =
+ [ property "background-image" "url(screenshot_shell.svg)"
+ , property "background-size" "contain"
+ , maxWidth (px 1040)
+ , maxHeight (px 850)
+ , padding (pct 1.875)
+ , paddingTop (pct 2.875)
+ , property "background-repeat" "no-repeat"
+ ]
+
+
+projectImage : List Style
+projectImage =
+ [ width (pct 100)
+ ]
+
+
+sidebar : List Style
+sidebar =
+ [ width (px 240)
+ , height (pct 100)
+ , padding (px 20)
+ , position fixed
+ , overflow auto
+ , Css.Media.withMedia mediumScreen
+ [ position static
+ , height auto
+ , width auto
+ ]
+ ]
+
+
+sidebarHeader : List Style
+sidebarHeader =
+ [ property "display" "flex"
+ , flexDirection column
+ , Css.Media.withMedia mediumScreen
+ [ flexDirection row
+ , paddingBottom (px 20)
+ , borderBottomStyle solid
+ , borderBottomColor (hex "999999")
+ , borderBottomWidth (px 1)
+ ]
+ ]
+
+
+sidebarLogoContainer : List Style
+sidebarLogoContainer =
+ [ Css.Media.withMedia mediumScreen
+ [ flex (int 1)
+ , maxWidth (pct 40)
+ ]
+ ]
+
+
+builtWithText : List Style
+builtWithText =
+ [ color (hex "999999") ]
+
+
+builtWithLink : List Style
+builtWithLink =
+ [ textDecoration none ]
+
+
+submitProject : List Style
+submitProject =
+ [ borderBottom3 (px 1) solid (hex "999999")
+ , marginBottom (px 20)
+ ]
+
+
+submitProjectHeader : List Style
+submitProjectHeader =
+ [ textAlign center ]
+
+
+builtBy : List Style
+builtBy =
+ [ fontSize (em 0.9) ]
+
+
+searchInput : List Style
+searchInput =
+ [ width (px 191)
+ , fontSize (em 1)
+ , padding (px 4)
+ , margin2 (px 10) zero
+ , border3 (px 1) solid (hex "eeeeee")
+ , borderRadius (px 6)
+ , Css.Media.withMedia mediumScreen [ width (pct 100) ]
+ ]
+
+
+searchContainer : List Style
+searchContainer =
+ [ borderBottom3 (px 1) solid (hex "999999")
+ , marginBottom (px 20)
+ ]
+
+
+logo : List Style
+logo =
+ [ Css.Media.withMedia mediumScreen
+ [ display block
+ , margin (px 0)
+ , maxWidth (pct 100)
+ ]
+ ]
diff --git a/src/Stylesheets.elm b/src/Stylesheets.elm
deleted file mode 100644
index 4d18918..0000000
--- a/src/Stylesheets.elm
+++ /dev/null
@@ -1,232 +0,0 @@
-port module Stylesheets exposing (..)
-
-import Css exposing (..)
-import Css.Elements exposing (..)
-import Css.Namespace exposing (namespace)
-import Css.File exposing (..)
-import Styles exposing (CssClasses(..), builtWithElmNamespace)
-
-
-port files : CssFileStructure -> Cmd msg
-
-
-css : Stylesheet
-css =
- (stylesheet << namespace builtWithElmNamespace)
- [ body
- [ margin (px 0)
- , height (pct 100)
- , descendants
- [ everything
- [ boxSizing borderBox
- ]
- ]
- ]
- , nav
- [ display inlineBlock
- , paddingBottom (px 12)
- ]
- , ((.) Button)
- [ fontFamilies [ "Helvetica" ]
- , fontSize (em 1.6)
- , fontWeight (int 400)
- , color (hex "5cb5cd")
- , backgroundColor (rgb 255 255 255)
- , property "border-width" "2px"
- , borderStyle solid
- , borderColor (hex "e5e5e5")
- , margin (px 1)
- , borderRadius (px 5)
- , padding (px 3)
- , flexBasis (pct 50)
- , minWidth (px 100)
- , property "outline" "0"
- , property "cursor" "pointer"
- , disabled
- [ color (hex "e5e5e5")
- ]
- ]
- , ((.) Dropdown)
- [ fontFamilies [ "Helvetica" ]
- , backgroundColor (rgb 255 255 255)
- , margin (px 1)
- , padding (px 3)
- , flexBasis (pct 50)
- , minWidth (px 100)
- , property "outline" "0"
- ]
- , ((.) Container)
- [ property "display" "flex"
- , height (pct 100)
- , position relative
- , fontFamilies [ qt "Source Sans Pro" ]
- ]
- , ((.) Sidebar)
- [ width (px 240)
- , height (pct 100)
- , padding (px 20)
- , position fixed
- , overflow auto
- ]
- , ((.) Content)
- [ marginLeft (px 240)
- , property "width" "calc(100% - 240px)"
- , paddingTop (px 20)
- , paddingRight (px 0)
- , paddingBottom (px 20)
- , paddingLeft (px 20)
- ]
- , ((.) ListContainer)
- [ padding4 (px 0) (px 20) (px 0) (px 20)
- , maxWidth (px 920)
- , marginTop (px 0)
- , marginBottom (px 0)
- , marginLeft auto
- , marginRight auto
- ]
- , ((.) Paging)
- [ property "display" "flex"
- , flexDirection row
- , property "justify-content" "center"
- , property "align-items" "center"
- , width (pct 50)
- , margin auto
- ]
- , ((.) Link)
- [ property "color" "inherit"
- ]
- , ((.) GithubLogo)
- [ width (em 2)
- ]
- , ((.) Project)
- [ marginBottom (px 40)
- ]
- , ((.) ProjectHeader)
- [ property "display" "flex"
- , property "justify-content" "space-between"
- ]
- , h2
- [ margin (px 0)
- ]
- , ((.) ProjectScreenshotShell)
- [ property "background-image" "url(screenshot_shell.svg)"
- , property "background-size" "contain"
- , maxWidth (px 1040)
- , maxHeight (px 850)
- , padding (pct 1.875)
- , paddingTop (pct 2.875)
- , property "background-repeat" "no-repeat"
- ]
- , ((.) ProjectImage)
- [ width (pct 100)
- ]
- , h1
- [ property "font-weight" "normal"
- , textAlign center
- , paddingBottom (px 20)
- , borderBottom3 (px 1) solid (hex "999999")
- ]
- , ((.) SidebarHeader)
- [ property "display" "flex"
- , flexDirection column
- ]
- , a
- [ property "color" "inherit"
- ]
- , p
- [ textAlign textJustify
- ]
- , ((.) BuiltWithText)
- [ color (hex "999999")
- ]
- , ((.) BuiltWithLink)
- [ textDecoration none
- ]
- , ((.) SubmitProject)
- [ borderBottom3 (px 1) solid (hex "999999")
- , marginBottom (px 20)
- ]
- , ((.) SubmitProjectHeader)
- [ textAlign center
- ]
- , ((.) BuiltBy)
- [ fontSize (em 0.9)
- ]
- , ((.) SearchInput)
- [ width (px 191)
- , fontSize (em 1)
- , padding (px 4)
- , margin2 (px 10) zero
- , border3 (px 1) solid (hex "eeeeee")
- , borderRadius (px 6)
- ]
- , ((.) SearchContainer)
- [ borderBottom3 (px 1) solid (hex "999999")
- , marginBottom (px 20)
- ]
- , mediaQuery "screen and (max-width: 768px)"
- [ ((.) Container)
- [ flexDirection column
- ]
- , ((.) Sidebar)
- [ position static
- , height auto
- , width auto
- ]
- , ((.) Logo)
- [ display block
- , maxWidth (pct 33)
- , marginTop (px 0)
- , marginBottom (px 0)
- , marginLeft auto
- , marginRight auto
- ]
- , ((.) Content)
- [ marginLeft auto
- , width auto
- , paddingLeft (px 0)
- ]
- , ((.) SidebarHeader)
- [ flexDirection row
- , paddingBottom (px 20)
- , borderBottomStyle solid
- , borderBottomColor (hex "999999")
- , borderBottomWidth (px 1)
- ]
- , ((.) SidebarLogoContainer)
- [ flex (int 1)
- , maxWidth (pct 40)
- ]
- , ((.) SearchInput)
- [ width (pct 100)
- ]
- , h1
- [ flex (int 1)
- , borderStyle none
- , property "display" "flex"
- , flexDirection column
- , property "justify-content" "center"
- , fontSize (em 3)
- , paddingBottom (px 0)
- ]
- , ((.) Logo)
- [ margin (px 0)
- , maxWidth (pct 100)
- ]
- ]
- , mediaQuery "screen and (max-width: 570px)"
- [ h1
- [ fontSize (em 1.8)
- ]
- ]
- ]
-
-
-cssFiles : CssFileStructure
-cssFiles =
- toFileStructure [ ( "assets/styles.css", Css.File.compile [ css ] ) ]
-
-
-main : CssCompilerProgram
-main =
- Css.File.compiler files cssFiles
diff --git a/src/Update.elm b/src/Update.elm
index 5829e2d..b29f6dd 100644
--- a/src/Update.elm
+++ b/src/Update.elm
@@ -1,23 +1,33 @@
-module Update exposing (update, Msg(..), init)
+module Update exposing (Msg(..), init, update)
+import Browser exposing (UrlRequest(..))
+import Browser.Navigation exposing (Key)
import Cmds exposing (loadProjects, newPage, notifyOffsetChanged)
import Http
import Model exposing (Model, Project)
+import Query exposing (parsePage)
+import Url exposing (Url)
type Msg
= NoOp
- | NewPage Int
| Next
| Prev
| SetPageSize String
| LoadProjects (Result Http.Error (List Project))
| UpdateSearchQuery String
+ | OnUrlRequest UrlRequest
+ | OnUrlChange Url
-init : Int -> ( Model, Cmd Msg )
-init page =
- ( { projects = []
+init : () -> Url -> Key -> ( Model, Cmd Msg )
+init _ url key =
+ let
+ page =
+ parsePage url
+ in
+ ( { key = key
+ , projects = []
, isLoading = True
, loadFailed = False
, page = page
@@ -50,23 +60,14 @@ update msg model =
, Cmd.none
)
- NewPage page ->
- ( { model
- | page = page
- }
- , notifyOffsetChanged
- )
-
Next ->
- ( model, newPage <| model.page + 1 )
+ ( model, newPage model.key (model.page + 1) )
Prev ->
- ( model, newPage <| model.page - 1 )
+ ( model, newPage model.key (model.page - 1) )
SetPageSize selected ->
- ( { model
- | pageSize = String.toInt selected |> Result.toMaybe |> Maybe.withDefault 5
- }
+ ( { model | pageSize = Maybe.withDefault 5 (String.toInt selected) }
, Cmd.none
)
@@ -76,3 +77,18 @@ update msg model =
}
, Cmd.none
)
+
+ OnUrlChange url ->
+ ( { model
+ | page = parsePage url
+ }
+ , notifyOffsetChanged
+ )
+
+ OnUrlRequest urlRequest ->
+ case urlRequest of
+ Internal url ->
+ ( model, Browser.Navigation.pushUrl model.key (Url.toString url) )
+
+ External url ->
+ ( model, Browser.Navigation.load url )
diff --git a/src/View.elm b/src/View.elm
index e5223e1..af88c2a 100644
--- a/src/View.elm
+++ b/src/View.elm
@@ -1,17 +1,17 @@
-module View exposing (..)
-
-import String
-import Html exposing (Html, button, div, text, h2, a, img, span, strong, h1, p, h3, input, label, select, option)
-import Html.Attributes exposing (style, disabled, href, src, target, type_, placeholder, value, autofocus)
-import Html.Events exposing (onClick, onInput)
-import Html.Keyed
-import Html.CssHelpers
+module View exposing (view)
+
+import Browser exposing (Document)
+import Css.Global
+import Html.Styled exposing (Html, button, div, h3, img, input, label, option, select, span, strong, text)
+import Html.Styled.Attributes exposing (autofocus, css, disabled, href, placeholder, src, style, target, type_, value)
+import Html.Styled.Events exposing (onClick, onInput)
+import Html.Styled.Keyed
import Model exposing (Model, Project)
+import Styles exposing (a, h1, h2, p)
import Update exposing (Msg(..))
-import Styles exposing (..)
-view : Model -> Html Msg
+view : Model -> Document Msg
view model =
let
disablePrev =
@@ -20,27 +20,27 @@ view model =
disableNext =
model.page * model.pageSize + model.pageSize >= List.length model.projects
in
- div
- [ class [ Container ]
- ]
- [ viewSidebar model
- , div
- [ class [ Content ]
- ]
- [ Html.Keyed.node "div"
- [ class [ ListContainer ]
- ]
- <|
- viewList model
+ { title = ""
+ , body =
+ [ Html.Styled.toUnstyled <|
+ div [ css Styles.container ]
+ [ viewSidebar model
, div
- [ class [ Paging ]
+ [ css Styles.content
]
- [ viewPageSizeSelect SetPageSize model.pageSize [ 5, 25, 50, 100 ]
- , viewPageButton Prev disablePrev "Newer"
- , viewPageButton Next disableNext "Older"
+ [ Html.Styled.Keyed.node "div"
+ [ css Styles.listContainer ]
+ (viewList model)
+ , div [ css Styles.paging ]
+ [ viewPageSizeSelect SetPageSize model.pageSize [ 5, 25, 50, 100 ]
+ , viewPageButton Prev disablePrev "Newer"
+ , viewPageButton Next disableNext "Older"
+ ]
]
]
- ]
+ , Html.Styled.toUnstyled <| Css.Global.global [ Styles.body ]
+ ]
+ }
viewPageButton : Msg -> Bool -> String -> Html Msg
@@ -49,30 +49,31 @@ viewPageButton msg isDisabled label =
textColor =
if isDisabled then
"#e5e5e5"
+
else
"#5cb5cd"
in
- button
- [ onClick msg
- , disabled isDisabled
- , class [ Button ]
- ]
- [ text label ]
+ button
+ [ onClick msg
+ , disabled isDisabled
+ , css Styles.button
+ ]
+ [ text label ]
viewPageSizeSelect : (String -> Msg) -> Int -> List Int -> Html Msg
viewPageSizeSelect msg current options =
let
toOption i =
- option [ value <| toString i ]
- [ text <| toString i ]
+ option [ value <| String.fromInt i ]
+ [ text <| String.fromInt i ]
in
- div [ class [ Dropdown ] ]
- [ label []
- [ text "Page size" ]
- , select [ value <| toString current, onInput msg ]
- (List.map toOption options)
- ]
+ div [ css Styles.dropdown ]
+ [ label []
+ [ text "Page size" ]
+ , select [ value <| String.fromInt current, onInput msg ]
+ (List.map toOption options)
+ ]
viewList : Model -> List ( String, Html Msg )
@@ -81,16 +82,18 @@ viewList model =
filterCriteria project =
String.contains (String.toLower model.searchQuery) (String.toLower project.name)
in
- if model.isLoading then
- [ ( "", h2 [] [ text "Loading" ] ) ]
- else if model.loadFailed then
- [ ( "", h2 [] [ text "Unable to load projects" ] ) ]
- else
- model.projects
- |> List.filter filterCriteria
- |> List.drop (model.page * model.pageSize)
- |> List.take model.pageSize
- |> List.map (\p -> ( p.primaryUrl, viewProject p ))
+ if model.isLoading then
+ [ ( "", h2 [] [ text "Loading" ] ) ]
+
+ else if model.loadFailed then
+ [ ( "", h2 [] [ text "Unable to load projects" ] ) ]
+
+ else
+ model.projects
+ |> List.filter filterCriteria
+ |> List.drop (model.page * model.pageSize)
+ |> List.take model.pageSize
+ |> List.map (\p -> ( p.primaryUrl, viewProject p ))
viewOpenSourceLink : Project -> Html Msg
@@ -100,11 +103,11 @@ viewOpenSourceLink project =
a
[ href url
, target "_blank"
- , class [ Link ]
+ , css Styles.link
]
[ img
[ src "assets/github.svg"
- , class [ GithubLogo ]
+ , css Styles.githubLogo
]
[]
]
@@ -115,16 +118,12 @@ viewOpenSourceLink project =
viewProject : Project -> Html Msg
viewProject project =
- div
- [ class [ Styles.Project ]
- ]
- [ div
- [ class [ ProjectHeader ]
- ]
+ div [ css Styles.project ]
+ [ div [ css Styles.projectHeader ]
[ a
[ href project.primaryUrl
, target "_blank"
- , class [ Link ]
+ , css Styles.link
]
[ h2 []
[ text project.name ]
@@ -132,12 +131,10 @@ viewProject project =
, viewOpenSourceLink project
]
, p [] [ text project.description ]
- , div
- [ class [ ProjectScreenshotShell ]
- ]
+ , div [ css Styles.projectScreenshotShell ]
[ img
[ src project.previewImageUrl
- , class [ ProjectImage ]
+ , css Styles.projectImage
]
[]
]
@@ -146,42 +143,40 @@ viewProject project =
viewSidebar : Model -> Html Msg
viewSidebar model =
- div [ class [ Sidebar ] ]
- [ div [ class [ SidebarHeader ] ]
+ div [ css Styles.sidebar ]
+ [ div [ css Styles.sidebarHeader ]
[ div
- [ class [ SidebarLogoContainer ]
+ [ css Styles.sidebarLogoContainer
]
[ a [ href "/" ]
- [ img [ src "assets/logo.svg", class [ Logo ] ] [] ]
+ [ img [ src "assets/logo.svg", css Styles.logo ] [] ]
]
, h1 []
[ a
[ href "/"
- , class [ BuiltWithLink ]
+ , css Styles.builtWithLink
]
[ span
- [ class [ BuiltWithText ]
+ [ css Styles.builtWithText
]
[ text "builtwith" ]
, span [] [ text "elm" ]
]
]
]
- , div [ class [ SearchContainer ] ]
+ , div [ css Styles.searchContainer ]
[ input
[ type_ "text"
, placeholder "Search"
, value model.searchQuery
, autofocus True
, onInput UpdateSearchQuery
- , class [ SearchInput ]
+ , css Styles.searchInput
]
[]
]
- , div [ class [ SubmitProject ] ]
- [ h3
- [ class [ SubmitProjectHeader ]
- ]
+ , div [ css Styles.submitProject ]
+ [ h3 [ css Styles.submitProjectHeader ]
[ text "Submit a project" ]
, p []
[ span [] [ text "Submit a pull request or post an issue to " ]
@@ -191,16 +186,10 @@ viewSidebar model =
, span [] [ text "." ]
]
]
- , div
- [ class [ BuiltBy ]
- ]
+ , div [ css Styles.builtBy ]
[ span [] [ text "Built by " ]
, a [ href "https://github.com/lukewestby", target "_blank" ] [ text "Luke Westby" ]
, span [] [ text " and " ]
, a [ href "https://github.com/elm-community/builtwithelm/graphs/contributors", target "_blank" ] [ text "the amazing Elm community." ]
]
]
-
-
-{ class } =
- Html.CssHelpers.withNamespace builtWithElmNamespace