mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-06-22 02:18:26 +00:00
Check query in client
This commit is contained in:
177
modules/webapp/src/main/elm/Comp/PowerSearchInput.elm
Normal file
177
modules/webapp/src/main/elm/Comp/PowerSearchInput.elm
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
module Comp.PowerSearchInput exposing
|
||||||
|
( Action(..)
|
||||||
|
, Model
|
||||||
|
, Msg
|
||||||
|
, init
|
||||||
|
, update
|
||||||
|
, viewInput
|
||||||
|
, viewResult
|
||||||
|
)
|
||||||
|
|
||||||
|
import Data.DropdownStyle
|
||||||
|
import Data.QueryParseResult exposing (QueryParseResult)
|
||||||
|
import Html exposing (..)
|
||||||
|
import Html.Attributes exposing (..)
|
||||||
|
import Html.Events exposing (onInput)
|
||||||
|
import Ports
|
||||||
|
import Styles as S
|
||||||
|
import Throttle exposing (Throttle)
|
||||||
|
import Time
|
||||||
|
import Util.Html exposing (KeyCode(..))
|
||||||
|
import Util.Maybe
|
||||||
|
|
||||||
|
|
||||||
|
type alias Model =
|
||||||
|
{ input : Maybe String
|
||||||
|
, result : QueryParseResult
|
||||||
|
, parseThrottle : Throttle Msg
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
init : Model
|
||||||
|
init =
|
||||||
|
{ input = Nothing
|
||||||
|
, result = Data.QueryParseResult.success
|
||||||
|
, parseThrottle = Throttle.create 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type Msg
|
||||||
|
= SetSearch String
|
||||||
|
| KeyUpMsg (Maybe KeyCode)
|
||||||
|
| ParseResultMsg QueryParseResult
|
||||||
|
| UpdateThrottle
|
||||||
|
|
||||||
|
|
||||||
|
type Action
|
||||||
|
= NoAction
|
||||||
|
| SubmitSearch
|
||||||
|
|
||||||
|
|
||||||
|
type alias Result =
|
||||||
|
{ model : Model
|
||||||
|
, cmd : Cmd Msg
|
||||||
|
, action : Action
|
||||||
|
, subs : Sub Msg
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- Update
|
||||||
|
|
||||||
|
|
||||||
|
update : Msg -> Model -> Result
|
||||||
|
update msg model =
|
||||||
|
case msg of
|
||||||
|
SetSearch str ->
|
||||||
|
let
|
||||||
|
parseCmd =
|
||||||
|
Ports.checkSearchQueryString str
|
||||||
|
|
||||||
|
parseSub =
|
||||||
|
Ports.receiveCheckQueryResult ParseResultMsg
|
||||||
|
|
||||||
|
( newThrottle, cmd ) =
|
||||||
|
Throttle.try parseCmd model.parseThrottle
|
||||||
|
|
||||||
|
model_ =
|
||||||
|
{ model
|
||||||
|
| input = Util.Maybe.fromString str
|
||||||
|
, parseThrottle = newThrottle
|
||||||
|
, result =
|
||||||
|
if str == "" then
|
||||||
|
Data.QueryParseResult.success
|
||||||
|
|
||||||
|
else
|
||||||
|
model.result
|
||||||
|
}
|
||||||
|
in
|
||||||
|
{ model = model_
|
||||||
|
, cmd = cmd
|
||||||
|
, action = NoAction
|
||||||
|
, subs = Sub.batch [ throttleUpdate model_, parseSub ]
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyUpMsg (Just Enter) ->
|
||||||
|
Result model Cmd.none SubmitSearch Sub.none
|
||||||
|
|
||||||
|
KeyUpMsg _ ->
|
||||||
|
let
|
||||||
|
parseSub =
|
||||||
|
Ports.receiveCheckQueryResult ParseResultMsg
|
||||||
|
in
|
||||||
|
Result model Cmd.none NoAction (Sub.batch [ throttleUpdate model, parseSub ])
|
||||||
|
|
||||||
|
ParseResultMsg lm ->
|
||||||
|
Result { model | result = lm } Cmd.none NoAction Sub.none
|
||||||
|
|
||||||
|
UpdateThrottle ->
|
||||||
|
let
|
||||||
|
parseSub =
|
||||||
|
Ports.receiveCheckQueryResult ParseResultMsg
|
||||||
|
|
||||||
|
( newThrottle, cmd ) =
|
||||||
|
Throttle.update model.parseThrottle
|
||||||
|
|
||||||
|
model_ =
|
||||||
|
{ model | parseThrottle = newThrottle }
|
||||||
|
in
|
||||||
|
{ model = model_
|
||||||
|
, cmd = cmd
|
||||||
|
, action = NoAction
|
||||||
|
, subs = Sub.batch [ throttleUpdate model_, parseSub ]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
throttleUpdate : Model -> Sub Msg
|
||||||
|
throttleUpdate model =
|
||||||
|
Throttle.ifNeeded
|
||||||
|
(Time.every 100 (\_ -> UpdateThrottle))
|
||||||
|
model.parseThrottle
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- View
|
||||||
|
|
||||||
|
|
||||||
|
viewInput : List (Attribute Msg) -> Model -> Html Msg
|
||||||
|
viewInput attrs model =
|
||||||
|
input
|
||||||
|
(attrs
|
||||||
|
++ [ type_ "text"
|
||||||
|
, placeholder "Search query …"
|
||||||
|
, onInput SetSearch
|
||||||
|
, Util.Html.onKeyUpCode KeyUpMsg
|
||||||
|
, Maybe.map value model.input
|
||||||
|
|> Maybe.withDefault (value "")
|
||||||
|
, class S.textInput
|
||||||
|
, class "text-sm "
|
||||||
|
]
|
||||||
|
)
|
||||||
|
[]
|
||||||
|
|
||||||
|
|
||||||
|
viewResult : List ( String, Bool ) -> Model -> Html Msg
|
||||||
|
viewResult classes model =
|
||||||
|
div
|
||||||
|
[ classList [ ( "hidden", model.result.success ) ]
|
||||||
|
, classList classes
|
||||||
|
, class resultStyle
|
||||||
|
]
|
||||||
|
[ p [ class "font-mono text-sm" ]
|
||||||
|
[ text model.result.input
|
||||||
|
]
|
||||||
|
, pre [ class "font-mono text-sm" ]
|
||||||
|
[ List.repeat model.result.index " "
|
||||||
|
|> String.join ""
|
||||||
|
|> text
|
||||||
|
, text "^"
|
||||||
|
]
|
||||||
|
, ul []
|
||||||
|
(List.map (\line -> li [] [ text line ]) model.result.messages)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
resultStyle : String
|
||||||
|
resultStyle =
|
||||||
|
S.warnMessageColors ++ " absolute left-0 max-h-44 w-full overflow-y-auto z-50 shadow-lg transition duration-200 top-9 border-0 border-b border-l border-r rounded-b px-2 py-2"
|
@ -106,8 +106,8 @@ render q =
|
|||||||
"="
|
"="
|
||||||
|
|
||||||
quoteStr =
|
quoteStr =
|
||||||
--TODO escape quotes
|
String.replace "\"" "\\\""
|
||||||
surround "\""
|
>> surround "\""
|
||||||
in
|
in
|
||||||
case q of
|
case q of
|
||||||
And inner ->
|
And inner ->
|
||||||
|
14
modules/webapp/src/main/elm/Data/QueryParseResult.elm
Normal file
14
modules/webapp/src/main/elm/Data/QueryParseResult.elm
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
module Data.QueryParseResult exposing (QueryParseResult, success)
|
||||||
|
|
||||||
|
|
||||||
|
type alias QueryParseResult =
|
||||||
|
{ success : Bool
|
||||||
|
, input : String
|
||||||
|
, index : Int
|
||||||
|
, messages : List String
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
success : QueryParseResult
|
||||||
|
success =
|
||||||
|
QueryParseResult True "" 0 []
|
@ -27,6 +27,7 @@ import Comp.ItemCardList
|
|||||||
import Comp.ItemDetail.FormChange exposing (FormChange)
|
import Comp.ItemDetail.FormChange exposing (FormChange)
|
||||||
import Comp.ItemDetail.MultiEditMenu exposing (SaveNameState(..))
|
import Comp.ItemDetail.MultiEditMenu exposing (SaveNameState(..))
|
||||||
import Comp.LinkTarget exposing (LinkTarget)
|
import Comp.LinkTarget exposing (LinkTarget)
|
||||||
|
import Comp.PowerSearchInput
|
||||||
import Comp.SearchMenu
|
import Comp.SearchMenu
|
||||||
import Comp.YesNoDimmer
|
import Comp.YesNoDimmer
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
@ -56,7 +57,7 @@ type alias Model =
|
|||||||
, dragDropData : DD.DragDropData
|
, dragDropData : DD.DragDropData
|
||||||
, scrollToCard : Maybe String
|
, scrollToCard : Maybe String
|
||||||
, searchStats : SearchStats
|
, searchStats : SearchStats
|
||||||
, powerSearchInput : Maybe String
|
, powerSearchInput : Comp.PowerSearchInput.Model
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -122,7 +123,7 @@ init flags viewMode =
|
|||||||
, scrollToCard = Nothing
|
, scrollToCard = Nothing
|
||||||
, viewMode = viewMode
|
, viewMode = viewMode
|
||||||
, searchStats = Api.Model.SearchStats.empty
|
, searchStats = Api.Model.SearchStats.empty
|
||||||
, powerSearchInput = Nothing
|
, powerSearchInput = Comp.PowerSearchInput.init
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -196,7 +197,7 @@ type Msg
|
|||||||
| SetLinkTarget LinkTarget
|
| SetLinkTarget LinkTarget
|
||||||
| SearchStatsResp (Result Http.Error SearchStats)
|
| SearchStatsResp (Result Http.Error SearchStats)
|
||||||
| TogglePreviewFullWidth
|
| TogglePreviewFullWidth
|
||||||
| SetPowerSearch String
|
| PowerSearchMsg Comp.PowerSearchInput.Msg
|
||||||
| KeyUpPowerSearchbarMsg (Maybe KeyCode)
|
| KeyUpPowerSearchbarMsg (Maybe KeyCode)
|
||||||
|
|
||||||
|
|
||||||
@ -247,7 +248,7 @@ doSearchDefaultCmd param model =
|
|||||||
Q.request <|
|
Q.request <|
|
||||||
Q.and
|
Q.and
|
||||||
[ Comp.SearchMenu.getItemQuery model.searchMenuModel
|
[ Comp.SearchMenu.getItemQuery model.searchMenuModel
|
||||||
, Maybe.map Q.Fragment model.powerSearchInput
|
, Maybe.map Q.Fragment model.powerSearchInput.input
|
||||||
]
|
]
|
||||||
|
|
||||||
mask =
|
mask =
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
module Page.Home.Update exposing (update)
|
module Page.Home.Update exposing (update)
|
||||||
|
|
||||||
import Api
|
import Api
|
||||||
import Api.Model.IdList exposing (IdList)
|
|
||||||
import Api.Model.ItemLightList exposing (ItemLightList)
|
import Api.Model.ItemLightList exposing (ItemLightList)
|
||||||
import Api.Model.ItemQuery
|
|
||||||
import Browser.Navigation as Nav
|
import Browser.Navigation as Nav
|
||||||
import Comp.FixedDropdown
|
import Comp.FixedDropdown
|
||||||
import Comp.ItemCardList
|
import Comp.ItemCardList
|
||||||
import Comp.ItemDetail.FormChange exposing (FormChange(..))
|
import Comp.ItemDetail.FormChange exposing (FormChange(..))
|
||||||
import Comp.ItemDetail.MultiEditMenu exposing (SaveNameState(..))
|
import Comp.ItemDetail.MultiEditMenu exposing (SaveNameState(..))
|
||||||
import Comp.LinkTarget exposing (LinkTarget)
|
import Comp.LinkTarget exposing (LinkTarget)
|
||||||
|
import Comp.PowerSearchInput
|
||||||
import Comp.SearchMenu
|
import Comp.SearchMenu
|
||||||
import Comp.YesNoDimmer
|
import Comp.YesNoDimmer
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
@ -54,7 +53,7 @@ update mId key flags settings msg model =
|
|||||||
ResetSearch ->
|
ResetSearch ->
|
||||||
let
|
let
|
||||||
nm =
|
nm =
|
||||||
{ model | searchOffset = 0, powerSearchInput = Nothing }
|
{ model | searchOffset = 0, powerSearchInput = Comp.PowerSearchInput.init }
|
||||||
in
|
in
|
||||||
update mId key flags settings (SearchMenuMsg Comp.SearchMenu.ResetForm) nm
|
update mId key flags settings (SearchMenuMsg Comp.SearchMenu.ResetForm) nm
|
||||||
|
|
||||||
@ -580,8 +579,23 @@ update mId key flags settings msg model =
|
|||||||
in
|
in
|
||||||
noSub ( model, cmd )
|
noSub ( model, cmd )
|
||||||
|
|
||||||
SetPowerSearch str ->
|
PowerSearchMsg lm ->
|
||||||
noSub ( { model | powerSearchInput = Util.Maybe.fromString str }, Cmd.none )
|
let
|
||||||
|
result =
|
||||||
|
Comp.PowerSearchInput.update lm model.powerSearchInput
|
||||||
|
|
||||||
|
cmd_ =
|
||||||
|
Cmd.map PowerSearchMsg result.cmd
|
||||||
|
|
||||||
|
model_ =
|
||||||
|
{ model | powerSearchInput = result.model }
|
||||||
|
in
|
||||||
|
case result.action of
|
||||||
|
Comp.PowerSearchInput.NoAction ->
|
||||||
|
( model_, cmd_, Sub.map PowerSearchMsg result.subs )
|
||||||
|
|
||||||
|
Comp.PowerSearchInput.SubmitSearch ->
|
||||||
|
update mId key flags settings (DoSearch model_.searchTypeDropdownValue) model_
|
||||||
|
|
||||||
KeyUpPowerSearchbarMsg (Just Enter) ->
|
KeyUpPowerSearchbarMsg (Just Enter) ->
|
||||||
update mId key flags settings (DoSearch model.searchTypeDropdownValue) model
|
update mId key flags settings (DoSearch model.searchTypeDropdownValue) model
|
||||||
|
@ -3,6 +3,7 @@ module Page.Home.View2 exposing (viewContent, viewSidebar)
|
|||||||
import Comp.Basic as B
|
import Comp.Basic as B
|
||||||
import Comp.ItemCardList
|
import Comp.ItemCardList
|
||||||
import Comp.MenuBar as MB
|
import Comp.MenuBar as MB
|
||||||
|
import Comp.PowerSearchInput
|
||||||
import Comp.SearchMenu
|
import Comp.SearchMenu
|
||||||
import Comp.SearchStatsView
|
import Comp.SearchStatsView
|
||||||
import Comp.YesNoDimmer
|
import Comp.YesNoDimmer
|
||||||
@ -135,17 +136,12 @@ defaultMenuBar _ settings model =
|
|||||||
powerSearchBar =
|
powerSearchBar =
|
||||||
div
|
div
|
||||||
[ class "relative flex flex-grow flex-row" ]
|
[ class "relative flex flex-grow flex-row" ]
|
||||||
[ input
|
[ Html.map PowerSearchMsg
|
||||||
[ type_ "text"
|
(Comp.PowerSearchInput.viewInput []
|
||||||
, placeholder "Search query …"
|
model.powerSearchInput
|
||||||
, onInput SetPowerSearch
|
)
|
||||||
, Util.Html.onKeyUpCode KeyUpPowerSearchbarMsg
|
, Html.map PowerSearchMsg
|
||||||
, Maybe.map value model.powerSearchInput
|
(Comp.PowerSearchInput.viewResult [] model.powerSearchInput)
|
||||||
|> Maybe.withDefault (value "")
|
|
||||||
, class S.textInput
|
|
||||||
, class "text-sm "
|
|
||||||
]
|
|
||||||
[]
|
|
||||||
]
|
]
|
||||||
in
|
in
|
||||||
MB.view
|
MB.view
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
port module Ports exposing
|
port module Ports exposing
|
||||||
( getUiSettings
|
( checkSearchQueryString
|
||||||
|
, getUiSettings
|
||||||
, initClipboard
|
, initClipboard
|
||||||
, loadUiSettings
|
, loadUiSettings
|
||||||
, onUiSettingsSaved
|
, onUiSettingsSaved
|
||||||
|
, receiveCheckQueryResult
|
||||||
, removeAccount
|
, removeAccount
|
||||||
, setAccount
|
, setAccount
|
||||||
, setUiTheme
|
, setUiTheme
|
||||||
@ -10,7 +12,9 @@ port module Ports exposing
|
|||||||
)
|
)
|
||||||
|
|
||||||
import Api.Model.AuthResult exposing (AuthResult)
|
import Api.Model.AuthResult exposing (AuthResult)
|
||||||
|
import Api.Model.BasicResult exposing (BasicResult)
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
|
import Data.QueryParseResult exposing (QueryParseResult)
|
||||||
import Data.UiSettings exposing (StoredUiSettings, UiSettings)
|
import Data.UiSettings exposing (StoredUiSettings, UiSettings)
|
||||||
import Data.UiTheme exposing (UiTheme)
|
import Data.UiTheme exposing (UiTheme)
|
||||||
|
|
||||||
@ -38,6 +42,12 @@ port uiSettingsSaved : (() -> msg) -> Sub msg
|
|||||||
port internalSetUiTheme : String -> Cmd msg
|
port internalSetUiTheme : String -> Cmd msg
|
||||||
|
|
||||||
|
|
||||||
|
port checkSearchQueryString : String -> Cmd msg
|
||||||
|
|
||||||
|
|
||||||
|
port receiveCheckQueryResult : (QueryParseResult -> msg) -> Sub msg
|
||||||
|
|
||||||
|
|
||||||
setUiTheme : UiTheme -> Cmd msg
|
setUiTheme : UiTheme -> Cmd msg
|
||||||
setUiTheme theme =
|
setUiTheme theme =
|
||||||
internalSetUiTheme (Data.UiTheme.toString theme)
|
internalSetUiTheme (Data.UiTheme.toString theme)
|
||||||
|
@ -43,7 +43,12 @@ errorMessage =
|
|||||||
|
|
||||||
warnMessage : String
|
warnMessage : String
|
||||||
warnMessage =
|
warnMessage =
|
||||||
" border border-yellow-800 bg-yellow-50 text-yellow-800 dark:border-amber-200 dark:bg-amber-800 dark:text-amber-200 dark:bg-opacity-25 px-2 py-2 rounded "
|
warnMessageColors ++ " border dark:bg-opacity-25 px-2 py-2 rounded "
|
||||||
|
|
||||||
|
|
||||||
|
warnMessageColors : String
|
||||||
|
warnMessageColors =
|
||||||
|
" border-yellow-800 bg-yellow-50 text-yellow-800 dark:border-amber-200 dark:bg-amber-800 dark:text-amber-200 "
|
||||||
|
|
||||||
|
|
||||||
infoMessage : String
|
infoMessage : String
|
||||||
|
@ -97,3 +97,29 @@ elmApp.ports.initClipboard.subscribe(function(args) {
|
|||||||
docspell_clipboards[page] = new ClipboardJS(sel);
|
docspell_clipboards[page] = new ClipboardJS(sel);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
elmApp.ports.checkSearchQueryString.subscribe(function(args) {
|
||||||
|
var qStr = args;
|
||||||
|
if (qStr && DsItemQueryParser && DsItemQueryParser['parseToFailure']) {
|
||||||
|
var result = DsItemQueryParser.parseToFailure(qStr);
|
||||||
|
var answer;
|
||||||
|
if (result) {
|
||||||
|
answer =
|
||||||
|
{ success: false,
|
||||||
|
input: result.input,
|
||||||
|
index: result.failedAt,
|
||||||
|
messages: result.messages
|
||||||
|
};
|
||||||
|
|
||||||
|
} else {
|
||||||
|
answer =
|
||||||
|
{ success: true,
|
||||||
|
input: qStr,
|
||||||
|
index: 0,
|
||||||
|
messages: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
console.log("Sending: " + answer.success);
|
||||||
|
elmApp.ports.receiveCheckQueryResult.send(answer);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Reference in New Issue
Block a user