mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-06-23 19:08:26 +00:00
First version of new ui based on tailwind
This drops fomantic-ui as css toolkit and introduces tailwindcss. With tailwind there are no predefined components, but it's very easy to create those. So customizing the look&feel is much simpler, most of the time no additional css is needed. This requires a complete rewrite of the markup + styles. Luckily all logic can be kept as is. The now old ui is not removed, it is still available by using a request header `Docspell-Ui` with a value of `1` for the old ui and `2` for the new ui. Another addition is "dev mode", where docspell serves assets with a no-cache header, to disable browser caching. This makes developing a lot easier.
This commit is contained in:
@ -6,6 +6,7 @@ module App.Data exposing
|
||||
)
|
||||
|
||||
import Api.Model.AuthResult exposing (AuthResult)
|
||||
import Api.Model.BasicResult exposing (BasicResult)
|
||||
import Api.Model.VersionInfo exposing (VersionInfo)
|
||||
import Browser exposing (UrlRequest)
|
||||
import Browser.Navigation exposing (Key)
|
||||
@ -45,6 +46,7 @@ type alias Model =
|
||||
, userMenuOpen : Bool
|
||||
, subs : Sub Msg
|
||||
, uiSettings : UiSettings
|
||||
, sidebarVisible : Bool
|
||||
}
|
||||
|
||||
|
||||
@ -92,6 +94,7 @@ init key url flags_ settings =
|
||||
, userMenuOpen = False
|
||||
, subs = Sub.none
|
||||
, uiSettings = settings
|
||||
, sidebarVisible = settings.sideMenuVisible
|
||||
}
|
||||
, Cmd.batch
|
||||
[ Cmd.map UserSettingsMsg uc
|
||||
@ -145,6 +148,8 @@ type Msg
|
||||
| ToggleNavMenu
|
||||
| ToggleUserMenu
|
||||
| GetUiSettings UiSettings
|
||||
| ToggleSidebar
|
||||
| ToggleDarkMode
|
||||
|
||||
|
||||
defaultPage : Flags -> Page
|
||||
|
@ -8,6 +8,7 @@ import App.Data exposing (..)
|
||||
import Browser exposing (UrlRequest(..))
|
||||
import Browser.Navigation as Nav
|
||||
import Data.Flags
|
||||
import Data.UiTheme
|
||||
import Page exposing (Page(..))
|
||||
import Page.CollectiveSettings.Data
|
||||
import Page.CollectiveSettings.Update
|
||||
@ -47,6 +48,38 @@ update msg model =
|
||||
updateWithSub : Msg -> Model -> ( Model, Cmd Msg, Sub Msg )
|
||||
updateWithSub msg model =
|
||||
case msg of
|
||||
ToggleSidebar ->
|
||||
( { model | sidebarVisible = not model.sidebarVisible }, Cmd.none, Sub.none )
|
||||
|
||||
ToggleDarkMode ->
|
||||
let
|
||||
settings =
|
||||
model.uiSettings
|
||||
|
||||
next =
|
||||
Data.UiTheme.cycle settings.uiTheme
|
||||
|
||||
newSettings =
|
||||
{ settings | uiTheme = next }
|
||||
in
|
||||
case model.flags.account of
|
||||
Just _ ->
|
||||
-- when authenticated, store it in settings only
|
||||
-- once new settings arrive via a subscription,
|
||||
-- the ui is updated. so it is also updated on
|
||||
-- page refresh
|
||||
( { model | userMenuOpen = False }
|
||||
, Ports.storeUiSettings model.flags newSettings
|
||||
, Sub.none
|
||||
)
|
||||
|
||||
Nothing ->
|
||||
-- when not logged in, simply set the theme
|
||||
( { model | userMenuOpen = False }
|
||||
, Ports.setUiTheme next
|
||||
, Sub.none
|
||||
)
|
||||
|
||||
HomeMsg lm ->
|
||||
updateHome lm model
|
||||
|
||||
@ -213,8 +246,17 @@ updateWithSub msg model =
|
||||
)
|
||||
|
||||
GetUiSettings settings ->
|
||||
let
|
||||
setTheme =
|
||||
Ports.setUiTheme settings.uiTheme
|
||||
in
|
||||
Util.Update.andThen2
|
||||
[ updateUserSettings Page.UserSettings.Data.UpdateSettings
|
||||
[ \m ->
|
||||
( { m | sidebarVisible = settings.sideMenuVisible }
|
||||
, setTheme
|
||||
, Sub.none
|
||||
)
|
||||
, updateUserSettings Page.UserSettings.Data.UpdateSettings
|
||||
, updateHome Page.Home.Data.UiSettingsUpdated
|
||||
, updateItemDetail Page.ItemDetail.Data.UiSettingsUpdated
|
||||
]
|
||||
|
465
modules/webapp/src/main/elm/App/View2.elm
Normal file
465
modules/webapp/src/main/elm/App/View2.elm
Normal file
@ -0,0 +1,465 @@
|
||||
module App.View2 exposing (view)
|
||||
|
||||
import Api.Model.AuthResult exposing (AuthResult)
|
||||
import App.Data exposing (..)
|
||||
import Comp.Basic as B
|
||||
import Data.Flags
|
||||
import Html exposing (..)
|
||||
import Html.Attributes exposing (..)
|
||||
import Html.Events exposing (onClick)
|
||||
import Page exposing (Page(..))
|
||||
import Page.CollectiveSettings.View2 as CollectiveSettings
|
||||
import Page.Home.Data
|
||||
import Page.Home.View2 as Home
|
||||
import Page.ItemDetail.View2 as ItemDetail
|
||||
import Page.Login.View2 as Login
|
||||
import Page.ManageData.View2 as ManageData
|
||||
import Page.NewInvite.View2 as NewInvite
|
||||
import Page.Queue.View2 as Queue
|
||||
import Page.Register.View2 as Register
|
||||
import Page.Upload.View2 as Upload
|
||||
import Page.UserSettings.View2 as UserSettings
|
||||
import Styles as S
|
||||
|
||||
|
||||
view : Model -> List (Html Msg)
|
||||
view model =
|
||||
[ topNavbar model
|
||||
, mainContent model
|
||||
]
|
||||
|
||||
|
||||
topNavbar : Model -> Html Msg
|
||||
topNavbar model =
|
||||
case model.flags.account of
|
||||
Just acc ->
|
||||
topNavUser acc model
|
||||
|
||||
Nothing ->
|
||||
topNavAnon model
|
||||
|
||||
|
||||
topNavUser : AuthResult -> Model -> Html Msg
|
||||
topNavUser auth model =
|
||||
nav
|
||||
[ id "top-nav"
|
||||
, class styleTopNav
|
||||
]
|
||||
[ B.genericButton
|
||||
{ label = ""
|
||||
, icon = "fa fa-bars"
|
||||
, handler = onClick ToggleSidebar
|
||||
, disabled = not (Page.hasSidebar model.page)
|
||||
, attrs = [ href "#" ]
|
||||
, baseStyle = "font-bold inline-flex items-center px-4 py-2"
|
||||
, activeStyle = "hover:bg-blue-200 dark:hover:bg-bluegray-800 w-12"
|
||||
}
|
||||
, headerNavItem model
|
||||
, div [ class "flex flex-grow justify-end" ]
|
||||
[ userMenu auth model
|
||||
, dataMenu auth model
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
topNavAnon : Model -> Html Msg
|
||||
topNavAnon model =
|
||||
nav
|
||||
[ id "top-nav"
|
||||
, class styleTopNav
|
||||
]
|
||||
[ headerNavItem model
|
||||
, div [ class "flex flex-grow justify-end" ]
|
||||
[ a
|
||||
[ href "#"
|
||||
, onClick ToggleDarkMode
|
||||
, class dropdownLink
|
||||
]
|
||||
[ i [ class "fa fa-adjust w-6" ] []
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
headerNavItem : Model -> Html Msg
|
||||
headerNavItem model =
|
||||
a
|
||||
[ class "font-bold hover:bg-blue-200 dark:hover:bg-bluegray-800 w-40 inline-flex items-center px-4"
|
||||
, Page.href HomePage
|
||||
]
|
||||
[ img
|
||||
[ src (model.flags.config.docspellAssetPath ++ "/img/logo-96.png")
|
||||
, class "h-full inline-block py-2 pr-2"
|
||||
]
|
||||
[]
|
||||
, div [ class "" ]
|
||||
[ text "Docspell"
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
mainContent : Model -> Html Msg
|
||||
mainContent model =
|
||||
div
|
||||
[ id "main"
|
||||
, class styleMain
|
||||
]
|
||||
(case model.page of
|
||||
HomePage ->
|
||||
viewHome model
|
||||
|
||||
CollectiveSettingPage ->
|
||||
viewCollectiveSettings model
|
||||
|
||||
LoginPage _ ->
|
||||
viewLogin model
|
||||
|
||||
ManageDataPage ->
|
||||
viewManageData model
|
||||
|
||||
UserSettingPage ->
|
||||
viewUserSettings model
|
||||
|
||||
QueuePage ->
|
||||
viewQueue model
|
||||
|
||||
RegisterPage ->
|
||||
viewRegister model
|
||||
|
||||
UploadPage mid ->
|
||||
viewUpload mid model
|
||||
|
||||
NewInvitePage ->
|
||||
viewNewInvite model
|
||||
|
||||
ItemDetailPage id ->
|
||||
viewItemDetail id model
|
||||
)
|
||||
|
||||
|
||||
|
||||
--- Helpers
|
||||
|
||||
|
||||
styleTopNav : String
|
||||
styleTopNav =
|
||||
"top-0 fixed z-50 w-full flex flex-row justify-start shadow-sm h-12 bg-blue-100 dark:bg-bluegray-900 text-gray-800 dark:text-bluegray-200 antialiased"
|
||||
|
||||
|
||||
styleMain : String
|
||||
styleMain =
|
||||
"mt-12 flex md:flex-row flex-col w-full h-screen-12 overflow-y-hidden bg-white dark:bg-bluegray-800 text-gray-800 dark:text-bluegray-300 antialiased"
|
||||
|
||||
|
||||
dataMenu : AuthResult -> Model -> Html Msg
|
||||
dataMenu acc model =
|
||||
div [ class "relative" ]
|
||||
[ a
|
||||
[ class dropdownLink
|
||||
, onClick ToggleNavMenu
|
||||
, href "#"
|
||||
]
|
||||
[ i [ class "fa fa-cogs" ] []
|
||||
]
|
||||
, div
|
||||
[ class dropdownMenu
|
||||
, classList [ ( "hidden", not model.navMenuOpen ) ]
|
||||
]
|
||||
[ dataPageLink model
|
||||
HomePage
|
||||
[]
|
||||
[ img
|
||||
[ class "w-4 inline-block"
|
||||
, src (model.flags.config.docspellAssetPath ++ "/img/logo-mc-96.png")
|
||||
]
|
||||
[]
|
||||
, div [ class "inline-block ml-2" ]
|
||||
[ text "Items"
|
||||
]
|
||||
]
|
||||
, div [ class "py-1" ] [ hr [ class S.border ] [] ]
|
||||
, dataPageLink model
|
||||
ManageDataPage
|
||||
[]
|
||||
[ i [ class "fa fa-cubes w-6" ] []
|
||||
, span [ class "ml-1" ]
|
||||
[ text "Manage Data"
|
||||
]
|
||||
]
|
||||
, div [ class "divider" ] []
|
||||
, dataPageLink model
|
||||
(UploadPage Nothing)
|
||||
[]
|
||||
[ i [ class "fa fa-upload w-6" ] []
|
||||
, span [ class "ml-1" ]
|
||||
[ text "Upload files"
|
||||
]
|
||||
]
|
||||
, dataPageLink model
|
||||
QueuePage
|
||||
[]
|
||||
[ i [ class "fa fa-tachometer-alt w-6" ] []
|
||||
, span [ class "ml-1" ]
|
||||
[ text "Processing Queue"
|
||||
]
|
||||
]
|
||||
, div
|
||||
[ classList
|
||||
[ ( "py-1", True )
|
||||
, ( "hidden", model.flags.config.signupMode /= "invite" )
|
||||
]
|
||||
]
|
||||
[ hr [ class S.border ] [] ]
|
||||
, dataPageLink model
|
||||
NewInvitePage
|
||||
[ ( "hidden", model.flags.config.signupMode /= "invite" ) ]
|
||||
[ i [ class "fa fa-key w-6" ] []
|
||||
, span [ class "ml-1" ]
|
||||
[ text "New Invites"
|
||||
]
|
||||
]
|
||||
, div [ class "py-1" ]
|
||||
[ hr [ class S.border ]
|
||||
[]
|
||||
]
|
||||
, a
|
||||
[ class dropdownItem
|
||||
, href "https://docspell.org/docs"
|
||||
, target "_new"
|
||||
, title "Opens https://docspell.org/docs"
|
||||
]
|
||||
[ i [ class "fa fa-question-circle w-6" ] []
|
||||
, span [ class "ml-1" ] [ text "Help" ]
|
||||
, span [ class "float-right" ]
|
||||
[ i [ class "fa fa-external-link-alt w-6" ] []
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
userMenu : AuthResult -> Model -> Html Msg
|
||||
userMenu acc model =
|
||||
div [ class "relative" ]
|
||||
[ a
|
||||
[ class dropdownLink
|
||||
, onClick ToggleUserMenu
|
||||
, href "#"
|
||||
]
|
||||
[ i [ class "fa fa-user w-6" ] []
|
||||
]
|
||||
, div
|
||||
[ class dropdownMenu
|
||||
, classList [ ( "hidden", not model.userMenuOpen ) ]
|
||||
]
|
||||
[ div [ class dropdownHeadItem ]
|
||||
[ i [ class "fa fa-user pr-2 font-thin" ] []
|
||||
, span [ class "ml-3 text-sm" ]
|
||||
[ Data.Flags.accountString acc |> text
|
||||
]
|
||||
]
|
||||
, div [ class "py-1" ] [ hr [ class S.border ] [] ]
|
||||
, userPageLink model
|
||||
CollectiveSettingPage
|
||||
[ i [ class "fa fa-users w-6" ] []
|
||||
, span [ class "ml-1" ]
|
||||
[ text "Collective Profile"
|
||||
]
|
||||
]
|
||||
, userPageLink model
|
||||
UserSettingPage
|
||||
[ i [ class "fa fa-user-circle w-6" ] []
|
||||
, span [ class "ml-1" ]
|
||||
[ text "User Profile"
|
||||
]
|
||||
]
|
||||
, a
|
||||
[ href "#"
|
||||
, onClick ToggleDarkMode
|
||||
, class dropdownItem
|
||||
]
|
||||
[ i [ class "fa fa-adjust w-6" ] []
|
||||
, span [ class "ml-1" ]
|
||||
[ text "Light/Dark"
|
||||
]
|
||||
]
|
||||
, div [ class "py-1" ] [ hr [ class S.border ] [] ]
|
||||
, a
|
||||
[ href "#"
|
||||
, class dropdownItem
|
||||
, onClick Logout
|
||||
]
|
||||
[ i [ class "fa fa-sign-out-alt w-6" ] []
|
||||
, span [ class "ml-1" ]
|
||||
[ text "Logout"
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
userPageLink : Model -> Page -> List (Html Msg) -> Html Msg
|
||||
userPageLink model page children =
|
||||
a
|
||||
[ classList
|
||||
[ ( dropdownItem, True )
|
||||
, ( "bg-gray-200 dark:bg-bluegray-700", model.page == page )
|
||||
]
|
||||
, onClick ToggleUserMenu
|
||||
, Page.href page
|
||||
]
|
||||
children
|
||||
|
||||
|
||||
dataPageLink : Model -> Page -> List ( String, Bool ) -> List (Html Msg) -> Html Msg
|
||||
dataPageLink model page classes children =
|
||||
a
|
||||
[ classList
|
||||
([ ( dropdownItem, True )
|
||||
, ( "bg-gray-200 dark:bg-bluegray-700", model.page == page )
|
||||
]
|
||||
++ classes
|
||||
)
|
||||
, onClick ToggleNavMenu
|
||||
, Page.href page
|
||||
]
|
||||
children
|
||||
|
||||
|
||||
dropdownLink : String
|
||||
dropdownLink =
|
||||
"px-4 py-2 w-12 font-bold inline-flex h-full items-center hover:bg-blue-200 dark:hover:bg-bluegray-800"
|
||||
|
||||
|
||||
dropdownItem : String
|
||||
dropdownItem =
|
||||
"transition-colors duration-200 items-center block px-4 py-2 text-normal hover:bg-gray-200 dark:hover:bg-bluegray-700 dark:hover:text-bluegray-50"
|
||||
|
||||
|
||||
dropdownHeadItem : String
|
||||
dropdownHeadItem =
|
||||
"transition-colors duration-200 items-center block px-4 py-2 font-semibold uppercase"
|
||||
|
||||
|
||||
dropdownMenu : String
|
||||
dropdownMenu =
|
||||
" absolute right-0 bg-white dark:bg-bluegray-800 border dark:border-bluegray-700 dark:text-bluegray-300 shadow-lg opacity-1 transition duration-200 min-w-max "
|
||||
|
||||
|
||||
viewHome : Model -> List (Html Msg)
|
||||
viewHome model =
|
||||
[ Html.map HomeMsg (Home.viewSidebar model.sidebarVisible model.flags model.uiSettings model.homeModel)
|
||||
, Html.map HomeMsg (Home.viewContent model.flags model.uiSettings model.homeModel)
|
||||
]
|
||||
|
||||
|
||||
viewCollectiveSettings : Model -> List (Html Msg)
|
||||
viewCollectiveSettings model =
|
||||
[ Html.map CollSettingsMsg
|
||||
(CollectiveSettings.viewSidebar model.sidebarVisible
|
||||
model.flags
|
||||
model.uiSettings
|
||||
model.collSettingsModel
|
||||
)
|
||||
, Html.map CollSettingsMsg
|
||||
(CollectiveSettings.viewContent model.flags
|
||||
model.uiSettings
|
||||
model.collSettingsModel
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
viewLogin : Model -> List (Html Msg)
|
||||
viewLogin model =
|
||||
[ Html.map LoginMsg
|
||||
(Login.viewSidebar model.sidebarVisible model.flags model.uiSettings model.loginModel)
|
||||
, Html.map LoginMsg
|
||||
(Login.viewContent model.flags model.uiSettings model.loginModel)
|
||||
]
|
||||
|
||||
|
||||
viewManageData : Model -> List (Html Msg)
|
||||
viewManageData model =
|
||||
[ Html.map ManageDataMsg
|
||||
(ManageData.viewSidebar model.sidebarVisible model.flags model.uiSettings model.manageDataModel)
|
||||
, Html.map ManageDataMsg
|
||||
(ManageData.viewContent model.flags model.uiSettings model.manageDataModel)
|
||||
]
|
||||
|
||||
|
||||
viewUserSettings : Model -> List (Html Msg)
|
||||
viewUserSettings model =
|
||||
[ Html.map UserSettingsMsg
|
||||
(UserSettings.viewSidebar model.sidebarVisible model.flags model.uiSettings model.userSettingsModel)
|
||||
, Html.map UserSettingsMsg
|
||||
(UserSettings.viewContent model.flags model.uiSettings model.userSettingsModel)
|
||||
]
|
||||
|
||||
|
||||
viewQueue : Model -> List (Html Msg)
|
||||
viewQueue model =
|
||||
[ Html.map QueueMsg
|
||||
(Queue.viewSidebar model.sidebarVisible model.flags model.uiSettings model.queueModel)
|
||||
, Html.map QueueMsg
|
||||
(Queue.viewContent model.flags model.uiSettings model.queueModel)
|
||||
]
|
||||
|
||||
|
||||
viewRegister : Model -> List (Html Msg)
|
||||
viewRegister model =
|
||||
[ Html.map RegisterMsg
|
||||
(Register.viewSidebar model.sidebarVisible model.flags model.uiSettings model.registerModel)
|
||||
, Html.map RegisterMsg
|
||||
(Register.viewContent model.flags model.uiSettings model.registerModel)
|
||||
]
|
||||
|
||||
|
||||
viewNewInvite : Model -> List (Html Msg)
|
||||
viewNewInvite model =
|
||||
[ Html.map NewInviteMsg
|
||||
(NewInvite.viewSidebar model.sidebarVisible model.flags model.uiSettings model.newInviteModel)
|
||||
, Html.map NewInviteMsg
|
||||
(NewInvite.viewContent model.flags model.uiSettings model.newInviteModel)
|
||||
]
|
||||
|
||||
|
||||
viewUpload : Maybe String -> Model -> List (Html Msg)
|
||||
viewUpload mid model =
|
||||
[ Html.map UploadMsg
|
||||
(Upload.viewSidebar
|
||||
mid
|
||||
model.sidebarVisible
|
||||
model.flags
|
||||
model.uiSettings
|
||||
model.uploadModel
|
||||
)
|
||||
, Html.map UploadMsg
|
||||
(Upload.viewContent mid
|
||||
model.flags
|
||||
model.uiSettings
|
||||
model.uploadModel
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
viewItemDetail : String -> Model -> List (Html Msg)
|
||||
viewItemDetail id model =
|
||||
let
|
||||
inav =
|
||||
Page.Home.Data.itemNav id model.homeModel
|
||||
in
|
||||
[ Html.map ItemDetailMsg
|
||||
(ItemDetail.viewSidebar
|
||||
model.sidebarVisible
|
||||
model.flags
|
||||
model.uiSettings
|
||||
model.itemDetailModel
|
||||
)
|
||||
, Html.map ItemDetailMsg
|
||||
(ItemDetail.viewContent
|
||||
inav
|
||||
model.flags
|
||||
model.uiSettings
|
||||
model.itemDetailModel
|
||||
)
|
||||
]
|
Reference in New Issue
Block a user