diff --git a/elm.json b/elm.json index 5bfb661..2c214b4 100644 --- a/elm.json +++ b/elm.json @@ -9,7 +9,8 @@ "elm/browser": "1.0.1", "elm/core": "1.0.2", "elm/html": "1.0.0", - "elm/url": "1.0.0" + "elm/url": "1.0.0", + "elm-community/maybe-extra": "5.1.0" }, "indirect": { "elm/json": "1.1.2", @@ -25,4 +26,4 @@ "elm/random": "1.0.0" } } -} \ No newline at end of file +} diff --git a/src/Main.elm b/src/Main.elm index 3123125..e9b64f3 100644 --- a/src/Main.elm +++ b/src/Main.elm @@ -1,11 +1,12 @@ -module Main exposing (Model, Msg(..), codeOfConduct, init, main, update, view) +module Main exposing (main) import Browser exposing (UrlRequest) import Browser.Dom -import Browser.Navigation -import Html exposing (Html, a, div, footer, h1, h2, h3, h5, iframe, img, li, main_, nav, p, section, span, text, ul) +import Browser.Navigation as Nav +import Html exposing (Html, a, div, footer, h1, h2, h3, iframe, img, li, main_, nav, p, section, span, text, ul) import Html.Attributes exposing (alt, class, href, id, src, style, target) -import Html.Events exposing (onClick) +import Maybe.Extra as Maybe +import Route exposing (Route(..)) import Task import Url exposing (Url) @@ -15,14 +16,14 @@ import Url exposing (Url) type alias Model = - { navigationKey : Browser.Navigation.Key - , url : Url + { navigationKey : Nav.Key + , route : Route } -init : () -> Url -> Browser.Navigation.Key -> ( Model, Cmd Msg ) +init : () -> Url -> Nav.Key -> ( Model, Cmd Msg ) init _ url key = - ( { navigationKey = key, url = url }, Cmd.none ) + ( { navigationKey = key, route = Route.fromUrl url }, Cmd.none ) @@ -30,46 +31,54 @@ init _ url key = type Msg - = OnUrlRequest UrlRequest + = NoOp + | OnUrlRequest UrlRequest | OnUrlChange Url - | NavigateTo String - | ScrollTo String - | NoOp + | ScrollTo (Result Browser.Dom.Error Browser.Dom.Element) -scrollTo : Float -> Float -> Cmd Msg -scrollTo x y = - Task.attempt (\_ -> NoOp) (Browser.Dom.setViewport x y) - -scrollToById : String -> Cmd Msg -scrollToById id = - Browser.Dom.getElement id - |> Task.andThen (\info -> Browser.Dom.setViewport 0 info.element.y) - |> Task.attempt (\_ -> NoOp) - update : Msg -> Model -> ( Model, Cmd Msg ) update msg model = case msg of - OnUrlRequest _ -> - ( model, Cmd.none ) + OnUrlRequest urlRequest -> + case urlRequest of + Browser.Internal url -> + ( model, Nav.pushUrl model.navigationKey (Url.toString url) ) - OnUrlChange url -> - ( { model | url = url }, Cmd.none ) + Browser.External href -> + ( model, Nav.load href ) - NavigateTo urlString -> - ( model, Cmd.batch [ scrollTo 0.0 0.0 , Browser.Navigation.pushUrl model.navigationKey urlString] ) + OnUrlChange url -> + ( { model | route = Route.fromUrl url } + , [ Browser.Dom.getElement >> Task.attempt ScrollTo + , Browser.Dom.focus >> Task.attempt (\_ -> NoOp) + ] + |> List.map (ifFragment url.fragment) + |> Cmd.batch + ) - ScrollTo id -> + ScrollTo (Ok element) -> ( model - , Cmd.batch - [ - Browser.Navigation.pushUrl model.navigationKey ("../#" ++ id) - , scrollToById id - ] + , Browser.Dom.setViewport element.element.x element.element.y + |> Task.perform (\_ -> NoOp) ) + ScrollTo (Err _) -> + ( model, Cmd.none ) + NoOp -> - ( model, Cmd.none ) + ( model, Cmd.none ) + + +ifFragment maybeFragment applyFunction = + let + defaultPlacementCmd = + Browser.Dom.setViewport 0 0 + |> Task.perform (\_ -> NoOp) + in + Maybe.unwrap defaultPlacementCmd applyFunction maybeFragment + + ---- VIEW ---- @@ -84,48 +93,48 @@ sponsorshipPath = "/sponsorship" -view : Model -> Html Msg +view : Model -> Browser.Document Msg view model = - if model.url.path == codeOfConductPath then - codeOfConduct - - else if model.url.path == sponsorshipPath then - sponsorship - - else - mainContent - - -mainContent : Html Msg -mainContent = - main_ - [ ] - [ navigationContent - , homeContent - , detailsContent - , divider - , grantsInfoSection - , divider - , newsletterContent - , divider - , speakersContent - , divider - , sponsorsContent - , divider - , footerContent - ] - - -homeContent : Html Msg -homeContent = - section - [ id "home" ] - [ div [ class "hero__headline" ] - [ h1 [ class "a11y-hidden" ] [ text "Elm In the Spring 2020" ] - , img [ src "%PUBLIC_URL%/images/hero-logo.png", alt "Elm In the Spring" ] [] + case model.route of + Sponsorship -> + viewSponsorship model + + CodeOfConduct -> + viewCodeOfConduct model + + _ -> + viewHome model + + +viewHome : Model -> Browser.Document Msg +viewHome _ = + { title = "Elm in the Spring - Conference 2020" + , body = + [ main_ + [] + [ navigationContent + , section + [ id "home" ] + [ div [ class "hero__headline" ] + [ h1 [ class "a11y-hidden" ] [ text "Elm In the Spring 2020" ] + , img [ src "%PUBLIC_URL%/images/hero-logo.png", alt "Elm In the Spring" ] [] + ] + , div [ class "ribbon" ] [ text "Chicago ❀ May 1, 2020" ] + ] + , detailsContent + , divider + , grantsInfoSection + , divider + , newsletterContent + , divider + , speakersContent + , divider + , sponsorsContent + , divider + , footerContent ] - , div [ class "ribbon" ] [ text "Chicago ❀ May 1, 2020" ] ] + } detailsContent : Html Msg @@ -138,7 +147,7 @@ detailsContent = [ h2 [] [ text "All Elm, all day!" ] , p [] [ text "Elm in the Spring is a single-track, single-day conference for developers who love Elm. Whether you’re an Elm expert scaling up your production app or you're just starting out with your first Elm project, join us for a great day of learning, teaching, and community" ] , p [] [ text "Elm in the Spring 2020 will take place on Friday, May 1st at the ", a [ href "https://www.google.com/maps?q=Newberry+Library+Chicago", target "_blank", class "animate" ] [ text "Newberry Library" ], span [] [ text " in Chicago." ] ] - , p [] [ text "All attendees are expected to observe the conference ", a [ href codeOfConductPath, onClick (NavigateTo codeOfConductPath), class "animate" ] [ text "Code of Conduct" ], span [] [ text "." ] ] + , p [] [ text "All attendees are expected to observe the conference ", a [ href codeOfConductPath, class "animate" ] [ text "Code of Conduct" ], span [] [ text "." ] ] , a [ class "btn btn--yellow", href "https://ti.to/elm-in-the-spring/chicago-2020/", target "_blank" ] [ text "Get Your Tickets" ] ] ] @@ -157,68 +166,68 @@ grantsInfoSection = speakersContent : Html Msg speakersContent = section - [id "speakers"] - [div [class "content"] - [ h1 [class "callout left"] [text "Speakers"] - , div [class "speakers"] [ - div [class "speaker columns"] [ - div [class "speaker__profile_img is-half column", style "background-image" "url(%PUBLIC_URL%/images/speakers/emma.jpg)", style "background-position" "top center"] [ - h3 [] [text "Emma Cunningham"] + [ id "speakers" ] + [ div [ class "content" ] + [ h1 [ class "callout left" ] [ text "Speakers" ] + , div [ class "speakers" ] + [ div [ class "speaker columns" ] + [ div [ class "speaker__profile_img is-half column", style "background-image" "url(%PUBLIC_URL%/images/speakers/emma.jpg)", style "background-position" "top center" ] + [ h3 [] [ text "Emma Cunningham" ] ] - , div [class "speaker__bio is-half column"] [ - div [class "speaker__social"] [ - a [href "https://gitlab.com/emmacunningham", target "_blank"] [span [class "fab fa-gitlab"] []] - , a [href "https://twitter.com/emmatcu", target "_blank"] [span [class "fab fa-twitter"] []] - ] - , div [class "highlights"] [span [class "highlight"] [text "Keynote Speaker"]] - , p [] [text "Emma Cunningham is a formal semanticist turned software engineer who currently is interested in thinking about distributed systems, data pipeline tooling, data visualization, and optimizing queries both for speed and semantic value. As a former linguist, they often think about how expressive type systems, reliable error messaging, and higher order logic can help solve these concerns in a maintainable and scalable manner. As a human being, their passions are in cooperation, abolition, magic, fermentation, descriptivist grammars, and a just transition away from extractive economies."] + , div [ class "speaker__bio is-half column" ] + [ div [ class "speaker__social" ] + [ a [ href "https://gitlab.com/emmacunningham", target "_blank" ] [ span [ class "fab fa-gitlab" ] [] ] + , a [ href "https://twitter.com/emmatcu", target "_blank" ] [ span [ class "fab fa-twitter" ] [] ] ] + , div [ class "highlights" ] [ span [ class "highlight" ] [ text "Keynote Speaker" ] ] + , p [] [ text "Emma Cunningham is a formal semanticist turned software engineer who currently is interested in thinking about distributed systems, data pipeline tooling, data visualization, and optimizing queries both for speed and semantic value. As a former linguist, they often think about how expressive type systems, reliable error messaging, and higher order logic can help solve these concerns in a maintainable and scalable manner. As a human being, their passions are in cooperation, abolition, magic, fermentation, descriptivist grammars, and a just transition away from extractive economies." ] + ] ] - , div [class "speaker columns"] [ - div [class "speaker__profile_img is-half column", style "background-image" "url(%PUBLIC_URL%/images/speakers/yonatan.jpg)", style "background-position" "top center"] [ - h3 [] [text "Yonatan Kogan"] + , div [ class "speaker columns" ] + [ div [ class "speaker__profile_img is-half column", style "background-image" "url(%PUBLIC_URL%/images/speakers/yonatan.jpg)", style "background-position" "top center" ] + [ h3 [] [ text "Yonatan Kogan" ] ] - , div [class "speaker__bio is-half column"] [ - div [class "speaker__social"] [ - a [href "https://github.com/yjkogan", target "_blank"] [span [class "fab fa-github"] []] - ,a [href "https://twitter.com/yjkogan", target "_blank"] [span [class "fab fa-twitter"] []] + , div [ class "speaker__bio is-half column" ] + [ div [ class "speaker__social" ] + [ a [ href "https://github.com/yjkogan", target "_blank" ] [ span [ class "fab fa-github" ] [] ] + , a [ href "https://twitter.com/yjkogan", target "_blank" ] [ span [ class "fab fa-twitter" ] [] ] ] - , div [class "highlights"] [ - span [class "highlight talk"] [text "Put your Model in the Cloud"] - , span [class "highlight info small"] [text "Co-Presenter"] - ] - , p [] [text "Yonatan is originally from San Francisco and now lives in Columbia, SC. He is a Senior Software Engineer at ActBlue, an online fundraising platform for Democratic candidates up and down the ballot, progressive organizations, and nonprofits. He previously worked at Tock, a Chicago-based restaurant reservations company, Optimizely, an A/B testing platform, and Romotive, a Sequoia-backed robotics startup. He is also a founding member of GenderAvenger, a community dedicated to ensuring women are represented in the public dialog."] - , p [] [text "Outside of work, Yonatan enjoys cooking, biking, sci-fi, watching Jeopardy, and working to improve his community."] + , div [ class "highlights" ] + [ span [ class "highlight talk" ] [ text "Put your Model in the Cloud" ] + , span [ class "highlight info small" ] [ text "Co-Presenter" ] ] + , p [] [ text "Yonatan is originally from San Francisco and now lives in Columbia, SC. He is a Senior Software Engineer at ActBlue, an online fundraising platform for Democratic candidates up and down the ballot, progressive organizations, and nonprofits. He previously worked at Tock, a Chicago-based restaurant reservations company, Optimizely, an A/B testing platform, and Romotive, a Sequoia-backed robotics startup. He is also a founding member of GenderAvenger, a community dedicated to ensuring women are represented in the public dialog." ] + , p [] [ text "Outside of work, Yonatan enjoys cooking, biking, sci-fi, watching Jeopardy, and working to improve his community." ] + ] ] - , div [class "speaker columns"] [ - div [class "speaker__profile_img is-half column", style "background-image" "url(%PUBLIC_URL%/images/speakers/jacob.jpg)", style "background-position" "50% 15%"] [ - h3 [] [text "Jacob Matthews"] + , div [ class "speaker columns" ] + [ div [ class "speaker__profile_img is-half column", style "background-image" "url(%PUBLIC_URL%/images/speakers/jacob.jpg)", style "background-position" "50% 15%" ] + [ h3 [] [ text "Jacob Matthews" ] ] - , div [class "speaker__bio is-half column"] [ - div [class "speaker__social"] [ - a [href "https://twitter.com/jmatthews", target "_blank"] [span [class "fab fa-twitter"] []] - ], - div [class "highlights"] [ - span [class "highlight talk"] [text "Put your Model in the Cloud"] - , span [class "highlight info small"] [text "Co-Presenter"] + , div [ class "speaker__bio is-half column" ] + [ div [ class "speaker__social" ] + [ a [ href "https://twitter.com/jmatthews", target "_blank" ] [ span [ class "fab fa-twitter" ] [] ] ] - , p [] [text "Jacob Matthews is a senior staff software engineer at Tock. Before becoming a professional programmer, he was a computer scientist who studied functional programming. He gravitated to Elm as a way of combining these two interests. When he's not programming, Jacob likes performing improv and playing with his baby. "] + , div [ class "highlights" ] + [ span [ class "highlight talk" ] [ text "Put your Model in the Cloud" ] + , span [ class "highlight info small" ] [ text "Co-Presenter" ] ] + , p [] [ text "Jacob Matthews is a senior staff software engineer at Tock. Before becoming a professional programmer, he was a computer scientist who studied functional programming. He gravitated to Elm as a way of combining these two interests. When he's not programming, Jacob likes performing improv and playing with his baby. " ] + ] ] - , div [class "speaker columns"] [ - div [class "speaker__profile_img is-half column", style "background-image" "url(%PUBLIC_URL%/images/curtains.jpg)", style "background-position" "50% 80%"] [ - h3 [] [text "You?"] + , div [ class "speaker columns" ] + [ div [ class "speaker__profile_img is-half column", style "background-image" "url(%PUBLIC_URL%/images/curtains.jpg)", style "background-position" "50% 80%" ] + [ h3 [] [ text "You?" ] + ] + , div [ class "speaker__bio is-half column" ] + [ div [ class "highlights" ] [ span [ class "highlight" ] [ text "More Speakers Coming Soon!" ] ] + , p [] [ text "Want to share your idea? ", a [ href "https://www.papercall.io/elm-in-the-spring-2020", target "_blank" ] [ text "Submit a talk!" ] ] + , p [] [ text "Elm in the Spring welcomes new and seasoned speakers to give a talk in Chicago! Each talk slot is 30 minutes. We’re reserving a minimum of two (2) talk spots for first-time speakers." ] + , p [] [ text "Want to see last year’s talks? Check out our ", a [ href "https://www.youtube.com/elminthespring", target "_blank" ] [ text "Youtube channel!" ] ] ] - , div [class "speaker__bio is-half column"] [ - div [class "highlights"] [span [class "highlight"] [text "More Speakers Coming Soon!"]] - , p [] [text "Want to share your idea? ", a [href "https://www.papercall.io/elm-in-the-spring-2020", target "_blank"] [text "Submit a talk!"]] - , p [] [text "Elm in the Spring welcomes new and seasoned speakers to give a talk in Chicago! Each talk slot is 30 minutes. We’re reserving a minimum of two (2) talk spots for first-time speakers."] - , p [] [text "Want to see last year’s talks? Check out our ", a [href "https://www.youtube.com/elminthespring", target "_blank"] [text "Youtube channel!"]] - ] ] - ] ] + ] ] @@ -230,7 +239,7 @@ sponsorsContent = [ h1 [ class "callout right" ] [ text "Sponsors" ] , div [ class "copy" ] [ h2 [] [ text "Thank you to our Sponsors" ] - , p [] [ text "Without the support, involvement, and enthusiasm of generous sponsors and supporters, Elm in the Spring would not be possible . You or your company can become a sponsor for Elm in the Spring 2020. For more info, check out ", a [ href sponsorshipPath, onClick (NavigateTo sponsorshipPath), class "animate black" ] [ text " becoming a sponsor." ] ] + , p [] [ text "Without the support, involvement, and enthusiasm of generous sponsors and supporters, Elm in the Spring would not be possible . You or your company can become a sponsor for Elm in the Spring 2020. For more info, check out ", a [ href sponsorshipPath, class "animate black" ] [ text " becoming a sponsor." ] ] ] , div [ class "sponsors columns" ] [ div [ class "sponsor column is-one-third" ] [ img [ src "%PUBLIC_URL%/images/sponsors/espark-logo.svg", alt "eSpark Learning" ] [] ] @@ -247,10 +256,10 @@ navigationContent = [ class "menu" ] [ ul [] [ li [] - [ a [ href "#details", onClick (ScrollTo "details") ] [ text "Details" ] + [ a [ href "/#details" ] [ text "Details" ] ] , li [] - [ a [ href "#speakers", onClick (ScrollTo "speakers") ] [ text "Speakers" ] + [ a [ href "/#speakers" ] [ text "Speakers" ] ] , li [] [ a [ href "https://www.papercall.io/elm-in-the-spring-2020", target "_blank" ] [ text "Submit A Talk" ] @@ -295,7 +304,7 @@ standAlonePageHeader : Html Msg standAlonePageHeader = div [ class "hero" ] [ div [ class "logo-container" ] - [ a [ href "./#home", onClick (NavigateTo "./#home") ] [ img [ src "%PUBLIC_URL%/images/hero-logo.png", alt "Elm In the Spring", class "logo" ] [] ] ] + [ a [ href "/" ] [ img [ src "%PUBLIC_URL%/images/hero-logo.png", alt "Elm In the Spring", class "logo" ] [] ] ] , navigationContent ] @@ -311,65 +320,73 @@ wrapContentInStandAlonePage innerHtml = ] -codeOfConduct : Html Msg -codeOfConduct = - main_ [ class "page--stand-alone" ] - [ wrapContentInStandAlonePage - (div [ class "container container--wrapper" ] - [ h1 [ id "conference-code-of-conduct" ] [ text "Conference Code of Conduct" ] - , p [] [ text "All attendees, speakers, sponsors and volunteers at our conference are required to agree with the following code of conduct. Organisers will enforce this code throughout the event. We expect cooperation from all participants to help ensure a safe environment for everybody." ] - , h2 [ id "need-help" ] [ text "Need Help?" ] - , p [] - [ text "In advance of the conference, please contact the organizing team at " - , a [ href "mailto:hello@elminthespring.org", target "_blank", class "animate black" ] [ text "hello@elminthespring.org" ] - , text " any time you need help or have questions or concerns. In the weeks before the conference, we’ll send out direct contact info for the organizers so you can reach out to us in real time via text, phone, or Twitter." +viewCodeOfConduct : Model -> Browser.Document Msg +viewCodeOfConduct _ = + { title = "Elm in the Spring - Conference 2020 - Code of Conduct" + , body = + [ main_ [ class "page--stand-alone" ] + [ wrapContentInStandAlonePage + (div [ class "container container--wrapper" ] + [ h1 [ id "conference-code-of-conduct" ] [ text "Conference Code of Conduct" ] + , p [] [ text "All attendees, speakers, sponsors and volunteers at our conference are required to agree with the following code of conduct. Organisers will enforce this code throughout the event. We expect cooperation from all participants to help ensure a safe environment for everybody." ] + , h2 [ id "need-help" ] [ text "Need Help?" ] + , p [] + [ text "In advance of the conference, please contact the organizing team at " + , a [ href "mailto:hello@elminthespring.org", target "_blank", class "animate black" ] [ text "hello@elminthespring.org" ] + , text " any time you need help or have questions or concerns. In the weeks before the conference, we’ll send out direct contact info for the organizers so you can reach out to us in real time via text, phone, or Twitter." + ] + , h2 [ id "the-quick-version" ] [ text "The Quick Version" ] + , p [] [ text "Our conference is dedicated to providing a harassment-free conference experience for everyone, regardless of gender, gender identity and expression, age, sexual orientation, disability, physical appearance, body size, race, ethnicity, religion (or lack thereof), or technology choices. We do not tolerate harassment of conference participants in any form. Sexual language and imagery is not appropriate for any conference venue, including talks, workshops, parties, Twitter and other online media. Conference participants violating these rules may be sanctioned or expelled from the conference without a refund at the discretion of the conference organisers." ] + , h2 [ id "the-less-quick-version" ] [ text "The Less Quick Version" ] + , p [] [ text "Harassment includes offensive verbal comments related to gender, gender identity and expression, age, sexual orientation, disability, physical appearance, body size, race, ethnicity, religion, technology choices, sexual images in public spaces, deliberate intimidation, stalking, following, harassing photography or recording, sustained disruption of talks or other events, inappropriate physical contact, and unwelcome sexual attention. " ] + , p [] [ text "Participants asked to stop any harassing behavior are expected to comply immediately. " ] + , p [] [ text "Sponsors are also subject to the anti-harassment policy. In particular, sponsors should not use sexualised images, activities, or other material. Booth staff (including volunteers) should not use sexualised clothing/uniforms/costumes, or otherwise create a sexualised environment. " ] + , p [] [ text "If a participant engages in harassing behavior, the conference organisers may take any action they deem appropriate, including warning the offender or expulsion from the conference with no refund. " ] + , p [] + [ text "If you are being harassed, notice that someone else is being harassed, or have any other concerns, please contact a member of conference staff immediately." ] + , p [] [ text " Conference staff can be identified as they'll be wearing branded clothing and/or badges. " ] + , p [] [ text "Conference staff will be happy to help participants contact hotel/venue security or local law enforcement, provide escorts, or otherwise assist those experiencing harassment to feel safe for the duration of the conference. We value your attendance. " ] + , p [] [ text "We expect participants to follow these rules at conference and workshop venues and conference-related social events." ] ] - , h2 [ id "the-quick-version" ] [ text "The Quick Version" ] - , p [] [ text "Our conference is dedicated to providing a harassment-free conference experience for everyone, regardless of gender, gender identity and expression, age, sexual orientation, disability, physical appearance, body size, race, ethnicity, religion (or lack thereof), or technology choices. We do not tolerate harassment of conference participants in any form. Sexual language and imagery is not appropriate for any conference venue, including talks, workshops, parties, Twitter and other online media. Conference participants violating these rules may be sanctioned or expelled from the conference without a refund at the discretion of the conference organisers." ] - , h2 [ id "the-less-quick-version" ] [ text "The Less Quick Version" ] - , p [] [ text "Harassment includes offensive verbal comments related to gender, gender identity and expression, age, sexual orientation, disability, physical appearance, body size, race, ethnicity, religion, technology choices, sexual images in public spaces, deliberate intimidation, stalking, following, harassing photography or recording, sustained disruption of talks or other events, inappropriate physical contact, and unwelcome sexual attention. " ] - , p [] [ text "Participants asked to stop any harassing behavior are expected to comply immediately. " ] - , p [] [ text "Sponsors are also subject to the anti-harassment policy. In particular, sponsors should not use sexualised images, activities, or other material. Booth staff (including volunteers) should not use sexualised clothing/uniforms/costumes, or otherwise create a sexualised environment. " ] - , p [] [ text "If a participant engages in harassing behavior, the conference organisers may take any action they deem appropriate, including warning the offender or expulsion from the conference with no refund. " ] - , p [] - [ text "If you are being harassed, notice that someone else is being harassed, or have any other concerns, please contact a member of conference staff immediately." ] - , p [] [ text " Conference staff can be identified as they'll be wearing branded clothing and/or badges. " ] - , p [] [ text "Conference staff will be happy to help participants contact hotel/venue security or local law enforcement, provide escorts, or otherwise assist those experiencing harassment to feel safe for the duration of the conference. We value your attendance. " ] - , p [] [ text "We expect participants to follow these rules at conference and workshop venues and conference-related social events." ] - ] - ) + ) + ] ] + } -sponsorship : Html Msg -sponsorship = - main_ [ class "page--stand-alone" ] - [ wrapContentInStandAlonePage - (div [ class "container container--wrapper" ] - [ h1 [] [ text "Sponsorship" ] - , p [] [ text "Interested in supporting the community? Sponsor Elm in the Spring! Contact us at ", a [ href "mailto:hello@elminthespring.org", target "_blank", class "animate black" ] [ text "hello@elminthespring.org" ], span [] [ text " for more information." ] ] - , h2 [] [ text "Old Grove" ] - , p [] [ text "Price: $2,500" ] - , ul [] - [ li [] [ text "20% discount on ticket purchases" ] - , li [] [ text "On-stage banner and speaker introduction opportunity. Limited space, first come first served!" ] - , li [] [ text "Logo included in videos and displayed on presentation screen between talks" ] - ] - , h2 [] [ text "Shade Tree" ] - , p [] [ text "Price: $1,000" ] - , ul [] - [ li [] [ text "15% discount on ticket purchases" ] - , li [] [ text "Special thank-you from the organizers during announcements" ] - , li [] [ text "Logo displayed on presentation screen between talks" ] - ] - , h2 [] [ text "Spring Sapling" ] - , p [] [ text "Price: $500" ] - , ul [] - [ li [] [ text "10% discount on ticket purchases" ] +viewSponsorship : Model -> Browser.Document Msg +viewSponsorship _ = + { title = "Elm in the Spring - Conference 2020 - Sponsorship" + , body = + [ main_ [ class "page--stand-alone" ] + [ wrapContentInStandAlonePage + (div [ class "container container--wrapper" ] + [ h1 [] [ text "Sponsorship" ] + , p [] [ text "Interested in supporting the community? Sponsor Elm in the Spring! Contact us at ", a [ href "mailto:hello@elminthespring.org", target "_blank", class "animate black" ] [ text "hello@elminthespring.org" ], span [] [ text " for more information." ] ] + , h2 [] [ text "Old Grove" ] + , p [] [ text "Price: $2,500" ] + , ul [] + [ li [] [ text "20% discount on ticket purchases" ] + , li [] [ text "On-stage banner and speaker introduction opportunity. Limited space, first come first served!" ] + , li [] [ text "Logo included in videos and displayed on presentation screen between talks" ] + ] + , h2 [] [ text "Shade Tree" ] + , p [] [ text "Price: $1,000" ] + , ul [] + [ li [] [ text "15% discount on ticket purchases" ] + , li [] [ text "Special thank-you from the organizers during announcements" ] + , li [] [ text "Logo displayed on presentation screen between talks" ] + ] + , h2 [] [ text "Spring Sapling" ] + , p [] [ text "Price: $500" ] + , ul [] + [ li [] [ text "10% discount on ticket purchases" ] + ] ] - ] - ) + ) + ] ] + } @@ -379,11 +396,7 @@ sponsorship = main : Program () Model Msg main = Browser.application - { view = - \model -> - { title = "Elm in the Spring" - , body = [ view model ] - } + { view = view , init = init , update = update , subscriptions = always Sub.none diff --git a/src/Route.elm b/src/Route.elm new file mode 100644 index 0000000..272b350 --- /dev/null +++ b/src/Route.elm @@ -0,0 +1,63 @@ +module Route exposing (Route(..), fromUrl, href, toString) + +import Html exposing (Attribute) +import Html.Attributes as Attr +import Maybe.Extra as Maybe +import Url exposing (Url) +import Url.Builder exposing (Root(..)) +import Url.Parser as Parser exposing ((), Parser, fragment, s, top) + + +type alias SectionId = + Maybe String + + +type Route + = Root + | NotFound + | Home SectionId + | Sponsorship + | CodeOfConduct + + +toString : Route -> String +toString route = + case route of + Root -> + "/" + + NotFound -> + toString Root + + Home sectionId -> + Maybe.unwrap + (toString Root) + (Url.Builder.custom Relative [] []) + (Just sectionId) + + Sponsorship -> + "/sponsorship" + + CodeOfConduct -> + "/code-of-conduct" + + +parser : Parser (Route -> a) a +parser = + Parser.oneOf + [ Parser.map Root top + , Parser.map Home (top fragment identity) + , Parser.map Sponsorship (s "sponsorship") + , Parser.map CodeOfConduct (s "code-of-conduct") + ] + + +fromUrl : Url -> Route +fromUrl url = + Parser.parse parser url + |> Maybe.withDefault Root + + +href : Route -> Attribute msg +href targetRoute = + Attr.href (toString targetRoute)