Simplify search bar and search menu

The functionality of the search bar is now in the search menu, too.
The search menu shows one input field for "textual search", which is
either the fulltext search (if enabled) or a basic search in various
names.
This commit is contained in:
Eike Kettner 2020-12-05 22:38:27 +01:00
parent 5882405f30
commit 2aed7ba142
4 changed files with 233 additions and 192 deletions

View File

@ -2,8 +2,12 @@ module Comp.SearchMenu exposing
( Model ( Model
, Msg(..) , Msg(..)
, NextState , NextState
, TextSearchModel
, getItemSearch , getItemSearch
, init , init
, isFulltextSearch
, isNamesSearch
, textSearchString
, update , update
, updateDrop , updateDrop
, view , view
@ -66,18 +70,21 @@ type alias Model =
, untilDueDateModel : DatePicker , untilDueDateModel : DatePicker
, untilDueDate : Maybe Int , untilDueDate : Maybe Int
, nameModel : Maybe String , nameModel : Maybe String
, allNameModel : Maybe String , textSearchModel : TextSearchModel
, fulltextModel : Maybe String
, datePickerInitialized : Bool , datePickerInitialized : Bool
, showNameHelp : Bool
, customFieldModel : Comp.CustomFieldMultiInput.Model , customFieldModel : Comp.CustomFieldMultiInput.Model
, customValues : CustomFieldValueCollect , customValues : CustomFieldValueCollect
, sourceModel : Maybe String , sourceModel : Maybe String
} }
init : Model type TextSearchModel
init = = Fulltext (Maybe String)
| Names (Maybe String)
init : Flags -> Model
init flags =
{ tagSelectModel = Comp.TagSelect.init Comp.TagSelect.emptySelection [] { tagSelectModel = Comp.TagSelect.init Comp.TagSelect.emptySelection []
, tagSelection = Comp.TagSelect.emptySelection , tagSelection = Comp.TagSelect.emptySelection
, directionModel = , directionModel =
@ -124,16 +131,87 @@ init =
, untilDueDateModel = Comp.DatePicker.emptyModel , untilDueDateModel = Comp.DatePicker.emptyModel
, untilDueDate = Nothing , untilDueDate = Nothing
, nameModel = Nothing , nameModel = Nothing
, allNameModel = Nothing , textSearchModel =
, fulltextModel = Nothing if flags.config.fullTextSearchEnabled then
Fulltext Nothing
else
Names Nothing
, datePickerInitialized = False , datePickerInitialized = False
, showNameHelp = False
, customFieldModel = Comp.CustomFieldMultiInput.initWith [] , customFieldModel = Comp.CustomFieldMultiInput.initWith []
, customValues = Data.CustomFieldChange.emptyCollect , customValues = Data.CustomFieldChange.emptyCollect
, sourceModel = Nothing , sourceModel = Nothing
} }
updateTextSearch : String -> TextSearchModel -> TextSearchModel
updateTextSearch str model =
let
next =
Util.Maybe.fromString str
in
case model of
Fulltext _ ->
Fulltext next
Names _ ->
Names next
swapTextSearch : TextSearchModel -> TextSearchModel
swapTextSearch model =
case model of
Fulltext s ->
Names s
Names s ->
Fulltext s
textSearchValue : TextSearchModel -> { nameSearch : Maybe String, fullText : Maybe String }
textSearchValue model =
case model of
Fulltext s ->
{ nameSearch = Nothing
, fullText = s
}
Names s ->
{ nameSearch = s
, fullText = Nothing
}
textSearchString : TextSearchModel -> Maybe String
textSearchString model =
case model of
Fulltext s ->
s
Names s ->
s
isFulltextSearch : Model -> Bool
isFulltextSearch model =
case model.textSearchModel of
Fulltext _ ->
True
Names _ ->
False
isNamesSearch : Model -> Bool
isNamesSearch model =
case model.textSearchModel of
Fulltext _ ->
False
Names _ ->
True
getDirection : Model -> Maybe Direction getDirection : Model -> Maybe Direction
getDirection model = getDirection model =
let let
@ -164,6 +242,9 @@ getItemSearch model =
else else
"*" ++ s ++ "*" "*" ++ s ++ "*"
textSearch =
textSearchValue model.textSearchModel
in in
{ e { e
| tagsInclude = model.tagSelection.includeTags |> List.map .tag |> List.map .id | tagsInclude = model.tagSelection.includeTags |> List.map .tag |> List.map .id
@ -186,9 +267,9 @@ getItemSearch model =
model.nameModel model.nameModel
|> Maybe.map amendWildcards |> Maybe.map amendWildcards
, allNames = , allNames =
model.allNameModel textSearch.nameSearch
|> Maybe.map amendWildcards |> Maybe.map amendWildcards
, fullText = model.fulltextModel , fullText = textSearch.fullText
, tagCategoriesInclude = model.tagSelection.includeCats |> List.map .name , tagCategoriesInclude = model.tagSelection.includeCats |> List.map .name
, tagCategoriesExclude = model.tagSelection.excludeCats |> List.map .name , tagCategoriesExclude = model.tagSelection.excludeCats |> List.map .name
, customValues = Data.CustomFieldChange.toFieldValues model.customValues , customValues = Data.CustomFieldChange.toFieldValues model.customValues
@ -225,8 +306,13 @@ resetModel model =
, fromDueDate = Nothing , fromDueDate = Nothing
, untilDueDate = Nothing , untilDueDate = Nothing
, nameModel = Nothing , nameModel = Nothing
, allNameModel = Nothing , textSearchModel =
, fulltextModel = Nothing case model.textSearchModel of
Fulltext _ ->
Fulltext Nothing
Names _ ->
Names Nothing
, customFieldModel = , customFieldModel =
Comp.CustomFieldMultiInput.reset Comp.CustomFieldMultiInput.reset
model.customFieldModel model.customFieldModel
@ -257,11 +343,12 @@ type Msg
| GetEquipResp (Result Http.Error EquipmentList) | GetEquipResp (Result Http.Error EquipmentList)
| GetPersonResp (Result Http.Error PersonList) | GetPersonResp (Result Http.Error PersonList)
| SetName String | SetName String
| SetAllName String | SetTextSearch String
| SetFulltext String | SwapTextSearch
| SetFulltextSearch
| SetNamesSearch
| ResetForm | ResetForm
| KeyUpMsg (Maybe KeyCode) | KeyUpMsg (Maybe KeyCode)
| ToggleNameHelp
| FolderSelectMsg Comp.FolderSelect.Msg | FolderSelectMsg Comp.FolderSelect.Msg
| GetFolderResp (Result Http.Error FolderList) | GetFolderResp (Result Http.Error FolderList)
| SetCorrOrg IdName | SetCorrOrg IdName
@ -641,23 +728,55 @@ updateDrop ddm flags settings msg model =
, dragDrop = DD.DragDropData ddm Nothing , dragDrop = DD.DragDropData ddm Nothing
} }
SetAllName str -> SetTextSearch str ->
let { model = { model | textSearchModel = updateTextSearch str model.textSearchModel }
next =
Util.Maybe.fromString str
in
{ model = { model | allNameModel = next }
, cmd = Cmd.none , cmd = Cmd.none
, stateChange = False , stateChange = False
, dragDrop = DD.DragDropData ddm Nothing , dragDrop = DD.DragDropData ddm Nothing
} }
SetFulltext str -> SwapTextSearch ->
let if flags.config.fullTextSearchEnabled then
next = { model = { model | textSearchModel = swapTextSearch model.textSearchModel }
Util.Maybe.fromString str , cmd = Cmd.none
in , stateChange = False
{ model = { model | fulltextModel = next } , dragDrop = DD.DragDropData ddm Nothing
}
else
{ model = model
, cmd = Cmd.none
, stateChange = False
, dragDrop = DD.DragDropData ddm Nothing
}
SetFulltextSearch ->
case model.textSearchModel of
Fulltext _ ->
{ model = model
, cmd = Cmd.none
, stateChange = False
, dragDrop = DD.DragDropData ddm Nothing
}
Names s ->
{ model = { model | textSearchModel = Fulltext s }
, cmd = Cmd.none
, stateChange = False
, dragDrop = DD.DragDropData ddm Nothing
}
SetNamesSearch ->
case model.textSearchModel of
Fulltext s ->
{ model = { model | textSearchModel = Names s }
, cmd = Cmd.none
, stateChange = False
, dragDrop = DD.DragDropData ddm Nothing
}
Names _ ->
{ model = model
, cmd = Cmd.none , cmd = Cmd.none
, stateChange = False , stateChange = False
, dragDrop = DD.DragDropData ddm Nothing , dragDrop = DD.DragDropData ddm Nothing
@ -677,13 +796,6 @@ updateDrop ddm flags settings msg model =
, dragDrop = DD.DragDropData ddm Nothing , dragDrop = DD.DragDropData ddm Nothing
} }
ToggleNameHelp ->
{ model = { model | showNameHelp = not model.showNameHelp }
, cmd = Cmd.none
, stateChange = False
, dragDrop = DD.DragDropData ddm Nothing
}
GetFolderResp (Ok fs) -> GetFolderResp (Ok fs) ->
let let
model_ = model_ =
@ -804,6 +916,54 @@ viewDrop ddd flags settings model =
] ]
] ]
] ]
, div [ class segmentClass ]
[ div
[ class "field"
]
[ label []
[ text
(case model.textSearchModel of
Fulltext _ ->
"Fulltext Search"
Names _ ->
"Search in names"
)
, a
[ classList
[ ( "right-float", True )
, ( "invisible hidden", not flags.config.fullTextSearchEnabled )
]
, href "#"
, onClick SwapTextSearch
, title "Switch between text search modes"
]
[ i [ class "small grey exchange alternate icon" ] []
]
]
, input
[ type_ "text"
, onInput SetTextSearch
, Util.Html.onKeyUpCode KeyUpMsg
, textSearchString model.textSearchModel |> Maybe.withDefault "" |> value
, case model.textSearchModel of
Fulltext _ ->
placeholder "Content search"
Names _ ->
placeholder "Search in various names"
]
[]
, span [ class "small-info" ]
[ case model.textSearchModel of
Fulltext _ ->
text "Fulltext search in document contents and notes."
Names _ ->
text "Looks in correspondents, concerned entities, item name and notes."
]
]
]
, div , div
[ classList [ classList
[ ( segmentClass, True ) [ ( segmentClass, True )
@ -883,68 +1043,6 @@ viewDrop ddd flags settings model =
model.customFieldModel model.customFieldModel
) )
] ]
, div [ class segmentClass ]
[ formHeader (Icons.searchIcon "") "Text Search"
, div
[ classList
[ ( "field", True )
, ( "invisible hidden", not flags.config.fullTextSearchEnabled )
]
]
[ label [] [ text "Fulltext Search" ]
, input
[ type_ "text"
, onInput SetFulltext
, Util.Html.onKeyUpCode KeyUpMsg
, model.fulltextModel |> Maybe.withDefault "" |> value
, placeholder "Fulltext search in results"
]
[]
, span [ class "small-info" ]
[ text "Fulltext search in document contents and notes."
]
]
, div [ class "field" ]
[ label []
[ text "Names"
, a
[ class "right-float"
, href "#"
, onClick ToggleNameHelp
]
[ i [ class "small grey help link icon" ] []
]
]
, input
[ type_ "text"
, onInput SetAllName
, Util.Html.onKeyUpCode KeyUpMsg
, model.allNameModel |> Maybe.withDefault "" |> value
, placeholder "Search in various names"
]
[]
, span
[ classList
[ ( "small-info", True )
]
]
[ text "Looks in correspondents, concerned entities, item name and notes."
]
, p
[ classList
[ ( "small-info", True )
, ( "invisible hidden", not model.showNameHelp )
]
]
[ text "Use wildcards "
, code [] [ text "*" ]
, text " at beginning or end. They are added automatically on both sides "
, text "if not present in the search term and the term is not quoted. Press "
, em [] [ text "Enter" ]
, text " to start searching."
]
]
]
, div , div
[ classList [ classList
[ ( segmentClass, True ) [ ( segmentClass, True )

View File

@ -6,7 +6,6 @@ module Page.Home.Data exposing
, SelectActionMode(..) , SelectActionMode(..)
, SelectViewModel , SelectViewModel
, ViewMode(..) , ViewMode(..)
, defaultSearchType
, doSearchCmd , doSearchCmd
, init , init
, initSelectViewModel , initSelectViewModel
@ -20,7 +19,6 @@ module Page.Home.Data exposing
import Api import Api
import Api.Model.BasicResult exposing (BasicResult) import Api.Model.BasicResult exposing (BasicResult)
import Api.Model.ItemLightList exposing (ItemLightList) import Api.Model.ItemLightList exposing (ItemLightList)
import Api.Model.ItemSearch
import Browser.Dom as Dom import Browser.Dom as Dom
import Comp.FixedDropdown import Comp.FixedDropdown
import Comp.ItemCardList import Comp.ItemCardList
@ -52,7 +50,6 @@ type alias Model =
, searchTypeDropdown : Comp.FixedDropdown.Model SearchType , searchTypeDropdown : Comp.FixedDropdown.Model SearchType
, searchTypeDropdownValue : SearchType , searchTypeDropdownValue : SearchType
, lastSearchType : SearchType , lastSearchType : SearchType
, contentOnlySearch : Maybe String
, dragDropData : DD.DragDropData , dragDropData : DD.DragDropData
, scrollToCard : Maybe String , scrollToCard : Maybe String
} }
@ -88,6 +85,9 @@ type ViewMode
init : Flags -> ViewMode -> Model init : Flags -> ViewMode -> Model
init flags viewMode = init flags viewMode =
let let
searchMenuModel =
Comp.SearchMenu.init flags
searchTypeOptions = searchTypeOptions =
if flags.config.fullTextSearchEnabled then if flags.config.fullTextSearchEnabled then
[ BasicSearch, ContentOnlySearch ] [ BasicSearch, ContentOnlySearch ]
@ -95,7 +95,7 @@ init flags viewMode =
else else
[ BasicSearch ] [ BasicSearch ]
in in
{ searchMenuModel = Comp.SearchMenu.init { searchMenuModel = searchMenuModel
, itemListModel = Comp.ItemCardList.init , itemListModel = Comp.ItemCardList.init
, searchInProgress = False , searchInProgress = False
, searchOffset = 0 , searchOffset = 0
@ -105,9 +105,13 @@ init flags viewMode =
, searchTypeDropdown = , searchTypeDropdown =
Comp.FixedDropdown.initMap searchTypeString Comp.FixedDropdown.initMap searchTypeString
searchTypeOptions searchTypeOptions
, searchTypeDropdownValue = defaultSearchType flags , searchTypeDropdownValue =
if Comp.SearchMenu.isFulltextSearch searchMenuModel then
ContentOnlySearch
else
BasicSearch
, lastSearchType = BasicSearch , lastSearchType = BasicSearch
, contentOnlySearch = Nothing
, dragDropData = , dragDropData =
DD.DragDropData DD.init Nothing DD.DragDropData DD.init Nothing
, scrollToCard = Nothing , scrollToCard = Nothing
@ -115,15 +119,6 @@ init flags viewMode =
} }
defaultSearchType : Flags -> SearchType
defaultSearchType flags =
if flags.config.fullTextSearchEnabled then
ContentOnlySearch
else
BasicSearch
menuCollapsed : Model -> Bool menuCollapsed : Model -> Bool
menuCollapsed model = menuCollapsed model =
case model.viewMode of case model.viewMode of
@ -165,7 +160,6 @@ type Msg
| SetBasicSearch String | SetBasicSearch String
| SearchTypeMsg (Comp.FixedDropdown.Msg SearchType) | SearchTypeMsg (Comp.FixedDropdown.Msg SearchType)
| KeyUpSearchbarMsg (Maybe KeyCode) | KeyUpSearchbarMsg (Maybe KeyCode)
| SetContentOnly String
| ScrollResult (Result Dom.Error ()) | ScrollResult (Result Dom.Error ())
| ClearItemDetailId | ClearItemDetailId
| SelectAllItems | SelectAllItems
@ -227,13 +221,8 @@ itemNav id model =
doSearchCmd : SearchParam -> Model -> Cmd Msg doSearchCmd : SearchParam -> Model -> Cmd Msg
doSearchCmd param model = doSearchCmd param model =
case param.searchType of
BasicSearch ->
doSearchDefaultCmd param model doSearchDefaultCmd param model
ContentOnlySearch ->
doSearchIndexCmd param model
doSearchDefaultCmd : SearchParam -> Model -> Cmd Msg doSearchDefaultCmd : SearchParam -> Model -> Cmd Msg
doSearchDefaultCmd param model = doSearchDefaultCmd param model =
@ -254,36 +243,6 @@ doSearchDefaultCmd param model =
Api.itemSearch param.flags mask ItemSearchAddResp Api.itemSearch param.flags mask ItemSearchAddResp
doSearchIndexCmd : SearchParam -> Model -> Cmd Msg
doSearchIndexCmd param model =
case model.contentOnlySearch of
Just q ->
let
mask =
{ query = q
, limit = param.pageSize
, offset = param.offset
}
in
if param.offset == 0 then
Api.itemIndexSearch param.flags mask (ItemSearchResp param.scroll)
else
Api.itemIndexSearch param.flags mask ItemSearchAddResp
Nothing ->
-- If there is no fulltext query, render simply the most
-- current ones
let
emptyMask =
Api.Model.ItemSearch.empty
mask =
{ emptyMask | limit = param.pageSize }
in
Api.itemSearch param.flags mask (ItemSearchResp param.scroll)
resultsBelowLimit : UiSettings -> Model -> Bool resultsBelowLimit : UiSettings -> Model -> Bool
resultsBelowLimit settings model = resultsBelowLimit settings model =
let let

View File

@ -52,10 +52,7 @@ update mId key flags settings msg model =
ResetSearch -> ResetSearch ->
let let
nm = nm =
{ model { model | searchOffset = 0 }
| searchOffset = 0
, contentOnlySearch = Nothing
}
in in
update mId key flags settings (SearchMenuMsg Comp.SearchMenu.ResetForm) nm update mId key flags settings (SearchMenuMsg Comp.SearchMenu.ResetForm) nm
@ -76,6 +73,12 @@ update mId key flags settings msg model =
{ model { model
| searchMenuModel = nextState.model | searchMenuModel = nextState.model
, dragDropData = nextState.dragDrop , dragDropData = nextState.dragDrop
, searchTypeDropdownValue =
if Comp.SearchMenu.isFulltextSearch nextState.model then
ContentOnlySearch
else
BasicSearch
} }
( m2, c2, s2 ) = ( m2, c2, s2 ) =
@ -261,21 +264,10 @@ update mId key flags settings msg model =
SetBasicSearch str -> SetBasicSearch str ->
let let
smMsg = smMsg =
case model.searchTypeDropdownValue of SearchMenuMsg (Comp.SearchMenu.SetTextSearch str)
BasicSearch ->
SearchMenuMsg (Comp.SearchMenu.SetAllName str)
ContentOnlySearch ->
SetContentOnly str
in in
update mId key flags settings smMsg model update mId key flags settings smMsg model
SetContentOnly str ->
withSub
( { model | contentOnlySearch = Util.Maybe.fromString str }
, Cmd.none
)
SearchTypeMsg lm -> SearchTypeMsg lm ->
let let
( sm, mv ) = ( sm, mv ) =
@ -293,23 +285,17 @@ update mId key flags settings msg model =
next = next =
case mvChange of case mvChange of
Just BasicSearch -> Just BasicSearch ->
Just Just Comp.SearchMenu.SetNamesSearch
( { m0 | contentOnlySearch = Nothing }
, Maybe.withDefault "" model.contentOnlySearch
)
Just ContentOnlySearch -> Just ContentOnlySearch ->
Just Just Comp.SearchMenu.SetFulltextSearch
( { m0 | contentOnlySearch = model.searchMenuModel.allNameModel }
, ""
)
_ -> _ ->
Nothing Nothing
in in
case next of case next of
Just ( m_, nstr ) -> Just lm_ ->
update mId key flags settings (SearchMenuMsg (Comp.SearchMenu.SetAllName nstr)) m_ update mId key flags settings (SearchMenuMsg lm_) m0
Nothing -> Nothing ->
withSub ( m0, Cmd.none ) withSub ( m0, Cmd.none )

View File

@ -285,12 +285,8 @@ viewSearchBar flags model =
(searchTypeString model.searchTypeDropdownValue) (searchTypeString model.searchTypeDropdownValue)
searchInput = searchInput =
case model.searchTypeDropdownValue of Comp.SearchMenu.textSearchString
BasicSearch -> model.searchMenuModel.textSearchModel
model.searchMenuModel.allNameModel
ContentOnlySearch ->
model.contentOnlySearch
searchTypeClass = searchTypeClass =
if flags.config.fullTextSearchEnabled then if flags.config.fullTextSearchEnabled then
@ -328,7 +324,7 @@ viewSearchBar flags model =
, href "#" , href "#"
, onClick (DoSearch model.searchTypeDropdownValue) , onClick (DoSearch model.searchTypeDropdownValue)
] ]
(if hasMoreSearch model && model.searchTypeDropdownValue == BasicSearch then (if hasMoreSearch model then
[ i [ class "icons search-corner-icons" ] [ i [ class "icons search-corner-icons" ]
[ i [ class "tiny blue circle icon" ] [] [ i [ class "tiny blue circle icon" ] []
] ]
@ -339,7 +335,14 @@ viewSearchBar flags model =
) )
, input , input
[ type_ "text" [ type_ "text"
, placeholder "Quick Search " , placeholder
(case model.searchTypeDropdownValue of
ContentOnlySearch ->
"Content search"
BasicSearch ->
"Search in names"
)
, onInput SetBasicSearch , onInput SetBasicSearch
, Util.Html.onKeyUpCode KeyUpSearchbarMsg , Util.Html.onKeyUpCode KeyUpSearchbarMsg
, Maybe.map value searchInput , Maybe.map value searchInput
@ -381,12 +384,7 @@ hasMoreSearch model =
Comp.SearchMenu.getItemSearch model.searchMenuModel Comp.SearchMenu.getItemSearch model.searchMenuModel
is_ = is_ =
case model.lastSearchType of { is | allNames = Nothing, fullText = Nothing }
BasicSearch ->
{ is | allNames = Nothing }
ContentOnlySearch ->
Api.Model.ItemSearch.empty
in in
is_ /= Api.Model.ItemSearch.empty is_ /= Api.Model.ItemSearch.empty