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