diff --git a/ui/src/Layout.elm b/ui/src/Layout.elm index 7a58956..18f1d6f 100644 --- a/ui/src/Layout.elm +++ b/ui/src/Layout.elm @@ -36,6 +36,8 @@ type alias FrameControls msg = , recentOptions : List String , recentActive : String , clearFlash : msg + , showMenu : Bool + , toggleMenu : msg } @@ -43,26 +45,30 @@ frame : FrameControls msg -> Session -> Page -> Maybe (Html msg) -> List (Html m frame controls session activePage modal content = div [ class "app" ] [ header [] - [ ul [ class "navbar", attribute "role" "navigation" ] - [ li [ class "navbar-brand" ] + [ nav [ class "navbar" ] + [ span [ class "navbar-toggle", Events.onClick controls.toggleMenu ] + [ i [ class "fas fa-bars" ] [] ] + , span [ class "navbar-brand" ] [ a [ Route.href Route.Home ] [ text "@ inbucket" ] ] - , 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" - , placeholder "mailbox" - , value controls.mailboxValue - , Events.onInput controls.mailboxOnInput + , ul [ classList [ ( "main-nav", True ), ( "active", controls.showMenu ) ] ] + [ li [ class "navbar-mailbox" ] + [ form [ Events.onSubmit (controls.viewMailbox controls.mailboxValue) ] + [ input + [ type_ "text" + , placeholder "mailbox" + , value controls.mailboxValue + , Events.onInput controls.mailboxOnInput + ] + [] ] - [] ] + , if session.config.monitorVisible then + navbarLink Monitor Route.Monitor [ text "Monitor" ] activePage + + else + text "" + , navbarLink Status Route.Status [ text "Status" ] activePage + , navbarRecent activePage controls ] ] ] @@ -123,7 +129,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 [ Route.href route ] linkContent ] + [ a [ class "navbar-active-bg", Route.href route ] linkContent ] {-| Renders list of recent mailboxes, selecting the currently active mailbox. @@ -157,6 +163,6 @@ navbarRecent page controls = [ class "navbar-recent" , classList [ ( "navbar-dropdown", True ), ( "navbar-active", active ) ] ] - [ span [] [ text title ] + [ span [ class "navbar-active-bg" ] [ text title ] , div [ class "navbar-dropdown-content" ] (List.map recentLink recentMailboxes) ] diff --git a/ui/src/Main.elm b/ui/src/Main.elm index cebecaf..10b50d9 100644 --- a/ui/src/Main.elm +++ b/ui/src/Main.elm @@ -25,6 +25,7 @@ import Url exposing (Url) type alias Model = { page : PageModel , mailboxName : String + , showMenu : Bool } @@ -63,6 +64,7 @@ init configValue location key = initModel = { page = Home subModel , mailboxName = "" + , showMenu = False } route = @@ -82,6 +84,7 @@ type Msg | ClearFlash | OnMailboxNameInput String | ViewMailbox String + | ToggleMenu | HomeMsg Home.Msg | MailboxMsg Mailbox.Msg | MonitorMsg Monitor.Msg @@ -213,6 +216,9 @@ updateMain msg model session = , Route.pushUrl session.key (Route.Mailbox name) ) + ToggleMenu -> + ( { model | showMenu = not model.showMenu }, Cmd.none ) + _ -> updatePage msg model @@ -248,6 +254,9 @@ changeRouteTo route model = let session = getSession model + + newModel = + { model | showMenu = False } in case route of Route.Unknown path -> @@ -257,26 +266,26 @@ changeRouteTo route model = , table = [ ( "Path", path ) ] } in - ( applyToModelSession (Session.showFlash flash) model + ( applyToModelSession (Session.showFlash flash) newModel , Cmd.none ) Route.Home -> Home.init session - |> updateWith Home HomeMsg model + |> updateWith Home HomeMsg newModel Route.Mailbox name -> Mailbox.init session name Nothing - |> updateWith Mailbox MailboxMsg model + |> updateWith Mailbox MailboxMsg newModel Route.Message mailbox id -> Mailbox.init session mailbox (Just id) - |> updateWith Mailbox MailboxMsg model + |> updateWith Mailbox MailboxMsg newModel Route.Monitor -> if session.config.monitorVisible then Monitor.init session - |> updateWith Monitor MonitorMsg model + |> updateWith Monitor MonitorMsg newModel else let @@ -285,13 +294,13 @@ changeRouteTo route model = , table = [ ( "Error", "Monitor disabled by configuration." ) ] } in - ( applyToModelSession (Session.showFlash flash) model + ( applyToModelSession (Session.showFlash flash) newModel , Cmd.none ) Route.Status -> Status.init session - |> updateWith Status StatusMsg model + |> updateWith Status StatusMsg newModel getSession : Model -> Session @@ -370,6 +379,8 @@ view model = , recentOptions = session.persistent.recentMailboxes , recentActive = mailbox , clearFlash = ClearFlash + , showMenu = model.showMenu + , toggleMenu = ToggleMenu } framePage : diff --git a/ui/src/index.js b/ui/src/index.js index 6cc7225..f6ed9c4 100644 --- a/ui/src/index.js +++ b/ui/src/index.js @@ -1,4 +1,5 @@ import './main.css' +import './navbar.css' import '@fortawesome/fontawesome-free/css/all.css' import { Elm } from './Main.elm' import './monitorMessages' diff --git a/ui/src/main.css b/ui/src/main.css index 1b8801e..01505ab 100644 --- a/ui/src/main.css +++ b/ui/src/main.css @@ -211,100 +211,6 @@ h3 { padding: 10px !important; } - -/** NAV BAR */ - -.navbar, -.navbar-bg { - height: 50px; -} - -.navbar { - display: flex; - line-height: 20px; - list-style: none; - padding: 0; - text-shadow: 0 -1px 0 rgba(0,0,0,0.2); -} - -.navbar-bg { - background-color: #222; - background-image: linear-gradient(to bottom, #3c3c3c 0, #222 100%); - grid-column: 1 / 4; - grid-row: 1; - width: 100%; - z-index: -1; -} - -.navbar li { - color: #9d9d9d; -} - -.navbar a, -.navbar-dropdown span { - color: #9d9d9d; - display: inline-block; - padding: 15px; - text-decoration: none; -} - -li.navbar-active { - background-color: #080808; -} - -li.navbar-active a, -li.navbar-active span, -.navbar a:hover { - color: #ffffff; -} - -.navbar-brand { - font-size: 18px; - margin-left: -15px; -} - -.navbar-recent { - margin: 0 auto; -} - -.navbar-mailbox { - padding: 8px 0 !important; -} - -.navbar-mailbox input { - border: 1px solid var(--border-color); - border-radius: 4px; - padding: 5px 10px; - margin-top: 1px; - width: 250px; -} - -.navbar-dropdown-content { - background-color: var(--bg-color); - border: 1px solid var(--border-color); - border-radius: 4px; - box-shadow: 0 1px 2px rgba(0,0,0,.05); - display: none; - min-width: 160px; - position: absolute; - text-shadow: none; - z-index: 1; -} - -.navbar-dropdown:hover .navbar-dropdown-content { - display: block; -} - -.navbar-dropdown-content a { - color: var(--primary-color) !important; - display: block; - padding: 5px 15px; -} - -.navbar-dropdown-content a:hover { - background-color: var(--selected-color); -} - /** BUTTONS */ .button-bar button { diff --git a/ui/src/navbar.css b/ui/src/navbar.css new file mode 100644 index 0000000..431eadf --- /dev/null +++ b/ui/src/navbar.css @@ -0,0 +1,167 @@ +/** NAV BAR */ + +:root { + --bg-color: #fff; + --primary-color: #333; + --border-color: #ddd; + --selected-color: #eee; + --navbar-color: #9d9d9d; + --navbar-bg: #222; + --navbar-image: linear-gradient(to bottom, #3c3c3c 0, #222 100%); + --navbar-height: 50px; +} + +.navbar { + background-color: var(--navbar-bg); + background-image: var(--navbar-image); + text-shadow: 0 -1px 0 rgba(0,0,0,0.2); + min-height: var(--navbar-height); +} + +.main-nav { + display: none; + list-style: none; +} + +.main-nav.active { + display: block; +} + +.navbar-brand { + font-size: 18px; +} + +.navbar-toggle { + color: var(--navbar-color); + font-size: 24px; + position: absolute; + top: 10px; + right: 20px; +} + +.navbar a, +.navbar-dropdown span { + color: var(--navbar-color); + display: block; + padding: 15px; + text-decoration: none; +} + +.navbar-brand a { + display: inline-block; + padding: 12px 15px; +} + +.navbar li { + color: var(--navbar-color); +} + +li.navbar-active .navbar-active-bg { + background-color: #080808; +} + +li.navbar-active a, +li.navbar-active span, +.navbar a:hover { + color: #ffffff; +} + +.navbar-dropdown-content a { + color: var(--navbar-color) !important; + display: block; +} + +.navbar-dropdown-content a:hover { + color: #ffffff !important; +} + +.navbar-mailbox { + padding: 15px; +} + +.navbar-mailbox input { + border: 1px solid var(--border-color); + border-radius: 4px; + padding: 5px 10px; + width: 250px; +} + +@media screen and (min-width: 1000px) { + .main-nav, + .navbar-bg { + height: var(--navbar-height); + } + + .navbar { + align-items: center; + background: none; + display: flex; + } + + .main-nav { + display: flex; + flex-grow: 2; + line-height: 20px; + padding: 0; + } + + .main-nav.active { + display: flex; + } + + .navbar-bg { + background-color: var(--navbar-bg); + background-image: var(--navbar-image); + grid-column: 1 / 4; + grid-row: 1; + width: 100%; + z-index: -1; + } + + .navbar-brand { + margin-left: -15px; + } + + .navbar-recent { + margin: 0 auto; + } + + .navbar-mailbox { + order: 1; + padding: 8px 0 !important; + } + + .navbar-mailbox input { + margin-top: 1px; + } + + .navbar-dropdown-content { + background-color: var(--bg-color); + border: 1px solid var(--border-color); + border-radius: 4px; + box-shadow: 0 1px 2px rgba(0,0,0,.05); + display: none; + min-width: 160px; + position: absolute; + text-shadow: none; + z-index: 1; + } + + .navbar-dropdown:hover .navbar-dropdown-content { + display: block; + } + + .navbar-dropdown-content a { + color: var(--primary-color) !important; + padding: 5px 15px; + } + + .navbar-dropdown-content a:hover { + color: var(--primary-color) !important; + background-color: var(--selected-color); + } + + .navbar-toggle { + display: none; + } +}