From b82cafc338b122eaa992c14bba42e69389fbc4d5 Mon Sep 17 00:00:00 2001 From: James Hillyerd Date: Fri, 15 Feb 2019 18:47:05 -0800 Subject: [PATCH 1/9] ui: mailbox input to end of nav for kbd sanity --- ui/src/Layout.elm | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ui/src/Layout.elm b/ui/src/Layout.elm index 18f1d6f..bd93c53 100644 --- a/ui/src/Layout.elm +++ b/ui/src/Layout.elm @@ -51,7 +51,14 @@ frame controls session activePage modal content = , span [ class "navbar-brand" ] [ a [ Route.href Route.Home ] [ text "@ inbucket" ] ] , ul [ classList [ ( "main-nav", True ), ( "active", controls.showMenu ) ] ] - [ li [ class "navbar-mailbox" ] + [ if session.config.monitorVisible then + navbarLink Monitor Route.Monitor [ text "Monitor" ] activePage + + else + text "" + , navbarLink Status Route.Status [ text "Status" ] activePage + , navbarRecent activePage controls + , li [ class "navbar-mailbox" ] [ form [ Events.onSubmit (controls.viewMailbox controls.mailboxValue) ] [ input [ type_ "text" @@ -62,13 +69,6 @@ frame controls session activePage modal content = [] ] ] - , if session.config.monitorVisible then - navbarLink Monitor Route.Monitor [ text "Monitor" ] activePage - - else - text "" - , navbarLink Status Route.Status [ text "Status" ] activePage - , navbarRecent activePage controls ] ] ] From d933d591d83b363942436ce5f1493722301f22a0 Mon Sep 17 00:00:00 2001 From: James Hillyerd Date: Fri, 15 Feb 2019 19:50:46 -0800 Subject: [PATCH 2/9] ui: Add button to open recent menu via keyboard --- ui/src/Layout.elm | 45 ++++++++++++++++++++++++++++++++++++++------- ui/src/Main.elm | 22 +++++++++++++++------- ui/src/navbar.css | 15 ++++++++++++++- 3 files changed, 67 insertions(+), 15 deletions(-) diff --git a/ui/src/Layout.elm b/ui/src/Layout.elm index bd93c53..1c4ef2c 100644 --- a/ui/src/Layout.elm +++ b/ui/src/Layout.elm @@ -30,14 +30,16 @@ type Page type alias FrameControls msg = - { viewMailbox : String -> msg + { menuVisible : Bool + , toggleMenu : msg + , recentVisible : Bool + , showRecent : Bool -> msg + , viewMailbox : String -> msg , mailboxOnInput : String -> msg , mailboxValue : String , recentOptions : List String , recentActive : String , clearFlash : msg - , showMenu : Bool - , toggleMenu : msg } @@ -50,7 +52,7 @@ frame controls session activePage modal content = [ i [ class "fas fa-bars" ] [] ] , span [ class "navbar-brand" ] [ a [ Route.href Route.Home ] [ text "@ inbucket" ] ] - , ul [ classList [ ( "main-nav", True ), ( "active", controls.showMenu ) ] ] + , ul [ class "main-nav", classList [ ( "active", controls.menuVisible ) ] ] [ if session.config.monitorVisible then navbarLink Monitor Route.Monitor [ text "Monitor" ] activePage @@ -137,6 +139,7 @@ navbarLink page route linkContent activePage = navbarRecent : Page -> FrameControls msg -> Html msg navbarRecent page controls = let + -- Active means we are viewing a specific mailbox. active = page == Mailbox @@ -156,13 +159,41 @@ navbarRecent page controls = else controls.recentOptions + dropdownExpanded = + if controls.recentVisible then + "true" + + else + "false" + recentLink mailbox = a [ Route.href (Route.Mailbox mailbox) ] [ text mailbox ] in li - [ class "navbar-recent" - , classList [ ( "navbar-dropdown", True ), ( "navbar-active", active ) ] + [ class "navbar-recent navbar-dropdown" + , classList [ ( "navbar-active", active ) ] + , attribute "aria-haspopup" "true" + , ariaExpanded controls.recentVisible + , Events.onMouseOver (controls.showRecent True) + , Events.onMouseOut (controls.showRecent False) ] - [ span [ class "navbar-active-bg" ] [ text title ] + [ span [ class "navbar-active-bg" ] + [ text title + , button + [ class "navbar-dropdown-button" + , Events.onClick (controls.showRecent (not controls.recentVisible)) + ] + [ i [ class "fas fa-chevron-down" ] [] ] + ] , div [ class "navbar-dropdown-content" ] (List.map recentLink recentMailboxes) ] + + +ariaExpanded : Bool -> Attribute msg +ariaExpanded value = + attribute "aria-expanded" <| + if value then + "true" + + else + "false" diff --git a/ui/src/Main.elm b/ui/src/Main.elm index 10b50d9..858c781 100644 --- a/ui/src/Main.elm +++ b/ui/src/Main.elm @@ -25,7 +25,8 @@ import Url exposing (Url) type alias Model = { page : PageModel , mailboxName : String - , showMenu : Bool + , menuVisible : Bool + , recentVisible : Bool } @@ -64,7 +65,8 @@ init configValue location key = initModel = { page = Home subModel , mailboxName = "" - , showMenu = False + , menuVisible = False + , recentVisible = False } route = @@ -85,6 +87,7 @@ type Msg | OnMailboxNameInput String | ViewMailbox String | ToggleMenu + | ShowRecent Bool | HomeMsg Home.Msg | MailboxMsg Mailbox.Msg | MonitorMsg Monitor.Msg @@ -217,7 +220,10 @@ updateMain msg model session = ) ToggleMenu -> - ( { model | showMenu = not model.showMenu }, Cmd.none ) + ( { model | menuVisible = not model.menuVisible }, Cmd.none ) + + ShowRecent visible -> + ( { model | recentVisible = visible }, Cmd.none ) _ -> updatePage msg model @@ -256,7 +262,7 @@ changeRouteTo route model = getSession model newModel = - { model | showMenu = False } + { model | menuVisible = False, recentVisible = False } in case route of Route.Unknown path -> @@ -373,14 +379,16 @@ view model = "" controls = - { viewMailbox = ViewMailbox + { menuVisible = model.menuVisible + , toggleMenu = ToggleMenu + , recentVisible = model.recentVisible + , showRecent = ShowRecent + , viewMailbox = ViewMailbox , mailboxOnInput = OnMailboxNameInput , mailboxValue = model.mailboxName , recentOptions = session.persistent.recentMailboxes , recentActive = mailbox , clearFlash = ClearFlash - , showMenu = model.showMenu - , toggleMenu = ToggleMenu } framePage : diff --git a/ui/src/navbar.css b/ui/src/navbar.css index 3f1e09c..733d086 100644 --- a/ui/src/navbar.css +++ b/ui/src/navbar.css @@ -43,11 +43,16 @@ text-decoration: none; } +/* This takes precendence over .navbar a above */ .navbar-brand a { display: inline-block; padding: 12px 15px; } +.navbar-dropdown-button { + display: none; +} + .navbar li { color: var(--navbar-color); } @@ -131,6 +136,14 @@ li.navbar-active span, margin-top: 1px; } + .navbar-dropdown-button { + background: none; + border: none; + color: var(--navbar-color); + display: inline; + padding-right: 0; + } + .navbar-dropdown-content { background-color: var(--bg-color); border: 1px solid var(--border-color); @@ -143,7 +156,7 @@ li.navbar-active span, z-index: 1; } - .navbar-dropdown:hover .navbar-dropdown-content { + .navbar-dropdown[aria-expanded="true"] .navbar-dropdown-content { display: block; } From 35969e0b0fe0580966527e25c94fe8f692f799b2 Mon Sep 17 00:00:00 2001 From: James Hillyerd Date: Fri, 15 Feb 2019 20:56:18 -0800 Subject: [PATCH 3/9] ui: Reorg navbar CSS --- ui/src/Layout.elm | 6 +++--- ui/src/navbar.css | 32 ++++++++++++++++++-------------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/ui/src/Layout.elm b/ui/src/Layout.elm index 1c4ef2c..eb15a7e 100644 --- a/ui/src/Layout.elm +++ b/ui/src/Layout.elm @@ -131,7 +131,7 @@ externalLink url title = navbarLink : Page -> Route -> List (Html a) -> Page -> Html a navbarLink page route linkContent activePage = li [ classList [ ( "navbar-active", page == activePage ) ] ] - [ a [ class "navbar-active-bg", Route.href route ] linkContent ] + [ a [ Route.href route ] linkContent ] {-| Renders list of recent mailboxes, selecting the currently active mailbox. @@ -170,14 +170,14 @@ navbarRecent page controls = a [ Route.href (Route.Mailbox mailbox) ] [ text mailbox ] in li - [ class "navbar-recent navbar-dropdown" + [ class "navbar-dropdown-container" , classList [ ( "navbar-active", active ) ] , attribute "aria-haspopup" "true" , ariaExpanded controls.recentVisible , Events.onMouseOver (controls.showRecent True) , Events.onMouseOut (controls.showRecent False) ] - [ span [ class "navbar-active-bg" ] + [ span [ class "navbar-dropdown" ] [ text title , button [ class "navbar-dropdown-button" diff --git a/ui/src/navbar.css b/ui/src/navbar.css index 733d086..e601c2a 100644 --- a/ui/src/navbar.css +++ b/ui/src/navbar.css @@ -36,7 +36,7 @@ } .navbar a, -.navbar-dropdown span { +.navbar-dropdown { color: var(--navbar-color); display: block; padding: 15px; @@ -57,7 +57,7 @@ color: var(--navbar-color); } -li.navbar-active .navbar-active-bg { +li.navbar-active > *:first-child { background-color: #080808; } @@ -88,8 +88,7 @@ li.navbar-active span, } @media screen and (min-width: 1000px) { - .main-nav, - .navbar-bg { + .main-nav { height: var(--navbar-height); } @@ -115,16 +114,17 @@ li.navbar-active span, background-image: var(--navbar-image); grid-column: 1 / 4; grid-row: 1; + height: var(--navbar-height); width: 100%; z-index: -1; } - .navbar-brand { - margin-left: -15px; + .navbar-toggle { + display: none; } - .navbar-recent { - margin: 0 auto; + .navbar-brand { + margin-left: -15px; } .navbar-mailbox { @@ -136,12 +136,20 @@ li.navbar-active span, margin-top: 1px; } + .navbar-dropdown-container { + margin: 0 auto; + } + + .navbar-dropdown { + padding: 15px 19px 15px 25px; + } + .navbar-dropdown-button { background: none; border: none; color: var(--navbar-color); display: inline; - padding-right: 0; + margin-left: 3px; } .navbar-dropdown-content { @@ -156,7 +164,7 @@ li.navbar-active span, z-index: 1; } - .navbar-dropdown[aria-expanded="true"] .navbar-dropdown-content { + .navbar-dropdown-container[aria-expanded="true"] .navbar-dropdown-content { display: block; } @@ -169,8 +177,4 @@ li.navbar-active span, color: var(--primary-color) !important; background-color: var(--selected-color); } - - .navbar-toggle { - display: none; - } } From 3c2b302a5f149e26a06b5cb2e07122f780fc22e1 Mon Sep 17 00:00:00 2001 From: James Hillyerd Date: Sat, 16 Feb 2019 09:35:41 -0800 Subject: [PATCH 4/9] ui: Fix Source & Raw HTML tab nav --- ui/src/Page/Mailbox.elm | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/ui/src/Page/Mailbox.elm b/ui/src/Page/Mailbox.elm index be43ec0..7bcb131 100644 --- a/ui/src/Page/Mailbox.elm +++ b/ui/src/Page/Mailbox.elm @@ -17,6 +17,7 @@ import Html.Attributes , id , placeholder , property + , tabindex , target , type_ , value @@ -586,18 +587,16 @@ viewMessage zone message bodyMode = text "" else - a - [ href htmlUrl, target "_blank" ] - [ button [] [ text "Raw HTML" ] ] + a [ href htmlUrl, target "_blank" ] + [ button [ tabindex -1 ] [ text "Raw HTML" ] ] in div [] [ div [ class "button-bar" ] [ button [ class "message-close light", onClick CloseMessage ] [ i [ class "fas fa-arrow-left" ] [] ] , button [ class "danger", onClick (DeleteMessage message) ] [ text "Delete" ] - , a - [ href sourceUrl, target "_blank" ] - [ button [] [ text "Source" ] ] + , a [ href sourceUrl, target "_blank" ] + [ button [ tabindex -1 ] [ text "Source" ] ] , htmlButton ] , dl [ class "message-header" ] From 7cd45ff3c7601437d002b9173174744685b547e3 Mon Sep 17 00:00:00 2001 From: James Hillyerd Date: Sat, 16 Feb 2019 10:34:48 -0800 Subject: [PATCH 5/9] ui: Allow tab+enter nav of monitor messages --- ui/src/Page/Monitor.elm | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/ui/src/Page/Monitor.elm b/ui/src/Page/Monitor.elm index 2fdcf60..855e831 100644 --- a/ui/src/Page/Monitor.elm +++ b/ui/src/Page/Monitor.elm @@ -36,6 +36,7 @@ type Msg | MessageReceived D.Value | Clear | OpenMessage MessageHeader + | MessageKeyPress MessageHeader Int update : Msg -> Model -> ( Model, Cmd Msg ) @@ -69,9 +70,22 @@ update msg model = ( { model | messages = [] }, Cmd.none ) OpenMessage header -> - ( model - , Route.pushUrl model.session.key (Route.Message header.mailbox header.id) - ) + openMessage header model + + MessageKeyPress header keyCode -> + case keyCode of + 13 -> + openMessage header model + + _ -> + ( model, Cmd.none ) + + +openMessage : MessageHeader -> Model -> ( Model, Cmd Msg ) +openMessage header model = + ( model + , Route.pushUrl model.session.key (Route.Message header.mailbox header.id) + ) @@ -121,7 +135,11 @@ view model = viewMessage : Time.Zone -> MessageHeader -> Html Msg viewMessage zone message = - tr [ Events.onClick (OpenMessage message) ] + tr + [ tabindex 0 + , Events.onClick (OpenMessage message) + , onKeyUp (MessageKeyPress message) + ] [ td [] [ shortDate zone message.date ] , td [ class "desktop" ] [ text message.from ] , td [] [ text message.mailbox ] @@ -147,3 +165,8 @@ shortDate zone date = zone date |> text + + +onKeyUp : (Int -> msg) -> Attribute msg +onKeyUp tagger = + Events.on "keyup" (D.map tagger Events.keyCode) From cfbd30d8b0fd62f118a83797f1d663c0d665d90e Mon Sep 17 00:00:00 2001 From: James Hillyerd Date: Sun, 17 Feb 2019 10:24:07 -0800 Subject: [PATCH 6/9] ui: Give Layout it's own Model/Msg --- ui/src/Layout.elm | 111 +++++++++++++++++++++++++++++++++------------- ui/src/Main.elm | 55 +++++++---------------- 2 files changed, 95 insertions(+), 71 deletions(-) diff --git a/ui/src/Layout.elm b/ui/src/Layout.elm index eb15a7e..18c18ff 100644 --- a/ui/src/Layout.elm +++ b/ui/src/Layout.elm @@ -1,4 +1,4 @@ -module Layout exposing (Page(..), frame) +module Layout exposing (Model, Msg, Page(..), frame, init, reset, update) import Data.Session as Session exposing (Session) import Html exposing (..) @@ -29,44 +29,91 @@ type Page | Status -type alias FrameControls msg = - { menuVisible : Bool - , toggleMenu : msg - , recentVisible : Bool - , showRecent : Bool -> msg - , viewMailbox : String -> msg - , mailboxOnInput : String -> msg - , mailboxValue : String - , recentOptions : List String - , recentActive : String +type alias Model msg = + { mapMsg : Msg -> msg , clearFlash : msg + , viewMailbox : String -> msg + , menuVisible : Bool + , recentVisible : Bool + , mailboxName : String } -frame : FrameControls msg -> Session -> Page -> Maybe (Html msg) -> List (Html msg) -> Html msg -frame controls session activePage modal content = +init : (Msg -> msg) -> msg -> (String -> msg) -> Model msg +init mapMsg clearFlash viewMailbox = + { mapMsg = mapMsg + , clearFlash = clearFlash + , viewMailbox = viewMailbox + , menuVisible = False + , recentVisible = False + , mailboxName = "" + } + + +{-| Resets layout state, used when navigating to a new page. +-} +reset : Model msg -> Model msg +reset model = + { model + | menuVisible = False + , recentVisible = False + , mailboxName = "" + } + + +type Msg + = ToggleMenu + | ShowRecent Bool + | OnMailboxNameInput String + + +update : Msg -> Model msg -> Model msg +update msg model = + case msg of + ToggleMenu -> + { model | menuVisible = not model.menuVisible } + + ShowRecent visible -> + { model | recentVisible = visible } + + OnMailboxNameInput name -> + { model | mailboxName = name } + + +type alias State msg = + { model : Model msg + , session : Session + , activePage : Page + , activeMailbox : String + , modal : Maybe (Html msg) + , content : List (Html msg) + } + + +frame : State msg -> Html msg +frame { model, session, activePage, activeMailbox, modal, content } = div [ class "app" ] [ header [] [ nav [ class "navbar" ] - [ span [ class "navbar-toggle", Events.onClick controls.toggleMenu ] + [ span [ class "navbar-toggle", Events.onClick (ToggleMenu |> model.mapMsg) ] [ i [ class "fas fa-bars" ] [] ] , span [ class "navbar-brand" ] [ a [ Route.href Route.Home ] [ text "@ inbucket" ] ] - , ul [ class "main-nav", classList [ ( "active", controls.menuVisible ) ] ] + , ul [ class "main-nav", classList [ ( "active", model.menuVisible ) ] ] [ if session.config.monitorVisible then navbarLink Monitor Route.Monitor [ text "Monitor" ] activePage else text "" , navbarLink Status Route.Status [ text "Status" ] activePage - , navbarRecent activePage controls + , navbarRecent activePage activeMailbox model session , li [ class "navbar-mailbox" ] - [ form [ Events.onSubmit (controls.viewMailbox controls.mailboxValue) ] + [ form [ Events.onSubmit (model.viewMailbox model.mailboxName) ] [ input [ type_ "text" , placeholder "mailbox" - , value controls.mailboxValue - , Events.onInput controls.mailboxOnInput + , value model.mailboxName + , Events.onInput (OnMailboxNameInput >> model.mapMsg) ] [] ] @@ -76,11 +123,11 @@ frame controls session activePage modal content = ] , div [ class "navbar-bg" ] [ text "" ] , frameModal modal - , div [ class "page" ] ([ errorFlash controls session.flash ] ++ content) + , div [ class "page" ] ([ errorFlash model session.flash ] ++ content) , footer [] [ div [ class "footer" ] [ externalLink "https://www.inbucket.org" "Inbucket" - , text " is an open source projected hosted at " + , text " is an open source project hosted on " , externalLink "https://github.com/jhillyerd/inbucket" "GitHub" , text "." ] @@ -100,7 +147,7 @@ frameModal maybeModal = text "" -errorFlash : FrameControls msg -> Maybe Session.Flash -> Html msg +errorFlash : Model msg -> Maybe Session.Flash -> Html msg errorFlash controls maybeFlash = let row ( heading, message ) = @@ -136,8 +183,8 @@ navbarLink page route linkContent activePage = {-| Renders list of recent mailboxes, selecting the currently active mailbox. -} -navbarRecent : Page -> FrameControls msg -> Html msg -navbarRecent page controls = +navbarRecent : Page -> String -> Model msg -> Session -> Html msg +navbarRecent page activeMailbox model session = let -- Active means we are viewing a specific mailbox. active = @@ -146,7 +193,7 @@ navbarRecent page controls = -- Recent tab title is the name of the current mailbox when active. title = if active then - controls.recentActive + activeMailbox else "Recent Mailboxes" @@ -154,13 +201,13 @@ navbarRecent page controls = -- Mailboxes to show in recent list, doesn't include active mailbox. recentMailboxes = if active then - List.tail controls.recentOptions |> Maybe.withDefault [] + List.tail session.persistent.recentMailboxes |> Maybe.withDefault [] else - controls.recentOptions + session.persistent.recentMailboxes dropdownExpanded = - if controls.recentVisible then + if model.recentVisible then "true" else @@ -173,15 +220,15 @@ navbarRecent page controls = [ class "navbar-dropdown-container" , classList [ ( "navbar-active", active ) ] , attribute "aria-haspopup" "true" - , ariaExpanded controls.recentVisible - , Events.onMouseOver (controls.showRecent True) - , Events.onMouseOut (controls.showRecent False) + , ariaExpanded model.recentVisible + , Events.onMouseOver (ShowRecent True |> model.mapMsg) + , Events.onMouseOut (ShowRecent False |> model.mapMsg) ] [ span [ class "navbar-dropdown" ] [ text title , button [ class "navbar-dropdown-button" - , Events.onClick (controls.showRecent (not controls.recentVisible)) + , Events.onClick (ShowRecent (not model.recentVisible) |> model.mapMsg) ] [ i [ class "fas fa-chevron-down" ] [] ] ] diff --git a/ui/src/Main.elm b/ui/src/Main.elm index 858c781..26b104d 100644 --- a/ui/src/Main.elm +++ b/ui/src/Main.elm @@ -23,10 +23,8 @@ import Url exposing (Url) type alias Model = - { page : PageModel - , mailboxName : String - , menuVisible : Bool - , recentVisible : Bool + { layout : Layout.Model Msg + , page : PageModel } @@ -63,10 +61,8 @@ init configValue location key = Home.init session initModel = - { page = Home subModel - , mailboxName = "" - , menuVisible = False - , recentVisible = False + { layout = Layout.init LayoutMsg ClearFlash ViewMailbox + , page = Home subModel } route = @@ -84,10 +80,8 @@ type Msg | SessionUpdated (Result D.Error Session.Persistent) | TimeZoneLoaded Time.Zone | ClearFlash - | OnMailboxNameInput String | ViewMailbox String - | ToggleMenu - | ShowRecent Bool + | LayoutMsg Layout.Msg | HomeMsg Home.Msg | MailboxMsg Mailbox.Msg | MonitorMsg Monitor.Msg @@ -211,19 +205,13 @@ updateMain msg model session = , Cmd.none ) - OnMailboxNameInput name -> - ( { model | mailboxName = name }, Cmd.none ) - ViewMailbox name -> - ( applyToModelSession Session.clearFlash { model | mailboxName = "" } + ( applyToModelSession Session.clearFlash model , Route.pushUrl session.key (Route.Mailbox name) ) - ToggleMenu -> - ( { model | menuVisible = not model.menuVisible }, Cmd.none ) - - ShowRecent visible -> - ( { model | recentVisible = visible }, Cmd.none ) + LayoutMsg subMsg -> + ( { model | layout = Layout.update subMsg model.layout }, Cmd.none ) _ -> updatePage msg model @@ -262,7 +250,7 @@ changeRouteTo route model = getSession model newModel = - { model | menuVisible = False, recentVisible = False } + { model | layout = Layout.reset model.layout } in case route of Route.Unknown path -> @@ -378,19 +366,6 @@ view model = _ -> "" - controls = - { menuVisible = model.menuVisible - , toggleMenu = ToggleMenu - , recentVisible = model.recentVisible - , showRecent = ShowRecent - , viewMailbox = ViewMailbox - , mailboxOnInput = OnMailboxNameInput - , mailboxValue = model.mailboxName - , recentOptions = session.persistent.recentMailboxes - , recentActive = mailbox - , clearFlash = ClearFlash - } - framePage : Layout.Page -> (msg -> Msg) @@ -399,11 +374,13 @@ view model = framePage page toMsg { title, modal, content } = Document title [ Layout.frame - controls - session - page - (Maybe.map (Html.map toMsg) modal) - (List.map (Html.map toMsg) content) + { model = model.layout + , session = session + , activePage = page + , activeMailbox = mailbox + , modal = Maybe.map (Html.map toMsg) modal + , content = List.map (Html.map toMsg) content + } ] in case model.page of From 34799b9a04ad5d9633db245523f5452cfd993d59 Mon Sep 17 00:00:00 2001 From: James Hillyerd Date: Sun, 17 Feb 2019 11:57:10 -0800 Subject: [PATCH 7/9] ui: Move ClearFlash & ViewMailbox into Layout --- ui/src/Layout.elm | 57 +++++++++++++++++++++++++++++++---------------- ui/src/Main.elm | 22 +++++++----------- 2 files changed, 46 insertions(+), 33 deletions(-) diff --git a/ui/src/Layout.elm b/ui/src/Layout.elm index 18c18ff..d2eddc8 100644 --- a/ui/src/Layout.elm +++ b/ui/src/Layout.elm @@ -31,19 +31,15 @@ type Page type alias Model msg = { mapMsg : Msg -> msg - , clearFlash : msg - , viewMailbox : String -> msg , menuVisible : Bool , recentVisible : Bool , mailboxName : String } -init : (Msg -> msg) -> msg -> (String -> msg) -> Model msg -init mapMsg clearFlash viewMailbox = +init : (Msg -> msg) -> Model msg +init mapMsg = { mapMsg = mapMsg - , clearFlash = clearFlash - , viewMailbox = viewMailbox , menuVisible = False , recentVisible = False , mailboxName = "" @@ -62,22 +58,45 @@ reset model = type Msg - = ToggleMenu - | ShowRecent Bool + = ClearFlash | OnMailboxNameInput String + | OpenMailbox + | ShowRecent Bool + | ToggleMenu -update : Msg -> Model msg -> Model msg -update msg model = +update : Msg -> Model msg -> Session -> ( Model msg, Session, Cmd msg ) +update msg model session = case msg of - ToggleMenu -> - { model | menuVisible = not model.menuVisible } - - ShowRecent visible -> - { model | recentVisible = visible } + ClearFlash -> + ( model + , Session.clearFlash session + , Cmd.none + ) OnMailboxNameInput name -> - { model | mailboxName = name } + ( { model | mailboxName = name } + , session + , Cmd.none + ) + + OpenMailbox -> + ( model + , session + , Route.pushUrl session.key (Route.Mailbox model.mailboxName) + ) + + ShowRecent visible -> + ( { model | recentVisible = visible } + , session + , Cmd.none + ) + + ToggleMenu -> + ( { model | menuVisible = not model.menuVisible } + , session + , Cmd.none + ) type alias State msg = @@ -108,7 +127,7 @@ frame { model, session, activePage, activeMailbox, modal, content } = , navbarLink Status Route.Status [ text "Status" ] activePage , navbarRecent activePage activeMailbox model session , li [ class "navbar-mailbox" ] - [ form [ Events.onSubmit (model.viewMailbox model.mailboxName) ] + [ form [ Events.onSubmit (OpenMailbox |> model.mapMsg) ] [ input [ type_ "text" , placeholder "mailbox" @@ -148,7 +167,7 @@ frameModal maybeModal = errorFlash : Model msg -> Maybe Session.Flash -> Html msg -errorFlash controls maybeFlash = +errorFlash model maybeFlash = let row ( heading, message ) = tr [] @@ -164,7 +183,7 @@ errorFlash controls maybeFlash = div [ class "well well-error" ] [ div [ class "flash-header" ] [ h2 [] [ text flash.title ] - , a [ href "#", Events.onClick controls.clearFlash ] [ text "Close" ] + , a [ href "#", Events.onClick (ClearFlash |> model.mapMsg) ] [ text "Close" ] ] , div [ class "flash-table" ] (List.map row flash.table) ] diff --git a/ui/src/Main.elm b/ui/src/Main.elm index 26b104d..1bd0833 100644 --- a/ui/src/Main.elm +++ b/ui/src/Main.elm @@ -61,7 +61,7 @@ init configValue location key = Home.init session initModel = - { layout = Layout.init LayoutMsg ClearFlash ViewMailbox + { layout = Layout.init LayoutMsg , page = Home subModel } @@ -79,8 +79,6 @@ type Msg | LinkClicked UrlRequest | SessionUpdated (Result D.Error Session.Persistent) | TimeZoneLoaded Time.Zone - | ClearFlash - | ViewMailbox String | LayoutMsg Layout.Msg | HomeMsg Home.Msg | MailboxMsg Mailbox.Msg @@ -179,11 +177,6 @@ updateMain msg model session = , Cmd.none ) - ClearFlash -> - ( applyToModelSession Session.clearFlash model - , Cmd.none - ) - SessionUpdated (Ok persistent) -> ( updateSession model { session | persistent = persistent } , Cmd.none @@ -205,13 +198,14 @@ updateMain msg model session = , Cmd.none ) - ViewMailbox name -> - ( applyToModelSession Session.clearFlash model - , Route.pushUrl session.key (Route.Mailbox name) - ) - LayoutMsg subMsg -> - ( { model | layout = Layout.update subMsg model.layout }, Cmd.none ) + let + ( layout, newSession, cmd ) = + Layout.update subMsg model.layout session + in + ( updateSession { model | layout = layout } newSession + , cmd + ) _ -> updatePage msg model From b2255fefab079956550d37459b2c19e4f487a7b1 Mon Sep 17 00:00:00 2001 From: James Hillyerd Date: Sun, 17 Feb 2019 12:59:05 -0800 Subject: [PATCH 8/9] ui: Allow keyboard navigation of message list --- ui/src/Page/Mailbox.elm | 56 ++++++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 20 deletions(-) diff --git a/ui/src/Page/Mailbox.elm b/ui/src/Page/Mailbox.elm index 7bcb131..73155a1 100644 --- a/ui/src/Page/Mailbox.elm +++ b/ui/src/Page/Mailbox.elm @@ -22,11 +22,11 @@ import Html.Attributes , type_ , value ) -import Html.Events exposing (..) +import Html.Events as Events import Http exposing (Error) import HttpUtil -import Json.Decode as Decode exposing (Decoder) -import Json.Encode as Encode +import Json.Decode as D +import Json.Encode as E import Ports import Route import Task @@ -136,6 +136,7 @@ subscriptions model = type Msg = ListLoaded (Result HttpUtil.Error (List MessageHeader)) | ClickMessage MessageID + | ListKeyPress String Int | OpenMessage MessageID | CloseMessage | MessageLoaded (Result HttpUtil.Error Message) @@ -166,7 +167,7 @@ update msg model = ) OpenMessage id -> - updateOpenMessage model.session model id + updateOpenMessage model id CloseMessage -> case model.state of @@ -187,6 +188,14 @@ update msg model = , Cmd.none ) + ListKeyPress id keyCode -> + case keyCode of + 13 -> + updateOpenMessage model id + + _ -> + ( model, Cmd.none ) + ListLoaded (Ok headers) -> case model.state of LoadingList selection -> @@ -198,7 +207,7 @@ update msg model = in case selection of Just id -> - updateOpenMessage model.session newModel id + updateOpenMessage newModel id Nothing -> ( { newModel @@ -459,8 +468,8 @@ updateMarkMessageSeen model message = ( model, Cmd.none ) -updateOpenMessage : Session -> Model -> String -> ( Model, Cmd Msg ) -updateOpenMessage session model id = +updateOpenMessage : Model -> String -> ( Model, Cmd Msg ) +updateOpenMessage model id = let newModel = { model | session = Session.addRecent model.mailboxName model.session } @@ -493,12 +502,12 @@ view model = [ input [ type_ "search" , placeholder "search" - , onInput OnSearchInput + , Events.onInput OnSearchInput , value model.searchInput ] [] , button - [ onClick PurgeMailboxPrompt + [ Events.onClick PurgeMailboxPrompt , alt "Purge Mailbox" ] [ i [ class "fas fa-trash" ] [] ] @@ -534,8 +543,8 @@ viewModal promptPurge = div [] [ p [] [ text "Are you sure you want to delete all messages in this mailbox?" ] , div [ class "button-bar" ] - [ button [ onClick PurgeMailboxConfirmed, class "danger" ] [ text "Yes" ] - , button [ onClick PurgeMailboxCanceled ] [ text "Cancel" ] + [ button [ Events.onClick PurgeMailboxConfirmed, class "danger" ] [ text "Yes" ] + , button [ Events.onClick PurgeMailboxCanceled ] [ text "Cancel" ] ] ] @@ -560,12 +569,14 @@ viewMessageList session model = messageChip : Model -> Maybe MessageID -> MessageHeader -> Html Msg messageChip model selected message = div - [ classList - [ ( "message-list-entry", True ) - , ( "selected", selected == Just message.id ) + [ class "message-list-entry" + , classList + [ ( "selected", selected == Just message.id ) , ( "unseen", not message.seen ) ] - , onClick (ClickMessage message.id) + , Events.onClick (ClickMessage message.id) + , onKeyUp (ListKeyPress message.id) + , tabindex 0 ] [ div [ class "subject" ] [ text message.subject ] , div [ class "from" ] [ text message.from ] @@ -592,9 +603,9 @@ viewMessage zone message bodyMode = in div [] [ div [ class "button-bar" ] - [ button [ class "message-close light", onClick CloseMessage ] + [ button [ class "message-close light", Events.onClick CloseMessage ] [ i [ class "fas fa-arrow-left" ] [] ] - , button [ class "danger", onClick (DeleteMessage message) ] [ text "Delete" ] + , button [ class "danger", Events.onClick (DeleteMessage message) ] [ text "Delete" ] , a [ href sourceUrl, target "_blank" ] [ button [ tabindex -1 ] [ text "Source" ] ] , htmlButton @@ -643,7 +654,7 @@ messageBody message bodyMode = bodyModeTab mode label = a [ classList [ ( "active", bodyMode == mode ) ] - , onClick (MessageBody mode) + , Events.onClick (MessageBody mode) , href "#" ] [ text label ] @@ -666,10 +677,10 @@ messageBody message bodyMode = , article [ class "message-body" ] [ case bodyMode of SafeHtmlBody -> - Html.node "rendered-html" [ property "content" (Encode.string message.html) ] [] + Html.node "rendered-html" [ property "content" (E.string message.html) ] [] TextBody -> - Html.node "rendered-html" [ property "content" (Encode.string message.text) ] [] + Html.node "rendered-html" [ property "content" (E.string message.text) ] [] ] ] @@ -749,3 +760,8 @@ filterMessageList list = || String.contains list.searchFilter (String.toLower header.from) in List.filter matches list.headers + + +onKeyUp : (Int -> msg) -> Attribute msg +onKeyUp tagger = + Events.on "keyup" (D.map tagger Events.keyCode) From a467829103878446d7c611a317b7b907019d467c Mon Sep 17 00:00:00 2001 From: James Hillyerd Date: Sun, 17 Feb 2019 13:40:56 -0800 Subject: [PATCH 9/9] ui: Convert hamburger to button --- ui/src/Layout.elm | 2 +- ui/src/mailbox.css | 12 ++++++++++++ ui/src/main.css | 8 ++++++-- ui/src/navbar.css | 3 ++- 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/ui/src/Layout.elm b/ui/src/Layout.elm index d2eddc8..cf674a9 100644 --- a/ui/src/Layout.elm +++ b/ui/src/Layout.elm @@ -114,7 +114,7 @@ frame { model, session, activePage, activeMailbox, modal, content } = div [ class "app" ] [ header [] [ nav [ class "navbar" ] - [ span [ class "navbar-toggle", Events.onClick (ToggleMenu |> model.mapMsg) ] + [ button [ class "navbar-toggle", Events.onClick (ToggleMenu |> model.mapMsg) ] [ i [ class "fas fa-bars" ] [] ] , span [ class "navbar-brand" ] [ a [ Route.href Route.Home ] [ text "@ inbucket" ] ] diff --git a/ui/src/mailbox.css b/ui/src/mailbox.css index e61bf13..353b81e 100644 --- a/ui/src/mailbox.css +++ b/ui/src/mailbox.css @@ -18,6 +18,18 @@ display: flex; } +.message-list-controls button, +.message-list-controls input[type="search"] { + border: 1px solid var(--border-color); + border-radius: 3px; +} + +.message-list-controls button { + color: var(--low-color); + margin-left: 1px; + padding: 0 6px; +} + .message-list-controls input[type="search"] { flex: 1 1 auto; padding: 2px 4px; diff --git a/ui/src/main.css b/ui/src/main.css index e4809f3..9039b20 100644 --- a/ui/src/main.css +++ b/ui/src/main.css @@ -15,7 +15,7 @@ h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, -b, u, i, center, +b, u, i, center, button, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, @@ -44,13 +44,17 @@ body { background-color: var(--bg-color); } -body, input, table { +body, button, input, table { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 14px; line-height: 1.43; color: var(--primary-color); } +button { + background: none; +} + h1, h2, h3, h4, h5, h6, p { margin-bottom: 10px; } diff --git a/ui/src/navbar.css b/ui/src/navbar.css index e601c2a..9bed562 100644 --- a/ui/src/navbar.css +++ b/ui/src/navbar.css @@ -30,9 +30,10 @@ .navbar-toggle { color: var(--navbar-color); font-size: 24px; + padding: 0 6px; position: absolute; top: 10px; - right: 20px; + right: 14px; } .navbar a,