init
This commit is contained in:
26
app/Api.elm
Normal file
26
app/Api.elm
Normal file
@ -0,0 +1,26 @@
|
||||
module Api exposing (routes)
|
||||
|
||||
import ApiRoute exposing (ApiRoute)
|
||||
import BackendTask exposing (BackendTask)
|
||||
import FatalError exposing (FatalError)
|
||||
import Html exposing (Html)
|
||||
import Pages.Manifest as Manifest
|
||||
import Route exposing (Route)
|
||||
|
||||
|
||||
routes :
|
||||
BackendTask FatalError (List Route)
|
||||
-> (Maybe { indent : Int, newLines : Bool } -> Html Never -> String)
|
||||
-> List (ApiRoute ApiRoute.Response)
|
||||
routes getStaticRoutes htmlToString =
|
||||
[]
|
||||
|
||||
|
||||
manifest : Manifest.Config
|
||||
manifest =
|
||||
Manifest.init
|
||||
{ name = "Site Name"
|
||||
, description = "Description"
|
||||
, startUrl = Route.Index |> Route.toPath
|
||||
, icons = []
|
||||
}
|
155
app/Effect.elm
Normal file
155
app/Effect.elm
Normal file
@ -0,0 +1,155 @@
|
||||
module Effect exposing (Effect(..), batch, fromCmd, map, none, perform)
|
||||
|
||||
{-|
|
||||
|
||||
@docs Effect, batch, fromCmd, map, none, perform
|
||||
|
||||
-}
|
||||
|
||||
import Browser.Navigation
|
||||
import Form
|
||||
import Http
|
||||
import Json.Decode as Decode
|
||||
import Pages.Fetcher
|
||||
import Url exposing (Url)
|
||||
|
||||
|
||||
{-| -}
|
||||
type Effect msg
|
||||
= None
|
||||
| Cmd (Cmd msg)
|
||||
| Batch (List (Effect msg))
|
||||
| GetStargazers (Result Http.Error Int -> msg)
|
||||
| SetField { formId : String, name : String, value : String }
|
||||
| FetchRouteData
|
||||
{ data : Maybe FormData
|
||||
, toMsg : Result Http.Error Url -> msg
|
||||
}
|
||||
| Submit
|
||||
{ values : FormData
|
||||
, toMsg : Result Http.Error Url -> msg
|
||||
}
|
||||
| SubmitFetcher (Pages.Fetcher.Fetcher msg)
|
||||
|
||||
|
||||
{-| -}
|
||||
type alias RequestInfo =
|
||||
{ contentType : String
|
||||
, body : String
|
||||
}
|
||||
|
||||
|
||||
{-| -}
|
||||
none : Effect msg
|
||||
none =
|
||||
None
|
||||
|
||||
|
||||
{-| -}
|
||||
batch : List (Effect msg) -> Effect msg
|
||||
batch =
|
||||
Batch
|
||||
|
||||
|
||||
{-| -}
|
||||
fromCmd : Cmd msg -> Effect msg
|
||||
fromCmd =
|
||||
Cmd
|
||||
|
||||
|
||||
{-| -}
|
||||
map : (a -> b) -> Effect a -> Effect b
|
||||
map fn effect =
|
||||
case effect of
|
||||
None ->
|
||||
None
|
||||
|
||||
Cmd cmd ->
|
||||
Cmd (Cmd.map fn cmd)
|
||||
|
||||
Batch list ->
|
||||
Batch (List.map (map fn) list)
|
||||
|
||||
GetStargazers toMsg ->
|
||||
GetStargazers (toMsg >> fn)
|
||||
|
||||
FetchRouteData fetchInfo ->
|
||||
FetchRouteData
|
||||
{ data = fetchInfo.data
|
||||
, toMsg = fetchInfo.toMsg >> fn
|
||||
}
|
||||
|
||||
Submit fetchInfo ->
|
||||
Submit
|
||||
{ values = fetchInfo.values
|
||||
, toMsg = fetchInfo.toMsg >> fn
|
||||
}
|
||||
|
||||
SetField info ->
|
||||
SetField info
|
||||
|
||||
SubmitFetcher fetcher ->
|
||||
fetcher
|
||||
|> Pages.Fetcher.map fn
|
||||
|> SubmitFetcher
|
||||
|
||||
|
||||
{-| -}
|
||||
perform :
|
||||
{ fetchRouteData :
|
||||
{ data : Maybe FormData
|
||||
, toMsg : Result Http.Error Url -> pageMsg
|
||||
}
|
||||
-> Cmd msg
|
||||
, submit :
|
||||
{ values : FormData
|
||||
, toMsg : Result Http.Error Url -> pageMsg
|
||||
}
|
||||
-> Cmd msg
|
||||
, runFetcher :
|
||||
Pages.Fetcher.Fetcher pageMsg
|
||||
-> Cmd msg
|
||||
, fromPageMsg : pageMsg -> msg
|
||||
, key : Browser.Navigation.Key
|
||||
, setField : { formId : String, name : String, value : String } -> Cmd msg
|
||||
}
|
||||
-> Effect pageMsg
|
||||
-> Cmd msg
|
||||
perform ({ fromPageMsg, key } as helpers) effect =
|
||||
case effect of
|
||||
None ->
|
||||
Cmd.none
|
||||
|
||||
Cmd cmd ->
|
||||
Cmd.map fromPageMsg cmd
|
||||
|
||||
SetField info ->
|
||||
helpers.setField info
|
||||
|
||||
Batch list ->
|
||||
Cmd.batch (List.map (perform helpers) list)
|
||||
|
||||
GetStargazers toMsg ->
|
||||
Http.get
|
||||
{ url =
|
||||
"https://api.github.com/repos/dillonkearns/elm-pages"
|
||||
, expect = Http.expectJson (toMsg >> fromPageMsg) (Decode.field "stargazers_count" Decode.int)
|
||||
}
|
||||
|
||||
FetchRouteData fetchInfo ->
|
||||
helpers.fetchRouteData
|
||||
fetchInfo
|
||||
|
||||
Submit record ->
|
||||
helpers.submit record
|
||||
|
||||
SubmitFetcher record ->
|
||||
helpers.runFetcher record
|
||||
|
||||
|
||||
type alias FormData =
|
||||
{ fields : List ( String, String )
|
||||
, method : Form.Method
|
||||
, action : String
|
||||
, id : Maybe String
|
||||
}
|
81
app/ErrorPage.elm
Normal file
81
app/ErrorPage.elm
Normal file
@ -0,0 +1,81 @@
|
||||
module ErrorPage exposing (ErrorPage(..), Model, Msg, head, init, internalError, notFound, statusCode, update, view)
|
||||
|
||||
import Effect exposing (Effect)
|
||||
import Head
|
||||
import Html.Styled as Html
|
||||
import Html.Styled.Events exposing (onClick)
|
||||
import View exposing (View)
|
||||
|
||||
|
||||
type Msg
|
||||
= Increment
|
||||
|
||||
|
||||
type alias Model =
|
||||
{ count : Int
|
||||
}
|
||||
|
||||
|
||||
init : ErrorPage -> ( Model, Effect Msg )
|
||||
init errorPage =
|
||||
( { count = 0 }
|
||||
, Effect.none
|
||||
)
|
||||
|
||||
|
||||
update : ErrorPage -> Msg -> Model -> ( Model, Effect Msg )
|
||||
update errorPage msg model =
|
||||
case msg of
|
||||
Increment ->
|
||||
( { model | count = model.count + 1 }, Effect.none )
|
||||
|
||||
|
||||
head : ErrorPage -> List Head.Tag
|
||||
head errorPage =
|
||||
[]
|
||||
|
||||
|
||||
type ErrorPage
|
||||
= NotFound
|
||||
| InternalError String
|
||||
|
||||
|
||||
notFound : ErrorPage
|
||||
notFound =
|
||||
NotFound
|
||||
|
||||
|
||||
internalError : String -> ErrorPage
|
||||
internalError =
|
||||
InternalError
|
||||
|
||||
|
||||
view : ErrorPage -> Model -> View Msg
|
||||
view error model =
|
||||
{ body =
|
||||
[ Html.div []
|
||||
[ Html.p [] [ Html.text "Page not found. Maybe try another URL?" ]
|
||||
, Html.div []
|
||||
[ Html.button
|
||||
[ onClick Increment
|
||||
]
|
||||
[ Html.text
|
||||
(model.count
|
||||
|> String.fromInt
|
||||
)
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
, title = "This is a NotFound Error"
|
||||
}
|
||||
|
||||
|
||||
statusCode : ErrorPage -> number
|
||||
statusCode error =
|
||||
case error of
|
||||
NotFound ->
|
||||
404
|
||||
|
||||
InternalError _ ->
|
||||
500
|
145
app/Route/Blog/Slug_.elm
Normal file
145
app/Route/Blog/Slug_.elm
Normal file
@ -0,0 +1,145 @@
|
||||
module Route.Blog.Slug_ exposing (ActionData, Data, Model, Msg, route)
|
||||
|
||||
import Article
|
||||
import BackendTask exposing (BackendTask)
|
||||
import Date exposing (Date)
|
||||
import FatalError exposing (FatalError)
|
||||
import Head
|
||||
import Head.Seo as Seo
|
||||
import Html.Styled exposing (..)
|
||||
import Json.Decode as Decode exposing (Decoder)
|
||||
import Json.Decode.Extra
|
||||
import Pages.Url
|
||||
import PagesMsg exposing (PagesMsg)
|
||||
import RouteBuilder exposing (App, StatelessRoute)
|
||||
import Shared
|
||||
import View exposing (View)
|
||||
|
||||
|
||||
import Markdown.Block
|
||||
import Markdown.Renderer
|
||||
import MarkdownCodec
|
||||
import TailwindMarkdownRenderer
|
||||
import Tailwind.Utilities as Tw
|
||||
|
||||
|
||||
type alias Model =
|
||||
{}
|
||||
|
||||
|
||||
type alias Msg =
|
||||
()
|
||||
|
||||
|
||||
type alias RouteParams =
|
||||
{ slug : String }
|
||||
|
||||
|
||||
route : StatelessRoute RouteParams Data ActionData
|
||||
route =
|
||||
RouteBuilder.preRender
|
||||
{ head = head
|
||||
, pages = pages
|
||||
, data = data
|
||||
}
|
||||
|> RouteBuilder.buildNoState { view = view }
|
||||
|
||||
|
||||
pages : BackendTask FatalError (List RouteParams)
|
||||
pages =
|
||||
Article.blogPostsGlob
|
||||
|> BackendTask.map
|
||||
(List.map
|
||||
(\globData ->
|
||||
{ slug = globData.slug }
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
type alias Data =
|
||||
{ metadata : ArticleMetadata
|
||||
, body : List Markdown.Block.Block
|
||||
}
|
||||
|
||||
type alias ActionData =
|
||||
{}
|
||||
|
||||
|
||||
data : RouteParams -> BackendTask FatalError Data
|
||||
data routeParams =
|
||||
MarkdownCodec.withFrontmatter Data
|
||||
frontmatterDecoder
|
||||
TailwindMarkdownRenderer.renderer
|
||||
("content/blog/" ++ routeParams.slug ++ ".md")
|
||||
|
||||
|
||||
type alias ArticleMetadata =
|
||||
{ title : String
|
||||
, description : String
|
||||
, published : Date
|
||||
-- , image : Pages.Url.Url
|
||||
, draft : Bool
|
||||
}
|
||||
|
||||
|
||||
frontmatterDecoder : Decoder ArticleMetadata
|
||||
frontmatterDecoder =
|
||||
Decode.map4 ArticleMetadata
|
||||
(Decode.field "title" Decode.string)
|
||||
(Decode.field "description" Decode.string)
|
||||
(Decode.field "published"
|
||||
(Decode.string
|
||||
|> Decode.andThen
|
||||
(\isoString ->
|
||||
Date.fromIsoString isoString
|
||||
|> Json.Decode.Extra.fromResult
|
||||
)
|
||||
)
|
||||
)
|
||||
-- (Decode.oneOf
|
||||
-- [ Decode.field "image" imageDecoder
|
||||
-- , Decode.field "unsplash" UnsplashImage.decoder |> Decode.map UnsplashImage.imagePath
|
||||
-- ]
|
||||
-- )
|
||||
(Decode.field "draft" Decode.bool
|
||||
|> Decode.maybe
|
||||
|> Decode.map (Maybe.withDefault False)
|
||||
)
|
||||
|
||||
head :
|
||||
App Data ActionData RouteParams
|
||||
-> List Head.Tag
|
||||
head app =
|
||||
Seo.summary
|
||||
{ canonicalUrlOverride = Nothing
|
||||
, siteName = "elm-pages"
|
||||
, image =
|
||||
{ url = Pages.Url.external "TODO"
|
||||
, alt = "elm-pages logo"
|
||||
, dimensions = Nothing
|
||||
, mimeType = Nothing
|
||||
}
|
||||
, description = "TODO"
|
||||
, locale = Nothing
|
||||
, title = "TODO title" -- metadata.title -- TODO
|
||||
}
|
||||
|> Seo.website
|
||||
|
||||
|
||||
view :
|
||||
App Data ActionData RouteParams
|
||||
-> Shared.Model
|
||||
-> View (PagesMsg Msg)
|
||||
view app shared =
|
||||
{ title = "title"
|
||||
, body =
|
||||
(app.data.body
|
||||
|> Markdown.Renderer.render TailwindMarkdownRenderer.renderer
|
||||
|> Result.withDefault []
|
||||
|> processReturn
|
||||
)
|
||||
}
|
||||
|
||||
processReturn : List (Html Msg) -> List (Html (PagesMsg Msg))
|
||||
processReturn =
|
||||
List.map (Html.Styled.map (PagesMsg.fromMsg))
|
94
app/Route/Index.elm
Normal file
94
app/Route/Index.elm
Normal file
@ -0,0 +1,94 @@
|
||||
module Route.Index exposing (ActionData, Data, Model, Msg, route)
|
||||
|
||||
import BackendTask exposing (BackendTask)
|
||||
import FatalError exposing (FatalError)
|
||||
import Head
|
||||
import Head.Seo as Seo
|
||||
import Html.Styled as Html
|
||||
import Link exposing (Link)
|
||||
import Pages.Url
|
||||
import PagesMsg exposing (PagesMsg)
|
||||
import UrlPath
|
||||
import Route
|
||||
import RouteBuilder exposing (App, StatelessRoute)
|
||||
import Shared
|
||||
import View exposing (View)
|
||||
|
||||
|
||||
type alias Model =
|
||||
{}
|
||||
|
||||
|
||||
type alias Msg =
|
||||
()
|
||||
|
||||
|
||||
type alias RouteParams =
|
||||
{}
|
||||
|
||||
|
||||
type alias Data =
|
||||
{ message : String
|
||||
}
|
||||
|
||||
|
||||
type alias ActionData =
|
||||
{}
|
||||
|
||||
|
||||
route : StatelessRoute RouteParams Data ActionData
|
||||
route =
|
||||
RouteBuilder.single
|
||||
{ head = head
|
||||
, data = data
|
||||
}
|
||||
|> RouteBuilder.buildNoState { view = view }
|
||||
|
||||
|
||||
data : BackendTask FatalError Data
|
||||
data =
|
||||
BackendTask.succeed Data
|
||||
|> BackendTask.andMap
|
||||
(BackendTask.succeed "Hello!")
|
||||
|
||||
|
||||
head :
|
||||
App Data ActionData RouteParams
|
||||
-> List Head.Tag
|
||||
head app =
|
||||
Seo.summary
|
||||
{ canonicalUrlOverride = Nothing
|
||||
, siteName = "elm-pages"
|
||||
, image =
|
||||
{ url = [ "images", "icon-png.png" ] |> UrlPath.join |> Pages.Url.fromPath
|
||||
, alt = "elm-pages logo"
|
||||
, dimensions = Nothing
|
||||
, mimeType = Nothing
|
||||
}
|
||||
, description = "Welcome to elm-pages!"
|
||||
, locale = Nothing
|
||||
, title = "elm-pages is running"
|
||||
}
|
||||
|> Seo.website
|
||||
|
||||
|
||||
view :
|
||||
App Data ActionData RouteParams
|
||||
-> Shared.Model
|
||||
-> View (PagesMsg Msg)
|
||||
view app shared =
|
||||
{ title = "elm-pages is running"
|
||||
, body =
|
||||
[ Html.h1 [] [ Html.text "elm-pages is up and running!" ]
|
||||
, Html.p []
|
||||
[ Html.text <| "The message is: " ++ app.data.message
|
||||
]
|
||||
, Link.link (Link.internal (Route.Blog__Slug_ { slug = "a" })) [] [ Html.text "My blog post" ]
|
||||
]
|
||||
|> processReturn
|
||||
}
|
||||
|
||||
|
||||
processReturn : List (Html.Html Msg) -> List (Html.Html (PagesMsg Msg))
|
||||
processReturn =
|
||||
List.map (Html.map (PagesMsg.fromMsg))
|
135
app/Shared.elm
Normal file
135
app/Shared.elm
Normal file
@ -0,0 +1,135 @@
|
||||
module Shared exposing (Data, Model, Msg(..), SharedMsg(..), template)
|
||||
|
||||
import BackendTask exposing (BackendTask)
|
||||
import Effect exposing (Effect)
|
||||
import FatalError exposing (FatalError)
|
||||
import Html exposing (Html)
|
||||
import Html.Styled
|
||||
import Html.Styled.Events
|
||||
import Pages.Flags
|
||||
import Pages.PageUrl exposing (PageUrl)
|
||||
import UrlPath exposing (UrlPath)
|
||||
import Route exposing (Route)
|
||||
import SharedTemplate exposing (SharedTemplate)
|
||||
import View exposing (View)
|
||||
|
||||
|
||||
template : SharedTemplate Msg Model Data msg
|
||||
template =
|
||||
{ init = init
|
||||
, update = update
|
||||
, view = view
|
||||
, data = data
|
||||
, subscriptions = subscriptions
|
||||
, onPageChange = Nothing
|
||||
}
|
||||
|
||||
|
||||
type Msg
|
||||
= SharedMsg SharedMsg
|
||||
| MenuClicked
|
||||
|
||||
|
||||
type alias Data =
|
||||
()
|
||||
|
||||
|
||||
type SharedMsg
|
||||
= NoOp
|
||||
|
||||
|
||||
type alias Model =
|
||||
{ showMenu : Bool
|
||||
}
|
||||
|
||||
|
||||
init :
|
||||
Pages.Flags.Flags
|
||||
->
|
||||
Maybe
|
||||
{ path :
|
||||
{ path : UrlPath
|
||||
, query : Maybe String
|
||||
, fragment : Maybe String
|
||||
}
|
||||
, metadata : route
|
||||
, pageUrl : Maybe PageUrl
|
||||
}
|
||||
-> ( Model, Effect Msg )
|
||||
init flags maybePagePath =
|
||||
( { showMenu = False }
|
||||
, Effect.none
|
||||
)
|
||||
|
||||
|
||||
update : Msg -> Model -> ( Model, Effect Msg )
|
||||
update msg model =
|
||||
case msg of
|
||||
SharedMsg globalMsg ->
|
||||
( model, Effect.none )
|
||||
|
||||
MenuClicked ->
|
||||
( { model | showMenu = not model.showMenu }, Effect.none )
|
||||
|
||||
|
||||
subscriptions : UrlPath -> Model -> Sub Msg
|
||||
subscriptions _ _ =
|
||||
Sub.none
|
||||
|
||||
|
||||
data : BackendTask FatalError Data
|
||||
data =
|
||||
BackendTask.succeed ()
|
||||
|
||||
|
||||
view :
|
||||
Data
|
||||
->
|
||||
{ path : UrlPath
|
||||
, route : Maybe Route
|
||||
}
|
||||
-> Model
|
||||
-> (Msg -> msg)
|
||||
-> View msg
|
||||
-> { body : List (Html msg), title : String }
|
||||
view tableOfContents page model toMsg pageView =
|
||||
{ body =
|
||||
[
|
||||
-- ((View.Header.view ToggleMobileMenu 123 page.path
|
||||
-- |> Html.Styled.map toMsg
|
||||
-- )
|
||||
-- :: TableOfContents.view model.showMobileMenu False Nothing tableOfContents
|
||||
pageView.body
|
||||
-- )
|
||||
|> Html.Styled.div []
|
||||
|> Html.Styled.toUnstyled
|
||||
]
|
||||
, title = pageView.title
|
||||
}
|
||||
-- view sharedData page model toMsg pageView =
|
||||
-- { body =
|
||||
-- [ Html.Styled.nav []
|
||||
-- [ Html.Styled.button
|
||||
-- [ Html.Styled.Events.onClick MenuClicked ]
|
||||
-- [ Html.Styled.text
|
||||
-- (if model.showMenu then
|
||||
-- "Close Menu"
|
||||
|
||||
-- else
|
||||
-- "Open Menu"
|
||||
-- )
|
||||
-- ]
|
||||
-- , if model.showMenu then
|
||||
-- Html.Styled.ul []
|
||||
-- [ Html.Styled.li [] [ Html.Styled.text "Menu item 1" ]
|
||||
-- , Html.Styled.li [] [ Html.Styled.text "Menu item 2" ]
|
||||
-- ]
|
||||
|
||||
-- else
|
||||
-- Html.Styled.text ""
|
||||
-- ]
|
||||
-- |> Html.Styled.map toMsg
|
||||
-- , Html.Styled.main_ [] pageView.body
|
||||
-- ]
|
||||
-- , title = pageView.title
|
||||
-- }
|
21
app/Site.elm
Normal file
21
app/Site.elm
Normal file
@ -0,0 +1,21 @@
|
||||
module Site exposing (config)
|
||||
|
||||
import BackendTask exposing (BackendTask)
|
||||
import FatalError exposing (FatalError)
|
||||
import Head
|
||||
import SiteConfig exposing (SiteConfig)
|
||||
|
||||
|
||||
config : SiteConfig
|
||||
config =
|
||||
{ canonicalUrl = "https://elm-pages.com"
|
||||
, head = head
|
||||
}
|
||||
|
||||
|
||||
head : BackendTask FatalError (List Head.Tag)
|
||||
head =
|
||||
[ Head.metaName "viewport" (Head.raw "width=device-width,initial-scale=1")
|
||||
, Head.sitemapLink "/sitemap.xml"
|
||||
]
|
||||
|> BackendTask.succeed
|
24
app/View.elm
Normal file
24
app/View.elm
Normal file
@ -0,0 +1,24 @@
|
||||
module View exposing (View, map)
|
||||
|
||||
{-|
|
||||
|
||||
@docs View, map
|
||||
|
||||
-}
|
||||
|
||||
import Html.Styled as Html exposing (Html)
|
||||
|
||||
|
||||
{-| -}
|
||||
type alias View msg =
|
||||
{ title : String
|
||||
, body : List (Html msg)
|
||||
}
|
||||
|
||||
|
||||
{-| -}
|
||||
map : (msg1 -> msg2) -> View msg1 -> View msg2
|
||||
map fn doc =
|
||||
{ title = doc.title
|
||||
, body = List.map (Html.map fn) doc.body
|
||||
}
|
Reference in New Issue
Block a user