mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-06-23 19:08:26 +00:00
Allow to select multiple items for deletion and edit
This commit is contained in:
@ -2,12 +2,18 @@ module Page.Home.Data exposing
|
||||
( Model
|
||||
, Msg(..)
|
||||
, SearchType(..)
|
||||
, SelectActionMode(..)
|
||||
, SelectViewModel
|
||||
, ViewMode(..)
|
||||
, defaultSearchType
|
||||
, doSearchCmd
|
||||
, init
|
||||
, initSelectViewModel
|
||||
, itemNav
|
||||
, menuCollapsed
|
||||
, resultsBelowLimit
|
||||
, searchTypeString
|
||||
, selectActive
|
||||
)
|
||||
|
||||
import Api
|
||||
@ -16,12 +22,16 @@ import Api.Model.ItemSearch
|
||||
import Browser.Dom as Dom
|
||||
import Comp.FixedDropdown
|
||||
import Comp.ItemCardList
|
||||
import Comp.ItemDetail.EditMenu
|
||||
import Comp.SearchMenu
|
||||
import Comp.YesNoDimmer
|
||||
import Data.Flags exposing (Flags)
|
||||
import Data.ItemNav exposing (ItemNav)
|
||||
import Data.ItemSelection exposing (ItemSelection)
|
||||
import Data.Items
|
||||
import Data.UiSettings exposing (UiSettings)
|
||||
import Http
|
||||
import Set exposing (Set)
|
||||
import Throttle exposing (Throttle)
|
||||
import Util.Html exposing (KeyCode(..))
|
||||
import Util.ItemDragDrop as DD
|
||||
@ -31,7 +41,7 @@ type alias Model =
|
||||
{ searchMenuModel : Comp.SearchMenu.Model
|
||||
, itemListModel : Comp.ItemCardList.Model
|
||||
, searchInProgress : Bool
|
||||
, menuCollapsed : Bool
|
||||
, viewMode : ViewMode
|
||||
, searchOffset : Int
|
||||
, moreAvailable : Bool
|
||||
, moreInProgress : Bool
|
||||
@ -45,6 +55,29 @@ type alias Model =
|
||||
}
|
||||
|
||||
|
||||
type alias SelectViewModel =
|
||||
{ ids : Set String
|
||||
, action : SelectActionMode
|
||||
, deleteAllConfirm : Comp.YesNoDimmer.Model
|
||||
, editModel : Comp.ItemDetail.EditMenu.Model
|
||||
}
|
||||
|
||||
|
||||
initSelectViewModel : SelectViewModel
|
||||
initSelectViewModel =
|
||||
{ ids = Set.empty
|
||||
, action = NoneAction
|
||||
, deleteAllConfirm = Comp.YesNoDimmer.initActive
|
||||
, editModel = Comp.ItemDetail.EditMenu.init
|
||||
}
|
||||
|
||||
|
||||
type ViewMode
|
||||
= SimpleView
|
||||
| SearchView
|
||||
| SelectView SelectViewModel
|
||||
|
||||
|
||||
init : Flags -> Model
|
||||
init flags =
|
||||
let
|
||||
@ -58,7 +91,6 @@ init flags =
|
||||
{ searchMenuModel = Comp.SearchMenu.init
|
||||
, itemListModel = Comp.ItemCardList.init
|
||||
, searchInProgress = False
|
||||
, menuCollapsed = True
|
||||
, searchOffset = 0
|
||||
, moreAvailable = True
|
||||
, moreInProgress = False
|
||||
@ -72,6 +104,7 @@ init flags =
|
||||
, dragDropData =
|
||||
DD.DragDropData DD.init Nothing
|
||||
, scrollToCard = Nothing
|
||||
, viewMode = SimpleView
|
||||
}
|
||||
|
||||
|
||||
@ -84,6 +117,32 @@ defaultSearchType flags =
|
||||
BasicSearch
|
||||
|
||||
|
||||
menuCollapsed : Model -> Bool
|
||||
menuCollapsed model =
|
||||
case model.viewMode of
|
||||
SimpleView ->
|
||||
True
|
||||
|
||||
SearchView ->
|
||||
False
|
||||
|
||||
SelectView _ ->
|
||||
False
|
||||
|
||||
|
||||
selectActive : Model -> Bool
|
||||
selectActive model =
|
||||
case model.viewMode of
|
||||
SimpleView ->
|
||||
False
|
||||
|
||||
SearchView ->
|
||||
False
|
||||
|
||||
SelectView _ ->
|
||||
True
|
||||
|
||||
|
||||
type Msg
|
||||
= Init
|
||||
| SearchMenuMsg Comp.SearchMenu.Msg
|
||||
@ -93,6 +152,7 @@ type Msg
|
||||
| ItemSearchAddResp (Result Http.Error ItemLightList)
|
||||
| DoSearch
|
||||
| ToggleSearchMenu
|
||||
| ToggleSelectView
|
||||
| LoadMore
|
||||
| UpdateThrottle
|
||||
| SetBasicSearch String
|
||||
@ -101,6 +161,12 @@ type Msg
|
||||
| SetContentOnly String
|
||||
| ScrollResult (Result Dom.Error ())
|
||||
| ClearItemDetailId
|
||||
| SelectAllItems
|
||||
| SelectNoItems
|
||||
| RequestDeleteSelected
|
||||
| DeleteSelectedConfirmMsg Comp.YesNoDimmer.Msg
|
||||
| EditSelectedItems
|
||||
| EditMenuMsg Comp.ItemDetail.EditMenu.Msg
|
||||
|
||||
|
||||
type SearchType
|
||||
@ -109,6 +175,12 @@ type SearchType
|
||||
| ContentOnlySearch
|
||||
|
||||
|
||||
type SelectActionMode
|
||||
= NoneAction
|
||||
| DeleteSelected
|
||||
| EditSelected
|
||||
|
||||
|
||||
searchTypeString : SearchType -> String
|
||||
searchTypeString st =
|
||||
case st of
|
||||
|
@ -3,13 +3,18 @@ module Page.Home.Update exposing (update)
|
||||
import Browser.Navigation as Nav
|
||||
import Comp.FixedDropdown
|
||||
import Comp.ItemCardList
|
||||
import Comp.ItemDetail.EditMenu
|
||||
import Comp.SearchMenu
|
||||
import Comp.YesNoDimmer
|
||||
import Data.Flags exposing (Flags)
|
||||
import Data.ItemSelection
|
||||
import Data.Items
|
||||
import Data.UiSettings exposing (UiSettings)
|
||||
import Page exposing (Page(..))
|
||||
import Page.Home.Data exposing (..)
|
||||
import Process
|
||||
import Scroll
|
||||
import Set
|
||||
import Task
|
||||
import Throttle
|
||||
import Time
|
||||
@ -82,10 +87,19 @@ update mId key flags settings msg model =
|
||||
flags
|
||||
m
|
||||
model.itemListModel
|
||||
|
||||
nextView =
|
||||
case ( model.viewMode, result.selection ) of
|
||||
( SelectView svm, Data.ItemSelection.Active ids ) ->
|
||||
SelectView { svm | ids = ids }
|
||||
|
||||
( v, _ ) ->
|
||||
v
|
||||
in
|
||||
withSub
|
||||
( { model
|
||||
| itemListModel = result.model
|
||||
, viewMode = nextView
|
||||
, dragDropData = DD.DragDropData result.dragModel Nothing
|
||||
}
|
||||
, Cmd.batch [ Cmd.map ItemCardListMsg result.cmd ]
|
||||
@ -159,11 +173,43 @@ update mId key flags settings msg model =
|
||||
doSearch flags settings False nm
|
||||
|
||||
ToggleSearchMenu ->
|
||||
let
|
||||
nextView =
|
||||
case model.viewMode of
|
||||
SimpleView ->
|
||||
SearchView
|
||||
|
||||
SearchView ->
|
||||
SimpleView
|
||||
|
||||
SelectView _ ->
|
||||
SimpleView
|
||||
in
|
||||
withSub
|
||||
( { model | menuCollapsed = not model.menuCollapsed }
|
||||
( { model | viewMode = nextView }
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
ToggleSelectView ->
|
||||
let
|
||||
( nextView, cmd ) =
|
||||
case model.viewMode of
|
||||
SimpleView ->
|
||||
( SelectView initSelectViewModel, loadEditModel flags )
|
||||
|
||||
SearchView ->
|
||||
( SelectView initSelectViewModel, loadEditModel flags )
|
||||
|
||||
SelectView _ ->
|
||||
( SearchView, Cmd.none )
|
||||
in
|
||||
withSub
|
||||
( { model
|
||||
| viewMode = nextView
|
||||
}
|
||||
, cmd
|
||||
)
|
||||
|
||||
LoadMore ->
|
||||
if model.moreAvailable then
|
||||
doSearchMore flags settings model |> withSub
|
||||
@ -253,6 +299,139 @@ update mId key flags settings msg model =
|
||||
ClearItemDetailId ->
|
||||
noSub ( { model | scrollToCard = Nothing }, Cmd.none )
|
||||
|
||||
SelectAllItems ->
|
||||
case model.viewMode of
|
||||
SelectView svm ->
|
||||
let
|
||||
visible =
|
||||
Data.Items.idSet model.itemListModel.results
|
||||
|
||||
svm_ =
|
||||
{ svm | ids = Set.union svm.ids visible }
|
||||
in
|
||||
noSub
|
||||
( { model | viewMode = SelectView svm_ }
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
_ ->
|
||||
noSub ( model, Cmd.none )
|
||||
|
||||
SelectNoItems ->
|
||||
case model.viewMode of
|
||||
SelectView svm ->
|
||||
let
|
||||
svm_ =
|
||||
{ svm | ids = Set.empty }
|
||||
in
|
||||
noSub
|
||||
( { model | viewMode = SelectView svm_ }
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
_ ->
|
||||
noSub ( model, Cmd.none )
|
||||
|
||||
DeleteSelectedConfirmMsg lmsg ->
|
||||
case model.viewMode of
|
||||
SelectView svm ->
|
||||
let
|
||||
( confirmModel, confirmed ) =
|
||||
Comp.YesNoDimmer.update lmsg svm.deleteAllConfirm
|
||||
|
||||
cmd =
|
||||
if confirmed then
|
||||
Cmd.none
|
||||
|
||||
else
|
||||
Cmd.none
|
||||
|
||||
act =
|
||||
if confirmModel.active || confirmed then
|
||||
DeleteSelected
|
||||
|
||||
else
|
||||
NoneAction
|
||||
in
|
||||
noSub
|
||||
( { model
|
||||
| viewMode =
|
||||
SelectView
|
||||
{ svm
|
||||
| deleteAllConfirm = confirmModel
|
||||
, action = act
|
||||
}
|
||||
}
|
||||
, cmd
|
||||
)
|
||||
|
||||
_ ->
|
||||
noSub ( model, Cmd.none )
|
||||
|
||||
RequestDeleteSelected ->
|
||||
case model.viewMode of
|
||||
SelectView svm ->
|
||||
if svm.ids == Set.empty then
|
||||
noSub ( model, Cmd.none )
|
||||
|
||||
else
|
||||
let
|
||||
lmsg =
|
||||
DeleteSelectedConfirmMsg Comp.YesNoDimmer.activate
|
||||
|
||||
model_ =
|
||||
{ model | viewMode = SelectView { svm | action = DeleteSelected } }
|
||||
in
|
||||
update mId key flags settings lmsg model_
|
||||
|
||||
_ ->
|
||||
noSub ( model, Cmd.none )
|
||||
|
||||
EditSelectedItems ->
|
||||
case model.viewMode of
|
||||
SelectView svm ->
|
||||
if svm.action == EditSelected then
|
||||
noSub
|
||||
( { model | viewMode = SelectView { svm | action = NoneAction } }
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
else if svm.ids == Set.empty then
|
||||
noSub ( model, Cmd.none )
|
||||
|
||||
else
|
||||
noSub
|
||||
( { model | viewMode = SelectView { svm | action = EditSelected } }
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
_ ->
|
||||
noSub ( model, Cmd.none )
|
||||
|
||||
EditMenuMsg lmsg ->
|
||||
case model.viewMode of
|
||||
SelectView svm ->
|
||||
let
|
||||
res =
|
||||
Comp.ItemDetail.EditMenu.update flags lmsg svm.editModel
|
||||
|
||||
svm_ =
|
||||
{ svm | editModel = res.model }
|
||||
|
||||
cmd_ =
|
||||
Cmd.map EditMenuMsg res.cmd
|
||||
|
||||
sub_ =
|
||||
Sub.map EditMenuMsg res.sub
|
||||
|
||||
_ =
|
||||
Debug.log "change" res.change
|
||||
in
|
||||
( { model | viewMode = SelectView svm_ }, cmd_, sub_ )
|
||||
|
||||
_ ->
|
||||
noSub ( model, Cmd.none )
|
||||
|
||||
|
||||
|
||||
--- Helpers
|
||||
@ -275,12 +454,17 @@ scrollToCard mId model =
|
||||
( model, Cmd.none, Sub.none )
|
||||
|
||||
|
||||
loadEditModel : Flags -> Cmd Msg
|
||||
loadEditModel flags =
|
||||
Cmd.map EditMenuMsg (Comp.ItemDetail.EditMenu.loadModel flags)
|
||||
|
||||
|
||||
doSearch : Flags -> UiSettings -> Bool -> Model -> ( Model, Cmd Msg, Sub Msg )
|
||||
doSearch flags settings scroll model =
|
||||
let
|
||||
stype =
|
||||
if
|
||||
not model.menuCollapsed
|
||||
not (menuCollapsed model)
|
||||
|| Util.String.isNothingOrBlank model.contentOnlySearch
|
||||
then
|
||||
BasicSearch
|
||||
|
@ -3,26 +3,51 @@ module Page.Home.View exposing (view)
|
||||
import Api.Model.ItemSearch
|
||||
import Comp.FixedDropdown
|
||||
import Comp.ItemCardList
|
||||
import Comp.ItemDetail.EditMenu
|
||||
import Comp.SearchMenu
|
||||
import Comp.YesNoDimmer
|
||||
import Data.Flags exposing (Flags)
|
||||
import Data.ItemSelection
|
||||
import Data.UiSettings exposing (UiSettings)
|
||||
import Html exposing (..)
|
||||
import Html.Attributes exposing (..)
|
||||
import Html.Events exposing (onClick, onInput)
|
||||
import Page exposing (Page(..))
|
||||
import Page.Home.Data exposing (..)
|
||||
import Set
|
||||
import Util.Html
|
||||
|
||||
|
||||
view : Flags -> UiSettings -> Model -> Html Msg
|
||||
view flags settings model =
|
||||
let
|
||||
itemViewCfg =
|
||||
case model.viewMode of
|
||||
SelectView svm ->
|
||||
Comp.ItemCardList.ViewConfig
|
||||
model.scrollToCard
|
||||
(Data.ItemSelection.Active svm.ids)
|
||||
|
||||
_ ->
|
||||
Comp.ItemCardList.ViewConfig
|
||||
model.scrollToCard
|
||||
Data.ItemSelection.Inactive
|
||||
|
||||
selectAction =
|
||||
case model.viewMode of
|
||||
SelectView svm ->
|
||||
svm.action
|
||||
|
||||
_ ->
|
||||
NoneAction
|
||||
in
|
||||
div [ class "home-page ui padded grid" ]
|
||||
[ div
|
||||
[ classList
|
||||
[ ( "sixteen wide mobile six wide tablet four wide computer search-menu column"
|
||||
, True
|
||||
)
|
||||
, ( "invisible hidden", model.menuCollapsed )
|
||||
, ( "invisible hidden", menuCollapsed model )
|
||||
]
|
||||
]
|
||||
[ div
|
||||
@ -38,6 +63,17 @@ view flags settings model =
|
||||
]
|
||||
, div [ class "right floated menu" ]
|
||||
[ a
|
||||
[ classList
|
||||
[ ( "borderless item", True )
|
||||
, ( "active", selectActive model )
|
||||
]
|
||||
, href "#"
|
||||
, title "Select items"
|
||||
, onClick ToggleSelectView
|
||||
]
|
||||
[ i [ class "tasks icon" ] []
|
||||
]
|
||||
, a
|
||||
[ class "borderless item"
|
||||
, onClick ResetSearch
|
||||
, title "Reset form"
|
||||
@ -63,26 +99,30 @@ view flags settings model =
|
||||
]
|
||||
]
|
||||
, div [ class "" ]
|
||||
[ Html.map SearchMenuMsg
|
||||
(Comp.SearchMenu.viewDrop model.dragDropData
|
||||
flags
|
||||
settings
|
||||
model.searchMenuModel
|
||||
)
|
||||
]
|
||||
(viewLeftMenu flags settings model)
|
||||
]
|
||||
, div
|
||||
[ classList
|
||||
[ ( "sixteen wide mobile ten wide tablet twelve wide computer column"
|
||||
, not model.menuCollapsed
|
||||
, not (menuCollapsed model)
|
||||
)
|
||||
, ( "sixteen wide column", model.menuCollapsed )
|
||||
, ( "sixteen wide column", menuCollapsed model )
|
||||
, ( "item-card-list", True )
|
||||
]
|
||||
]
|
||||
[ viewSearchBar flags model
|
||||
[ viewBar flags model
|
||||
, case model.viewMode of
|
||||
SelectView svm ->
|
||||
Html.map DeleteSelectedConfirmMsg
|
||||
(Comp.YesNoDimmer.view2 (selectAction == DeleteSelected)
|
||||
deleteAllDimmer
|
||||
svm.deleteAllConfirm
|
||||
)
|
||||
|
||||
_ ->
|
||||
span [ class "invisible" ] []
|
||||
, Html.map ItemCardListMsg
|
||||
(Comp.ItemCardList.view model.scrollToCard settings model.itemListModel)
|
||||
(Comp.ItemCardList.view itemViewCfg settings model.itemListModel)
|
||||
]
|
||||
, div
|
||||
[ classList
|
||||
@ -117,6 +157,113 @@ view flags settings model =
|
||||
]
|
||||
|
||||
|
||||
viewLeftMenu : Flags -> UiSettings -> Model -> List (Html Msg)
|
||||
viewLeftMenu flags settings model =
|
||||
let
|
||||
searchMenu =
|
||||
[ Html.map SearchMenuMsg
|
||||
(Comp.SearchMenu.viewDrop model.dragDropData
|
||||
flags
|
||||
settings
|
||||
model.searchMenuModel
|
||||
)
|
||||
]
|
||||
in
|
||||
case model.viewMode of
|
||||
SelectView svm ->
|
||||
case svm.action of
|
||||
EditSelected ->
|
||||
let
|
||||
cfg =
|
||||
Comp.ItemDetail.EditMenu.defaultViewConfig
|
||||
in
|
||||
[ div [ class "ui dividing header" ]
|
||||
[ text "Multi-Edit"
|
||||
]
|
||||
, div [ class "ui info message" ]
|
||||
[ text "Note that a change here immediatly affects all selected items on the right!"
|
||||
]
|
||||
, Html.map EditMenuMsg
|
||||
(Comp.ItemDetail.EditMenu.view cfg settings svm.editModel)
|
||||
]
|
||||
|
||||
_ ->
|
||||
searchMenu
|
||||
|
||||
_ ->
|
||||
searchMenu
|
||||
|
||||
|
||||
viewBar : Flags -> Model -> Html Msg
|
||||
viewBar flags model =
|
||||
case model.viewMode of
|
||||
SimpleView ->
|
||||
viewSearchBar flags model
|
||||
|
||||
SearchView ->
|
||||
div [ class "hidden invisible" ] []
|
||||
|
||||
SelectView svm ->
|
||||
viewActionBar flags svm model
|
||||
|
||||
|
||||
viewActionBar : Flags -> SelectViewModel -> Model -> Html Msg
|
||||
viewActionBar _ svm model =
|
||||
let
|
||||
selectCount =
|
||||
Set.size svm.ids |> String.fromInt
|
||||
in
|
||||
div
|
||||
[ class "ui ablue-comp icon menu"
|
||||
]
|
||||
[ a
|
||||
[ classList
|
||||
[ ( "borderless item", True )
|
||||
, ( "active", svm.action == EditSelected )
|
||||
]
|
||||
, href "#"
|
||||
, title <| "Edit " ++ selectCount ++ " selected items"
|
||||
, onClick EditSelectedItems
|
||||
]
|
||||
[ i [ class "ui edit icon" ] []
|
||||
]
|
||||
, a
|
||||
[ classList
|
||||
[ ( "borderless item", True )
|
||||
, ( "active", svm.action == DeleteSelected )
|
||||
]
|
||||
, href "#"
|
||||
, title <| "Delete " ++ selectCount ++ " selected items"
|
||||
, onClick RequestDeleteSelected
|
||||
]
|
||||
[ i [ class "trash icon" ] []
|
||||
]
|
||||
, div [ class "right menu" ]
|
||||
[ a
|
||||
[ class "item"
|
||||
, href "#"
|
||||
, onClick SelectAllItems
|
||||
, title "Select all"
|
||||
]
|
||||
[ i [ class "check square outline icon" ] []
|
||||
]
|
||||
, a
|
||||
[ class "borderless item"
|
||||
, href "#"
|
||||
, title "Select none"
|
||||
, onClick SelectNoItems
|
||||
]
|
||||
[ i [ class "square outline icon" ] []
|
||||
]
|
||||
, div [ class "borderless label item" ]
|
||||
[ div [ class "ui circular purple icon label" ]
|
||||
[ text selectCount
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
viewSearchBar : Flags -> Model -> Html Msg
|
||||
viewSearchBar flags model =
|
||||
let
|
||||
@ -145,7 +292,7 @@ viewSearchBar flags model =
|
||||
in
|
||||
div
|
||||
[ classList
|
||||
[ ( "invisible hidden", not model.menuCollapsed )
|
||||
[ ( "invisible hidden", not (menuCollapsed model) )
|
||||
, ( "ui secondary stackable menu container", True )
|
||||
]
|
||||
]
|
||||
@ -221,3 +368,15 @@ hasMoreSearch model =
|
||||
Api.Model.ItemSearch.empty
|
||||
in
|
||||
is_ /= Api.Model.ItemSearch.empty
|
||||
|
||||
|
||||
deleteAllDimmer : Comp.YesNoDimmer.Settings
|
||||
deleteAllDimmer =
|
||||
{ message = "Really delete all selected items?"
|
||||
, headerIcon = "exclamation icon"
|
||||
, headerClass = "ui inverted icon header"
|
||||
, confirmButton = "Yes"
|
||||
, cancelButton = "No"
|
||||
, invertedDimmer = False
|
||||
, extraClass = "top aligned"
|
||||
}
|
||||
|
Reference in New Issue
Block a user